nokogiri 1.5.0.beta.2 → 1.5.0.beta.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of nokogiri might be problematic. Click here for more details.

Files changed (107) hide show
  1. data/CHANGELOG.ja.rdoc +63 -0
  2. data/CHANGELOG.rdoc +44 -1
  3. data/Manifest.txt +3 -3
  4. data/README.ja.rdoc +4 -4
  5. data/README.rdoc +4 -4
  6. data/Rakefile +3 -0
  7. data/bin/nokogiri +6 -1
  8. data/ext/java/nokogiri/EncodingHandler.java +32 -0
  9. data/ext/java/nokogiri/HtmlDocument.java +36 -0
  10. data/ext/java/nokogiri/HtmlElementDescription.java +34 -0
  11. data/ext/java/nokogiri/HtmlEntityLookup.java +34 -0
  12. data/ext/java/nokogiri/HtmlSaxParserContext.java +41 -3
  13. data/ext/java/nokogiri/NokogiriService.java +109 -13
  14. data/ext/java/nokogiri/XmlAttr.java +40 -4
  15. data/ext/java/nokogiri/XmlAttributeDecl.java +32 -0
  16. data/ext/java/nokogiri/XmlCdata.java +41 -2
  17. data/ext/java/nokogiri/XmlComment.java +38 -1
  18. data/ext/java/nokogiri/XmlDocument.java +56 -11
  19. data/ext/java/nokogiri/XmlDocumentFragment.java +39 -30
  20. data/ext/java/nokogiri/XmlDtd.java +37 -0
  21. data/ext/java/nokogiri/XmlElement.java +51 -2
  22. data/ext/java/nokogiri/XmlElementContent.java +32 -0
  23. data/ext/java/nokogiri/XmlElementDecl.java +32 -0
  24. data/ext/java/nokogiri/XmlEntityDecl.java +32 -0
  25. data/ext/java/nokogiri/XmlEntityReference.java +35 -2
  26. data/ext/java/nokogiri/XmlNamespace.java +55 -5
  27. data/ext/java/nokogiri/XmlNode.java +129 -136
  28. data/ext/java/nokogiri/XmlNodeSet.java +36 -0
  29. data/ext/java/nokogiri/XmlProcessingInstruction.java +34 -1
  30. data/ext/java/nokogiri/XmlReader.java +36 -0
  31. data/ext/java/nokogiri/XmlRelaxng.java +34 -1
  32. data/ext/java/nokogiri/XmlSaxParserContext.java +52 -7
  33. data/ext/java/nokogiri/XmlSaxPushParser.java +36 -0
  34. data/ext/java/nokogiri/XmlSchema.java +34 -1
  35. data/ext/java/nokogiri/XmlSyntaxError.java +48 -18
  36. data/ext/java/nokogiri/XmlText.java +45 -6
  37. data/ext/java/nokogiri/XmlXpathContext.java +45 -0
  38. data/ext/java/nokogiri/XsltStylesheet.java +58 -3
  39. data/ext/java/nokogiri/internals/HtmlDomParserContext.java +50 -26
  40. data/ext/java/nokogiri/internals/NokogiriDocumentCache.java +35 -1
  41. data/ext/java/nokogiri/internals/NokogiriErrorHandler.java +51 -13
  42. data/ext/java/nokogiri/internals/NokogiriHandler.java +70 -21
  43. data/ext/java/nokogiri/internals/NokogiriHelpers.java +95 -38
  44. data/ext/java/nokogiri/internals/NokogiriNamespaceCache.java +37 -3
  45. data/ext/java/nokogiri/internals/NokogiriNamespaceContext.java +39 -1
  46. data/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler.java +43 -7
  47. data/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler4NekoHtml.java +77 -10
  48. data/ext/java/nokogiri/internals/NokogiriStrictErrorHandler.java +49 -20
  49. data/ext/java/nokogiri/internals/NokogiriXPathFunction.java +34 -2
  50. data/ext/java/nokogiri/internals/NokogiriXPathFunctionResolver.java +34 -1
  51. data/ext/java/nokogiri/internals/ParserContext.java +32 -0
  52. data/ext/java/nokogiri/internals/PushInputStream.java +33 -3
  53. data/ext/java/nokogiri/internals/ReaderNode.java +50 -8
  54. data/ext/java/nokogiri/internals/SaveContext.java +35 -2
  55. data/ext/java/nokogiri/internals/SchemaErrorHandler.java +34 -1
  56. data/ext/java/nokogiri/internals/XmlDeclHandler.java +32 -0
  57. data/ext/java/nokogiri/internals/XmlDomParser.java +32 -0
  58. data/ext/java/nokogiri/internals/XmlDomParserContext.java +43 -11
  59. data/ext/java/nokogiri/internals/XmlSaxParser.java +32 -0
  60. data/ext/java/nokogiri/internals/XsltExtensionFunction.java +72 -0
  61. data/ext/nokogiri/depend +358 -32
  62. data/ext/nokogiri/extconf.rb +1 -3
  63. data/ext/nokogiri/nokogiri.c +2 -0
  64. data/ext/nokogiri/nokogiri.h +7 -0
  65. data/ext/nokogiri/xml_dtd.c +2 -2
  66. data/ext/nokogiri/xml_io.c +2 -2
  67. data/ext/nokogiri/xml_node.c +31 -6
  68. data/ext/nokogiri/xml_node_set.c +1 -1
  69. data/ext/nokogiri/xml_sax_parser.c +1 -1
  70. data/ext/nokogiri/xml_sax_parser_context.c +40 -0
  71. data/ext/nokogiri/xml_xpath_context.c +33 -2
  72. data/ext/nokogiri/xslt_stylesheet.c +122 -6
  73. data/lib/nokogiri.rb +12 -5
  74. data/lib/nokogiri/css/generated_tokenizer.rb +1 -2
  75. data/lib/nokogiri/css/xpath_visitor.rb +15 -7
  76. data/lib/nokogiri/decorators/slop.rb +5 -3
  77. data/lib/nokogiri/html/document.rb +3 -3
  78. data/lib/nokogiri/html/document_fragment.rb +19 -17
  79. data/lib/nokogiri/version.rb +1 -1
  80. data/lib/nokogiri/xml/document.rb +26 -1
  81. data/lib/nokogiri/xml/document_fragment.rb +2 -2
  82. data/lib/nokogiri/xml/dtd.rb +11 -0
  83. data/lib/nokogiri/xml/node.rb +156 -45
  84. data/lib/nokogiri/xml/node_set.rb +2 -2
  85. data/lib/nokogiri/xml/reader.rb +36 -0
  86. data/lib/nokogiri/xml/sax/document.rb +4 -2
  87. data/lib/nokogiri/xslt.rb +9 -5
  88. data/lib/nokogiri/xslt/stylesheet.rb +1 -1
  89. data/tasks/cross_compile.rb +27 -8
  90. data/test/css/test_parser.rb +29 -18
  91. data/test/decorators/test_slop.rb +16 -0
  92. data/test/html/test_document_fragment.rb +46 -3
  93. data/test/html/test_node.rb +9 -0
  94. data/test/xml/sax/test_parser.rb +11 -3
  95. data/test/xml/sax/test_parser_context.rb +50 -0
  96. data/test/xml/sax/test_push_parser.rb +18 -1
  97. data/test/xml/test_document_fragment.rb +15 -8
  98. data/test/xml/test_dtd.rb +15 -0
  99. data/test/xml/test_node.rb +31 -2
  100. data/test/xml/test_node_reparenting.rb +59 -31
  101. data/test/xml/test_node_set.rb +13 -0
  102. data/test/xml/test_xpath.rb +32 -0
  103. data/test/xslt/test_custom_functions.rb +94 -0
  104. metadata +83 -81
  105. data/lib/nokogiri/nokogiri.jar +0 -0
  106. data/spec/helper.rb +0 -3
  107. data/spec/xml/reader_spec.rb +0 -307
@@ -91,14 +91,12 @@ def asplode(lib)
91
91
  abort "-----\n#{lib} is missing. please visit http://nokogiri.org/tutorials/installing_nokogiri.html for help with installing dependencies.\n-----"
92
92
  end
93
93
 
94
- # Use this with cross compiling
95
- # PKG_CONFIG_PATH=/Users/apatterson/git/nokogiri/tmp/cross/lib/pkgconfig/ \
96
- # rake cross compile RUBY_CC_VERSION=1.9.1
97
94
  pkg_config('libxslt') if RUBY_PLATFORM =~ /mingw/
98
95
 
99
96
  asplode "libxml2" unless find_header('libxml/parser.h')
100
97
  asplode "libxslt" unless find_header('libxslt/xslt.h')
101
98
  asplode "libexslt" unless find_header('libexslt/exslt.h')
99
+ asplode "libiconv" unless have_func('iconv_open', 'iconv.h') or have_library('iconv', 'iconv_open', 'iconv.h')
102
100
  asplode "libxml2" unless find_library("#{lib_prefix}xml2", 'xmlParseDoc')
103
101
  asplode "libxslt" unless find_library("#{lib_prefix}xslt", 'xsltParseStylesheetDoc')
104
102
  asplode "libexslt" unless find_library("#{lib_prefix}exslt", 'exsltFuncRegister')
@@ -25,12 +25,14 @@ int vasprintf (char **strp, const char *fmt, va_list ap)
25
25
 
26
26
  void Init_nokogiri()
27
27
  {
28
+ #ifndef __MACRUBY__
28
29
  xmlMemSetup(
29
30
  (xmlFreeFunc)ruby_xfree,
30
31
  (xmlMallocFunc)ruby_xmalloc,
31
32
  (xmlReallocFunc)ruby_xrealloc,
32
33
  strdup
33
34
  );
35
+ #endif
34
36
 
35
37
  mNokogiri = rb_define_module("Nokogiri");
36
38
  mNokogiriXml = rb_define_module_under(mNokogiri, "XML");
@@ -27,6 +27,7 @@ int vasprintf (char **strp, const char *fmt, va_list ap);
27
27
  #include <libxml/HTMLparser.h>
28
28
  #include <libxml/HTMLtree.h>
29
29
  #include <libxml/relaxng.h>
30
+ #include <libxslt/extensions.h>
30
31
  #include <ruby.h>
31
32
 
32
33
  #ifdef HAVE_RUBY_ENCODING_H
@@ -132,6 +133,8 @@ extern VALUE mNokogiriXslt ;
132
133
  #define NOKOGIRI_DEBUG_START(p)
133
134
  #define NOKOGIRI_DEBUG_END(p)
134
135
 
136
+ #endif
137
+
135
138
  #ifndef RSTRING_PTR
136
139
  #define RSTRING_PTR(s) (RSTRING(s)->ptr)
137
140
  #endif
@@ -148,6 +151,10 @@ extern VALUE mNokogiriXslt ;
148
151
  #define RARRAY_LEN(a) RARRAY(a)->len
149
152
  #endif
150
153
 
154
+ #ifndef __builtin_expect
155
+ # if defined(__GNUC__)
156
+ # define __builtin_expect(expr, c) __builtin_expect((long)(expr), (long)(c))
157
+ # endif
151
158
  #endif
152
159
 
153
160
  #endif
@@ -84,10 +84,10 @@ static VALUE attributes(VALUE self)
84
84
 
85
85
  Data_Get_Struct(self, xmlDtd, dtd);
86
86
 
87
- if(!dtd->attributes) return Qnil;
88
-
89
87
  hash = rb_hash_new();
90
88
 
89
+ if(!dtd->attributes) return hash;
90
+
91
91
  xmlHashScan((xmlHashTablePtr)dtd->attributes, element_copier, (void *)hash);
92
92
 
93
93
  return hash;
@@ -8,14 +8,14 @@ int io_read_callback(void * ctx, char * buffer, int len) {
8
8
 
9
9
  if(NIL_P(string)) return 0;
10
10
 
11
- memcpy(buffer, StringValuePtr(string), (unsigned int)RSTRING_LEN(string));
11
+ memcpy(buffer, StringValuePtr(string), (size_t)RSTRING_LEN(string));
12
12
 
13
13
  return (int)RSTRING_LEN(string);
14
14
  }
15
15
 
16
16
  int io_write_callback(void * ctx, char * buffer, int len) {
17
17
  VALUE io = (VALUE)ctx;
18
- VALUE string = rb_str_new(buffer, len);
18
+ VALUE string = rb_str_new(buffer, (long)len);
19
19
 
20
20
  rb_funcall(io, id_write, 1, string);
21
21
  return len;
@@ -245,7 +245,13 @@ static VALUE encode_special_chars(VALUE self, VALUE string)
245
245
  * call-seq:
246
246
  * create_internal_subset(name, external_id, system_id)
247
247
  *
248
- * Create an internal subset
248
+ * Create the internal subset of a document.
249
+ *
250
+ * doc.create_internal_subset("chapter", "-//OASIS//DTD DocBook XML//EN", "chapter.dtd")
251
+ * # => <!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML//EN" "chapter.dtd">
252
+ *
253
+ * doc.create_internal_subset("chapter", nil, "chapter.dtd")
254
+ * # => <!DOCTYPE chapter SYSTEM "chapter.dtd">
249
255
  */
250
256
  static VALUE create_internal_subset(VALUE self, VALUE name, VALUE external_id, VALUE system_id)
251
257
  {
@@ -371,6 +377,8 @@ static VALUE duplicate_node(int argc, VALUE *argv, VALUE self)
371
377
  dup = xmlDocCopyNode(node, node->doc, (int)NUM2INT(level));
372
378
  if(dup == NULL) return Qnil;
373
379
 
380
+ NOKOGIRI_ROOT_NODE(dup);
381
+
374
382
  return Nokogiri_wrap_xml_node(rb_obj_class(self), dup);
375
383
  }
376
384
 
@@ -768,7 +776,8 @@ static VALUE attribute_nodes(VALUE self)
768
776
  * call-seq:
769
777
  * namespace()
770
778
  *
771
- * returns the Nokogiri::XML::Namespace for the node, if one exists.
779
+ * returns the default namespace set on this node (as with an "xmlns="
780
+ * attribute), as a Namespace object.
772
781
  */
773
782
  static VALUE namespace(VALUE self)
774
783
  {
@@ -785,7 +794,7 @@ static VALUE namespace(VALUE self)
785
794
  * call-seq:
786
795
  * namespace_definitions()
787
796
  *
788
- * returns a list of Namespace nodes defined on _self_
797
+ * returns namespaces defined on self element directly, as an array of Namespace objects. Includes both a default namespace (as in"xmlns="), and prefixed namespaces (as in "xmlns:prefix=").
789
798
  */
790
799
  static VALUE namespace_definitions(VALUE self)
791
800
  {
@@ -814,8 +823,10 @@ static VALUE namespace_definitions(VALUE self)
814
823
  * call-seq:
815
824
  * namespace_scopes()
816
825
  *
817
- * returns a list of Namespace nodes in scope for _self_. this is all
818
- * namespaces defined in the node, or in any ancestor node.
826
+ * returns namespaces in scope for self -- those defined on self element
827
+ * directly or any ancestor node -- as an array of Namespace objects. Default
828
+ * namespaces ("xmlns=" style) for self are included in this array; Default
829
+ * namespaces for ancestors, however, are not. See also #namespaces
819
830
  */
820
831
  static VALUE namespace_scopes(VALUE self)
821
832
  {
@@ -1039,7 +1050,12 @@ static VALUE line(VALUE self)
1039
1050
  * call-seq:
1040
1051
  * add_namespace_definition(prefix, href)
1041
1052
  *
1042
- * Adds a namespace definition with +prefix+ using +href+
1053
+ * Adds a namespace definition with +prefix+ using +href+ value. The result is
1054
+ * as if parsed XML for this node had included an attribute
1055
+ * 'xmlns:prefix=value'. A default namespace for this node ("xmlns=") can be
1056
+ * added by passing 'nil' for prefix. Namespaces added this way will not
1057
+ * show up in #attributes, but they will be included as an xmlns attribute
1058
+ * when the node is serialized to XML.
1043
1059
  */
1044
1060
  static VALUE add_namespace_definition(VALUE self, VALUE prefix, VALUE href)
1045
1061
  {
@@ -1149,6 +1165,7 @@ static VALUE in_context(VALUE self, VALUE _str, VALUE _options)
1149
1165
  {
1150
1166
  xmlNodePtr node;
1151
1167
  xmlNodePtr list;
1168
+ xmlNodePtr child_iter;
1152
1169
  xmlNodeSetPtr set;
1153
1170
  xmlParserErrors error;
1154
1171
  VALUE doc, err;
@@ -1174,6 +1191,14 @@ static VALUE in_context(VALUE self, VALUE _str, VALUE _options)
1174
1191
  (int)NUM2INT(_options),
1175
1192
  &list);
1176
1193
 
1194
+ /* make sure parent/child pointers are coherent so an unlink will work properly (#331) */
1195
+ child_iter = node->doc->children ;
1196
+ while (child_iter) {
1197
+ if (child_iter->parent != (xmlNodePtr)node->doc)
1198
+ child_iter->parent = (xmlNodePtr)node->doc ;
1199
+ child_iter = child_iter->next ;
1200
+ }
1201
+
1177
1202
  #ifndef HTML_PARSE_NOIMPLIED
1178
1203
  htmlHandleOmittedElem(1);
1179
1204
  #endif
@@ -257,7 +257,7 @@ static VALUE slice(int argc, VALUE *argv, VALUE self)
257
257
  }
258
258
 
259
259
  /* if arg is Range */
260
- switch (rb_range_beg_len(arg, &beg, &len, node_set->nodeNr, 0)) {
260
+ switch (rb_range_beg_len(arg, &beg, &len, (long)node_set->nodeNr, 0)) {
261
261
  case Qfalse:
262
262
  break;
263
263
  case Qnil:
@@ -238,7 +238,7 @@ static void deallocate(xmlSAXHandlerPtr handler)
238
238
 
239
239
  static VALUE allocate(VALUE klass)
240
240
  {
241
- xmlSAXHandlerPtr handler = calloc(1, sizeof(xmlSAXHandler));
241
+ xmlSAXHandlerPtr handler = calloc((size_t)1, sizeof(xmlSAXHandler));
242
242
 
243
243
  xmlSetStructuredErrorFunc(NULL, NULL);
244
244
 
@@ -140,6 +140,44 @@ static VALUE get_replace_entities(VALUE self)
140
140
  return Qtrue;
141
141
  }
142
142
 
143
+ /*
144
+ * call-seq: line
145
+ *
146
+ * Get the current line the parser context is processing.
147
+ */
148
+ static VALUE line(VALUE self)
149
+ {
150
+ xmlParserCtxtPtr ctxt;
151
+ xmlParserInputPtr io;
152
+
153
+ Data_Get_Struct(self, xmlParserCtxt, ctxt);
154
+
155
+ io = ctxt->input;
156
+ if(io)
157
+ return INT2NUM(io->line);
158
+
159
+ return Qnil;
160
+ }
161
+
162
+ /*
163
+ * call-seq: column
164
+ *
165
+ * Get the current column the parser context is processing.
166
+ */
167
+ static VALUE column(VALUE self)
168
+ {
169
+ xmlParserCtxtPtr ctxt;
170
+ xmlParserInputPtr io;
171
+
172
+ Data_Get_Struct(self, xmlParserCtxt, ctxt);
173
+
174
+ io = ctxt->input;
175
+ if(io)
176
+ return INT2NUM(io->col);
177
+
178
+ return Qnil;
179
+ }
180
+
143
181
  void init_xml_sax_parser_context()
144
182
  {
145
183
  VALUE nokogiri = rb_define_module("Nokogiri");
@@ -156,4 +194,6 @@ void init_xml_sax_parser_context()
156
194
  rb_define_method(klass, "parse_with", parse_with, 1);
157
195
  rb_define_method(klass, "replace_entities=", set_replace_entities, 1);
158
196
  rb_define_method(klass, "replace_entities", get_replace_entities, 0);
197
+ rb_define_method(klass, "line", line, 0);
198
+ rb_define_method(klass, "column", column, 0);
159
199
  }
@@ -27,6 +27,28 @@ static VALUE register_ns(VALUE self, VALUE prefix, VALUE uri)
27
27
  return self;
28
28
  }
29
29
 
30
+ /*
31
+ * call-seq:
32
+ * register_variable(name, value)
33
+ *
34
+ * Register the variable +name+ with +value+.
35
+ */
36
+ static VALUE register_variable(VALUE self, VALUE name, VALUE value)
37
+ {
38
+ xmlXPathContextPtr ctx;
39
+ xmlXPathObjectPtr xmlValue;
40
+ Data_Get_Struct(self, xmlXPathContext, ctx);
41
+
42
+ xmlValue = xmlXPathNewCString(StringValuePtr(value));
43
+
44
+ xmlXPathRegisterVariable( ctx,
45
+ (const xmlChar *)StringValuePtr(name),
46
+ xmlValue
47
+ );
48
+
49
+ return self;
50
+ }
51
+
30
52
  static void ruby_funcall(xmlXPathParserContextPtr ctx, int nargs)
31
53
  {
32
54
  VALUE xpath_handler = Qnil;
@@ -46,7 +68,11 @@ static void ruby_funcall(xmlXPathParserContextPtr ctx, int nargs)
46
68
 
47
69
  xpath_handler = (VALUE)(ctx->context->userData);
48
70
 
49
- argv = (VALUE *)calloc((unsigned int)nargs, sizeof(VALUE));
71
+ argv = (VALUE *)calloc((size_t)nargs, sizeof(VALUE));
72
+ for (i = 0 ; i < nargs ; ++i) {
73
+ rb_gc_register_address(&argv[i]);
74
+ }
75
+
50
76
  doc = DOC_RUBY_OBJECT(ctx->context->doc);
51
77
 
52
78
  i = nargs - 1;
@@ -77,6 +103,10 @@ static void ruby_funcall(xmlXPathParserContextPtr ctx, int nargs)
77
103
  nargs,
78
104
  argv
79
105
  );
106
+
107
+ for (i = 0 ; i < nargs ; ++i) {
108
+ rb_gc_unregister_address(&argv[i]);
109
+ }
80
110
  free(argv);
81
111
 
82
112
  switch(TYPE(result)) {
@@ -156,7 +186,7 @@ static void xpath_generic_exception_handler(void * ctx, const char *msg, ...)
156
186
 
157
187
  /*
158
188
  * call-seq:
159
- * evaluate(search_path)
189
+ * evaluate(search_path, handler = nil)
160
190
  *
161
191
  * Evaluate the +search_path+ returning an XML::XPath object.
162
192
  */
@@ -274,5 +304,6 @@ void init_xml_xpath_context(void)
274
304
 
275
305
  rb_define_singleton_method(klass, "new", new, 1);
276
306
  rb_define_method(klass, "evaluate", evaluate, -1);
307
+ rb_define_method(klass, "register_variable", register_variable, 2);
277
308
  rb_define_method(klass, "register_ns", register_ns, 2);
278
309
  }
@@ -5,6 +5,8 @@
5
5
  #include <libxslt/transform.h>
6
6
  #include <libexslt/exslt.h>
7
7
 
8
+ VALUE xslt;
9
+
8
10
  int vasprintf (char **strp, const char *fmt, va_list ap);
9
11
 
10
12
  static void dealloc(xsltStylesheetPtr doc)
@@ -24,7 +26,7 @@ static void xslt_generic_error_handler(void * ctx, const char *msg, ...)
24
26
  vasprintf(&message, msg, args);
25
27
  va_end(args);
26
28
 
27
- rb_raise(rb_eRuntimeError, message);
29
+ rb_exc_raise(rb_exc_new2(rb_eRuntimeError, message));
28
30
  }
29
31
 
30
32
  /*
@@ -97,7 +99,7 @@ static VALUE transform(int argc, VALUE* argv, VALUE self)
97
99
  long param_len, j ;
98
100
 
99
101
  rb_scan_args(argc, argv, "11", &xmldoc, &paramobj);
100
- if (NIL_P(paramobj)) { paramobj = rb_ary_new2(0) ; }
102
+ if (NIL_P(paramobj)) { paramobj = rb_ary_new2(0L) ; }
101
103
 
102
104
  /* handle hashes as arguments. */
103
105
  if(T_HASH == TYPE(paramobj)) {
@@ -124,19 +126,133 @@ static VALUE transform(int argc, VALUE* argv, VALUE self)
124
126
 
125
127
  if (!result) rb_raise(rb_eRuntimeError, "could not perform xslt transform on document");
126
128
 
127
- return Nokogiri_wrap_xml_document(0, result) ;
129
+ return Nokogiri_wrap_xml_document((VALUE)0, result) ;
130
+ }
131
+
132
+ static void method_caller(xmlXPathParserContextPtr ctxt, int nargs)
133
+ {
134
+ const xmlChar * function;
135
+ const xmlChar * functionURI;
136
+ size_t i, count;
137
+
138
+ xsltTransformContextPtr transform;
139
+ xmlXPathObjectPtr xpath;
140
+ VALUE obj;
141
+ VALUE *args;
142
+ VALUE result;
143
+
144
+ transform = xsltXPathGetTransformContext(ctxt);
145
+
146
+ function = ctxt->context->function;
147
+ functionURI = ctxt->context->functionURI;
148
+ obj = (VALUE)xsltGetExtData(transform, functionURI);
149
+
150
+ count = (size_t)ctxt->valueNr;
151
+ args = calloc(count, sizeof(VALUE *));
152
+
153
+ for(i = 0; i < count; i++) {
154
+ VALUE thing;
155
+
156
+ xpath = valuePop(ctxt);
157
+ switch(xpath->type) {
158
+ case XPATH_STRING:
159
+ thing = NOKOGIRI_STR_NEW2(xpath->stringval);
160
+ break;
161
+ case XPATH_NODESET:
162
+ if(NULL == xpath->nodesetval) {
163
+ thing = Nokogiri_wrap_xml_node_set(
164
+ xmlXPathNodeSetCreate(NULL),
165
+ DOC_RUBY_OBJECT(ctxt->context->doc));
166
+ } else {
167
+ thing = Nokogiri_wrap_xml_node_set(xpath->nodesetval,
168
+ DOC_RUBY_OBJECT(ctxt->context->doc));
169
+ }
170
+ break;
171
+ default:
172
+ rb_raise(rb_eRuntimeError, "do not handle type: %d", xpath->type);
173
+ }
174
+ args[i] = thing;
175
+ }
176
+ result = rb_funcall3(obj, rb_intern((const char *)function), (int)count, args);
177
+ switch(TYPE(result)) {
178
+ case T_FLOAT:
179
+ case T_BIGNUM:
180
+ case T_FIXNUM:
181
+ xmlXPathReturnNumber(ctxt, NUM2DBL(result));
182
+ break;
183
+ case T_STRING:
184
+ xmlXPathReturnString(
185
+ ctxt,
186
+ xmlStrdup((xmlChar *)StringValuePtr(result))
187
+ );
188
+ break;
189
+ case T_TRUE:
190
+ xmlXPathReturnTrue(ctxt);
191
+ break;
192
+ case T_FALSE:
193
+ xmlXPathReturnFalse(ctxt);
194
+ break;
195
+ case T_NIL:
196
+ break;
197
+ default:
198
+ rb_raise(rb_eRuntimeError, "Invalid return type");
199
+ }
200
+ }
201
+
202
+ static void * initFunc(xsltTransformContextPtr ctxt, const xmlChar *uri)
203
+ {
204
+ VALUE modules = rb_iv_get(xslt, "@modules");
205
+ VALUE obj = rb_hash_aref(modules, rb_str_new2((const char *)uri));
206
+ VALUE args = { Qfalse };
207
+ VALUE methods = rb_funcall(obj, rb_intern("instance_methods"), 1, args);
208
+ int i;
209
+
210
+ for(i = 0; i < RARRAY_LEN(methods); i++) {
211
+ VALUE method_name = rb_obj_as_string(RARRAY_PTR(methods)[i]);
212
+ xsltRegisterExtFunction(ctxt,
213
+ (unsigned char *)StringValuePtr(method_name), uri, method_caller);
214
+ }
215
+
216
+ return (void *)rb_class_new_instance(0, NULL, obj);
217
+ }
218
+
219
+ static void shutdownFunc(xsltTransformContextPtr ctxt,
220
+ const xmlChar *uri, void *data)
221
+ {
222
+ }
223
+
224
+ /*
225
+ * call-seq:
226
+ * register(uri, custom_handler_class)
227
+ *
228
+ * Register a class that implements custom XLST transformation functions.
229
+ */
230
+ static VALUE registr(VALUE self, VALUE uri, VALUE obj)
231
+ {
232
+ VALUE modules = rb_iv_get(self, "@modules");
233
+ if(NIL_P(modules)) rb_raise(rb_eRuntimeError, "wtf! @modules isn't set");
234
+
235
+ rb_hash_aset(modules, uri, obj);
236
+ xsltRegisterExtModule((unsigned char *)StringValuePtr(uri), initFunc, shutdownFunc);
237
+ return self;
128
238
  }
129
239
 
130
240
  VALUE cNokogiriXsltStylesheet ;
131
241
  void init_xslt_stylesheet()
132
242
  {
133
- VALUE nokogiri = rb_define_module("Nokogiri");
134
- VALUE xslt = rb_define_module_under(nokogiri, "XSLT");
135
- VALUE klass = rb_define_class_under(xslt, "Stylesheet", rb_cObject);
243
+ VALUE nokogiri;
244
+ VALUE klass;
245
+
246
+ nokogiri = rb_define_module("Nokogiri");
247
+ xslt = rb_define_module_under(nokogiri, "XSLT");
248
+ klass = rb_define_class_under(xslt, "Stylesheet", rb_cObject);
249
+
250
+ rb_iv_set(xslt, "@modules", rb_hash_new());
136
251
 
137
252
  cNokogiriXsltStylesheet = klass;
138
253
 
139
254
  rb_define_singleton_method(klass, "parse_stylesheet_doc", parse_stylesheet_doc, 1);
255
+ rb_define_singleton_method(xslt, "register", registr, 2);
140
256
  rb_define_method(klass, "serialize", serialize, 1);
141
257
  rb_define_method(klass, "transform", transform, -1);
142
258
  }