5
string tagname; // name without NS
13
void create(string tag, mapping attr, void|object root_node, void|object father_node)
16
if ( sscanf(name, "%s:%s", ns_prefix, tagname) != 2 )
23
foreach ( indices(attributes), string attr) {
25
if ( attr == "xmlns" ) {
26
ns_current = attributes[attr];
28
if ( sscanf(attr, "xmlns:%s", nsprefix) == 1 ) {
29
namespaces[nsprefix] = attributes[attr];
36
if ( objectp(father) )
37
father->add_child(this_object());
39
mapping get_namespaces() { return namespaces; }
40
string ns_get_current() {
43
string ns_lookup(void|string prefix) {
44
if ( !stringp(prefix) ) {
46
if ( stringp(ns_current) )
49
if ( namespaces[prefix] )
50
return namespaces[prefix];
52
if ( objectp(father) )
53
return father->ns_lookup(prefix);
56
string get_ns_prefix() {
62
mapping get_pi() { return root->get_pi(); }
63
void add_child(Node child) {
64
child->set_parent(this_object());
65
children += ({ child });
67
void add_children(array childs) {
68
foreach(childs, object child)
69
child->set_parent(this_object());
72
void replace_children(array childs) {
74
foreach(children, object child)
75
child->set_parent(this_object());
77
void replace_child(Node child, array(Node) childs) {
78
array new_childs = ({ });
79
foreach(children, object s) {
80
if ( s == child && s->get_data() == child->get_data() )
83
new_childs += ({ s });
85
children = new_childs;
86
foreach(children, object child)
87
child->set_parent(this_object());
89
void set_parent(object f) { father = f; }
90
Node get_first_child() {
91
if ( sizeof(children) == 0 )
95
Node get_last_child() {
96
if ( sizeof(children) == 0 )
98
return children[sizeof(children)-1];
100
array(Node) get_leafs() {
102
foreach( children, object child ) {
103
if ( !objectp(children->get_first_child()) )
104
nodes += ({ child });
106
nodes += child->get_leafs();
111
if ( objectp(father) )
112
return father->get_root();
113
return this_object();
116
string get_data() { return data; }
117
string get_text() { return data; }
118
string value_of_node() { return data; }
119
string get_name() { return name; }
120
string get_tag_name() {
123
array(Node) get_children() { return children; }
124
Node get_parent() { return father; }
126
void set_data(string str) { data = str; }
127
void add_data(string str) { data += str; }
130
array(Node) get_nodes(string element) {
131
if ( element[0] == '/' )
132
return root->get_nodes(element[1..]);
133
array(Node) nodes = ({ });
136
sscanf(element, "%s/%s", element, rest_xpath);
137
array matches = match_children(element);
139
foreach(matches, object child) {
140
if ( stringp(rest_xpath) && strlen(rest_xpath) > 0 )
141
nodes += child->get_nodes(rest_xpath);
143
nodes += ({ child });
148
Node get_node(string xpath) {
153
if ( !stringp(xpath) || strlen(xpath) == 0 )
154
return this_object();
156
if ( xpath[0] == '/' )
157
return get_root()->get_node(xpath[1..]);
159
if ( sscanf(xpath, "%s/%s", element, xpath) != 2 ) {
165
array nodes = match_children(element);
167
foreach(nodes, object child) {
168
object res = child->get_node(xpath);
169
// only one path must match!
176
static array(Node) match_children(string single_xpath) {
177
string element, constrain, attr, val;
179
if ( !sscanf(single_xpath, "%s[%s]", element, constrain) )
180
element = single_xpath;
182
sscanf(constrain, "%d", req_num);
183
sscanf(constrain, "@%s=%s", attr, val);
186
array matches = ({ });
188
foreach(children, object child) {
190
if ( element == "*" ||
191
child->tagname == element ||
192
child->name == element )
194
if ( !stringp(attr) || child->attributes[attr] == val ) {
196
if ( req_num == -1 || req_num == cnt )
197
matches += ({ child });
203
void join(object node) {
204
// this node is joined with the node "node", nodes are identical
205
// check childs of both node
206
data = node->get_data();
207
foreach( node->get_children(), object rmtChild ) {
208
object matchNode = 0;
209
foreach ( children, object child ) {
210
if ( child == rmtChild ) {
212
matchNode = rmtChild;
213
child->join(rmtChild);
216
if ( !objectp(matchNode) ) {
217
// add the remote child into our tree
223
array(Node) replace_node(string|Node|array(Node) data, void|string xpath) {
224
// parse data and replace...
225
// remove ?xml at beginning of data;
226
array(Node) replacements;
230
if ( objectp(data) ) {
231
if ( stringp(xpath) )
232
replacements = data->get_nodes(xpath);
234
replacements = ({ data });
236
else if ( stringp(data) ) {
237
Node node = parse(data);
238
if ( !objectp(node) )
239
error("Cannot replace node- unparseable data !");
241
replacements = node->get_nodes(xpath);
243
replacements = ({ node });
246
father->replace_child(this_object(), replacements);
251
return xml.utf8_to_html(dump());
253
mapping get_attributes() {
256
string string_attributes() {
258
foreach(indices(attributes), string attr)
259
a += attr + "='" + attributes[attr]+ "' ";
264
xml += "<"+name+" " + string_attributes()+">";
266
foreach( children, object child)
268
xml += "</"+name+">\n";
272
if ( objectp(cmp) ) {
273
// two nodes are identical if name and attributes match!
274
if ( !functionp(cmp->get_name) || cmp->get_name() != name )
276
array index = indices(attributes);
277
if ( !functionp(cmp->get_attributes) )
279
mapping cmpAttr = cmp->get_attributes();
280
if ( sizeof(index & indices(cmp->get_attributes())) !=
281
sizeof(indices(cmpAttr)) )
283
foreach ( index, string idx ) {
284
if ( attributes[idx] != cmpAttr[idx] )
291
string describe() { return _sprintf(); }
292
string _sprintf() { return "Node("+name+"," + sizeof(children)+" childs, "+
293
(strlen(data) > 0 ? "data": "null")+")"; }
300
void add_pi(string name, string data) {
301
if ( arrayp(pi[name]) )
304
pi[name] = ({ pi[name], data });
316
inherit "AbstractCallbacks";
319
array arr_errors = ({ });
320
static ADT.Stack NodeQueue = ADT.Stack();
322
Node get_root() { return root; }
325
root = RootNode("root", ([ ]));
326
NodeQueue->push(root);
329
int store_data(string data) {
330
Node active = NodeQueue->pop();
331
active->add_data(data);
332
NodeQueue->push(active);
336
string errorSAX(object parser, string err, void|mixed userData)
338
arr_errors += ({ err });
345
string get_first_error()
347
if ( sizeof(arr_errors) > 0 )
348
return arr_errors[0];
352
void startElementSAX(object parser, string name,
353
mapping(string:string) attrs, void|mixed userData)
357
father = NodeQueue->pop();
358
Node active = Node(name, attrs, root, father);
359
NodeQueue->push(father);
361
NodeQueue->push(active);
362
// new element on data queue
366
void endElementSAX(object parser, string name, void|mixed userData)
368
Node old = NodeQueue->pop(); // remove old node from queue...
369
if ( old->attributes->name == "NAVIGATIONTAB2_CONTENT" )
370
werror("Parsed language term: %O\n", old->attributes);
372
void cdataBlockSAX(object parser, string value, void|mixed userData)
376
void charactersSAX(object parser, string chars, void|mixed userData)
378
if ( !store_data(chars) )
379
error("Unable to store characters in Node...");
381
void commentSAX(object parser, string value, void|mixed userData)
383
store_data("<!--"+value+"-->");
385
void referenceSAX(object parser, string name, void|mixed userData)
387
werror("referenceSAX(%s)\n", name);
389
void entityDeclSAX(object parser, string name, int type, string publicId,
390
string systemId, string content, void|mixed userData)
392
werror("entityDecl(%s)\n", name);
394
void notationDeclSAX(object parser, string name, string publicId,
395
string systemId, void|mixed userData)
397
werror("notationDecl(%s)\n", name);
399
void unparsedEntityDeclSAX(object parser, string name, string publicId,
400
string systemId, string notationName,
403
werror("unparsedEntityDecl(%s)\n", name);
405
string getEntitySAX(object parser, string name, void|mixed userData)
407
werror("getEntitySax(%s)\n", name);
409
string processingInstructionSAX(object parser, string name, string data, void|mixed userData)
411
root->add_pi(name, data);
413
void attributeDeclSAX(object parser, string elem, string fullname,
414
int type, int def, void|mixed userData)
416
werror("attributeDeclSAX(%s, %s)\n", elem, fullname);
418
void internalSubsetSAX(object parser, string name, string externalID,
419
string systemID, void|mixed uData)
421
werror("internalSubset(%s)\n", name);
423
void ignorableWhitespaceSAX(object parser, string chars, void|mixed uData)
429
* Create a mapping from an XML Tree.
431
* @param NodeXML n - the root-node to transform to a mapping.
432
* @return converted mapping.
434
mapping xmlMap(Node n)
437
foreach ( n->children, Node children) {
438
if ( children->name == "member" ) {
440
foreach(children->children, object o) {
442
if ( o->name == "key" )
443
key = unserialize(o->children[0]);
444
else if ( o->name == "value" )
445
value = unserialize(o->children[0]);
454
* Create an array with the childrens of the given Node.
456
* @param NodeXML n - the current node to unserialize.
457
* @return Array with unserialized childrens.
459
array xmlArray(Node n)
462
foreach ( n->children, Node children) {
463
res += ({ unserialize(children) });
469
* Create an object from its XML representation, id or path tags are possible
471
* @param NodeXML n - the node to unserialize
472
* @return the steam object
474
object xmlObject(Node n)
481
node = n->get_node("id");
483
return find_object((int)node->get_data());
484
node = n->get_node("path");
486
return find_object(node->get_data());
488
#if constant(_Persistence)
489
node = n->get_node("group");
491
return _Persistence->lookup_group(node->get_data());
492
node = n->get_node("user");
494
return _Persistence->lookup_user(node->get_data());
501
* Create some data structure from an XML Tree.
503
* @param NodeXML n - the root-node of the XML Tree to unserialize.
504
* @return some data structure describing the tree.
506
mixed unserialize(Node n)
519
return (float)n->data;
531
Node parse(string|object html)
533
object cb = saxHandler();
535
int v = (stringp(html) ? 1:0);
536
object sax = xml.SAX(html, cb, ([ ]), 0, v);
537
mixed err = catch(sax->parse());
538
if ( err != 0 || stringp(cb->get_first_error()) )
539
throw(({"Error parsing: " + (cb->get_errors()*"\n")+"\n",
540
(arrayp(err) ? err[1] : backtrace()) }));
542
Node root = cb->get_root();
543
return root->get_first_child();
548
string xml = "<?steam config='xmlfile'?><a><b>bb</b><b text='3'>bbb</b><b/><x/><b/><b/></a>";
549
string rpl = "<xml><b>xxx zzz</b><b><![CDATA[yyy&zzz ]]></b>"+
550
"<b><![CDATA[<img src='test'>IMG</img>]]></b></xml>";
552
write("Parsed: " + a->dump()+"\n");
553
Node x = a->get_node("x");
554
x->replace_node(rpl);
555
array nodes = a->get_nodes("b");
556
write("Nodes: %O\n", nodes);
557
nodes = a->get_nodes("b[@text=3]");
558
write("Nodes with text 3: %O\n", nodes);
559
nodes = a->get_nodes("/a");
560
write("From root = %O\n", nodes);
561
write("PI=%O\n", a->get_pi());
564
xml = "<config><edit><editor name='1'>test</editor><editor name='2'>test2</editor></edit></config>";
565
Node cfg = parse(xml);
566
// test join operations
567
xml = "<config><edit><editor name='3'>test3</editor></edit></config>";
568
Node cfg2 = parse(xml);
570
write("Configuration join (editor1,2,3 expected)\n" + cfg->get_xml() + "\n");
571
xml = "<config><edit><editor name='2'>newtest2</editor></edit></config>";
572
Node cfg3 = parse(xml);
574
write("Another join, overwrite test for configuration edit 2, newtest2 expected !\n" + cfg->get_xml());