nokogiri 1.10.9-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 (158) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE-DEPENDENCIES.md +1015 -947
  3. data/README.md +24 -22
  4. data/ext/java/nokogiri/HtmlDocument.java +34 -46
  5. data/ext/java/nokogiri/HtmlSaxParserContext.java +88 -58
  6. data/ext/java/nokogiri/HtmlSaxPushParser.java +1 -1
  7. data/ext/java/nokogiri/NokogiriService.java +1 -1
  8. data/ext/java/nokogiri/XmlAttr.java +13 -20
  9. data/ext/java/nokogiri/XmlAttributeDecl.java +11 -12
  10. data/ext/java/nokogiri/XmlCdata.java +3 -4
  11. data/ext/java/nokogiri/XmlComment.java +1 -1
  12. data/ext/java/nokogiri/XmlDocument.java +148 -175
  13. data/ext/java/nokogiri/XmlDocumentFragment.java +13 -31
  14. data/ext/java/nokogiri/XmlDtd.java +5 -8
  15. data/ext/java/nokogiri/XmlElement.java +1 -20
  16. data/ext/java/nokogiri/XmlElementDecl.java +23 -28
  17. data/ext/java/nokogiri/XmlEntityDecl.java +23 -27
  18. data/ext/java/nokogiri/XmlEntityReference.java +2 -2
  19. data/ext/java/nokogiri/XmlNamespace.java +72 -89
  20. data/ext/java/nokogiri/XmlNode.java +303 -406
  21. data/ext/java/nokogiri/XmlNodeSet.java +70 -76
  22. data/ext/java/nokogiri/XmlReader.java +12 -13
  23. data/ext/java/nokogiri/XmlRelaxng.java +10 -3
  24. data/ext/java/nokogiri/XmlSaxParserContext.java +15 -10
  25. data/ext/java/nokogiri/XmlSchema.java +87 -27
  26. data/ext/java/nokogiri/XmlSyntaxError.java +2 -6
  27. data/ext/java/nokogiri/XmlText.java +12 -9
  28. data/ext/java/nokogiri/XmlXpathContext.java +55 -25
  29. data/ext/java/nokogiri/XsltStylesheet.java +7 -15
  30. data/ext/java/nokogiri/internals/HtmlDomParserContext.java +52 -46
  31. data/ext/java/nokogiri/internals/NokogiriHandler.java +1 -1
  32. data/ext/java/nokogiri/internals/NokogiriHelpers.java +71 -135
  33. data/ext/java/nokogiri/internals/NokogiriNamespaceCache.java +90 -58
  34. data/ext/java/nokogiri/internals/NokogiriNamespaceContext.java +9 -2
  35. data/ext/java/nokogiri/internals/NokogiriXPathFunction.java +67 -10
  36. data/ext/java/nokogiri/internals/NokogiriXPathFunctionResolver.java +4 -2
  37. data/ext/java/nokogiri/internals/ParserContext.java +27 -73
  38. data/ext/java/nokogiri/internals/ReaderNode.java +2 -4
  39. data/ext/java/nokogiri/internals/XmlDomParserContext.java +18 -33
  40. data/ext/nokogiri/depend +476 -357
  41. data/ext/nokogiri/extconf.rb +485 -352
  42. data/ext/nokogiri/html_document.c +79 -78
  43. data/ext/nokogiri/html_sax_parser_context.c +2 -2
  44. data/ext/nokogiri/nokogiri.c +34 -40
  45. data/ext/nokogiri/xml_document.c +18 -4
  46. data/ext/nokogiri/xml_io.c +8 -6
  47. data/ext/nokogiri/xml_node.c +21 -1
  48. data/ext/nokogiri/xml_node_set.c +1 -1
  49. data/ext/nokogiri/xml_reader.c +6 -17
  50. data/ext/nokogiri/xml_relax_ng.c +29 -11
  51. data/ext/nokogiri/xml_sax_parser.c +2 -7
  52. data/ext/nokogiri/xml_sax_parser_context.c +2 -2
  53. data/ext/nokogiri/xml_schema.c +55 -13
  54. data/ext/nokogiri/xml_xpath_context.c +80 -4
  55. data/ext/nokogiri/xslt_stylesheet.c +1 -8
  56. data/lib/nokogiri.rb +4 -21
  57. data/lib/nokogiri/css.rb +1 -0
  58. data/lib/nokogiri/css/node.rb +1 -0
  59. data/lib/nokogiri/css/parser.rb +63 -62
  60. data/lib/nokogiri/css/parser.y +2 -2
  61. data/lib/nokogiri/css/parser_extras.rb +39 -36
  62. data/lib/nokogiri/css/syntax_error.rb +1 -0
  63. data/lib/nokogiri/css/tokenizer.rb +1 -0
  64. data/lib/nokogiri/css/xpath_visitor.rb +73 -43
  65. data/lib/nokogiri/decorators/slop.rb +1 -0
  66. data/lib/nokogiri/html.rb +1 -0
  67. data/lib/nokogiri/html/builder.rb +1 -0
  68. data/lib/nokogiri/html/document.rb +13 -26
  69. data/lib/nokogiri/html/document_fragment.rb +1 -0
  70. data/lib/nokogiri/html/element_description.rb +1 -0
  71. data/lib/nokogiri/html/element_description_defaults.rb +1 -0
  72. data/lib/nokogiri/html/entity_lookup.rb +1 -0
  73. data/lib/nokogiri/html/sax/parser.rb +1 -0
  74. data/lib/nokogiri/html/sax/parser_context.rb +1 -0
  75. data/lib/nokogiri/html/sax/push_parser.rb +1 -0
  76. data/lib/nokogiri/jruby/dependencies.rb +20 -0
  77. data/lib/nokogiri/nokogiri.jar +0 -0
  78. data/lib/nokogiri/syntax_error.rb +1 -0
  79. data/lib/nokogiri/version.rb +3 -109
  80. data/lib/nokogiri/version/constant.rb +5 -0
  81. data/lib/nokogiri/version/info.rb +182 -0
  82. data/lib/nokogiri/xml.rb +1 -0
  83. data/lib/nokogiri/xml/attr.rb +1 -0
  84. data/lib/nokogiri/xml/attribute_decl.rb +1 -0
  85. data/lib/nokogiri/xml/builder.rb +3 -2
  86. data/lib/nokogiri/xml/cdata.rb +1 -0
  87. data/lib/nokogiri/xml/character_data.rb +1 -0
  88. data/lib/nokogiri/xml/document.rb +20 -15
  89. data/lib/nokogiri/xml/document_fragment.rb +5 -6
  90. data/lib/nokogiri/xml/dtd.rb +1 -0
  91. data/lib/nokogiri/xml/element_content.rb +1 -0
  92. data/lib/nokogiri/xml/element_decl.rb +1 -0
  93. data/lib/nokogiri/xml/entity_decl.rb +1 -0
  94. data/lib/nokogiri/xml/entity_reference.rb +1 -0
  95. data/lib/nokogiri/xml/namespace.rb +1 -0
  96. data/lib/nokogiri/xml/node.rb +587 -249
  97. data/lib/nokogiri/xml/node/save_options.rb +1 -0
  98. data/lib/nokogiri/xml/node_set.rb +1 -0
  99. data/lib/nokogiri/xml/notation.rb +1 -0
  100. data/lib/nokogiri/xml/parse_options.rb +10 -3
  101. data/lib/nokogiri/xml/pp.rb +1 -0
  102. data/lib/nokogiri/xml/pp/character_data.rb +1 -0
  103. data/lib/nokogiri/xml/pp/node.rb +1 -0
  104. data/lib/nokogiri/xml/processing_instruction.rb +1 -0
  105. data/lib/nokogiri/xml/reader.rb +7 -3
  106. data/lib/nokogiri/xml/relax_ng.rb +7 -2
  107. data/lib/nokogiri/xml/sax.rb +1 -0
  108. data/lib/nokogiri/xml/sax/document.rb +1 -0
  109. data/lib/nokogiri/xml/sax/parser.rb +1 -0
  110. data/lib/nokogiri/xml/sax/parser_context.rb +1 -0
  111. data/lib/nokogiri/xml/sax/push_parser.rb +1 -0
  112. data/lib/nokogiri/xml/schema.rb +13 -4
  113. data/lib/nokogiri/xml/searchable.rb +25 -16
  114. data/lib/nokogiri/xml/syntax_error.rb +1 -0
  115. data/lib/nokogiri/xml/text.rb +1 -0
  116. data/lib/nokogiri/xml/xpath.rb +1 -0
  117. data/lib/nokogiri/xml/xpath/syntax_error.rb +1 -0
  118. data/lib/nokogiri/xml/xpath_context.rb +1 -0
  119. data/lib/nokogiri/xslt.rb +1 -0
  120. data/lib/nokogiri/xslt/stylesheet.rb +1 -0
  121. data/lib/xsd/xmlparser/nokogiri.rb +1 -0
  122. metadata +89 -96
  123. data/ext/java/nokogiri/internals/NokogiriEncodingReaderWrapper.java +0 -107
  124. data/ext/java/nokogiri/internals/UncloseableInputStream.java +0 -102
  125. data/ext/nokogiri/html_document.h +0 -10
  126. data/ext/nokogiri/html_element_description.h +0 -10
  127. data/ext/nokogiri/html_entity_lookup.h +0 -8
  128. data/ext/nokogiri/html_sax_parser_context.h +0 -11
  129. data/ext/nokogiri/html_sax_push_parser.h +0 -9
  130. data/ext/nokogiri/nokogiri.h +0 -121
  131. data/ext/nokogiri/xml_attr.h +0 -9
  132. data/ext/nokogiri/xml_attribute_decl.h +0 -9
  133. data/ext/nokogiri/xml_cdata.h +0 -9
  134. data/ext/nokogiri/xml_comment.h +0 -9
  135. data/ext/nokogiri/xml_document.h +0 -23
  136. data/ext/nokogiri/xml_document_fragment.h +0 -10
  137. data/ext/nokogiri/xml_dtd.h +0 -10
  138. data/ext/nokogiri/xml_element_content.h +0 -10
  139. data/ext/nokogiri/xml_element_decl.h +0 -9
  140. data/ext/nokogiri/xml_encoding_handler.h +0 -8
  141. data/ext/nokogiri/xml_entity_decl.h +0 -10
  142. data/ext/nokogiri/xml_entity_reference.h +0 -9
  143. data/ext/nokogiri/xml_io.h +0 -11
  144. data/ext/nokogiri/xml_libxml2_hacks.h +0 -12
  145. data/ext/nokogiri/xml_namespace.h +0 -14
  146. data/ext/nokogiri/xml_node.h +0 -13
  147. data/ext/nokogiri/xml_node_set.h +0 -12
  148. data/ext/nokogiri/xml_processing_instruction.h +0 -9
  149. data/ext/nokogiri/xml_reader.h +0 -10
  150. data/ext/nokogiri/xml_relax_ng.h +0 -9
  151. data/ext/nokogiri/xml_sax_parser.h +0 -39
  152. data/ext/nokogiri/xml_sax_parser_context.h +0 -10
  153. data/ext/nokogiri/xml_sax_push_parser.h +0 -9
  154. data/ext/nokogiri/xml_schema.h +0 -9
  155. data/ext/nokogiri/xml_syntax_error.h +0 -13
  156. data/ext/nokogiri/xml_text.h +0 -9
  157. data/ext/nokogiri/xml_xpath_context.h +0 -10
  158. data/ext/nokogiri/xslt_stylesheet.h +0 -14
@@ -46,49 +46,43 @@ import org.w3c.dom.NamedNodeMap;
46
46
  import org.w3c.dom.Node;
47
47
 
48
48
  /**
49
- * Cache of namespages of each node. XmlDocument has one cache of this class.
49
+ * Cache of namespaces of each node. XmlDocument has one cache of this class.
50
50
  *
51
51
  * @author sergio
52
52
  * @author Yoko Harada <yokolet@gmail.com>
53
53
  */
54
54
  public class NokogiriNamespaceCache {
55
55
 
56
- private List<String[]> keys;
57
- private Map<String[], CacheEntry> cache; // pair of the index of a given key and entry
56
+ private final Map<CacheKey, CacheEntry> cache; // pair of the index of a given key and entry
58
57
  private XmlNamespace defaultNamespace = null;
59
58
 
60
59
  public NokogiriNamespaceCache() {
61
- keys = new ArrayList<String[]>(); // keys are [prefix, href]
62
- cache = new LinkedHashMap<String[], CacheEntry>();
60
+ this.cache = new LinkedHashMap<CacheKey, CacheEntry>(4);
63
61
  }
64
62
 
65
- public XmlNamespace getDefault() {
66
- return defaultNamespace;
63
+ public NokogiriNamespaceCache(NokogiriNamespaceCache cache) {
64
+ this.cache = new LinkedHashMap<CacheKey, CacheEntry>(cache.size() + 2);
65
+ this.cache.putAll(cache.cache);
67
66
  }
68
67
 
69
- private String[] getKey(String prefix, String href) {
70
- for (String[] key : keys) {
71
- if (key[0].equals(prefix) && key[1].equals(href)) return key;
72
- }
73
- return null;
68
+ public XmlNamespace getDefault() {
69
+ return defaultNamespace;
74
70
  }
75
71
 
76
72
  public XmlNamespace get(String prefix, String href) {
77
- // prefix should not be null.
78
- // In case of a default namespace, an empty string should be given to prefix argument.
79
- if (prefix == null || href == null) return null;
80
- String[] key = getKey(prefix, href);
81
- if (key != null) {
82
- return cache.get(key).namespace;
83
- }
84
- return null;
73
+ if (href == null) return null;
74
+
75
+ CacheEntry value = cache.get(new CacheKey(prefix, href));
76
+ return value == null ? null : value.namespace;
85
77
  }
86
78
 
87
79
  public XmlNamespace get(Node node, String prefix) {
88
80
  if (prefix == null) return defaultNamespace;
89
- for (String[] key : keys) {
90
- if (key[0].equals(prefix) && cache.get(key) != null && cache.get(key).isOwner(node)) {
91
- return cache.get(key).namespace;
81
+ for (Map.Entry<CacheKey, CacheEntry> entry : cache.entrySet()) {
82
+ if (entry.getKey().prefix.equals(prefix)) {
83
+ if (entry.getValue().isOwner(node)) {
84
+ return entry.getValue().namespace;
85
+ }
92
86
  }
93
87
  }
94
88
  return null;
@@ -100,9 +94,9 @@ public class NokogiriNamespaceCache {
100
94
  namespaces.add(defaultNamespace);
101
95
  return namespaces;
102
96
  }
103
- for (String[] key : keys) {
104
- if (key[0].equals(prefix) && cache.get(key) != null) {
105
- namespaces.add(cache.get(key).namespace);
97
+ for (Map.Entry<CacheKey, CacheEntry> entry : cache.entrySet()) {
98
+ if (entry.getKey().prefix.equals(prefix)) {
99
+ namespaces.add(entry.getValue().namespace);
106
100
  }
107
101
  }
108
102
  return namespaces;
@@ -110,63 +104,96 @@ public class NokogiriNamespaceCache {
110
104
 
111
105
  public List<XmlNamespace> get(Node node) {
112
106
  List<XmlNamespace> namespaces = new ArrayList<XmlNamespace>();
113
- for (String[] key : keys) {
114
- CacheEntry entry = cache.get(key);
115
- if (entry.isOwner(node)) {
116
- namespaces.add(entry.namespace);
107
+ for (Map.Entry<CacheKey, CacheEntry> entry : cache.entrySet()) {
108
+ if (entry.getValue().isOwner(node)) {
109
+ namespaces.add(entry.getValue().namespace);
117
110
  }
118
111
  }
119
112
  return namespaces;
120
113
  }
121
114
 
122
115
  public void put(XmlNamespace namespace, Node ownerNode) {
123
- // prefix should not be null.
124
- // In case of a default namespace, an empty string should be given to prefix argument.
125
- String prefixString = namespace.getPrefix();
126
- String hrefString = namespace.getHref();
127
- if (getKey(prefixString, hrefString) != null) return;
128
- String[] key = {prefixString, hrefString};
129
- keys.add(key);
130
- CacheEntry entry = new CacheEntry(namespace, ownerNode);
131
- cache.put(key, entry);
132
- if ("".equals(prefixString)) defaultNamespace = namespace;
116
+ String prefix = namespace.getPrefix();
117
+ String href = namespace.getHref();
118
+ if (href == null) return;
119
+
120
+ CacheKey key = new CacheKey(prefix, href);
121
+ if (cache.get(key) != null) return;
122
+ cache.put(key, new CacheEntry(namespace, ownerNode));
123
+ if ("".equals(prefix)) defaultNamespace = namespace;
133
124
  }
134
125
 
135
- public void remove(String prefix, String href) {
136
- String[] key = getKey(prefix, href);
137
- if (key == null) return;
138
- keys.remove(key);
139
- cache.remove(key);
126
+ public void remove(Node ownerNode) {
127
+ String prefix = ownerNode.getPrefix();
128
+ String href = ownerNode.getNamespaceURI();
129
+ if (href == null) return;
130
+
131
+ cache.remove(new CacheKey(prefix, href));
132
+ }
133
+
134
+ public int size() {
135
+ return cache.size();
140
136
  }
141
137
 
142
138
  public void clear() {
143
139
  // removes namespace declarations from node
144
- for (String[] key : cache.keySet()) {
145
- CacheEntry entry = cache.get(key);
140
+ for (CacheEntry entry : cache.values()) {
146
141
  NamedNodeMap attributes = entry.ownerNode.getAttributes();
147
142
  for (int j=0; j<attributes.getLength(); j++) {
148
- String name = ((Attr)attributes.item(j)).getName();
143
+ String name = ((Attr) attributes.item(j)).getName();
149
144
  if (isNamespace(name)) {
150
145
  attributes.removeNamedItem(name);
151
146
  }
152
147
  }
153
148
  }
154
- keys.clear();
155
149
  cache.clear();
156
150
  defaultNamespace = null;
157
151
  }
158
152
 
159
153
  public void replaceNode(Node oldNode, Node newNode) {
160
- for (String[] key : keys) {
161
- CacheEntry entry = cache.get(key);
162
- if (entry.isOwner(oldNode)) {
163
- entry.replaceOwner(newNode);
154
+ for (Map.Entry<CacheKey, CacheEntry> entry : cache.entrySet()) {
155
+ if (entry.getValue().isOwner(oldNode)) {
156
+ entry.getValue().replaceOwner(newNode);
157
+ }
158
+ }
159
+ }
160
+
161
+ @Override
162
+ public String toString() {
163
+ return getClass().getName() + '@' + Integer.toHexString(hashCode()) + '(' + cache + "default=" + defaultNamespace + ')';
164
+ }
165
+
166
+ private static class CacheKey {
167
+ final String prefix;
168
+ final String href;
169
+
170
+ CacheKey(String prefix, String href) {
171
+ this.prefix = prefix;
172
+ this.href = href;
173
+ }
174
+
175
+ @Override
176
+ public boolean equals(final Object obj) {
177
+ if (obj instanceof CacheKey) {
178
+ CacheKey that = (CacheKey) obj;
179
+ return prefix == null ? that.prefix == null : prefix.equals(that.prefix) && href.equals(that.href);
164
180
  }
181
+ return false;
182
+ }
183
+
184
+ @Override
185
+ public int hashCode() {
186
+ return (prefix == null ? 0 : prefix.hashCode()) + 37 * href.hashCode();
187
+ }
188
+
189
+ @Override
190
+ public String toString() {
191
+ return '[' + prefix + ']' + href;
165
192
  }
166
193
  }
167
194
 
168
- private class CacheEntry {
169
- private XmlNamespace namespace;
195
+ private static class CacheEntry {
196
+ final XmlNamespace namespace;
170
197
  private Node ownerNode;
171
198
 
172
199
  CacheEntry(XmlNamespace namespace, Node ownerNode) {
@@ -174,12 +201,17 @@ public class NokogiriNamespaceCache {
174
201
  this.ownerNode = ownerNode;
175
202
  }
176
203
 
177
- public Boolean isOwner(Node n) {
178
- return ownerNode.isSameNode(n);
204
+ boolean isOwner(Node node) {
205
+ return ownerNode.isSameNode(node);
179
206
  }
180
207
 
181
- public void replaceOwner(Node newNode) {
208
+ void replaceOwner(Node newNode) {
182
209
  this.ownerNode = newNode;
183
210
  }
211
+
212
+ @Override
213
+ public String toString() {
214
+ return namespace.toString();
215
+ }
184
216
  }
185
217
  }
@@ -50,9 +50,15 @@ import javax.xml.namespace.NamespaceContext;
50
50
  */
51
51
  public final class NokogiriNamespaceContext implements NamespaceContext {
52
52
 
53
- public static final String NOKOGIRI_PREFIX = "nokogiri";
53
+ /*
54
+ * these constants have matching declarations in
55
+ * ext/nokogiri/xml_xpath_context.c
56
+ */
57
+ public static final String NOKOGIRI_PREFIX = "nokogiri";
54
58
  public static final String NOKOGIRI_URI = "http://www.nokogiri.org/default_ns/ruby/extensions_functions";
55
- public static final String NOKOGIRI_TEMPORARY_ROOT_TAG = "nokogiri-temporary-root-tag";
59
+
60
+ public static final String NOKOGIRI_BUILTIN_PREFIX = "nokogiri-builtin";
61
+ public static final String NOKOGIRI_BUILTIN_URI = "https://www.nokogiri.org/default_ns/ruby/builtins";
56
62
 
57
63
  private final Map<String,String> register;
58
64
 
@@ -63,6 +69,7 @@ public final class NokogiriNamespaceContext implements NamespaceContext {
63
69
  private NokogiriNamespaceContext() {
64
70
  register = new HashMap<String, String>(6, 1);
65
71
  register.put(NOKOGIRI_PREFIX, NOKOGIRI_URI);
72
+ register.put(NOKOGIRI_BUILTIN_PREFIX, NOKOGIRI_BUILTIN_URI);
66
73
  register.put("xml", "http://www.w3.org/XML/1998/namespace");
67
74
  register.put("xhtml", "http://www.w3.org/1999/xhtml");
68
75
  }
@@ -36,6 +36,7 @@ import java.util.List;
36
36
 
37
37
  import javax.xml.xpath.XPathFunction;
38
38
  import javax.xml.xpath.XPathFunctionException;
39
+ import javax.xml.namespace.QName;
39
40
 
40
41
  import org.jruby.Ruby;
41
42
  import org.jruby.RubyArray;
@@ -53,6 +54,8 @@ import org.w3c.dom.NodeList;
53
54
  import nokogiri.XmlNode;
54
55
  import nokogiri.XmlNodeSet;
55
56
 
57
+ import static nokogiri.internals.NokogiriHelpers.nodeListToRubyArray;
58
+
56
59
  /**
57
60
  * Xpath function handler.
58
61
  *
@@ -62,14 +65,14 @@ import nokogiri.XmlNodeSet;
62
65
  public class NokogiriXPathFunction implements XPathFunction {
63
66
 
64
67
  private final IRubyObject handler;
65
- private final String name;
68
+ private final QName name;
66
69
  private final int arity;
67
70
 
68
- public static NokogiriXPathFunction create(IRubyObject handler, String name, int arity) {
71
+ public static NokogiriXPathFunction create(IRubyObject handler, QName name, int arity) {
69
72
  return new NokogiriXPathFunction(handler, name, arity);
70
73
  }
71
74
 
72
- private NokogiriXPathFunction(IRubyObject handler, String name, int arity) {
75
+ private NokogiriXPathFunction(IRubyObject handler, QName name, int arity) {
73
76
  this.handler = handler;
74
77
  this.name = name;
75
78
  this.arity = arity;
@@ -80,11 +83,20 @@ public class NokogiriXPathFunction implements XPathFunction {
80
83
  throw new XPathFunctionException("arity does not match");
81
84
  }
82
85
 
83
- final Ruby runtime = this.handler.getRuntime();
84
- ThreadContext context = runtime.getCurrentContext();
86
+ if (name.getNamespaceURI().equals(NokogiriNamespaceContext.NOKOGIRI_BUILTIN_URI)) {
87
+ if (name.getLocalPart().equals("css-class")) {
88
+ return builtinCssClass(args);
89
+ }
90
+ }
85
91
 
86
- IRubyObject result = Helpers.invoke(context, this.handler, this.name, fromObjectToRubyArgs(runtime, args));
92
+ if (this.handler.isNil()) {
93
+ throw new XPathFunctionException("no custom function handler declared for '" + name + "'");
94
+ }
87
95
 
96
+ final Ruby runtime = this.handler.getRuntime();
97
+ ThreadContext context = runtime.getCurrentContext();
98
+ IRubyObject result = Helpers.invoke(context, this.handler, this.name.getLocalPart(),
99
+ fromObjectToRubyArgs(runtime, args));
88
100
  return fromRubyToObject(runtime, result);
89
101
  }
90
102
 
@@ -99,9 +111,8 @@ public class NokogiriXPathFunction implements XPathFunction {
99
111
  private static IRubyObject fromObjectToRuby(final Ruby runtime, Object obj) {
100
112
  // argument object type is one of NodeList, String, Boolean, or Double.
101
113
  if (obj instanceof NodeList) {
102
- XmlNodeSet xmlNodeSet = XmlNodeSet.newEmptyNodeSet(runtime.getCurrentContext());
103
- xmlNodeSet.setNodeList((NodeList) obj);
104
- return xmlNodeSet;
114
+ IRubyObject[] nodes = nodeListToRubyArray(runtime, (NodeList) obj);
115
+ return XmlNodeSet.newNodeSet(runtime, nodes);
105
116
  }
106
117
  return JavaUtil.convertJavaToUsableRubyObject(runtime, obj);
107
118
  }
@@ -116,8 +127,54 @@ public class NokogiriXPathFunction implements XPathFunction {
116
127
  }
117
128
  if (obj instanceof XmlNodeSet) return obj;
118
129
  if (obj instanceof RubyArray) {
119
- return XmlNodeSet.newXmlNodeSet(runtime.getCurrentContext(), ((RubyArray) obj).toJavaArray());
130
+ return XmlNodeSet.newNodeSet(runtime, ((RubyArray) obj).toJavaArray());
120
131
  }
121
132
  /*if (o instanceof XmlNode)*/ return ((XmlNode) obj).getNode();
122
133
  }
134
+
135
+ private static boolean builtinCssClass(List args) throws XPathFunctionException {
136
+ if (args.size() != 2) {
137
+ throw new XPathFunctionException("builtin function nokogiri:css-class takes two arguments");
138
+ }
139
+
140
+ String hay = args.get(0).toString();
141
+ String needle = args.get(1).toString();
142
+
143
+ if (needle.length() == 0) {
144
+ return true;
145
+ }
146
+
147
+ int j = 0;
148
+ int j_lim = hay.length() - needle.length();
149
+ while (j <= j_lim) {
150
+ int k;
151
+ for (k = 0; k < needle.length(); k++) {
152
+ if (needle.charAt(k) != hay.charAt(j+k)) {
153
+ break;
154
+ }
155
+ }
156
+ if (k == needle.length()) {
157
+ if ((hay.length() == (j+k)) || isWhitespace(hay.charAt(j+k))) {
158
+ return true ;
159
+ }
160
+ }
161
+
162
+ /* advance str to whitespace */
163
+ while (j <= j_lim && !isWhitespace(hay.charAt(j))) {
164
+ j++;
165
+ }
166
+
167
+ /* advance str to start of next word or end of string */
168
+ while (j <= j_lim && isWhitespace(hay.charAt(j))) {
169
+ j++;
170
+ }
171
+ }
172
+
173
+ return false;
174
+ }
175
+
176
+ private static boolean isWhitespace(char subject) {
177
+ // see libxml2's xmlIsBlank_ch()
178
+ return ((subject == 0x09) || (subject == 0x0A) || (subject == 0x0D) || (subject == 0x20));
179
+ }
123
180
  }
@@ -50,7 +50,9 @@ public final class NokogiriXPathFunctionResolver implements XPathFunctionResolve
50
50
 
51
51
  public static NokogiriXPathFunctionResolver create(IRubyObject handler) {
52
52
  NokogiriXPathFunctionResolver freshResolver = new NokogiriXPathFunctionResolver();
53
- freshResolver.setHandler(handler);
53
+ if (!handler.isNil()) {
54
+ freshResolver.setHandler(handler);
55
+ }
54
56
  return freshResolver;
55
57
  }
56
58
 
@@ -65,6 +67,6 @@ public final class NokogiriXPathFunctionResolver implements XPathFunctionResolve
65
67
  }
66
68
 
67
69
  public XPathFunction resolveFunction(QName name, int arity) {
68
- return NokogiriXPathFunction.create(handler, name.getLocalPart(), arity);
70
+ return NokogiriXPathFunction.create(handler, name, arity);
69
71
  }
70
72
  }
@@ -33,27 +33,22 @@
33
33
  package nokogiri.internals;
34
34
 
35
35
  import static nokogiri.internals.NokogiriHelpers.rubyStringToString;
36
- import static org.jruby.runtime.Helpers.invoke;
37
36
 
38
37
  import java.io.ByteArrayInputStream;
39
38
  import java.io.File;
40
39
  import java.io.IOException;
41
40
  import java.io.InputStream;
42
- import java.io.StringReader;
43
41
  import java.net.URI;
44
- import java.nio.charset.Charset;
45
- import java.nio.charset.UnsupportedCharsetException;
46
42
  import java.util.concurrent.Callable;
47
43
 
48
44
  import org.jruby.Ruby;
49
45
  import org.jruby.RubyClass;
50
- import org.jruby.RubyIO;
51
46
  import org.jruby.RubyObject;
52
47
  import org.jruby.RubyString;
53
48
  import org.jruby.runtime.ThreadContext;
54
49
  import org.jruby.runtime.builtin.IRubyObject;
55
50
  import org.jruby.util.ByteList;
56
- import org.jruby.util.TypeConverter;
51
+ import org.jruby.util.IOInputStream;
57
52
  import org.xml.sax.InputSource;
58
53
 
59
54
  /**
@@ -67,6 +62,7 @@ public abstract class ParserContext extends RubyObject {
67
62
  protected InputSource source = null;
68
63
  protected IRubyObject detected_encoding = null;
69
64
  protected int stringDataSize = -1;
65
+ protected String java_encoding;
70
66
 
71
67
  public ParserContext(Ruby runtime) {
72
68
  // default to class 'Object' because this class isn't exposed to Ruby
@@ -81,68 +77,42 @@ public abstract class ParserContext extends RubyObject {
81
77
  return source;
82
78
  }
83
79
 
84
- /**
85
- * Set the InputSource from <code>url</code> or <code>data</code>,
86
- * which may be an IO object, a String, or a StringIO.
87
- */
88
- public void setInputSource(ThreadContext context, IRubyObject data, IRubyObject url) {
80
+ public void setIOInputSource(ThreadContext context, IRubyObject data, IRubyObject url) {
89
81
  source = new InputSource();
90
-
91
- Ruby ruby = context.getRuntime();
92
-
93
82
  ParserContext.setUrl(context, source, url);
94
83
 
95
- // if setEncoding returned true, then the stream is set
96
- // to the EncodingReaderInputStream
97
- if (setEncoding(context, data))
98
- return;
99
-
100
- RubyString stringData = null;
101
- if (invoke(context, data, "respond_to?", ruby.newSymbol("to_io")).isTrue()) {
102
- RubyIO io =
103
- (RubyIO) TypeConverter.convertToType(data,
104
- ruby.getIO(),
105
- "to_io");
106
- // use unclosedable input stream to fix #495
107
- source.setByteStream(new UncloseableInputStream(io.getInStream()));
108
-
109
- } else if (invoke(context, data, "respond_to?", ruby.newSymbol("read")).isTrue()) {
110
- stringData = invoke(context, data, "read").convertToString();
84
+ source.setByteStream(new IOInputStream(data));
85
+ if (java_encoding != null) {
86
+ source.setEncoding(java_encoding);
87
+ }
88
+ }
111
89
 
112
- } else if (invoke(context, data, "respond_to?", ruby.newSymbol("string")).isTrue()) {
113
- stringData = invoke(context, data, "string").convertToString();
90
+ public void setStringInputSource(ThreadContext context, IRubyObject data, IRubyObject url) {
91
+ source = new InputSource();
92
+ ParserContext.setUrl(context, source, url);
114
93
 
115
- } else if (data instanceof RubyString) {
116
- stringData = (RubyString) data;
94
+ Ruby ruby = context.getRuntime();
117
95
 
118
- } else {
119
- throw ruby.newArgumentError("must be kind_of String or respond to :to_io, :read, or :string");
96
+ if (!(data instanceof RubyString)) {
97
+ throw ruby.newArgumentError("must be kind_of String");
120
98
  }
121
99
 
122
- if (stringData != null) {
123
- String encName = null;
124
- if (stringData.encoding(context) != null) {
125
- encName = stringData.encoding(context).toString();
126
- }
127
- Charset charset = null;
100
+ RubyString stringData = (RubyString) data;
101
+
102
+ if (stringData.encoding(context) != null) {
103
+ RubyString stringEncoding = stringData.encoding(context).asString();
104
+ String encName = NokogiriHelpers.getValidEncodingOrNull(stringEncoding);
128
105
  if (encName != null) {
129
- try {
130
- charset = Charset.forName(encName);
131
- } catch (UnsupportedCharsetException e) {
132
- // do nothing;
133
- }
134
- }
135
- ByteList bytes = stringData.getByteList();
136
- if (charset != null) {
137
- StringReader reader = new StringReader(new String(bytes.unsafeBytes(), bytes.begin(), bytes.length(), charset));
138
- source.setCharacterStream(reader);
139
- source.setEncoding(charset.name());
140
- } else {
141
- stringDataSize = bytes.length() - bytes.begin();
142
- ByteArrayInputStream stream = new ByteArrayInputStream(bytes.unsafeBytes(), bytes.begin(), bytes.length());
143
- source.setByteStream(stream);
106
+ java_encoding = encName;
144
107
  }
145
108
  }
109
+
110
+ ByteList bytes = stringData.getByteList();
111
+
112
+ stringDataSize = bytes.length() - bytes.begin();
113
+ ByteArrayInputStream stream = new ByteArrayInputStream(bytes.unsafeBytes(), bytes.begin(), bytes.length());
114
+ source.setByteStream(stream);
115
+ source.setEncoding(java_encoding);
146
116
  }
147
117
 
148
118
  public static void setUrl(ThreadContext context, InputSource source, IRubyObject url) {
@@ -171,22 +141,6 @@ public abstract class ParserContext extends RubyObject {
171
141
  }
172
142
  }
173
143
 
174
- private boolean setEncoding(ThreadContext context, IRubyObject data) {
175
- if (data.getType().respondsTo("detect_encoding")) {
176
- // in case of EncodingReader is used
177
- // since EncodingReader won't respond to :to_io
178
- NokogiriEncodingReaderWrapper reader = new NokogiriEncodingReaderWrapper(context, (RubyObject) data);
179
- source.setByteStream(reader);
180
- // data is EnocodingReader
181
- if(reader.detectEncoding()) {
182
- detected_encoding = reader.getEncoding();
183
- source.setEncoding(detected_encoding.asJavaString());
184
- }
185
- return true;
186
- }
187
- return false;
188
- }
189
-
190
144
  protected void setEncoding(String encoding) {
191
145
  source.setEncoding(encoding);
192
146
  }