[Aimmath-commit] AIM/WEB-INF/maple XML.mpl,NONE,1.1.2.1
Brought to you by:
gustav_delius,
npstrick
From: Neil S. <nps...@us...> - 2004-10-14 11:09:20
|
Update of /cvsroot/aimmath/AIM/WEB-INF/maple In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17595/WEB-INF/maple Added Files: Tag: aim-xml XML.mpl Log Message: General XML-handling facilities --- NEW FILE: XML.mpl --- # Copyright (C) 2004 Neil Strickland # Distributed without warranty under the GPL - see README for details read("Package.mpl"): Package("XML"," This package defines a number of convenient methods for working with XML. "): `Package/Dependencies` := []: ###################################################################### `Package/Assign`( `type/XML/Var`::type, "@type(x,`XML/Var`)@ is @true@ if @x@ is of the form `XML/Var`@(something)@ or `XML/ValueVar`@(something)@ or `XML/AttributeVar`@(something)@.", {specfunc(anything,`XML/Var`), specfunc(anything,`XML/ValueVar`), specfunc(anything,`XML/AttributeVar`)} ): `Package/Assign`( `type/XML/ValueVar`::type, "@type(x,`XML/ValueVar`)@ is @true@ if @x@ is of the form `XML/ValueVar`@(something)@.", {specfunc(anything,`XML/ValueVar`)} ): `Package/Assign`( `type/XML/AttributeVar`::type, "@type(x,`XML/AttributeVar`)@ is @true@ if @x@ is of the form `XML/AttributeVar`@(something)@.", {specfunc(anything,`XML/AttributeVar`)} ): `Package/Assign`( `type/XML/PackedTemplate`::type, "A packed template is a particular kind of XML element, namely a <seq> element whose contents are just strings or XML variables. Any template (ie any XML element, possibly with variable elements) can be converted to a packed template by converting the elements to strings as far as possible. ", proc(x::anything) RETURN(evalb( type(x,`XML/Element`) and x['Type'] = "seq" and type(x['Contents'],list({string,`XML/Var`})))); end ): ###################################################################### ###################################################################### `Package/Assign`(`XML/NamespaceTable`, "A table mapping three standard prefixes (the empty string, @xml@ and @xmlns@) to namespace URIs. ", table(["" = "", "xmlns" = "", "xml" = "http://www.w3.org/XML/1998/namespace"]) ): `Class/Declare`(`XML/Element`, "An object of type Element represents a XML element together with its attributes and contents. ", ['Constructor', "<p> If @head@ is a string, then it is used as the element type, and there are no attributes. </p><p> If @head@ is a list, then the first element is used as the element type, and the remaining elements are used as attribute equations. </p><p> Any arguments after the first are used as the contents of the element. <ul> <li> if an argument is a string or a element object, it is used directly</li> <li> if an argument is a list, then the constructor is applied recursively with the list entries as arguments </li> </ul> Additionally, if any attribute or content object is a set of the form @{x}@, it gets converted to @`XML/Var`(x)@. </p> ", proc(this,head::{string,list}) local msg,qn,i; this['Attributes'] := table([]); this['NamespaceTable'] := eval(`XML/NamespaceTable`); if nargs = 1 then return(); fi; if type(head,string) then qn := head; elif type(head,list) and nops(head) > 0 and type(head[1],string) then qn := head[1]; this['SetAttributes',op(head[2..-1])]; else msg := sprintf("Head should be a string or a list whose first element is a string: %A",head); ERROR(msg); fi; i := searchtext(":",qn); if i > 0 then this['Prefix'] := substring(qn,1..i-1); this['Name'] := substring(qn,i+1..-1); else this['Prefix'] := ""; this['Name'] := qn; fi; this['Contents'] := map(`XML/NewElementAux`,[args[3..-1]],this['NamespaceTable']); RETURN(); end ], ['Field','Type'::string, "The type of element. For example, for an image element, this would be the string "img". For a element like @<n:widget xmlns:n="http://www.gadgets.com/bits">@, this field just contains the string "widget". " ], ['Field','Prefix'::string, "For a element like @<xyz:widget xmlns:xyz="http://www.gadgets.com/bits">@, this field just contains the string "xyz". " ], ['Field','NamespaceTable'::table = eval(`XML/NamespaceTable`), "A table giving namespace URI's associated to various prefixes that may be used in this element, its descendants, and associated attributes. " ], ['Field','NamespaceTableInherited'::boolean=true, "For efficiency, the namespace table is often inherited from an ancestor element. In that case, this field should be set to @true@. This acts as a warning that we must copy the table before making changes to it. " ], ['Method','QName'::string, "", proc(this) local t,p; t := this['Type']; p := this['Prefix']; `if`(p="",t,cat(p,":",t)); end ], ['Method','GetURI'::string, "Fetch the URI associated to a specified prefix, returning @NULL@ if no URI is known. ", proc(this,prefix::string) local t; t := eval(this['NamespaceTable']); if assigned(t[prefix]) then return(t[prefix]); else return(NULL); fi; end ], ['Method','NamespaceURI'::string, "The URI describing the namespace for the name of this element", proc(this) local t,prefix; t := eval(this['NamespaceTable']); prefix := this['Prefix']; if assigned(t[prefix]) then return(t[prefix]); else return(NULL); fi; end ], ['Field','Attributes'::table, "The table of attributes of the element, indexed by pairs @(attribute prefix,attribute name)@. " ], ['Method','SetAttributes'::'void', "The arguments should be attribute equations; they are added to the attributes of the element. Qualified names can be given in the form @"foo:bar"@ or @["foo","bar"]@. Attributes that are namespace declarations will be added to the @NamespaceTable@ field in the obvious way. Attributes may also be given as expressions of the form <p> @_XML_Attribute(_XML_AttrName(...),_XML_AttrValue(...))@, </p> as generated by the @XMLTools@ module. ", proc(this) local x,qn,v,a,msg,i; a := eval(this['Attributes']); for x in args[2..-1] do if type(x,`MapleXML/Attribute`) then qn := [op([1,2],x),op([1,1],x)]; v := op([2,1],x); else if type(x,equation) then qn := lhs(x); v := rhs(x); else qn := x; v := "true"; fi; if type([qn],[string]) then i := searchtext(":",qn); if i > 0 then qn := [substring(qn,1..i-1),substring(qn,i+1..-1)]; else qn := ["",qn]; fi; fi; fi; if not(type([qn],[[string,string]])) then msg := sprintf( __("LHS of attribute equation is not a string or pair of strings: %a"), x); ERROR(msg); fi; if v = NULL then a[op(qn)] := NULL; next; fi; if nops([v]) > 1 then msg := sprintf(__("RHS of attribute equation is a sequence: %a"),x); ERROR(msg); fi; if type(v,set) and nops(v) = 1 then v := `XML/ValueVar`(op(v)); fi; if qn[1] = "xmlns" and type(v,string) then this['AddPrefix',qn[2],v]; fi; if type(v,{string,numeric,boolean,`XML/ValueVar`}) then a[op(qn)] := v; else msg := sprintf(__("Invalid attribute equation: %a"),x); ERROR(msg); fi; od; NULL; end ], ['Method','AddPrefix'::'void', "Adds a prefix and associated URI to the namespace table. If the namespace table was inherited from an ancestor, make a new copy before modifying it. ", proc(this,prefix::string,uri::string) local t; t := eval(this['NamespaceTable']); if this['NamespaceTableInherited'] then t := copy(t); this['NamespaceTable'] := eval(t); this['NamespaceTableInherited'] := false; fi; t[prefix] := uri; end ], ['Method','ClearAttributes'::'void', "Remove all attributes of the element. The namespace table is not changed, which could in principle lead to inconsistencies. ", proc(this) this['Attributes'] := table([]); NULL; end ], ['Field','Contents'::list, "The list of contents of the element. These may be strings, numbers, boolean values, elements, XML variables or packed menus. " ], ['Method','AddContents'::'void', "Apply the function #`XML/NewElementAux`# to each of the arguments, and append the results to the @Contents@ field", proc(this) local newcontents; newcontents := map(`XML/NewElementAux`,[args[2..-1]]); this['Contents'] := [op(this['Contents']), op(newcontents)]; NULL; end ], ['Method','ClearContents'::'void', "Remove all contents of this element.", proc(this) this['Contents'] := []; end ], ['Method','ContentTree'::anything, "The arguments should be a sequence of positive integers. For example, @element['ContentTree',6,2]@ is the second entry in the sixth entry of @element@.", proc(this) local i,j,k,t,c,msg; t := this; for k from 2 to nargs do i := args[k]; if not(type(k,posint)) then msg := __("Arguments to the ContentTree method must be positive integers"); ERROR(msg); fi; if not(type(t,`XML/Element`)) then msg := __("ContentTree index out of bounds"); ERROR(msg); fi; j := 0; c := t['Contents']; while ( j < nops(c) and i > 0 ) do j := j+1; if type(c[j],`XML/Element`) then i := i-1; fi; od; if (i = 0) then t := eval(c[j]); else msg := __("ContentTree index out of bounds"); ERROR(msg); fi; od; RETURN(eval(t)); end ], ['Method','ToSeq', "", proc(this) local elementtype,s,attribtable,namix,nam,val,contents,separator, allstrings,a,x,msg,prefix,qn; s := "<",this['QName']; attribtable := eval(this['Attributes']); for namix in indices(attribtable) do prefix := namix[1]; nam := namix[2]; qn := `if`(prefix="", nam, cat(prefix,":",nam)); val := attribtable[op(namix)]; # ignore attributes whose value is NULL if val = NULL then next; fi; if type(val,`XML/ValueVar`) then s := s,`XML/AttributeVar`(nam,op(val)); elif val <> NULL then s := s," ",qn,"=",`XML/ValueEscape`(val); fi; od; if this['Contents'] = [] then s := s,"/>"; return(`XML/Condense`(s)); fi; s := s,">"; contents := NULL; for x in this['Contents'] do if type(x,`XML/Element`) then contents := contents,x['ToSeq']; elif type(x,`XML/Var`) then contents := contents,x; elif type(x,{string,numeric,boolean}) then contents := contents,sprintf("%A",x); else msg := sprintf(__("Invalid contents in XML element: %a"),x); ERROR(msg); fi; od; s := s,contents,"</",this['QName'],">"; RETURN(`XML/Condense`(s)); end ], ['Method','Variables'::set(`XML/Var`), "Return the list of all XML variables that appear in this element.", proc(this) local v; v := {this['ToSeq']}; v := select(type,v,`XML/Var`); RETURN(v); end ], ['Method','ToPack'::`XML/Element`, "Return the result of converting this element to a packed template.", proc(this) local s,t; s := this['ToSeq']; t := `new/XML/Element`("seq"); t['Contents'] := [s]; RETURN(eval(t)); end ], ['Method','IsPacked'::boolean, "Return @true@ if this element is a packed template.", proc(this) evalb( this['Type'] = "seq" and type(this['Contents'], list({string,numeric,boolean,`XML/Var`}))); end ], ['Method','IsFilled'::boolean, "Return @true@ if this is a <seq> element whose contents are all strings, numbers or boolean values. ", proc(this) evalb( this['Type'] = "seq" and type(this['Contents'], list({string,numeric,boolean}))); end ], ['Method','ToString'::`XML/String`, "Return a string of XML representing this element.", proc(this) `XML/ToString`(this['ToSeq']); end ], ['Method','Print'::'void', "Print a string of XML representing this element.", proc(this) `XML/Print`(this); end ] ): ### Auxiliary function used in the constructor `Package/Assign`( `XML/NewElementAux`, "An auxiliary function used by the constructor #`new/XML/Element`#", proc(x,p_::table) local msg,p; if nargs > 1 then p := eval(p_); else p := NULL; fi; if type([x],{[string],[numeric],[boolean]}) then RETURN(x); elif type([x],[specfunc(string,_XML_Text)]) then RETURN(op(1,x)); elif type([x],[specfunc(string,_XML_Entity)]) then RETURN(cat("&",op(1,x),";")); elif type([x],[`XML/Element`]) then if x['Type'] = "seq" then RETURN(op(x['Contents'])); else RETURN(eval(x)); fi; elif type([x],[set]) and nops(x) = 1 then RETURN(`XML/Var`(op(x))); elif type([x],[list]) then RETURN(`new/XML/Element`(op(x))); elif type([x],[`MapleXML/Element`]) then RETURN(`XML/Objectify`(x,p)); else msg := sprintf("Invalid argument in XML/NewElementAux: %a",[x]); ERROR(msg); fi; end ): ###################################################################### `Package/Assign`( `XML/Condense`::exprseq(string), "Join together any consecutive sequences of strings in the arguments, and return the resulting sequence. For example, <pre> @`XML/Condense`(\"abc\",\"def\",3,\"x\",\"y\",\"z\") = \"abc\",3,\"xyz\" @ </pre> ", proc() local a,b,x,y; a := NULL; b := NULL; for x in args do if type(x,{boolean,numeric}) then y := sprintf("%A",x); else y := x; fi; if type(y,string) then b := b,y; else if b <> NULL then a := a,cat("",b); fi; b := NULL; a := a,y; fi; od; if b <> NULL then a := a,cat("",b); fi; RETURN(a); end ): ###################################################################### `Package/Assign`( `XML/ElementOpenStart`::string, "An auxiliary function for converting elements to strings. Special behaviour for a few elements is stored in the remember table for this function.", proc(elementtype::string) cat("<",elementtype); end ): `Package/Assign`( `XML/ElementOpenEnd`::string, "An auxiliary function for converting elements to strings. Special behaviour for a few elements is stored in the remember table for this function.", proc(elementtype::string) cat(">"); end ): `Package/Assign`( `XML/ElementClose`::string, "An auxiliary function for converting elements to strings. Special behaviour for a few elements is stored in the remember table for this function.", proc(elementtype::string) cat("</",elementtype,">"); end ): # Adjust whitespace `XML/ElementOpenEnd`("br") := " />\n": `XML/ElementOpenEnd`("hr") := " />\n": `XML/ElementOpenEnd`("p") := ">\n": `XML/ElementClose`("p") := "\n</p>\n": `XML/ElementOpenEnd`("div") := ">\n": `XML/ElementClose`("div") := "\n</div>\n": `XML/ElementOpenStart`("seq") := "": `XML/ElementOpenEnd`("seq") := "": `XML/ElementClose`("seq") := "": ###################################################################### `Package/Assign`( `XML/ToString`, "Convert all arguments to strings, concatenate, and return the result. The arguments can be XML elements or primitive object (strings, numbers or boolean values). XML variables are also converted to strings, but this is primarily intended for developing and debugging templates. ", proc() local s,x; s := ""; for x in args do if type(x,string) then s := s,x; elif type(x,{numeric,boolean}) then s := s,sprintf("%A",x); elif type(x,`XML/Element`) then s := s,`XML/ToString`(x['ToSeq']); elif type(x,`XML/ValueVar`) then s := s,sprintf("%a",{op(x)}); elif type(x,`XML/AttributeVar`) then s := s,sprintf(" %a={%a}",op(1,x),op(2,x)); elif type(x,`XML/Var`) then s := s, "<font color='green'>", `XML/Escape/String`(sprintf("%A",{x})), "</font>"; else s := s, "<font color='aqua'>", `XML/Escape/String`(sprintf("%A",x)), "</font>"; fi; od; RETURN(cat(s)); end ): ###################################################################### `Package/Assign`( `XML/ToPack`::`XML/PackedTemplate`, "Convert the arguments to a packed template. The arguments can be XML elements or variables, or <select> elements with indeterminate initial selection, or primitive types (strings, numbers or boolean values). ", proc() local pack,x,t; pack := ""; for x in args do if type(x,{numeric,boolean}) then pack := pack,sprintf("%A",x); elif type(x,`XML/Element`) then pack := pack,op(x['ToPack']['Contents']); else pack := pack,x; fi; od; pack := `XML/Condense`(pack); t := `new/XML/Element`("seq"); t['Contents'] := [pack]; RETURN(eval(t)); end ): ###################################################################### `Package/Assign`( `XML/Print`::'void', "Convert @x@ to a string using #`XML/ToString`# and print it.", proc(x) printf("%s\n\n",`XML/ToString`(x)); NULL; end ): ###################################################################### `Package/Assign`( `XML/Subs`, "The last argument @x@ is expected to be a packed template with some variable elements. The other arguments are equations like @n = v@. The return value is a copy of @x@ with all occurences of @`XML/Var`(n)@ replaced by @v@, and similarly for @`XML/ValueVar`@ and @`XML/AttributeVar`@ ", proc() local eqs,a,b,c,x,y,z,t,msg; if nargs = 0 then RETURN(NULL); fi; x := args[nargs]; if not(type(x,`XML/PackedTemplate`)) then msg := __("Last argument is not a packed template."); ERROR(msg); fi; eqs := NULL; t := table([args[1..-2]]); y := map(`XML/SubsAux`,x['Contents'],t); y := [`XML/Condense`(op(y))]; z := `new/XML/Element`("seq"); z['Contents'] := y; RETURN(eval(z)); end ): `Package/Assign`( `XML/SubsAux`, "An auxiliary function used by #`XML/Subs`#", proc(u,v::table) local a; if type(u,`XML/Var`) and assigned(v[op(u)]) then v[op(u)]; elif type(u,`XML/AttributeVar`) and assigned(v[op(2,u)]) then a := v[op(2,u)]; if a = NULL then NULL; elif type([a],[{string,numeric}]) then cat(" ",op(1,u),"=",`XML/ValueEscape`(a)); else u; fi; else u; fi; end ): ###################################################################### `Package/Assign`( `XML/Escape/String`::`XML/String`, "Return a copy of @s@ with all less-than signs, greater-than signs, ampersands and quotation marks replaced by the corresponding XML character entities (&lt;, &gt;, &amp; and &quot;). The result is suitable for use as XML text. ", proc(s::string) local b; b := convert(s,bytes); b := subs( 38 = (38, 97, 109, 112, 59), # & 60 = (38, 108, 116, 59), # < 62 = (38, 103, 116, 59), # > 34 = (38, 113, 117, 111, 116, 59), # " b); convert(b,bytes); end ): `Package/Assign`( `XML/Escape`::`XML/String`, "This is the same as #`XML/Escape/String`# except that it also accepts a variable number of arguments, converts non-strings to strings, and concatenates the results. ", proc() local x,html; html := ""; for x in args do if type(x,string) then html := html,x; else html := html,sprintf("%A",x); fi; od; RETURN(`XML/Escape/String`(cat(html))); end ): `Package/Assign`( `XML/ValueEscape/String`::`XML/String`, "Return a copy of @s@ with ampersands and quotation marks replaced by the corresponding XML character entities (&amp; and &quot;), wrapped in quotation marks. The result is suitable for use as an XML attribute value. ", proc(s::string) local b; b := convert(s,bytes); b := subs( 38 = (38, 97, 109, 112, 59), # & 60 = (38, 108, 116, 59), # < 62 = (38, 103, 116, 59), # > 34 = (38, 113, 117, 111, 116, 59), # " b); convert([34,op(b),34],bytes); end ): `Package/Assign`( `XML/ValueEscape`::`XML/String`, "This is the same as #`XML/ValueEscape/String`# except that it also converts non-strings to strings. ", proc(x) local s,b; if nargs = 0 then RETURN(NULL); fi; if type(x,{numeric,boolean}) then RETURN(sprintf("\"%A\"",x)); fi; if type(x,string) then s := x; else # This should not happen in a working application. The # specified behaviour is designed for debugging only. s := sprintf("%A",x); fi; RETURN(`XML/ValueEscape/String`(s)); end ): ###################################################################### `Package/Assign`( `type/MapleXML/Element`, "", proc(x) evalb( type(x,function) and op(0,x) = '_XML_Element' and nops(x) > 0 and type(op(1,x),`MapleXML/ElementType`) ); end ): `Package/Assign`( `type/MapleXML/ElementType`, "", proc(x) evalb( type(x,function) and op(0,x) = '_XML_ElementType' and type([op(x)],[string$3]) ); end ): `Package/Assign`( `type/MapleXML/Attribute`, "", proc(x) evalb( type(x,function) and op(0,x) = '_XML_Attribute' and type([op(x)],[`MapleXML/AttrName`,`MapleXML/AttrValue`]) ); end ): `Package/Assign`( `type/MapleXML/AttrName`, "", proc(x) evalb( type(x,function) and op(0,x) = '_XML_AttrName' and type([op(x)],[string$3]) ); end ): `Package/Assign`( `type/MapleXML/AttrValue`, "", proc(x) evalb( type(x,function) and op(0,x) = '_XML_AttrValue' and type([op(x)],[string]) ); end ): `Package/Assign`( `type/MapleXML/Text`, "", proc(x) evalb( type(x,function) and op(0,x) = '_XML_Text' and type([op(x)],[string]) ); end ): `Package/Assign`( `XML/Objectify`::`XML/Element`, "", proc(e::`MapleXML/Element`,p_::table) local t,x,p,n; t := op(1,e); x := `new/XML/Element`(); x['Type'] := op(1,t); x['Prefix'] := op(2,t); if nargs > 1 then x['NamespaceTable'] := eval(p); x['NamespaceTableInherited'] := true; elif op(2,t) <> "" or op(3,t) <> "" then n := copy(`XML/NamespaceTable`); n[op(2,t)] := op(3,t); fi; x['SetAttributes',op(op(2,e))]; x['Contents'] := map(`XML/NewElementAux`,op(3,e)); RETURN(eval(x)); end ): ###################################################################### EndPackage(): |