Monday, November 08, 2010 at 12:06 AM.

system.verbs.builtins.table.tableToXml

on tableToXml (adrtable, flIncludeAddress=false) {
	<<Changes:
		<<1/31/03; 3:39:54 PM by JES
			<<Encode "alpha" entities in attribute values in the correct order.
		<<1/29/03; 2:36:29 PM by JES
			<<If present, strip out the "OPML generated by" comment at the top of the text returned by op.outlineToXml, when adding script or outline objects to the table XML.
		<<1/24/03; 7:45:55 PM by JES
			<<Small string concatenation optimization.
		<<1/14/03; 2:55:59 PM by JES
			<<Fixed a bug in generating XML for a filespec -- the filespec must be converted to a string before entity-encoding can take place.
		<<6/29/01; 12:19:25 PM by JES
			<<Added code to handle addresses, filespecs and directions, as specified by the spec.
		<<6/27/01 at 4:39:58 PM by JES
			<<Created.
			<<Convert a Frontier table to its XML representation, as specified here:
			<<http://smurfturf.manilasites.com/stories/storyReader$214
			<<adrtable is the address of the table you want to serialize.
			<<If flIncludeAddress is true, then the top-level <table> tag will have an address attribute, specifying the address of the table in the object database.
			<<Returns the XML text representing the table.
	
	local (xmltext, indlev = 0);
	on add (s) {
		xmltext = xmltext + (string.filledString ('\t', indlev) + s + '\r');;
		return (true)};
	
	on entityEncode (s) {
		s = string.replaceall (s, "&", "&");
		s = string.replaceAll (s, "\'", "'");
		s = string.replaceall (s, "<", "<");
		s = string.replaceall (s, ">", ">");
		s = string.replaceall (s, "\"", """);
		s = xml.entityEncode (s, false);
		local (i = 1, num);
		while i <= sizeof (s) {
			num = number (s [i]);
			if num < 32 {
				s = string.delete (s, i, 1);
				s = string.insert ("&#" + string.padWithZeros (num, 3) + ";", s, i);
				i = i + 5};
			i++};
		return (s)};
	on getAtts (adr, name=nil) {
		local (t); new (tableType, @t);
		if name == nil and typeOf (name) == unknownType {
			t.name = entityEncode (nameOf (adr^))}
		else {
			if typeOf (name) != booleanType {
				t.name = entityEncode (name)}};
		return (t)};
	on addElement (elementname, cdata=nil, adratts=nil, flclose=true) {
		local (s = "<" + elementname);
		if adratts != nil {
			local (adratt);
			for adratt in adratts {
				s = s + " " + nameOf (adratt^) + "=\"" + adratt^ + "\""}};
		if flclose {
			if cdata == nil {
				s = s + "/>"}
			else {
				s = s + ">" + entityEncode (cdata) + "</" + elementname + ">"}}
		else {
			s = s + ">";
			if cdata != nil {
				s = s + entityEncode (cdata)}};
		add (s);
		return (true)};
	
	on doWpText (adr, name=nil) {
		local (t = getAtts (adr, name));
		addElement ("wptext", adratts:@t, flclose:false); indlev++;
		addElement ("head", flclose:false); indlev++;
		addElement ("dateCreated", date.netStandardString (timeCreated (adr)));
		addElement ("dateModified", date.netStandardString (timeModified (adr)));
		bundle { //add <windowXxx> elements
			local (windowtop, windowleft, windowheight, windowwidth);
			local (oldtarget = target.set (adr)); //work around a kernel bug
			window.getSize (adr, @windowwidth, @windowheight);
			window.getPosition (adr, @windowleft, @windowtop);
			addElement ("windowTop", string (windowtop));
			addElement ("windowLeft", string (windowleft));
			addElement ("windowBottom", string (windowtop + windowheight));
			addElement ("windowRight", string (windowleft + windowwidth));
			target.set (oldtarget)};
		add ("</head>"); indlev--;
		add ("<body>" + entityEncode (string (adr^)) + "</body>");
		add ("</wptext>"); indlev--;
		return (true)};
	on doOutline (adr, name=nil, flscript=false, commandKey="") {
		local (t = getAtts (adr, name));
		if commandKey != "" {
			t.commandKey = number (commandKey)};
		local (elementname = "outline");
		if flscript {
			elementname = "script"};
		addElement (elementname, adratts:@t, flclose:false); indlev++;
		local (opmltext = string.replaceAll (op.outlineToXml (adr), "\n", ""));
		opmltext = string.delete (opmltext, 1, sizeOf (string.nthField (opmltext, '\r', 1)) + 1);
		bundle { //remove "OPML generated by" comment, if present
			if opmltext beginsWith "<!-- OPML" {
				opmltext = string.delete (opmltext, 1, sizeOf (string.nthField (opmltext, '\r', 1)) + 1)}};
		opmltext = string.filledString ('\t', indlev) + opmltext;
		opmltext = string.replaceAll (opmltext, '\r', "\r" + string.filledString ('\t', indlev));
		opmltext = string.mid (opmltext, 1, sizeOf (opmltext) - 1);
		xmltext = xmltext + opmltext;
		indlev--;
		add ("</" + elementname + ">")};
	on doMenubar (adr, name=nil) {
		local (t = getAtts (adr, name));
		addElement ("menubar", adratts:@t, flclose:false); indlev++;
		
		local (localmenu = adr^);
		local (oldtarget = target.set (@localmenu));
		op.firstSummit ();
		op.fullExpand ();
		on doOneLevel () {
			loop {
				local (l = op.getLineText ());
				if l == "-" {
					addElement ("separator")}
				else { //a real menu command or sub-menu
					if op.countSubs (1) { //sub-menu
						local (t); new (tableType, @t);
						t.name = entityEncode (l);
						addElement ("menubar", adratts:@t, flclose:false); indlev++;
						op.go (right, 1);
						doOneLevel (); //dive
						add ("</menubar>"); indlev--;
						op.go (left, 1)}
					else { //menu command
						local (menuscript);
						menu.getScript (@menuscript);
						local (commandKey = menu.getCommandKey ());
						if commandKey != "" {
							commandKey = commandKey[sizeOf (commandKey)]};
						doOutline (@menuscript, l, true, commandKey)}};
				if not op.go (down, 1) {
					return}}};
		doOneLevel ();
		target.set (oldtarget);
		
		add ("</menubar>"); indlev--};
	on doListOrRecord (adr, name=nil) {
		local (t = getAtts (adr, name));
		local (elementname = "list");
		local (flrecord = false);
		if typeOf (adr^) == recordType {
			elementname = "record";
			flrecord = true};
		addElement (elementname, adratts:@t, flclose:false); indlev++;
		
		local (ct = sizeOf (adr^));
		for i = 1 to ct {
			local (item = adr^[i]);
			local (name = false);
			if flrecord {
				name = nameOf (adr^[i])};
			main (@item, name)};
		
		add ("</" + elementname + ">"); indlev--};
	on doScalar (adr, name=nil) {
		
		local (atts = getAtts (adr, name));
		local (elementname = string.typeToString (typeOf (adr^)));
		elementname = string.mid (elementname, 1, sizeOf (elementname) - 4);
		
		case typeOf (adr^) {
			filespecType;
			string4Type;
			stringType {
				atts.value = entityEncode (string (adr^))};
			doubleType;
			longType {
				atts.value = adr^};
			addressType {
				atts.value = entityEncode (string.popFileFromAddress (adr^))};
			dateType {
				atts.value = date.netStandardString (adr^)};
			charType {
				atts.value = number (adr^)};
			booleanType {
				if adr^ {
					atts.value = "true"}
				else {
					atts.value = "false"}};
			directionType {
				atts.value = string (adr^)};
			unknownType {
				if adr^ == nil {
					elementname = "nil"}
				else {
					scriptError ("Can't serialize " + string.popFileFromAddress (adr) + " because unknownType objects are not supported in XML at this time.")}}}
		else {
			scriptError ("Can't serialize " + string.popFileFromAddress (adr) + " because " + string.typeToString (typeOf (adr^)) + " objects are not supported in XML at this time.")};
		
		addElement (elementname, adratts:@atts);
		
		return (true)};
	on doTable (adrtable, name=nil, flIncludeAddress=false) {
		
		local (adrstruct); //this points at the xstruct for the sub-item we're encoding
		
		local (t = getAtts (adrtable, name));
		if flIncludeAddress { //add the address attribute to the <table>
			t.address = string.popFileFromAddress (adrtable)};
		addElement ("table", adratts:@t, flclose:false); indlev++;
		
		local (adr);
		for adr in adrtable {
			main (adr)};
		
		add ("</table>"); indlev--};
	on main (adr, name=nil) {
		case typeOf (adr^) {
			binaryType {
				local (t = getAtts (adr, name));
				t.type = getBinaryType (adr^);
				addElement ("binary", base64.encode (adr^, 0), @t)};
			listType;
			recordType {
				doListOrRecord (adr, name)};
			wpTextType {
				doWpText (adr, name)};
			outlineType {
				doOutline (adr, name)};
			scriptType {
				doOutline (adr, name, true)};
			menubarType {
				doMenubar (adr, name)};
			tableType {
				doTable (adr, name)}}
		else { //scalar
			doScalar (adr, name)};
		return (true)};
	
	doTable (adrtable, flIncludeAddress:flIncludeAddress);
	
	return (xmltext)}
<<bundle //testing
	<<op.newOutlineObject (table.tableToXml (@workspace, true), @scratchpad.tableXml)
	<<edit (@scratchpad.tableXml)



This listing is for code that runs in the OPML Editor environment. I created these listings because I wanted the search engines to index it, so that when I want to look up something in my codebase I don't have to use the much slower search functionality in my object database. Dave Winer.