# # $Id: encode.icn,v 1.2 2004/02/12 17:07:55 rparlett Exp $ # # This file is in the public domain. # # Author: Robert Parlett (parlett@dial.pipex.com) # package lang import util # Together with decode.icn, these functions provide a way to encode # and decode arbitrary objects to and from strings. Class instances # are handled specially, in that the instance must define how it is # to be encoded/decoded by implementing the methods in {ClassCoding}. # The {SelectiveClassCoding} class provides a flexible implementation of these # methods, allowing selected member variables to be saved using # independent tags. This allows the class to change considerably and # still be backward-compatible with old encodings. # # @example # @ import lang # @ ... # @ obj := ... any object ... # @ s := lang::encode(obj) # @ # s is now a string representation of obj # @ obj2 := lang::decode(s) # @ # now obj and obj2 should have the same value # # # Return a string, being the encoded representation of the given object. # procedure encode(o) local e e := Encode() e.encode(o) return e.get_string() end # # This class is used to encode arbitrary objects into strings. # # Classes to be encoded must subclass ClassCoding, which allows specification # of how to encode a class instance. # class Encode : Object( tag_count, seen, string_buff ) method get_string() return string_buff.get_string("|") end method encode_string(s) local buff static printable initial printable := cset(&ascii[33:128]) -- '|\\' buff := StringBuff() s ? { repeat { buff.add(tab(many(printable))) if pos(0) then return buff.get_string() buff.add("\\" || right(ord(move(1)), 3, "0")) } } end method encode_record(o) line_out(lang::get_name(o)) line_out(*o) seen[o] := (tag_count +:= 1) every encode(!o) end method encode(o) local im, ty if i := \seen[o] then { line_out(i) return } ty := lang::get_type(o) line_out(ty) case ty of { "class" : encode_class(o) "record" : encode_record(o) "procedure" : line_out(lang::get_name(o)) "null" : &null "string" : line_out(encode_string(o)) "cset" : line_out(encode_string(string(o))) "integer" | "real" : line_out(string(o)) "set" | "list" : { line_out(*o) seen[o] := (tag_count +:= 1) every encode(!o) } "table" : { seen[o] := (tag_count +:= 1) encode(o[[]]) pairs := sort(o) line_out(*pairs) every encode(!!pairs) } default : &null } end method line_out(s) string_buff.add(s) end method encode_class(o) local cname, t, m, e cname := lang::get_class_name(o) | stop("invalid class name") line_out(cname) seen[o] := (tag_count +:= 1) o.pre_encode() o.encode_obj(self) end initially() tag_count := 0 seen := table() string_buff := StringBuff() end