File: ruby_xml_xpath.c

package info (click to toggle)
ruby-libxml 2.3.2-1
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 1,812 kB
  • sloc: xml: 9,628; ruby: 7,119; ansic: 6,665; makefile: 2
file content (187 lines) | stat: -rw-r--r-- 6,021 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/*
 * Document-class: LibXML::XML::XPath
 *
 * The XML::XPath module is used to query XML documents. It is
 * usually accessed via the XML::Document#find or
 * XML::Node#find methods.  For example:
 *
 *  document.find('/foo', namespaces) -> XML::XPath::Object
 *
 * The optional namespaces parameter can be a string, array or
 * hash table.
 *
 *   document.find('/foo', 'xlink:http://www.w3.org/1999/xlink')
 *   document.find('/foo', ['xlink:http://www.w3.org/1999/xlink',
 *                          'xi:http://www.w3.org/2001/XInclude')
 *   document.find('/foo', 'xlink' => 'http://www.w3.org/1999/xlink',
 *                             'xi' => 'http://www.w3.org/2001/XInclude')
 *
 *
 * === Working With Default Namespaces
 *
 * Finding namespaced elements and attributes can be tricky.
 * Lets work through an example of a document with a default
 * namespace:
 *
 *  <?xml version="1.0" encoding="utf-8"?>
 *  <feed xmlns="http://www.w3.org/2005/Atom">
 *    <title type="text">Phil Bogle's Contacts</title>
 *  </feed>
 *
 * To find nodes you must define the atom namespace for
 * libxml.  One way to do this is:
 *
 *   node = doc.find('atom:title', 'atom:http://www.w3.org/2005/Atom')
 *
 * Alternatively, you can register the default namespace like this:
 *
 *   doc.root.namespaces.default_prefix = 'atom'
 *   node = doc.find('atom:title')
 *
 * === More Complex Namespace Examples
 *
 * Lets work through some more complex examples using the
 * following xml document:
 *
 *  <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
 *    <soap:Body>
 *      <getManufacturerNamesResponse xmlns="http://services.somewhere.com">
 *        <IDAndNameList xmlns="http://services.somewhere.com">
 *          <ns1:IdAndName xmlns:ns1="http://domain.somewhere.com"/>
 *        </IDAndNameList>
 *      </getManufacturerNamesResponse>
 *  </soap:Envelope>
 *
 *  # Since the soap namespace is defined on the root
 *  # node we can directly use it.
 *  doc.find('/soap:Envelope')
 *
 *  # Since the ns1 namespace is not defined on the root node
 *  # we have to first register it with the xpath engine.
 *  doc.find('//ns1:IdAndName',
 *           'ns1:http://domain.somewhere.com')
 *
 *  # Since the getManufacturerNamesResponse element uses a default
 *  # namespace we first have to give it a prefix and register
 *  # it with the xpath engine.
 *  doc.find('//ns:getManufacturerNamesResponse',
 *            'ns:http://services.somewhere.com')
 *
 *  # Here is an example showing a complex namespace aware
 *  # xpath expression.
 *  doc.find('/soap:Envelope/soap:Body/ns0:getManufacturerNamesResponse/ns0:IDAndNameList/ns1:IdAndName',
 *  ['ns0:http://services.somewhere.com', 'ns1:http://domain.somewhere.com'])
*/


#include "ruby_libxml.h"

VALUE mXPath;

VALUE
rxml_xpath_to_value(xmlXPathContextPtr xctxt, xmlXPathObjectPtr xobject) {
  VALUE result;
  int type;

  if (xobject == NULL) {
    /* xmlLastError is different than xctxt->lastError.  Use
     xmlLastError since it has the message set while xctxt->lastError
     does not. */
    xmlErrorPtr xerror = xmlGetLastError();
    rxml_raise(xerror);
  }

  switch (type = xobject->type) {
    case XPATH_NODESET:
      result = rxml_xpath_object_wrap(xctxt->doc, xobject);
      break;
    case XPATH_BOOLEAN:
      result = (xobject->boolval != 0) ? Qtrue : Qfalse;
      xmlXPathFreeObject(xobject);
      break;
    case XPATH_NUMBER:
      result = rb_float_new(xobject->floatval);
      xmlXPathFreeObject(xobject);
      break;
    case XPATH_STRING:
      result = rxml_new_cstr((const char*)xobject->stringval, xctxt->doc->encoding);
      xmlXPathFreeObject(xobject);
      break;
    default:
      xmlXPathFreeObject(xobject);
      rb_raise(rb_eTypeError,
        "can't convert XPath object of type %d to Ruby value", type
      );
  }

  return result;
}

xmlXPathObjectPtr
rxml_xpath_from_value(VALUE value) {
  xmlXPathObjectPtr result = NULL;

  switch (TYPE(value)) {
    case T_TRUE:
    case T_FALSE:
      result = xmlXPathNewBoolean(RTEST(value));
      break;
    case T_FIXNUM:
    case T_FLOAT:
      result = xmlXPathNewFloat(NUM2DBL(value));
      break;
    case T_STRING:
      result = xmlXPathWrapString(xmlStrdup((const xmlChar *)StringValuePtr(value)));
      break;
    case T_NIL:
      result = xmlXPathNewNodeSet(NULL);
      break;
    case T_ARRAY: {
      int i, j;
      result = xmlXPathNewNodeSet(NULL);

      for (i = RARRAY_LEN(value); i > 0; i--) {
        xmlXPathObjectPtr obj = rxml_xpath_from_value(rb_ary_shift(value));

        if ((obj->nodesetval != NULL) && (obj->nodesetval->nodeNr != 0)) {
          for (j = 0; j < obj->nodesetval->nodeNr; j++) {
            xmlXPathNodeSetAdd(result->nodesetval, obj->nodesetval->nodeTab[j]);
          }
        }
      }
      break;
    }
    default:
      rb_raise(rb_eTypeError,
        "can't convert object of type %s to XPath object", rb_obj_classname(value)
      );
  }

  return result;
}

void rxml_init_xpath(void)
{
  mXPath = rb_define_module_under(mXML, "XPath");

  /* 0: Undefined value. */
  rb_define_const(mXPath, "UNDEFINED", INT2NUM(XPATH_UNDEFINED));
  /* 1: A nodeset, will be wrapped by XPath Object. */
  rb_define_const(mXPath, "NODESET", INT2NUM(XPATH_NODESET));
  /* 2: A boolean value. */
  rb_define_const(mXPath, "BOOLEAN", INT2NUM(XPATH_BOOLEAN));
  /* 3: A numeric value. */
  rb_define_const(mXPath, "NUMBER", INT2NUM(XPATH_NUMBER));
  /* 4: A string value. */
  rb_define_const(mXPath, "STRING", INT2NUM(XPATH_STRING));
  /* 5: An xpointer point */
  rb_define_const(mXPath, "POINT", INT2NUM(XPATH_POINT));
  /* 6: An xpointer range */
  rb_define_const(mXPath, "RANGE", INT2NUM(XPATH_RANGE));
  /* 7: An xpointer location set */
  rb_define_const(mXPath, "LOCATIONSET", INT2NUM(XPATH_LOCATIONSET));
  /* 8: XPath user type */
  rb_define_const(mXPath, "USERS", INT2NUM(XPATH_USERS));
  /* 9: An XSLT value tree, non modifiable */
  rb_define_const(mXPath, "XSLT_TREE", INT2NUM(XPATH_XSLT_TREE));
}