~ubuntu-branches/ubuntu/wily/steam/wily

« back to all changes in this revision

Viewing changes to server/libraries/xmlDom.pmod

  • Committer: Package Import Robot
  • Author(s): Felix Geyer
  • Date: 2013-10-29 19:51:18 UTC
  • mfrom: (1.1.4) (0.1.4 trusty-proposed)
  • Revision ID: package-import@ubuntu.com-20131029195118-b9bxciz5hwx5z459
Tags: 1:1.0.0.39-2ubuntu1
Add an epoch to the version number as there was an unrelated steam package
in the archive with a higher version number.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
class Node {
2
 
    Node          father;
3
 
    Node            root;
4
 
    string          name;
5
 
    string       tagname; // name without NS
6
 
    string     ns_prefix;
7
 
    string          data;
8
 
    mapping   attributes;
9
 
    mapping   namespaces;
10
 
    string    ns_current;
11
 
    array(Node)  children;
12
 
    
13
 
    void create(string tag, mapping attr, void|object root_node, void|object father_node) 
14
 
    {
15
 
        name = tag;
16
 
        if ( sscanf(name, "%s:%s", ns_prefix, tagname) != 2 )
17
 
            tagname = name;
18
 
        if ( mappingp(attr) )
19
 
          attributes = attr;
20
 
        else
21
 
          attributes = ([ ]);
22
 
        namespaces = ([ ]);
23
 
        foreach ( indices(attributes), string attr) {
24
 
            string nsprefix;
25
 
            if ( attr == "xmlns" ) {
26
 
                ns_current = attributes[attr]; 
27
 
            }
28
 
            if ( sscanf(attr, "xmlns:%s", nsprefix) == 1 ) {
29
 
                namespaces[nsprefix] = attributes[attr];
30
 
            }
31
 
        }
32
 
        father = father_node;
33
 
        root = root_node;
34
 
        children = ({ });
35
 
        data = "";
36
 
        if ( objectp(father) )
37
 
            father->add_child(this_object());
38
 
    }
39
 
    mapping get_namespaces() { return namespaces; }
40
 
    string ns_get_current() {
41
 
        return ns_current;
42
 
    }
43
 
    string ns_lookup(void|string prefix) {
44
 
        if ( !stringp(prefix) ) {
45
 
            prefix = ns_prefix;
46
 
            if ( stringp(ns_current) )
47
 
                return ns_current;
48
 
        }
49
 
        if ( namespaces[prefix] )
50
 
            return namespaces[prefix];
51
 
        
52
 
        if ( objectp(father) )
53
 
            return father->ns_lookup(prefix);
54
 
        return 0;
55
 
    }
56
 
    string get_ns_prefix() {
57
 
        return ns_prefix;
58
 
    }
59
 
    string get_ns() {
60
 
        return ns_lookup();
61
 
    }
62
 
    mapping get_pi() { return root->get_pi(); }
63
 
    void add_child(Node child) {
64
 
        child->set_parent(this_object());
65
 
        children += ({ child });
66
 
    }
67
 
    void add_children(array childs) {
68
 
      foreach(childs, object child)
69
 
        child->set_parent(this_object());
70
 
      children += childs;
71
 
    }
72
 
    void replace_children(array childs) {
73
 
        children = childs;
74
 
        foreach(children, object child)
75
 
          child->set_parent(this_object());
76
 
    }
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() )
81
 
              new_childs += childs;
82
 
        else
83
 
          new_childs += ({ s });
84
 
      }
85
 
      children = new_childs;
86
 
      foreach(children, object child)
87
 
        child->set_parent(this_object());
88
 
    }
89
 
    void set_parent(object f) { father = f; }
90
 
    Node get_first_child() {
91
 
      if ( sizeof(children) == 0 )
92
 
        return 0;
93
 
      return children[0];
94
 
    }
95
 
    Node get_last_child() {
96
 
      if ( sizeof(children) == 0 )
97
 
        return 0;
98
 
      return children[sizeof(children)-1];
99
 
    }
100
 
    array(Node) get_leafs() {
101
 
      array nodes = ({ });
102
 
      foreach( children, object child ) {
103
 
        if ( !objectp(children->get_first_child()) )
104
 
          nodes += ({ child });
105
 
        else
106
 
          nodes += child->get_leafs();
107
 
      }
108
 
      return nodes;
109
 
    }
110
 
    Node get_root() {
111
 
      if ( objectp(father) )
112
 
        return father->get_root();
113
 
      return this_object();
114
 
    }
115
 
 
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() { 
121
 
        return tagname; 
122
 
    }
123
 
    array(Node) get_children() { return children; }
124
 
    Node get_parent() { return father; }
125
 
 
126
 
    void set_data(string str) { data = str; }
127
 
    void add_data(string str) { data += str; }
128
 
 
129
 
    // xpath
130
 
    array(Node) get_nodes(string element) {
131
 
        if ( element[0] == '/' )
132
 
          return root->get_nodes(element[1..]);
133
 
        array(Node) nodes = ({ });
134
 
        string rest_xpath;
135
 
 
136
 
        sscanf(element, "%s/%s", element, rest_xpath);
137
 
        array matches = match_children(element);
138
 
        
139
 
        foreach(matches, object child) {
140
 
          if ( stringp(rest_xpath) && strlen(rest_xpath) > 0 )
141
 
            nodes += child->get_nodes(rest_xpath);
142
 
          else
143
 
            nodes += ({ child });
144
 
        }
145
 
        return nodes;
146
 
    }
147
 
    
148
 
    Node get_node(string xpath) {
149
 
        string element, req;
150
 
        int         req_num; 
151
 
        mapping    req_attr;
152
 
 
153
 
        if ( !stringp(xpath) || strlen(xpath) == 0 )
154
 
            return this_object();
155
 
 
156
 
        if ( xpath[0] == '/' ) 
157
 
          return get_root()->get_node(xpath[1..]);
158
 
 
159
 
        if ( sscanf(xpath, "%s/%s", element, xpath) != 2 ) {
160
 
            element = xpath;
161
 
            xpath = "";
162
 
        }
163
 
 
164
 
 
165
 
        array nodes = match_children(element);
166
 
 
167
 
        foreach(nodes, object child) {
168
 
          object res = child->get_node(xpath);
169
 
          // only one path must match!
170
 
          if ( objectp(res) )
171
 
            return res;
172
 
        }
173
 
        return 0;
174
 
    }
175
 
 
176
 
    static array(Node) match_children(string single_xpath) {
177
 
      string element, constrain, attr, val;
178
 
      int req_num = -1;
179
 
      if ( !sscanf(single_xpath, "%s[%s]", element, constrain) )
180
 
        element = single_xpath;
181
 
      else {
182
 
        sscanf(constrain, "%d", req_num);
183
 
        sscanf(constrain, "@%s=%s", attr, val);
184
 
      }
185
 
        
186
 
      array matches = ({ });
187
 
      int cnt = 0;
188
 
      foreach(children, object child) {
189
 
        // match
190
 
        if ( element == "*" || 
191
 
             child->tagname == element || 
192
 
             child->name == element ) 
193
 
        {
194
 
          if ( !stringp(attr) || child->attributes[attr] == val ) {
195
 
            cnt++;
196
 
            if ( req_num == -1 || req_num == cnt )
197
 
              matches += ({ child });
198
 
          }
199
 
        }
200
 
      }
201
 
      return matches;
202
 
    }
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 ) {
211
 
                    // join remote node
212
 
                    matchNode = rmtChild;
213
 
                    child->join(rmtChild);
214
 
                }
215
 
            }
216
 
            if ( !objectp(matchNode) ) {
217
 
                // add the remote child into our tree
218
 
                add_child(rmtChild);
219
 
            }
220
 
        }
221
 
    }
222
 
    
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;
227
 
 
228
 
      if ( arrayp(data) )
229
 
        replacements = data;
230
 
      if ( objectp(data) ) {
231
 
        if ( stringp(xpath) )
232
 
          replacements = data->get_nodes(xpath);
233
 
        else
234
 
          replacements = ({ data });
235
 
      }
236
 
      else if ( stringp(data) ) {
237
 
        Node node = parse(data);
238
 
        if ( !objectp(node) )
239
 
          error("Cannot replace node- unparseable data !");
240
 
        if ( xpath )
241
 
          replacements = node->get_nodes(xpath);
242
 
        else
243
 
          replacements = ({ node });
244
 
      }
245
 
      
246
 
      father->replace_child(this_object(), replacements);
247
 
      return replacements;
248
 
    }
249
 
  
250
 
    string get_xml() {
251
 
      return xml.utf8_to_html(dump());
252
 
    }
253
 
    mapping get_attributes() {
254
 
        return attributes;
255
 
    }
256
 
    string string_attributes() {
257
 
      string a = "";
258
 
      foreach(indices(attributes), string attr)
259
 
        a += attr + "='" + attributes[attr]+ "' ";
260
 
      return a;
261
 
    }
262
 
    string dump() {
263
 
      string xml = "";
264
 
      xml += "<"+name+" " + string_attributes()+">";
265
 
      xml += get_data();
266
 
      foreach( children, object child)
267
 
        xml+= child->dump();
268
 
      xml += "</"+name+">\n";
269
 
      return xml;
270
 
    }
271
 
    int `==(mixed cmp) {
272
 
        if ( objectp(cmp) ) {
273
 
            // two nodes are identical if name and attributes match!
274
 
            if ( !functionp(cmp->get_name) || cmp->get_name() != name )
275
 
                return 0;
276
 
            array index = indices(attributes);
277
 
            if ( !functionp(cmp->get_attributes) )
278
 
                return 0;
279
 
            mapping cmpAttr = cmp->get_attributes();
280
 
            if ( sizeof(index & indices(cmp->get_attributes())) != 
281
 
                 sizeof(indices(cmpAttr)) )
282
 
                 return 0;
283
 
            foreach ( index, string idx ) {
284
 
                if ( attributes[idx] != cmpAttr[idx] )
285
 
                    return 0;
286
 
            }
287
 
            return 1; 
288
 
        }
289
 
        return 0;
290
 
    }
291
 
    string describe() { return _sprintf(); }
292
 
    string _sprintf() { return "Node("+name+"," + sizeof(children)+" childs, "+
293
 
                            (strlen(data) > 0 ? "data": "null")+")"; }
294
 
}
295
 
 
296
 
class RootNode {
297
 
  inherit Node;
298
 
  
299
 
  mapping pi = ([ ]);
300
 
  void add_pi(string name, string data) { 
301
 
    if ( arrayp(pi[name]) )
302
 
      pi[name] += data;
303
 
    else if ( pi[name] )
304
 
      pi[name] = ({ pi[name], data });
305
 
    else
306
 
      pi[name] = data; 
307
 
  }
308
 
  mixed get_pi() {
309
 
    return pi; 
310
 
  }
311
 
}
312
 
 
313
 
 
314
 
class saxHandler
315
 
{
316
 
    inherit "AbstractCallbacks";
317
 
 
318
 
    RootNode root;
319
 
    array arr_errors = ({ });
320
 
    static ADT.Stack NodeQueue     = ADT.Stack();
321
 
 
322
 
    Node get_root() { return root; }
323
 
 
324
 
    void create() {
325
 
        root = RootNode("root", ([ ]));
326
 
        NodeQueue->push(root);
327
 
    }
328
 
 
329
 
    int store_data(string data) {
330
 
      Node active = NodeQueue->pop();
331
 
      active->add_data(data);
332
 
      NodeQueue->push(active);
333
 
      return 1;
334
 
    }
335
 
    
336
 
    string errorSAX(object parser, string err, void|mixed userData) 
337
 
    {
338
 
      arr_errors += ({ err });
339
 
    }
340
 
 
341
 
    array get_errors() 
342
 
    {
343
 
      return arr_errors;
344
 
    }
345
 
    string get_first_error() 
346
 
    {
347
 
      if ( sizeof(arr_errors) > 0 )
348
 
        return arr_errors[0];
349
 
      return 0;
350
 
    }
351
 
 
352
 
    void startElementSAX(object parser, string name, 
353
 
                         mapping(string:string) attrs, void|mixed userData) 
354
 
    {
355
 
        Node father;
356
 
        // connect nodes;
357
 
        father = NodeQueue->pop();
358
 
        Node active = Node(name, attrs, root, father);
359
 
        NodeQueue->push(father);
360
 
 
361
 
        NodeQueue->push(active);
362
 
        // new element on data queue
363
 
    }
364
 
    
365
 
    
366
 
    void endElementSAX(object parser, string name, void|mixed userData)
367
 
    {
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);
371
 
    }
372
 
    void cdataBlockSAX(object parser, string value, void|mixed userData)
373
 
    {
374
 
      store_data(value);
375
 
    }
376
 
    void charactersSAX(object parser, string chars, void|mixed userData)
377
 
    {
378
 
        if ( !store_data(chars) )
379
 
            error("Unable to store characters in Node...");
380
 
    }
381
 
    void commentSAX(object parser, string value, void|mixed userData) 
382
 
    {
383
 
      store_data("<!--"+value+"-->");
384
 
    }
385
 
    void referenceSAX(object parser, string name, void|mixed userData)
386
 
    {
387
 
      werror("referenceSAX(%s)\n", name);
388
 
    }
389
 
    void entityDeclSAX(object parser, string name, int type, string publicId,
390
 
                       string systemId, string content, void|mixed userData)
391
 
    {
392
 
      werror("entityDecl(%s)\n", name);
393
 
    }
394
 
    void notationDeclSAX(object parser, string name, string publicId, 
395
 
                         string systemId, void|mixed userData) 
396
 
    {
397
 
        werror("notationDecl(%s)\n", name);
398
 
    }
399
 
    void unparsedEntityDeclSAX(object parser, string name, string publicId, 
400
 
                               string systemId, string notationName, 
401
 
                               void|mixed userData) 
402
 
    {
403
 
        werror("unparsedEntityDecl(%s)\n", name);
404
 
    }
405
 
    string getEntitySAX(object parser, string name, void|mixed userData)
406
 
    {
407
 
        werror("getEntitySax(%s)\n", name);
408
 
    }
409
 
  string processingInstructionSAX(object parser, string name, string data, void|mixed userData)
410
 
    {
411
 
      root->add_pi(name, data);
412
 
    } 
413
 
    void attributeDeclSAX(object parser, string elem, string fullname, 
414
 
                          int type, int def, void|mixed userData)
415
 
    {
416
 
        werror("attributeDeclSAX(%s, %s)\n", elem, fullname);
417
 
    }
418
 
    void internalSubsetSAX(object parser, string name, string externalID, 
419
 
                           string systemID, void|mixed uData)
420
 
    {
421
 
      werror("internalSubset(%s)\n", name);
422
 
    }
423
 
    void ignorableWhitespaceSAX(object parser, string chars, void|mixed uData)
424
 
    {
425
 
    }
426
 
};
427
 
 
428
 
/**
429
 
 * Create a mapping from an XML Tree.
430
 
 *  
431
 
 * @param NodeXML n - the root-node to transform to a mapping.
432
 
 * @return converted mapping.
433
 
 */
434
 
mapping xmlMap(Node n)
435
 
{
436
 
  mapping res = ([ ]);
437
 
  foreach ( n->children, Node children) {
438
 
    if ( children->name == "member" ) {
439
 
      mixed key,value;
440
 
      foreach(children->children, object o) {
441
 
 
442
 
        if ( o->name == "key" )
443
 
          key = unserialize(o->children[0]);
444
 
        else if ( o->name == "value" )
445
 
          value = unserialize(o->children[0]);
446
 
      }
447
 
      res[key] = value;
448
 
    }
449
 
  }
450
 
  return res;
451
 
}
452
 
 
453
 
/**
454
 
 * Create an array with the childrens of the given Node.
455
 
 *  
456
 
 * @param NodeXML n - the current node to unserialize.
457
 
 * @return Array with unserialized childrens.
458
 
 */
459
 
array xmlArray(Node n)
460
 
{
461
 
    array res = ({ });
462
 
    foreach ( n->children, Node children) {
463
 
        res += ({ unserialize(children) });
464
 
    }
465
 
    return res;
466
 
}
467
 
 
468
 
/**
469
 
 * Create an object from its XML representation, id or path tags are possible
470
 
 *  
471
 
 * @param NodeXML n - the node to unserialize
472
 
 * @return the steam object
473
 
 */
474
 
object xmlObject(Node n)
475
 
{
476
 
  object node;
477
 
 
478
 
  if ( !objectp(n) )
479
 
    return 0;
480
 
  
481
 
  node = n->get_node("id");
482
 
  if ( objectp(node) )
483
 
    return find_object((int)node->get_data());
484
 
  node = n->get_node("path");
485
 
  if ( objectp(node) )
486
 
    return find_object(node->get_data());
487
 
  
488
 
#if constant(_Persistence)
489
 
  node = n->get_node("group");
490
 
  if ( objectp(node) )
491
 
    return _Persistence->lookup_group(node->get_data());
492
 
  node = n->get_node("user");
493
 
  if ( objectp(node) )
494
 
    return _Persistence->lookup_user(node->get_data());
495
 
#endif
496
 
  return 0;
497
 
}
498
 
 
499
 
 
500
 
/**
501
 
 * Create some data structure from an XML Tree.
502
 
 *  
503
 
 * @param NodeXML n - the root-node of the XML Tree to unserialize.
504
 
 * @return some data structure describing the tree.
505
 
 */
506
 
mixed unserialize(Node n) 
507
 
{
508
 
    switch ( n->name ) {
509
 
    case "struct":
510
 
        return xmlMap(n);
511
 
        break;
512
 
    case "array":
513
 
        return xmlArray(n);
514
 
        break;
515
 
    case "int":
516
 
        return (int)n->data;
517
 
        break;
518
 
    case "float":
519
 
        return (float)n->data;
520
 
        break;
521
 
    case "string":
522
 
        return n->data;
523
 
        break;
524
 
    case "object":
525
 
      return xmlObject(n);
526
 
      break;
527
 
    }
528
 
    return -1;
529
 
}
530
 
 
531
 
Node parse(string|object html)
532
 
{
533
 
    object cb = saxHandler();
534
 
    
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()) }));
541
 
    
542
 
    Node root =  cb->get_root();
543
 
    return root->get_first_child();
544
 
}
545
 
 
546
 
void test()
547
 
{
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&#160;zzz</b><b><![CDATA[yyy&amp;zzz&nbsp;]]></b>"+
550
 
    "<b><![CDATA[<img src='test'>IMG</img>]]></b></xml>";
551
 
  Node a = parse(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());
562
 
  
563
 
 
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);
569
 
  cfg->join(cfg2);
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);
573
 
  cfg->join(cfg3);
574
 
  write("Another join, overwrite test for configuration edit 2, newtest2 expected !\n" + cfg->get_xml());
575
 
      
576
 
}