nokogiri 1.11.0.rc3-java → 1.11.0.rc4-java

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE-DEPENDENCIES.md +1015 -947
  3. data/README.md +1 -1
  4. data/ext/java/nokogiri/HtmlSaxParserContext.java +1 -1
  5. data/ext/java/nokogiri/HtmlSaxPushParser.java +1 -1
  6. data/ext/java/nokogiri/XmlNode.java +4 -6
  7. data/ext/java/nokogiri/XmlReader.java +2 -2
  8. data/ext/java/nokogiri/XmlRelaxng.java +10 -3
  9. data/ext/java/nokogiri/XmlSaxParserContext.java +8 -3
  10. data/ext/java/nokogiri/XmlSchema.java +84 -24
  11. data/ext/java/nokogiri/XmlSyntaxError.java +2 -6
  12. data/ext/java/nokogiri/XmlXpathContext.java +48 -18
  13. data/ext/java/nokogiri/internals/HtmlDomParserContext.java +51 -39
  14. data/ext/java/nokogiri/internals/NokogiriHandler.java +1 -1
  15. data/ext/java/nokogiri/internals/NokogiriNamespaceContext.java +9 -2
  16. data/ext/java/nokogiri/internals/NokogiriXPathFunction.java +62 -6
  17. data/ext/java/nokogiri/internals/NokogiriXPathFunctionResolver.java +4 -2
  18. data/ext/java/nokogiri/internals/XmlDomParserContext.java +2 -2
  19. data/ext/nokogiri/depend +476 -357
  20. data/ext/nokogiri/extconf.rb +441 -321
  21. data/ext/nokogiri/html_document.c +79 -78
  22. data/ext/nokogiri/html_sax_parser_context.c +2 -2
  23. data/ext/nokogiri/nokogiri.c +34 -46
  24. data/ext/nokogiri/xml_document.c +2 -2
  25. data/ext/nokogiri/xml_node.c +1 -1
  26. data/ext/nokogiri/xml_node_set.c +1 -1
  27. data/ext/nokogiri/xml_relax_ng.c +29 -11
  28. data/ext/nokogiri/xml_sax_parser.c +2 -7
  29. data/ext/nokogiri/xml_sax_parser_context.c +2 -2
  30. data/ext/nokogiri/xml_schema.c +55 -13
  31. data/ext/nokogiri/xml_xpath_context.c +80 -4
  32. data/ext/nokogiri/xslt_stylesheet.c +1 -4
  33. data/lib/nokogiri.rb +1 -1
  34. data/lib/nokogiri/css/parser.rb +3 -3
  35. data/lib/nokogiri/css/parser.y +2 -2
  36. data/lib/nokogiri/css/xpath_visitor.rb +70 -42
  37. data/lib/nokogiri/html/document.rb +12 -26
  38. data/lib/nokogiri/nokogiri.jar +0 -0
  39. data/lib/nokogiri/version.rb +2 -149
  40. data/lib/nokogiri/version/constant.rb +5 -0
  41. data/lib/nokogiri/version/info.rb +182 -0
  42. data/lib/nokogiri/xml/document.rb +17 -7
  43. data/lib/nokogiri/xml/document_fragment.rb +4 -6
  44. data/lib/nokogiri/xml/node.rb +50 -27
  45. data/lib/nokogiri/xml/parse_options.rb +6 -0
  46. data/lib/nokogiri/xml/relax_ng.rb +6 -2
  47. data/lib/nokogiri/xml/schema.rb +12 -4
  48. data/lib/nokogiri/xml/searchable.rb +3 -1
  49. metadata +47 -73
  50. data/ext/nokogiri/html_document.h +0 -10
  51. data/ext/nokogiri/html_element_description.h +0 -10
  52. data/ext/nokogiri/html_entity_lookup.h +0 -8
  53. data/ext/nokogiri/html_sax_parser_context.h +0 -11
  54. data/ext/nokogiri/html_sax_push_parser.h +0 -9
  55. data/ext/nokogiri/nokogiri.h +0 -134
  56. data/ext/nokogiri/xml_attr.h +0 -9
  57. data/ext/nokogiri/xml_attribute_decl.h +0 -9
  58. data/ext/nokogiri/xml_cdata.h +0 -9
  59. data/ext/nokogiri/xml_comment.h +0 -9
  60. data/ext/nokogiri/xml_document.h +0 -23
  61. data/ext/nokogiri/xml_document_fragment.h +0 -10
  62. data/ext/nokogiri/xml_dtd.h +0 -10
  63. data/ext/nokogiri/xml_element_content.h +0 -10
  64. data/ext/nokogiri/xml_element_decl.h +0 -9
  65. data/ext/nokogiri/xml_encoding_handler.h +0 -8
  66. data/ext/nokogiri/xml_entity_decl.h +0 -10
  67. data/ext/nokogiri/xml_entity_reference.h +0 -9
  68. data/ext/nokogiri/xml_io.h +0 -11
  69. data/ext/nokogiri/xml_libxml2_hacks.h +0 -12
  70. data/ext/nokogiri/xml_namespace.h +0 -14
  71. data/ext/nokogiri/xml_node.h +0 -13
  72. data/ext/nokogiri/xml_node_set.h +0 -12
  73. data/ext/nokogiri/xml_processing_instruction.h +0 -9
  74. data/ext/nokogiri/xml_reader.h +0 -10
  75. data/ext/nokogiri/xml_relax_ng.h +0 -9
  76. data/ext/nokogiri/xml_sax_parser.h +0 -39
  77. data/ext/nokogiri/xml_sax_parser_context.h +0 -10
  78. data/ext/nokogiri/xml_sax_push_parser.h +0 -9
  79. data/ext/nokogiri/xml_schema.h +0 -9
  80. data/ext/nokogiri/xml_syntax_error.h +0 -13
  81. data/ext/nokogiri/xml_text.h +0 -9
  82. data/ext/nokogiri/xml_xpath_context.h +0 -10
  83. data/ext/nokogiri/xslt_stylesheet.h +0 -14
data/README.md CHANGED
@@ -129,7 +129,7 @@ end
129
129
 
130
130
  ## Requirements
131
131
 
132
- Ruby 2.4.0 or higher, including any development packages necessary to compile native extensions.
132
+ Ruby 2.5.0 or higher, including any development packages necessary to compile native extensions.
133
133
 
134
134
  In Nokogiri 1.6.0 and later libxml2 and libxslt are bundled with the gem, but if you want to use the system versions:
135
135
 
@@ -194,7 +194,7 @@ public class HtmlSaxParserContext extends XmlSaxParserContext {
194
194
  throw context.getRuntime().newEncodingCompatibilityError(rubyEncoding + "is not supported");
195
195
  }
196
196
  catch (IllegalCharsetNameException e) {
197
- throw context.getRuntime().newInvalidEncoding(e.getMessage());
197
+ throw context.getRuntime().newEncodingError(e.getMessage());
198
198
  }
199
199
  }
200
200
 
@@ -129,7 +129,7 @@ public class HtmlSaxPushParser extends RubyObject {
129
129
  final ByteArrayInputStream data = NokogiriHelpers.stringBytesToStream(chunk);
130
130
  if (data == null) {
131
131
  terminateTask(context.runtime);
132
- throw new RaiseException(XmlSyntaxError.createHTMLSyntaxError(context.runtime)); // Nokogiri::HTML::SyntaxError
132
+ throw XmlSyntaxError.createHTMLSyntaxError(context.runtime).toThrowable(); // Nokogiri::HTML::SyntaxError
133
133
  }
134
134
 
135
135
  int errorCount0 = parserTask.getErrorCount();
@@ -720,7 +720,7 @@ public class XmlNode extends RubyObject {
720
720
  // Do not touch this if, if it's not for a good reason.
721
721
  if (node.getNodeType() == Node.DOCUMENT_NODE ||
722
722
  otherNode.getNodeType() == Node.DOCUMENT_NODE) {
723
- return context.runtime.newFixnum(-1);
723
+ return context.runtime.newFixnum(1);
724
724
  }
725
725
 
726
726
  try{
@@ -759,15 +759,13 @@ public class XmlNode extends RubyObject {
759
759
  klass = getNokogiriClass(runtime, "Nokogiri::HTML::Document");
760
760
  ctx = new HtmlDomParserContext(runtime, options);
761
761
  ((HtmlDomParserContext) ctx).enableDocumentFragment();
762
- istream = new ByteArrayInputStream((rubyStringToString(str)).getBytes());
762
+ ctx.setStringInputSource(context, str, context.nil);
763
763
  } else {
764
764
  klass = getNokogiriClass(runtime, "Nokogiri::XML::Document");
765
765
  ctx = new XmlDomParserContext(runtime, options);
766
- String input = rubyStringToString(str);
767
- istream = new ByteArrayInputStream(input.getBytes());
766
+ ctx.setStringInputSource(context, str, context.nil);
768
767
  }
769
768
 
770
- ctx.setInputSource(istream);
771
769
  // TODO: for some reason, document.getEncoding() can be null or nil (don't know why)
772
770
  // run `test_parse_with_unparented_html_text_context_node' few times to see this happen
773
771
  if (document instanceof HtmlDocument && !(document.getEncoding() == null || document.getEncoding().isNil())) {
@@ -1662,7 +1660,7 @@ public class XmlNode extends RubyObject {
1662
1660
  while(errors.getLength() > 0) {
1663
1661
  XmlSyntaxError error = (XmlSyntaxError)errors.shift(context);
1664
1662
  if (error.toString().contains("Include operation failed")) {
1665
- throw new RaiseException(error);
1663
+ throw error.toThrowable();
1666
1664
  }
1667
1665
  }
1668
1666
  return this;
@@ -328,7 +328,7 @@ public class XmlReader extends RubyObject {
328
328
  continueParsing = config.parse(false);
329
329
  }
330
330
  catch (XNIException e) {
331
- throw new RaiseException(XmlSyntaxError.createXMLSyntaxError(context.runtime, e)); // Nokogiri::XML::SyntaxError
331
+ throw XmlSyntaxError.createXMLSyntaxError(context.runtime, e).toThrowable(); // Nokogiri::XML::SyntaxError
332
332
  }
333
333
  catch (IOException e) {
334
334
  throw context.runtime.newRuntimeError(e.toString());
@@ -364,7 +364,7 @@ public class XmlReader extends RubyObject {
364
364
  errors.append(error);
365
365
  setInstanceVariable("@errors", errors);
366
366
 
367
- throw ex != null ? ex : new RaiseException((XmlSyntaxError) error);
367
+ throw ex != null ? ex : ((XmlSyntaxError) error).toThrowable();
368
368
  }
369
369
  if ( ex != null ) throw ex;
370
370
  return this;
@@ -56,6 +56,7 @@ import org.jruby.Ruby;
56
56
  import org.jruby.RubyClass;
57
57
  import org.jruby.anno.JRubyClass;
58
58
  import org.jruby.runtime.ThreadContext;
59
+ import org.jruby.runtime.builtin.IRubyObject;
59
60
  import org.w3c.dom.Document;
60
61
  import org.xml.sax.ErrorHandler;
61
62
  import org.xml.sax.SAXException;
@@ -78,11 +79,17 @@ public class XmlRelaxng extends XmlSchema {
78
79
  this.verifier = verifier;
79
80
  }
80
81
 
81
- static XmlSchema createSchemaInstance(ThreadContext context, RubyClass klazz, Source source) {
82
+ static XmlSchema createSchemaInstance(ThreadContext context, RubyClass klazz, Source source, IRubyObject parseOptions) {
82
83
  Ruby runtime = context.getRuntime();
83
84
  XmlRelaxng xmlRelaxng = (XmlRelaxng) NokogiriService.XML_RELAXNG_ALLOCATOR.allocate(runtime, klazz);
85
+
86
+ if (parseOptions == null) {
87
+ parseOptions = defaultParseOptions(context.getRuntime());
88
+ }
89
+
84
90
  xmlRelaxng.setInstanceVariable("@errors", runtime.newEmptyArray());
85
-
91
+ xmlRelaxng.setInstanceVariable("@parse_options", parseOptions);
92
+
86
93
  try {
87
94
  Schema schema = xmlRelaxng.getSchema(source, context);
88
95
  xmlRelaxng.setVerifier(schema.newVerifier());
@@ -128,7 +135,7 @@ public class XmlRelaxng extends XmlSchema {
128
135
  throw context.getRuntime()
129
136
  .newRuntimeError("Could not parse document: "+ex.getMessage());
130
137
  } catch (IOException ex) {
131
- throw context.getRuntime().newIOError(ex.getMessage());
138
+ throw context.getRuntime().newIOError(ex.getClass() + ": " + ex.getMessage());
132
139
  }
133
140
  }
134
141
 
@@ -94,7 +94,10 @@ public class XmlSaxParserContext extends ParserContext {
94
94
  parser = createParser();
95
95
  }
96
96
  catch (SAXException se) {
97
- throw RaiseException.createNativeRaiseException(runtime, se);
97
+ // Unexpected failure in XML subsystem
98
+ RaiseException ex = runtime.newRuntimeError(se.toString());
99
+ ex.initCause(se);
100
+ throw ex;
98
101
  }
99
102
  }
100
103
 
@@ -208,7 +211,8 @@ public class XmlSaxParserContext extends ParserContext {
208
211
  parser.setFeature(FEATURE_CONTINUE_AFTER_FATAL_ERROR, true);
209
212
  }
210
213
  catch (Exception e) {
211
- throw RaiseException.createNativeRaiseException(runtime, e);
214
+ // Unexpected failure in XML subsystem
215
+ throw runtime.newRuntimeError(e.getMessage());
212
216
  }
213
217
  }
214
218
  }
@@ -261,7 +265,8 @@ public class XmlSaxParserContext extends ParserContext {
261
265
  }
262
266
  }
263
267
  catch (SAXException ex) {
264
- throw RaiseException.createNativeRaiseException(runtime, ex);
268
+ // Unexpected failure in XML subsystem
269
+ throw runtime.newRuntimeError(ex.getMessage());
265
270
  }
266
271
  catch (IOException ex) {
267
272
  throw runtime.newIOErrorFromException(ex);
@@ -51,6 +51,8 @@ import javax.xml.validation.Validator;
51
51
  import nokogiri.internals.IgnoreSchemaErrorsErrorHandler;
52
52
  import nokogiri.internals.SchemaErrorHandler;
53
53
  import nokogiri.internals.XmlDomParserContext;
54
+ import nokogiri.internals.ParserContext;
55
+ import nokogiri.internals.ParserContext.Options;
54
56
 
55
57
  import org.jruby.Ruby;
56
58
  import org.jruby.RubyArray;
@@ -63,11 +65,13 @@ import org.jruby.exceptions.RaiseException;
63
65
  import org.jruby.runtime.ThreadContext;
64
66
  import org.jruby.runtime.Visibility;
65
67
  import org.jruby.runtime.builtin.IRubyObject;
68
+ import org.jruby.runtime.Helpers;
66
69
  import org.w3c.dom.Document;
67
70
  import org.w3c.dom.ls.LSInput;
68
71
  import org.w3c.dom.ls.LSResourceResolver;
69
72
  import org.xml.sax.ErrorHandler;
70
73
  import org.xml.sax.SAXException;
74
+ import org.xml.sax.SAXParseException;
71
75
 
72
76
  /**
73
77
  * Class for Nokogiri::XML::Schema
@@ -93,11 +97,20 @@ public class XmlSchema extends RubyObject {
93
97
  return super.clone();
94
98
  }
95
99
 
96
- private Schema getSchema(Source source, String currentDir, String scriptFileName) throws SAXException {
100
+ private Schema getSchema(Source source,
101
+ String currentDir,
102
+ String scriptFileName,
103
+ SchemaErrorHandler errorHandler,
104
+ long parseOptions) throws SAXException {
105
+ boolean noNet = new ParserContext.Options(parseOptions).noNet;
106
+
97
107
  SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
98
- SchemaResourceResolver resourceResolver = new SchemaResourceResolver(currentDir, scriptFileName, null);
108
+ SchemaResourceResolver resourceResolver =
109
+ new SchemaResourceResolver(currentDir, scriptFileName, null, errorHandler, noNet);
110
+
99
111
  schemaFactory.setResourceResolver(resourceResolver);
100
- schemaFactory.setErrorHandler(new IgnoreSchemaErrorsErrorHandler());
112
+ schemaFactory.setErrorHandler(errorHandler);
113
+
101
114
  return schemaFactory.newSchema(source);
102
115
  }
103
116
 
@@ -105,13 +118,27 @@ public class XmlSchema extends RubyObject {
105
118
  this.validator = validator;
106
119
  }
107
120
 
108
- static XmlSchema createSchemaInstance(ThreadContext context, RubyClass klazz, Source source) {
121
+ static XmlSchema createSchemaInstance(ThreadContext context, RubyClass klazz, Source source, IRubyObject parseOptions) {
109
122
  Ruby runtime = context.getRuntime();
110
123
  XmlSchema xmlSchema = (XmlSchema) NokogiriService.XML_SCHEMA_ALLOCATOR.allocate(runtime, klazz);
124
+
125
+ if (parseOptions == null) {
126
+ parseOptions = defaultParseOptions(context.getRuntime());
127
+ }
128
+ long intParseOptions = RubyFixnum.fix2long(Helpers.invoke(context, parseOptions, "to_i"));
129
+
111
130
  xmlSchema.setInstanceVariable("@errors", runtime.newEmptyArray());
131
+ xmlSchema.setInstanceVariable("@parse_options", parseOptions);
112
132
 
113
133
  try {
114
- Schema schema = xmlSchema.getSchema(source, context.getRuntime().getCurrentDirectory(), context.getRuntime().getInstanceConfig().getScriptFileName());
134
+ SchemaErrorHandler errorHandler =
135
+ new SchemaErrorHandler(context.getRuntime(), (RubyArray)xmlSchema.getInstanceVariable("@errors"));
136
+ Schema schema =
137
+ xmlSchema.getSchema(source,
138
+ context.getRuntime().getCurrentDirectory(),
139
+ context.getRuntime().getInstanceConfig().getScriptFileName(),
140
+ errorHandler,
141
+ intParseOptions);
115
142
  xmlSchema.setValidator(schema.newValidator());
116
143
  return xmlSchema;
117
144
  } catch (SAXException ex) {
@@ -119,19 +146,29 @@ public class XmlSchema extends RubyObject {
119
146
  }
120
147
  }
121
148
 
149
+ protected static IRubyObject defaultParseOptions(Ruby runtime) {
150
+ return ((RubyClass)runtime.getClassFromPath("Nokogiri::XML::ParseOptions")).getConstant("DEFAULT_SCHEMA");
151
+ }
152
+
122
153
  /*
123
154
  * call-seq:
124
155
  * from_document(doc)
125
156
  *
126
157
  * Create a new Schema from the Nokogiri::XML::Document +doc+
127
158
  */
128
- @JRubyMethod(meta=true)
129
- public static IRubyObject from_document(ThreadContext context, IRubyObject klazz, IRubyObject document) {
159
+ @JRubyMethod(meta=true, required=1, optional=1)
160
+ public static IRubyObject from_document(ThreadContext context, IRubyObject klazz, IRubyObject[] args) {
161
+ IRubyObject document = args[0];
162
+ IRubyObject parseOptions = null;
163
+ if (args.length > 1) {
164
+ parseOptions = args[1];
165
+ }
166
+
130
167
  XmlDocument doc = ((XmlDocument) ((XmlNode) document).document(context));
131
168
 
132
169
  RubyArray errors = (RubyArray) doc.getInstanceVariable("@errors");
133
170
  if (!errors.isEmpty()) {
134
- throw new RaiseException((XmlSyntaxError) errors.first());
171
+ throw ((XmlSyntaxError) errors.first()).toThrowable();
135
172
  }
136
173
 
137
174
  DOMSource source = new DOMSource(doc.getDocument());
@@ -142,25 +179,30 @@ public class XmlSchema extends RubyObject {
142
179
  source.setSystemId(uri.convertToString().asJavaString());
143
180
  }
144
181
 
145
- return getSchema(context, (RubyClass)klazz, source);
182
+ return getSchema(context, (RubyClass)klazz, source, parseOptions);
183
+ }
184
+
185
+ @JRubyMethod(meta=true, required=1, optional=1)
186
+ public static IRubyObject read_memory(ThreadContext context, IRubyObject klazz, IRubyObject[] args) {
187
+ IRubyObject content = args[0];
188
+ IRubyObject parseOptions = null;
189
+ if (args.length > 1) {
190
+ parseOptions = args[1];
191
+ }
192
+ String data = content.convertToString().asJavaString();
193
+ return getSchema(context, (RubyClass) klazz, new StreamSource(new StringReader(data)), parseOptions);
146
194
  }
147
195
 
148
- private static IRubyObject getSchema(ThreadContext context, RubyClass klazz, Source source) {
196
+ private static IRubyObject getSchema(ThreadContext context, RubyClass klazz, Source source, IRubyObject parseOptions) {
149
197
  String moduleName = klazz.getName();
150
198
  if ("Nokogiri::XML::Schema".equals(moduleName)) {
151
- return XmlSchema.createSchemaInstance(context, klazz, source);
199
+ return XmlSchema.createSchemaInstance(context, klazz, source, parseOptions);
152
200
  } else if ("Nokogiri::XML::RelaxNG".equals(moduleName)) {
153
- return XmlRelaxng.createSchemaInstance(context, klazz, source);
201
+ return XmlRelaxng.createSchemaInstance(context, klazz, source, parseOptions);
154
202
  }
155
203
  return context.getRuntime().getNil();
156
204
  }
157
205
 
158
- @JRubyMethod(meta=true)
159
- public static IRubyObject read_memory(ThreadContext context, IRubyObject klazz, IRubyObject content) {
160
- String data = content.convertToString().asJavaString();
161
- return getSchema(context, (RubyClass) klazz, new StreamSource(new StringReader(data)));
162
- }
163
-
164
206
  @JRubyMethod(visibility=Visibility.PRIVATE)
165
207
  public IRubyObject validate_document(ThreadContext context, IRubyObject document) {
166
208
  return validate_document_or_file(context, (XmlDocument)document);
@@ -209,11 +251,15 @@ public class XmlSchema extends RubyObject {
209
251
  SchemaLSInput lsInput = new SchemaLSInput();
210
252
  String currentDir;
211
253
  String scriptFileName;
254
+ SchemaErrorHandler errorHandler;
255
+ boolean noNet;
212
256
  //String defaultURI;
213
257
 
214
- SchemaResourceResolver(String currentDir, String scriptFileName, Object input) {
258
+ SchemaResourceResolver(String currentDir, String scriptFileName, Object input, SchemaErrorHandler errorHandler, boolean noNet) {
215
259
  this.currentDir = currentDir;
216
260
  this.scriptFileName = scriptFileName;
261
+ this.errorHandler = errorHandler;
262
+ this.noNet = noNet;
217
263
  if (input == null) return;
218
264
  if (input instanceof String) {
219
265
  lsInput.setStringData((String)input);
@@ -225,11 +271,25 @@ public class XmlSchema extends RubyObject {
225
271
  }
226
272
 
227
273
  @Override
228
- public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI) {
229
- String adjusted = adjustSystemIdIfNecessary(currentDir, scriptFileName, baseURI, systemId);
230
- lsInput.setPublicId(publicId);
231
- lsInput.setSystemId(adjusted != null? adjusted : systemId);
232
- lsInput.setBaseURI(baseURI);
274
+ public LSInput resolveResource(String type,
275
+ String namespaceURI,
276
+ String publicId,
277
+ String systemId,
278
+ String baseURI) {
279
+ if (noNet && (systemId.startsWith("http://") || systemId.startsWith("ftp://"))) {
280
+ if (systemId.startsWith(XMLConstants.W3C_XML_SCHEMA_NS_URI)) {
281
+ return null; // use default resolver
282
+ }
283
+ try {
284
+ this.errorHandler.warning(new SAXParseException(String.format("Attempt to load network entity '%s'", systemId), null));
285
+ } catch (SAXException ex) {
286
+ }
287
+ } else {
288
+ String adjusted = adjustSystemIdIfNecessary(currentDir, scriptFileName, baseURI, systemId);
289
+ lsInput.setPublicId(publicId);
290
+ lsInput.setSystemId(adjusted != null? adjusted : systemId);
291
+ lsInput.setBaseURI(baseURI);
292
+ }
233
293
  return lsInput;
234
294
  }
235
295
  }
@@ -120,13 +120,9 @@ public class XmlSyntaxError extends RubyException {
120
120
  setInstanceVariable("@file", stringOrNil(runtime, exception.getSystemId()));
121
121
  }
122
122
 
123
- // NOTE: special care - due JRuby 1.7.x
124
-
125
- @Override
126
- public IRubyObject to_s(ThreadContext context) { return to_s19(context); }
127
-
128
123
  @JRubyMethod(name = "to_s")
129
- public RubyString to_s19(ThreadContext context) {
124
+ @Override
125
+ public IRubyObject to_s(ThreadContext context) {
130
126
  RubyString msg = msg(context.runtime);
131
127
  return msg != null ? msg : super.to_s(context).asString();
132
128
  }
@@ -33,6 +33,8 @@
33
33
  package nokogiri;
34
34
 
35
35
  import java.util.Set;
36
+ import java.util.regex.Matcher;
37
+ import java.util.regex.Pattern;
36
38
 
37
39
  import javax.xml.transform.TransformerException;
38
40
 
@@ -68,7 +70,6 @@ import static nokogiri.internals.NokogiriHelpers.nodeListToRubyArray;
68
70
  */
69
71
  @JRubyClass(name="Nokogiri::XML::XPathContext")
70
72
  public class XmlXpathContext extends RubyObject {
71
-
72
73
  static {
73
74
  final String DTMManager = "org.apache.xml.dtm.DTMManager";
74
75
  if (SafePropertyAccessor.getProperty(DTMManager) == null) {
@@ -109,25 +110,53 @@ public class XmlXpathContext extends RubyObject {
109
110
  }
110
111
  }
111
112
 
113
+
114
+ // see https://en.wikipedia.org/wiki/QName
115
+ private static final String NameStartCharStr = "[_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]" ;
116
+ private static final String NameCharStr = "[-\\.0-9\u00B7\u0300-\u036F\u203F-\u2040]|" + NameStartCharStr ;
117
+ private static final String NCNameStr = "(?:" + NameStartCharStr + ")(?:" + NameCharStr + ")*";
118
+ private static final String XPathFunctionCaptureStr = "(" + NCNameStr + "(?=\\())";
119
+ private static final Pattern XPathFunctionCaptureRE = Pattern.compile(XPathFunctionCaptureStr);
120
+
112
121
  @JRubyMethod
113
- public IRubyObject evaluate(ThreadContext context, IRubyObject expr, IRubyObject handler) {
114
-
115
- String src = expr.convertToString().asJavaString();
116
- if (!handler.isNil()) {
117
- if (!isContainsPrefix(src)) {
118
- StringBuilder replacement = new StringBuilder();
119
- Set<String> methodNames = handler.getMetaClass().getMethods().keySet();
120
- final String PREFIX = NokogiriNamespaceContext.NOKOGIRI_PREFIX;
121
- for (String name : methodNames) {
122
- replacement.setLength(0);
123
- replacement.ensureCapacity(PREFIX.length() + 1 + name.length());
124
- replacement.append(PREFIX).append(':').append(name);
125
- src = src.replace(name, replacement); // replace(name, NOKOGIRI_PREFIX + ':' + name)
122
+ public IRubyObject evaluate(ThreadContext context, IRubyObject rbQuery, IRubyObject handler) {
123
+ String query = rbQuery.convertToString().asJavaString();
124
+
125
+ if (!handler.isNil() && !isContainsPrefix(query)) {
126
+ //
127
+ // The user has passed in a handler, but isn't using the `nokogiri:` prefix as
128
+ // instructed in JRuby land, so let's try to be clever and rewrite the query, inserting
129
+ // the nokogiri namespace where appropriate.
130
+ //
131
+ StringBuilder namespacedQuery = new StringBuilder();
132
+ int jchar = 0;
133
+
134
+ // Find the methods on the handler object
135
+ Set<String> methodNames = handler.getMetaClass().getMethods().keySet();
136
+
137
+ // Find the function calls in the xpath query
138
+ Matcher xpathFunctionCalls = XPathFunctionCaptureRE.matcher(query);
139
+
140
+ while (xpathFunctionCalls.find()) {
141
+ namespacedQuery.append(query.subSequence(jchar, xpathFunctionCalls.start()));
142
+ jchar = xpathFunctionCalls.start();
143
+
144
+ if (methodNames.contains(xpathFunctionCalls.group())) {
145
+ namespacedQuery.append(NokogiriNamespaceContext.NOKOGIRI_PREFIX);
146
+ namespacedQuery.append(":");
126
147
  }
148
+
149
+ namespacedQuery.append(query.subSequence(xpathFunctionCalls.start(), xpathFunctionCalls.end()));
150
+ jchar = xpathFunctionCalls.end();
151
+ }
152
+
153
+ if (jchar < query.length()-1) {
154
+ namespacedQuery.append(query.subSequence(jchar, query.length()));
127
155
  }
156
+ query = namespacedQuery.toString();
128
157
  }
129
158
 
130
- return node_set(context, src, handler);
159
+ return node_set(context, query, handler);
131
160
  }
132
161
 
133
162
  @JRubyMethod
@@ -157,13 +186,14 @@ public class XmlXpathContext extends RubyObject {
157
186
  }
158
187
 
159
188
  private IRubyObject node_set(ThreadContext context, String expr, IRubyObject handler) {
160
- final NokogiriXPathFunctionResolver fnResolver =
161
- handler.isNil() ? null : NokogiriXPathFunctionResolver.create(handler);
189
+ final NokogiriXPathFunctionResolver fnResolver = NokogiriXPathFunctionResolver.create(handler);
162
190
  try {
163
191
  return tryGetNodeSet(context, expr, fnResolver);
164
192
  }
165
193
  catch (TransformerException ex) {
166
- throw new RaiseException(XmlSyntaxError.createXMLXPathSyntaxError(context.runtime, expr, ex)); // Nokogiri::XML::XPath::SyntaxError
194
+ throw XmlSyntaxError.createXMLXPathSyntaxError(context.runtime,
195
+ (expr + ": " + ex.toString()),
196
+ ex).toThrowable();
167
197
  }
168
198
  }
169
199