~ubuntu-branches/ubuntu/hardy/steam/hardy

« back to all changes in this revision

Viewing changes to server/libraries/Visconte.pmod/SemWeb.pmod/CustomNSTree.pmod

  • Committer: Bazaar Package Importer
  • Author(s): Alain Schroeder
  • Date: 2006-11-21 16:03:12 UTC
  • mfrom: (2.1.4 feisty)
  • Revision ID: james.westby@ubuntu.com-20061121160312-nf96y6nihzsyd2uv
Tags: 2.2.31-3
Add patch to prevent inconsistent data after shutdown.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#pike __REAL_VERSION__
 
2
 
 
3
//! A namespace aware version of Parser.XML.Tree. This implementation
 
4
//! does as little validation as possible, so e.g. you can call your
 
5
//! namespace xmlfoo without complaints.
 
6
 
 
7
inherit Parser.XML.Tree;
 
8
// import Parser.XML;
 
9
//! Namespace aware node.
 
10
class NSNode {
 
11
  inherit Node;
 
12
 
 
13
  // New stuff
 
14
 
 
15
  /* static */ string default_ns;
 
16
  static mapping(string:string) nss;
 
17
  static string element_ns;
 
18
  static mapping(string:mapping(string:string)) ns_attrs = ([]);
 
19
 
 
20
  //! Returns the namespace in which the current element is defined in.
 
21
  string get_ns() { return element_ns; }
 
22
 
 
23
  //! Returns the default namespace in the current scope.
 
24
  string get_default_ns() { return default_ns; }
 
25
 
 
26
  //! Returns a mapping with all the namespaces defined in the current
 
27
  //! scope, except the default namespace.
 
28
  //! @note
 
29
  //!   The returned mapping is the same as the one in the node, so
 
30
  //!   destructive changes will affect the node.
 
31
  mapping(string:string) get_defined_nss() {
 
32
    return nss;
 
33
  }
 
34
 
 
35
  //! @decl mapping(string:string) get_ns_attributes(string namespace)
 
36
  //! Returns the attributes in this node that is declared in the provided
 
37
  //! namespace.
 
38
 
 
39
  //! @decl mapping(string:mapping(string:string)) get_ns_attributes()
 
40
  //! Returns all the attributes in all namespaces that is associated with
 
41
  //! this node.
 
42
  //! @note
 
43
  //!   The returned mapping is the same as the one in the node, so
 
44
  //!   destructive changes will affect the node.
 
45
 
 
46
  mapping(string:string)|mapping(string:mapping(string:string))
 
47
    get_ns_attributes(void|string namespace) {
 
48
    if(namespace)
 
49
      return ns_attrs[namespace] || ([]);
 
50
    return ns_attrs;
 
51
  }
 
52
 
 
53
  //! Adds a new namespace to this node. The preferred symbol to
 
54
  //! use to identify the namespace can be provided in the @[symbol]
 
55
  //! argument. If @[chain] is set, no attempts to overwrite an
 
56
  //! already defined namespace with the same identifier will be made.
 
57
  void add_namespace(string ns, void|string symbol, void|int(0..1) chain) {
 
58
    if(!symbol)
 
59
      symbol = make_prefix(ns);
 
60
    if(chain && nss[symbol])
 
61
      return;
 
62
    if(nss[symbol]==ns)
 
63
      return;
 
64
    nss[symbol] = ns;
 
65
    get_children()->add_namespace(ns, symbol, 1);
 
66
  }
 
67
 
 
68
  //! Returns the difference between this nodes and its parents namespaces.
 
69
  mapping(string:string) diff_namespaces() {
 
70
    mapping res = ([]);
 
71
    if(!nss) return res;
 
72
    foreach(nss; string sym; string ns)
 
73
      if(nss[sym]!=ns)
 
74
        res[sym]=nss[sym];
 
75
  }
 
76
 
 
77
  static string make_prefix(string ns) {
 
78
    // FIXME: Cache?
 
79
    // return String.string2hex(Crypto.MD5->hash(ns))[..10];
 
80
    return (string) hash(ns);
 
81
  }
 
82
 
 
83
  //! Returns the element name as it occurs in xml files. E.g.
 
84
  //! "zonk:name" for the element "name" defined in a namespace
 
85
  //! denoted with "zonk". It will look up a symbol for the namespace
 
86
  //! in the symbol tables for the node and its parents. If none is
 
87
  //! found a new label will be generated by hashing the namespace.
 
88
  string get_xml_name() {
 
89
    if(element_ns==default_ns) return mTagName;
 
90
    string prefix = search(nss, element_ns);
 
91
    if(!prefix)
 
92
      prefix = make_prefix(element_ns);
 
93
    return prefix + ":" + mTagName;
 
94
  }
 
95
 
 
96
  // Override old stuff
 
97
 
 
98
  void create(int type, string name, mapping attr, string text,
 
99
              void|NSNode parent) {
 
100
 
 
101
    // Get the parent namespace context.
 
102
    if(parent) {
 
103
      parent->add_child(this);
 
104
      nss = parent->get_defined_nss();
 
105
      default_ns = parent->get_default_ns();
 
106
    }
 
107
    else
 
108
      nss = ([]);
 
109
 
 
110
    if(type == XML_TEXT || type == XML_COMMENT) {
 
111
      ::create(type, name, attr, text);
 
112
      return;
 
113
    }
 
114
 
 
115
    // First handle all xmlns attributes.
 
116
    foreach(attr; string name; string value) {
 
117
 
 
118
      // Update namespace scope. Must be done before analyzing the namespace
 
119
      // of the current element.
 
120
      string lcname = lower_case(name);
 
121
      if(lcname=="xmlns") {
 
122
        default_ns = value;
 
123
        m_delete(attr, name);
 
124
      }
 
125
      else if(has_prefix(lcname, "xmlns:")) {
 
126
        nss = nss + ([ name[6..]:value ]); // No destructive changes.
 
127
        continue;
 
128
      }
 
129
    }
 
130
 
 
131
    // Assign the right namespace to this element
 
132
    if(has_value(name, ":")) {
 
133
      sscanf(name, "%s:%s", element_ns, name);
 
134
      if(!nss[element_ns])
 
135
        error("Unknown namespace %s.\n", element_ns);
 
136
      element_ns = nss[element_ns];
 
137
    }
 
138
    else
 
139
      element_ns = default_ns;
 
140
 
 
141
    // Then take care of the rest of the attributes.
 
142
    foreach(attr; string name; string value) {
 
143
 
 
144
      if(has_prefix(lower_case(name), "xmlns"))
 
145
        continue;
 
146
 
 
147
      // Move namespaced attributes to a mapping of their own.
 
148
      string ns, m;
 
149
      if( sscanf(name, "%s:%s", ns, m)==2 ) {
 
150
        if(!nss[ns]) {
 
151
          if(ns=="xml")
 
152
            add_namespace("xml","xml");
 
153
          else
 
154
            error("Unknown namespace %s.\n", ns);
 
155
        }
 
156
        ns = nss[ns];
 
157
        m_delete(attr, name);
 
158
      } else {
 
159
        // FIXME: This makes the RDF-tests work,
 
160
        //        but is it according to the spec?
 
161
        ns = element_ns;
 
162
        m = name;
 
163
        if (ns_attrs[ns] && !zero_type(ns_attrs[ns][m])) {
 
164
          // We have an explicit entry already,
 
165
          // so skip the implicit entry.
 
166
          continue;
 
167
        }
 
168
      }
 
169
      if(!ns_attrs[ns])
 
170
        ns_attrs[ns] = ([]);
 
171
      ns_attrs[ns][m] = value;
 
172
    }
 
173
 
 
174
    ::create(type, name, attr, text);
 
175
  }
 
176
 
 
177
  void set_parent(NSNode parent) {
 
178
    nss = parent->get_defined_nss() + diff_namespaces();
 
179
    ::set_parent(parent);
 
180
  }
 
181
 
 
182
  NSNode add_child(NSNode c) {
 
183
    c->set_parent(this);
 
184
    return ::add_child(c);
 
185
  }
 
186
 
 
187
  //! @decl void remove_child(NSNode child)
 
188
  //! The remove_child is a not updated to take care of name
 
189
  //! space issues. To properly remove all the parents name spaces
 
190
  //! from the chid, call @[remove_node] in the child.
 
191
 
 
192
  void remove_node() {
 
193
    nss = diff_namespaces();
 
194
    ::remove_node();
 
195
  }
 
196
 
 
197
  NSNode clone(void|int(-1..1) direction) {
 
198
    Node n = NSNode(get_node_type(), get_ns()+":"+get_tag_name(),
 
199
                    get_attributes(), get_text(), mParent);
 
200
 
 
201
    if(direction!=1) {
 
202
      Node p = get_parent();
 
203
      if(p)
 
204
        n->set_parent( p->clone(-1) );
 
205
    }
 
206
 
 
207
    if(direction!=-1)
 
208
      foreach(get_children(), Node child)
 
209
        n->add_child( child->clone(1) );
 
210
 
 
211
    return n;
 
212
  }
 
213
 
 
214
  string render_xml()
 
215
  {
 
216
    String.Buffer data = String.Buffer();
 
217
        
 
218
    walk_preorder_2(
 
219
                    lambda(Node n) {
 
220
                      switch(n->get_node_type()) {
 
221
 
 
222
                      case XML_TEXT:
 
223
                        data->add( text_quote(n->get_text()) );
 
224
                        break;
 
225
 
 
226
                      case XML_ELEMENT:
 
227
                        if (!sizeof(n->get_tag_name()))
 
228
                          break;
 
229
 
 
230
                        data->add("<", n->get_xml_name());
 
231
 
 
232
                        if (mapping attr = n->get_attributes()) { // FIXME
 
233
                          foreach(indices(attr), string a)
 
234
                            data->add(" ", a, "='",
 
235
                                      attribute_quote(attr[a]), "'");
 
236
                        }
 
237
                        /*
 
238
                        mapping attr = n->get_ns_attrubutes();
 
239
                        if(sizeof(attr)) {
 
240
                          foreach(attr; string ns; mapping attr) {
 
241
                          }
 
242
                        }
 
243
                        */
 
244
                        if (n->count_children())
 
245
                          data->add(">");
 
246
                        else
 
247
                          data->add("/>");
 
248
                        break;
 
249
                      }
 
250
                    },
 
251
 
 
252
                    lambda(Node n) {
 
253
                      if (n->get_node_type() == XML_ELEMENT)
 
254
                        if (n->count_children())
 
255
                          if (sizeof(n->get_tag_name()))
 
256
                            data->add("</", n->get_xml_name(), ">");
 
257
                    });
 
258
        
 
259
    return (string)data;
 
260
  }
 
261
 
 
262
  string _sprintf(int t) {
 
263
    if(t=='O') {
 
264
      mapping nt = ([ XML_ROOT:"ROOT",
 
265
                      XML_ELEMENT:"ELEMENT",
 
266
                      XML_TEXT:"TEXT",
 
267
                      XML_HEADER:"HEADER",
 
268
                      XML_PI:"PI",
 
269
                      XML_COMMENT:"COMMENT",
 
270
                      XML_DOCTYPE:"DOCTYPE",
 
271
                      XML_ATTR:"ATTR" ]);
 
272
      string n = get_any_name();
 
273
      if(!n || !sizeof(n))
 
274
        return sprintf("%O(%s)", this_program,
 
275
                       nt[get_node_type()] || "UNKNOWN");
 
276
      return sprintf("%O(%s,%O,%O)", this_program,
 
277
                     nt[get_node_type()] || "UNKNOWN", n, get_ns());
 
278
    }
 
279
  }
 
280
}
 
281
 
 
282
static NSNode|int(0..0) parse_xml_callback(string type, string name,
 
283
                                           mapping attr, string|array contents,
 
284
                                           mixed location, mixed ...extra)
 
285
{
 
286
  NSNode node;
 
287
  NSNode parent = sizeof(extra[0]) && extra[0]->top();
 
288
 
 
289
  switch (type) {
 
290
  case "":
 
291
  case "<![CDATA[":
 
292
    //  Create text node
 
293
    return NSNode(XML_TEXT, "", 0, contents, parent);
 
294
 
 
295
  case "<!--":
 
296
    //  Create comment node
 
297
    return NSNode(XML_COMMENT, "", 0, contents, parent);
 
298
 
 
299
  case "<?xml":
 
300
    //  XML header tag
 
301
    return NSNode(XML_HEADER, "", attr, "", parent);
 
302
 
 
303
  case "<?":
 
304
    //  XML processing instruction
 
305
    return NSNode(XML_PI, name, attr, contents, parent);
 
306
 
 
307
  case "<>":
 
308
    //  Create new tag node.
 
309
    return NSNode(XML_ELEMENT, name, attr, "", parent);
 
310
 
 
311
  case "<":
 
312
    // Create tree node for this contaier.
 
313
    extra[0]->push( NSNode(XML_ELEMENT, name, attr, "", parent) );
 
314
    return 0;
 
315
 
 
316
  case ">":
 
317
    return extra[0]->pop();
 
318
        
 
319
    //  We need to merge consecutive text
 
320
    //  children since two text elements can't be neighbors according to
 
321
    //  the W3 spec.
 
322
    // FIXME: When does this happen?
 
323
 
 
324
  case "error":
 
325
    if (location && mappingp(location))
 
326
      error(contents + " [Offset: " + location->location + "]\n");
 
327
    else
 
328
      error(contents + "\n");
 
329
 
 
330
  case "<!DOCTYPE":
 
331
  default:
 
332
    return 0;
 
333
  }
 
334
}
 
335
 
 
336
//! Takes a XML string @[data] and produces a namespace node tree.
 
337
//! If @[default_ns] is given, it will be used as the default namespace.
 
338
//! @throws
 
339
//!   Throws an @[error] when an error is encountered during XML
 
340
//!   parsing.
 
341
NSNode parse_input(string data, void|string default_ns)
 
342
{
 
343
  object xp = spider.XML();
 
344
  Node mRoot;
 
345
  
 
346
  xp->allow_rxml_entities(1);
 
347
  
 
348
  // Construct tree from string
 
349
  mRoot = NSNode(XML_ROOT, "", ([ ]), "");
 
350
  mRoot->default_ns = default_ns;
 
351
  ADT.Stack s = ADT.Stack();
 
352
  s->push(mRoot);
 
353
  xp->parse(data, parse_xml_callback, s);
 
354
  return mRoot;
 
355
}
 
356
 
 
357
//! Makes a visualization of a node graph suitable for
 
358
//! printing out on a terminal.
 
359
//!
 
360
//! @example
 
361
//!   > object x = parse_input("<a><b><c/>d</b><b><e/><f>g</f></b></a>");
 
362
//!   > write(visualize(x));
 
363
//!   Node(ROOT)
 
364
//!     NSNode(ELEMENT,"a")
 
365
//!       NSNode(ELEMENT,"b")
 
366
//!         NSNode(ELEMENT,"c")
 
367
//!         NSNode(TEXT)
 
368
//!       NSNode(ELEMENT,"b")
 
369
//!         NSNode(ELEMENT,"e")
 
370
//!         NSNode(ELEMENT,"f")
 
371
//!           NSNode(TEXT)
 
372
//!   Result 1: 201
 
373
//!
 
374
string visualize(Node n, void|string indent) {
 
375
  if(!indent)
 
376
    return sprintf("%O\n", n) + visualize(n, "  ");
 
377
 
 
378
  string ret = "";
 
379
  foreach(n->get_children(), Node c) {
 
380
    ret += sprintf("%s%O\n", indent, c);
 
381
    ret += visualize(c, indent+"  ");
 
382
  }
 
383
  return ret;
 
384
}