nokogiri 1.7.2-java → 1.8.0-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 (101) hide show
  1. checksums.yaml +4 -4
  2. data/.cross_rubies +4 -4
  3. data/.travis.yml +43 -24
  4. data/CHANGELOG.md +54 -6
  5. data/Gemfile +8 -7
  6. data/Gemfile-libxml-ruby +3 -0
  7. data/LICENSE-DEPENDENCIES.md +1612 -0
  8. data/{LICENSE.txt → LICENSE.md} +1 -1
  9. data/Manifest.txt +5 -8
  10. data/README.md +8 -5
  11. data/Rakefile +15 -31
  12. data/appveyor.yml +2 -0
  13. data/dependencies.yml +12 -7
  14. data/ext/java/nokogiri/HtmlDocument.java +2 -2
  15. data/ext/java/nokogiri/HtmlSaxParserContext.java +20 -21
  16. data/ext/java/nokogiri/HtmlSaxPushParser.java +6 -10
  17. data/ext/java/nokogiri/NokogiriService.java +10 -31
  18. data/ext/java/nokogiri/XmlAttr.java +1 -26
  19. data/ext/java/nokogiri/XmlCdata.java +0 -1
  20. data/ext/java/nokogiri/XmlComment.java +1 -1
  21. data/ext/java/nokogiri/XmlDocument.java +4 -5
  22. data/ext/java/nokogiri/XmlDocumentFragment.java +29 -21
  23. data/ext/java/nokogiri/XmlDtd.java +1 -1
  24. data/ext/java/nokogiri/XmlElement.java +9 -10
  25. data/ext/java/nokogiri/XmlEntityDecl.java +4 -5
  26. data/ext/java/nokogiri/XmlNode.java +105 -103
  27. data/ext/java/nokogiri/XmlNodeSet.java +64 -76
  28. data/ext/java/nokogiri/XmlReader.java +48 -48
  29. data/ext/java/nokogiri/XmlRelaxng.java +1 -1
  30. data/ext/java/nokogiri/XmlSaxPushParser.java +37 -17
  31. data/ext/java/nokogiri/XmlSchema.java +7 -5
  32. data/ext/java/nokogiri/XmlSyntaxError.java +47 -35
  33. data/ext/java/nokogiri/XmlXpathContext.java +160 -132
  34. data/ext/java/nokogiri/XsltStylesheet.java +15 -24
  35. data/ext/java/nokogiri/internals/HtmlDomParserContext.java +19 -23
  36. data/ext/java/nokogiri/internals/NokogiriDomParser.java +1 -1
  37. data/ext/java/nokogiri/internals/NokogiriEncodingReaderWrapper.java +1 -1
  38. data/ext/java/nokogiri/internals/NokogiriEntityResolver.java +11 -13
  39. data/ext/java/nokogiri/internals/NokogiriErrorHandler.java +5 -21
  40. data/ext/java/nokogiri/internals/NokogiriHandler.java +1 -1
  41. data/ext/java/nokogiri/internals/NokogiriHelpers.java +105 -142
  42. data/ext/java/nokogiri/internals/NokogiriNamespaceContext.java +16 -26
  43. data/ext/java/nokogiri/internals/NokogiriXPathFunction.java +32 -50
  44. data/ext/java/nokogiri/internals/NokogiriXPathFunctionResolver.java +10 -13
  45. data/ext/java/nokogiri/internals/NokogiriXPathVariableResolver.java +3 -10
  46. data/ext/java/nokogiri/internals/ParserContext.java +4 -8
  47. data/ext/java/nokogiri/internals/ReaderNode.java +53 -93
  48. data/ext/java/nokogiri/internals/SaveContextVisitor.java +77 -89
  49. data/ext/java/nokogiri/internals/SchemaErrorHandler.java +6 -9
  50. data/ext/java/nokogiri/internals/XalanDTMManagerPatch.java +167 -0
  51. data/ext/java/nokogiri/internals/XmlDomParserContext.java +17 -6
  52. data/ext/java/nokogiri/internals/c14n/Canonicalizer.java +1 -1
  53. data/ext/java/nokogiri/internals/c14n/Canonicalizer11.java +28 -28
  54. data/ext/java/nokogiri/internals/c14n/Canonicalizer20010315.java +3 -4
  55. data/ext/java/nokogiri/internals/c14n/Canonicalizer20010315Excl.java +2 -2
  56. data/ext/java/nokogiri/internals/c14n/CanonicalizerBase.java +10 -10
  57. data/ext/java/nokogiri/internals/c14n/ElementProxy.java +5 -5
  58. data/ext/java/nokogiri/internals/c14n/InclusiveNamespaces.java +2 -2
  59. data/ext/java/nokogiri/internals/c14n/NameSpaceSymbTable.java +1 -1
  60. data/ext/java/nokogiri/internals/c14n/XMLUtils.java +2 -2
  61. data/ext/java/org/apache/xml/dtm/ref/dom2dtm/DOM2DTMExt.java +1749 -0
  62. data/ext/nokogiri/extconf.rb +12 -17
  63. data/ext/nokogiri/nokogiri.h +0 -10
  64. data/ext/nokogiri/xml_attr.c +12 -8
  65. data/ext/nokogiri/xml_node.c +17 -14
  66. data/ext/nokogiri/xml_sax_push_parser.c +56 -12
  67. data/lib/nokogiri/html/sax/parser.rb +10 -0
  68. data/lib/nokogiri/nokogiri.jar +0 -0
  69. data/lib/nokogiri/version.rb +5 -4
  70. data/lib/nokogiri/xml/document.rb +9 -9
  71. data/lib/nokogiri/xml/node.rb +7 -7
  72. data/lib/nokogiri/xml/node_set.rb +12 -7
  73. data/lib/nokogiri/xml/sax/parser.rb +6 -7
  74. data/lib/nokogiri/xml/searchable.rb +34 -25
  75. data/lib/nokogiri/xml/syntax_error.rb +24 -1
  76. data/test/decorators/test_slop.rb +4 -1
  77. data/test/helper.rb +10 -0
  78. data/test/html/sax/test_parser.rb +27 -0
  79. data/test/html/test_document.rb +12 -1
  80. data/test/html/test_document_encoding.rb +1 -3
  81. data/test/html/test_document_fragment.rb +3 -0
  82. data/test/xml/sax/test_push_parser.rb +48 -0
  83. data/test/xml/test_attr.rb +7 -0
  84. data/test/xml/test_document.rb +1 -1
  85. data/test/xml/test_document_fragment.rb +27 -0
  86. data/test/xml/test_entity_reference.rb +2 -2
  87. data/test/xml/test_node.rb +12 -15
  88. data/test/xml/test_node_reparenting.rb +14 -0
  89. data/test/xml/test_node_set.rb +8 -6
  90. data/test/xml/test_reader.rb +19 -0
  91. data/test/xml/test_syntax_error.rb +21 -15
  92. data/test/xml/test_unparented_node.rb +54 -11
  93. data/test/xml/test_xpath.rb +23 -6
  94. metadata +32 -20
  95. data/ext/java/nokogiri/internals/NokogiriDocumentCache.java +0 -73
  96. data/ext/java/nokogiri/internals/XsltExtensionFunction.java +0 -72
  97. data/suppressions/nokogiri_ree-1.8.7.358.supp +0 -61
  98. data/suppressions/nokogiri_ruby-1.8.7.370.supp +0 -0
  99. data/suppressions/nokogiri_ruby-1.9.2.320.supp +0 -28
  100. data/suppressions/nokogiri_ruby-1.9.3.327.supp +0 -28
  101. data/test_all +0 -105
@@ -56,7 +56,7 @@ import org.w3c.dom.Node;
56
56
  */
57
57
 
58
58
  @JRubyClass(name="Nokogiri::XML::Attr", parent="Nokogiri::XML::Node")
59
- public class XmlAttr extends XmlNode{
59
+ public class XmlAttr extends XmlNode {
60
60
 
61
61
  public static final String[] HTML_BOOLEAN_ATTRS = {
62
62
  "checked", "compact", "declare", "defer", "disabled", "ismap",
@@ -117,31 +117,6 @@ public class XmlAttr extends XmlNode{
117
117
  return false;
118
118
  }
119
119
 
120
-
121
- private String serializeAttrTextContent(String s, boolean htmlDoc) {
122
- if (s == null) return "";
123
-
124
- char[] c = s.toCharArray();
125
- StringBuffer buffer = new StringBuffer(c.length);
126
-
127
- for(int i = 0; i < c.length; i++) {
128
- switch(c[i]){
129
- case '\n': buffer.append("&#10;"); break;
130
- case '\r': buffer.append("&#13;"); break;
131
- case '\t': buffer.append("&#9;"); break;
132
- case '"': if (htmlDoc) buffer.append("%22");
133
- else buffer.append("&quot;");
134
- break;
135
- case '<': buffer.append("&lt;"); break;
136
- case '>': buffer.append("&gt;"); break;
137
- case '&': buffer.append("&amp;"); break;
138
- default: buffer.append(c[i]);
139
- }
140
- }
141
-
142
- return buffer.toString();
143
- }
144
-
145
120
  @Override
146
121
  @JRubyMethod(name = {"content", "value", "to_s"})
147
122
  public IRubyObject content(ThreadContext context) {
@@ -70,7 +70,6 @@ public class XmlCdata extends XmlText {
70
70
  IRubyObject doc = args[0];
71
71
  content = args[1];
72
72
  XmlDocument xmlDoc =(XmlDocument) ((XmlNode) doc).document(context);
73
- doc = xmlDoc;
74
73
  Document document = xmlDoc.getDocument();
75
74
  Node node = document.createCDATASection((content.isNil()) ? null : rubyStringToString(content));
76
75
  setNode(context, node);
@@ -69,7 +69,7 @@ public class XmlComment extends XmlNode {
69
69
  IRubyObject doc = args[0];
70
70
  IRubyObject text = args[1];
71
71
 
72
- XmlDocument xmlDoc = null;
72
+ XmlDocument xmlDoc;
73
73
  if (doc instanceof XmlDocument) {
74
74
  xmlDoc = (XmlDocument) doc;
75
75
 
@@ -249,7 +249,7 @@ public class XmlDocument extends XmlNode {
249
249
  */
250
250
  @JRubyMethod(name="new", meta = true, rest = true, required=0)
251
251
  public static IRubyObject rbNew(ThreadContext context, IRubyObject klazz, IRubyObject[] args) {
252
- XmlDocument xmlDocument = null;
252
+ XmlDocument xmlDocument;
253
253
  try {
254
254
  Document docNode = createNewDocument();
255
255
  if ("Nokogiri::HTML::Document".equals(((RubyClass)klazz).getName())) {
@@ -281,8 +281,7 @@ public class XmlDocument extends XmlNode {
281
281
  }
282
282
 
283
283
  @Override
284
- @JRubyMethod
285
- public IRubyObject document(ThreadContext context) {
284
+ IRubyObject document(Ruby runtime) {
286
285
  return this;
287
286
  }
288
287
 
@@ -575,7 +574,7 @@ public class XmlDocument extends XmlNode {
575
574
  */
576
575
  @JRubyMethod(optional=3)
577
576
  public IRubyObject canonicalize(ThreadContext context, IRubyObject[] args, Block block) {
578
- Integer mode = 0;
577
+ int mode = 0;
579
578
  String inclusive_namespace = null;
580
579
  Boolean with_comments = false;
581
580
  if (args.length > 0 && !(args[0].isNil())) {
@@ -586,7 +585,7 @@ public class XmlDocument extends XmlNode {
586
585
  throw context.getRuntime().newTypeError("Expected array");
587
586
  }
588
587
  if (!args[1].isNil()) {
589
- inclusive_namespace = (String)((RubyArray)args[1])
588
+ inclusive_namespace = ((RubyArray)args[1])
590
589
  .join(context, context.getRuntime().newString(" "))
591
590
  .asString()
592
591
  .asJavaString(); // OMG I wish I knew JRuby better, this is ugly
@@ -53,6 +53,7 @@ import org.jruby.anno.JRubyMethod;
53
53
  import org.jruby.javasupport.util.RuntimeHelpers;
54
54
  import org.jruby.runtime.ThreadContext;
55
55
  import org.jruby.runtime.builtin.IRubyObject;
56
+ import org.jruby.util.ByteList;
56
57
  import org.w3c.dom.Attr;
57
58
  import org.w3c.dom.NamedNodeMap;
58
59
 
@@ -106,14 +107,12 @@ public class XmlDocumentFragment extends XmlNode {
106
107
  return fragment;
107
108
  }
108
109
 
109
- private static boolean isTag(RubyString ruby_string) {
110
- String str = rubyStringToString(ruby_string);
111
- if (str.startsWith("<") && str.endsWith(">")) return true;
112
- return false;
113
- }
110
+ private static final ByteList TAG_BEG = ByteList.create("<");
111
+ private static final ByteList TAG_END = ByteList.create(">");
114
112
 
115
- private static Pattern qname_pattern = Pattern.compile("[^</:>\\s]+:[^</:>=\\s]+");
116
- private static Pattern starttag_pattern = Pattern.compile("<[^</>]+>");
113
+ private static boolean isTag(final RubyString str) {
114
+ return str.getByteList().startsWith(TAG_BEG) && str.getByteList().endsWith(TAG_END);
115
+ }
117
116
 
118
117
  private static boolean isNamespaceDefined(String qName, NamedNodeMap nodeMap) {
119
118
  if (isNamespace(qName.intern())) return true;
@@ -128,39 +127,48 @@ public class XmlDocumentFragment extends XmlNode {
128
127
  }
129
128
  return false;
130
129
  }
131
-
130
+
131
+ private static final Pattern QNAME_RE = Pattern.compile("[^</:>\\s]+:[^</:>=\\s]+");
132
+ private static final Pattern START_TAG_RE = Pattern.compile("<[^</>]+>");
133
+
132
134
  private static String addNamespaceDeclIfNeeded(XmlDocument doc, String tags) {
133
135
  if (doc.getDocument() == null) return tags;
134
136
  if (doc.getDocument().getDocumentElement() == null) return tags;
135
- Matcher matcher = starttag_pattern.matcher(tags);
136
- Map<String, String> rewriteTable = new HashMap<String, String>();
137
- while(matcher.find()) {
137
+ Matcher matcher = START_TAG_RE.matcher(tags);
138
+ Map<CharSequence, CharSequence> rewriteTable = null;
139
+ while (matcher.find()) {
138
140
  String start_tag = matcher.group();
139
- Matcher matcher2 = qname_pattern.matcher(start_tag);
140
- while(matcher2.find()) {
141
+ Matcher matcher2 = QNAME_RE.matcher(start_tag);
142
+ while (matcher2.find()) {
141
143
  String qName = matcher2.group();
142
144
  NamedNodeMap nodeMap = doc.getDocument().getDocumentElement().getAttributes();
143
145
  if (isNamespaceDefined(qName, nodeMap)) {
144
- String namespaceDecl = getNamespceDecl(getPrefix(qName), nodeMap);
146
+ CharSequence namespaceDecl = getNamespaceDecl(getPrefix(qName), nodeMap);
145
147
  if (namespaceDecl != null) {
146
- rewriteTable.put("<"+qName+">", "<"+qName + " " + namespaceDecl+">");
148
+ if (rewriteTable == null) rewriteTable = new HashMap(8, 1);
149
+ StringBuilder str = new StringBuilder(qName.length() + namespaceDecl.length() + 3);
150
+ String key = str.append('<').append(qName).append('>').toString();
151
+ str.setCharAt(key.length() - 1, ' '); // (last) '>' -> ' '
152
+ rewriteTable.put(key, str.append(namespaceDecl).append('>'));
147
153
  }
148
154
  }
149
155
  }
150
156
  }
151
- Set<String> keys = rewriteTable.keySet();
152
- for (String key : keys) {
153
- tags = tags.replace(key, rewriteTable.get(key));
157
+ if (rewriteTable != null) {
158
+ for (Map.Entry<CharSequence, CharSequence> e : rewriteTable.entrySet()) {
159
+ tags = tags.replace(e.getKey(), e.getValue());
160
+ }
154
161
  }
155
162
 
156
163
  return tags;
157
164
  }
158
165
 
159
- private static String getNamespceDecl(String prefix, NamedNodeMap nodeMap) {
166
+ private static CharSequence getNamespaceDecl(final String prefix, NamedNodeMap nodeMap) {
160
167
  for (int i=0; i < nodeMap.getLength(); i++) {
161
- Attr attr = (Attr)nodeMap.item(i);
168
+ Attr attr = (Attr) nodeMap.item(i);
162
169
  if (prefix.equals(attr.getLocalName())) {
163
- return attr.getName() + "=\"" + attr.getValue() + "\"";
170
+ return new StringBuilder().
171
+ append(attr.getName()).append('=').append('"').append(attr.getValue()).append('"');
164
172
  }
165
173
  }
166
174
  return null;
@@ -130,7 +130,7 @@ public class XmlDtd extends XmlNode {
130
130
  IRubyObject external_id,
131
131
  IRubyObject system_id) {
132
132
 
133
- DocumentType placeholder = null;
133
+ DocumentType placeholder;
134
134
  if (doc.getDoctype() == null) {
135
135
  String javaName = NokogiriHelpers.rubyStringToString(name);
136
136
  String javaExternalId = NokogiriHelpers.rubyStringToString(external_id);
@@ -68,21 +68,20 @@ public class XmlElement extends XmlNode {
68
68
 
69
69
  @Override
70
70
  public void accept(ThreadContext context, SaveContextVisitor visitor) {
71
- visitor.enter((Element)node);
71
+ visitor.enter((Element) node);
72
72
  XmlNodeSet xmlNodeSet = (XmlNodeSet) children(context);
73
73
  if (xmlNodeSet.length() > 0) {
74
- RubyArray array = (RubyArray) xmlNodeSet.to_a(context);
75
- for(int i = 0; i < array.getLength(); i++) {
76
- Object item = array.get(i);
74
+ RubyArray nodes = xmlNodeSet.nodes;
75
+ for( int i = 0; i < nodes.size(); i++ ) {
76
+ Object item = nodes.eltInternal(i);
77
77
  if (item instanceof XmlNode) {
78
- XmlNode cur = (XmlNode) item;
79
- cur.accept(context, visitor);
80
- } else if (item instanceof XmlNamespace) {
81
- XmlNamespace cur = (XmlNamespace)item;
82
- cur.accept(context, visitor);
78
+ ((XmlNode) item).accept(context, visitor);
79
+ }
80
+ else if (item instanceof XmlNamespace) {
81
+ ((XmlNamespace) item).accept(context, visitor);
83
82
  }
84
83
  }
85
84
  }
86
- visitor.leave((Element)node);
85
+ visitor.leave((Element) node);
87
86
  }
88
87
  }
@@ -37,7 +37,6 @@ import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
37
37
  import org.jruby.Ruby;
38
38
  import org.jruby.RubyClass;
39
39
  import org.jruby.RubyFixnum;
40
- import org.jruby.RubyNil;
41
40
  import org.jruby.anno.JRubyClass;
42
41
  import org.jruby.anno.JRubyMethod;
43
42
  import org.jruby.runtime.ThreadContext;
@@ -117,7 +116,7 @@ public class XmlEntityDecl extends XmlNode {
117
116
  @JRubyMethod
118
117
  public IRubyObject node_name(ThreadContext context) {
119
118
  IRubyObject value = getAttribute(context, "name");
120
- if (value instanceof RubyNil) value = name;
119
+ if (value.isNil()) value = name;
121
120
  return value;
122
121
  }
123
122
 
@@ -131,7 +130,7 @@ public class XmlEntityDecl extends XmlNode {
131
130
  @JRubyMethod
132
131
  public IRubyObject content(ThreadContext context) {
133
132
  IRubyObject value = getAttribute(context, "value");
134
- if (value instanceof RubyNil) value = content;
133
+ if (value.isNil()) value = content;
135
134
  return value;
136
135
  }
137
136
 
@@ -144,14 +143,14 @@ public class XmlEntityDecl extends XmlNode {
144
143
  @JRubyMethod
145
144
  public IRubyObject system_id(ThreadContext context) {
146
145
  IRubyObject value = getAttribute(context, "sysid");
147
- if (value instanceof RubyNil) value = system_id;
146
+ if (value.isNil()) value = system_id;
148
147
  return value;
149
148
  }
150
149
 
151
150
  @JRubyMethod
152
151
  public IRubyObject external_id(ThreadContext context) {
153
152
  IRubyObject value = getAttribute(context, "pubid");
154
- if (value instanceof RubyNil) value = external_id;
153
+ if (value.isNil()) value = external_id;
155
154
  return value;
156
155
  }
157
156
 
@@ -36,6 +36,8 @@ import static java.lang.Math.max;
36
36
  import static nokogiri.internals.NokogiriHelpers.getCachedNodeOrCreate;
37
37
  import static nokogiri.internals.NokogiriHelpers.clearCachedNode;
38
38
  import static nokogiri.internals.NokogiriHelpers.clearXpathContext;
39
+ import static nokogiri.internals.NokogiriHelpers.convertEncoding;
40
+ import static nokogiri.internals.NokogiriHelpers.convertString;
39
41
  import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
40
42
  import static nokogiri.internals.NokogiriHelpers.nodeArrayToRubyArray;
41
43
  import static nokogiri.internals.NokogiriHelpers.nonEmptyStringOrNil;
@@ -44,6 +46,7 @@ import static nokogiri.internals.NokogiriHelpers.stringOrNil;
44
46
 
45
47
  import java.io.ByteArrayInputStream;
46
48
  import java.io.InputStream;
49
+ import java.nio.ByteBuffer;
47
50
  import java.nio.charset.CharacterCodingException;
48
51
  import java.nio.charset.Charset;
49
52
  import java.util.ArrayList;
@@ -60,6 +63,7 @@ import org.apache.xerces.dom.CoreDocumentImpl;
60
63
  import org.jruby.Ruby;
61
64
  import org.jruby.RubyArray;
62
65
  import org.jruby.RubyClass;
66
+ import org.jruby.RubyInteger;
63
67
  import org.jruby.RubyFixnum;
64
68
  import org.jruby.RubyModule;
65
69
  import org.jruby.RubyObject;
@@ -72,6 +76,7 @@ import org.jruby.runtime.Block;
72
76
  import org.jruby.runtime.ThreadContext;
73
77
  import org.jruby.runtime.Visibility;
74
78
  import org.jruby.runtime.builtin.IRubyObject;
79
+ import org.jruby.util.ByteList;
75
80
  import org.w3c.dom.Attr;
76
81
  import org.w3c.dom.Document;
77
82
  import org.w3c.dom.DocumentFragment;
@@ -119,12 +124,11 @@ public class XmlNode extends RubyObject {
119
124
  * in <code>context</code>.
120
125
  */
121
126
  protected static XmlNode asXmlNode(ThreadContext context, IRubyObject node) {
122
- if (node == null || !(node instanceof XmlNode)) {
123
- Ruby ruby = context.getRuntime();
124
- throw ruby.newTypeError(node, getNokogiriClass(ruby, "Nokogiri::XML::Node"));
125
- } else {
126
- return (XmlNode) node;
127
+ if ( !(node instanceof XmlNode) ) {
128
+ final Ruby runtime = context.getRuntime();
129
+ throw runtime.newTypeError(node == null ? runtime.getNil() : node, getNokogiriClass(runtime, "Nokogiri::XML::Node"));
127
130
  }
131
+ return (XmlNode) node;
128
132
  }
129
133
 
130
134
  /**
@@ -132,11 +136,8 @@ public class XmlNode extends RubyObject {
132
136
  * raise a type error in <code>context</code>.
133
137
  */
134
138
  protected static XmlNode asXmlNodeOrNull(ThreadContext context, IRubyObject node) {
135
- if (node == null || node.isNil()) {
136
- return null;
137
- } else {
138
- return asXmlNode(context, node);
139
- }
139
+ if (node == null || node.isNil()) return null;
140
+ return asXmlNode(context, node);
140
141
  }
141
142
 
142
143
  /**
@@ -219,15 +220,15 @@ public class XmlNode extends RubyObject {
219
220
  setNode(ruby.getCurrentContext(), node);
220
221
  }
221
222
 
222
- protected void decorate(Ruby ruby) {
223
+ protected void decorate(final ThreadContext context) {
223
224
  if (node != null) {
224
225
  resetCache();
225
226
 
226
227
  if (node.getNodeType() != Node.DOCUMENT_NODE) {
227
- doc = document(ruby.getCurrentContext());
228
+ doc = document(context.runtime);
228
229
 
229
- if (doc != null && doc.isTrue()) {
230
- RuntimeHelpers.invoke(ruby.getCurrentContext(), doc, "decorate", this);
230
+ if (doc != null && ! doc.isNil()) {
231
+ RuntimeHelpers.invoke(context, doc, "decorate", this);
231
232
  }
232
233
  }
233
234
  }
@@ -290,14 +291,14 @@ public class XmlNode extends RubyObject {
290
291
  Ruby ruby = context.getRuntime();
291
292
  RubyClass klazz = (RubyClass) cls;
292
293
 
293
- if (cls.equals(getNokogiriClass(ruby, "Nokogiri::XML::Node"))) {
294
+ if ("Nokogiri::XML::Node".equals(klazz.getName())) {
294
295
  klazz = getNokogiriClass(ruby, "Nokogiri::XML::Element");
295
296
  }
296
297
 
297
298
  XmlNode xmlNode = (XmlNode) klazz.allocate();
298
299
  xmlNode.init(context, args);
299
300
  xmlNode.callInit(args, block);
300
- if (xmlNode.node == null) context.getRuntime().newRuntimeError("NODE IS NULL");
301
+ assert xmlNode.node != null;
301
302
  if (block.isGiven()) block.call(context, xmlNode);
302
303
  return xmlNode;
303
304
  }
@@ -330,7 +331,7 @@ public class XmlNode extends RubyObject {
330
331
  throw getRuntime().newArgumentError("node must have owner document");
331
332
  }
332
333
 
333
- Element element = null;
334
+ Element element;
334
335
  String node_name = rubyStringToString(name);
335
336
  String prefix = NokogiriHelpers.getPrefix(node_name);
336
337
  String namespace_uri = null;
@@ -380,7 +381,7 @@ public class XmlNode extends RubyObject {
380
381
  boolean closingTag = false;
381
382
  String indentString = rubyStringToString(indentStringObject);
382
383
  int lengthInd = indentString.length();
383
- StringBuffer curInd = new StringBuffer();
384
+ StringBuilder curInd = new StringBuilder();
384
385
 
385
386
  resultLines[0] = lines[0];
386
387
 
@@ -404,10 +405,9 @@ public class XmlNode extends RubyObject {
404
405
  closingTag = false;
405
406
  }
406
407
 
407
- StringBuffer result = new StringBuffer();
408
+ StringBuilder result = new StringBuilder();
408
409
  for(int i = 0; i < resultLines.length; i++) {
409
- result.append(resultLines[i]);
410
- result.append("\n");
410
+ result.append(resultLines[i]).append('\n');
411
411
  }
412
412
 
413
413
  return result.toString();
@@ -479,7 +479,7 @@ public class XmlNode extends RubyObject {
479
479
  String nsURI = e.lookupNamespaceURI(prefix);
480
480
  this.node = NokogiriHelpers.renameNode(e, nsURI, e.getNodeName());
481
481
 
482
- if (nsURI == null || nsURI == "") {
482
+ if (nsURI == null || nsURI.isEmpty()) {
483
483
  return;
484
484
  }
485
485
 
@@ -488,12 +488,12 @@ public class XmlNode extends RubyObject {
488
488
 
489
489
  for (int i = 0; i < attrs.getLength(); i++) {
490
490
  Attr attr = (Attr) attrs.item(i);
491
- String nsUri = "";
492
491
  String attrPrefix = attr.getPrefix();
493
492
  if (attrPrefix == null) {
494
493
  attrPrefix = NokogiriHelpers.getPrefix(attr.getNodeName());
495
494
  }
496
495
  String nodeName = attr.getNodeName();
496
+ String nsUri;
497
497
  if ("xml".equals(attrPrefix)) {
498
498
  nsUri = "http://www.w3.org/XML/1998/namespace";
499
499
  } else if ("xmlns".equals(attrPrefix) || nodeName.equals("xmlns")) {
@@ -549,16 +549,22 @@ public class XmlNode extends RubyObject {
549
549
 
550
550
  public void setDocument(ThreadContext context, IRubyObject doc) {
551
551
  this.doc = doc;
552
- setInstanceVariable("@document", doc);
552
+
553
+ setDocumentAndDecorate(context, this, doc);
554
+ }
555
+
556
+ // shared logic with XmlNodeSet
557
+ static void setDocumentAndDecorate(ThreadContext context, RubyObject self, IRubyObject doc) {
558
+ self.setInstanceVariable("@document", doc);
553
559
  if (doc != null) {
554
- RuntimeHelpers.invoke(context, doc, "decorate", this);
560
+ RuntimeHelpers.invoke(context, doc, "decorate", self);
555
561
  }
556
562
  }
557
563
 
558
564
  public void setNode(ThreadContext context, Node node) {
559
565
  this.node = node;
560
566
 
561
- decorate(context.getRuntime());
567
+ decorate(context);
562
568
 
563
569
  if (this instanceof XmlAttr) {
564
570
  ((XmlAttr)this).setNamespaceIfNecessary(context.getRuntime());
@@ -583,17 +589,15 @@ public class XmlNode extends RubyObject {
583
589
 
584
590
  protected IRubyObject getNodeName(ThreadContext context) {
585
591
  if (name != null) return name;
586
- String str = null;
587
592
 
588
- if (this.name == null && node != null) {
593
+ String str = null;
594
+ if (node != null) {
589
595
  str = node.getNodeName();
590
596
  str = NokogiriHelpers.getLocalPart(str);
591
597
  }
592
598
  if (str == null) str = "";
593
599
  if (str.startsWith("#")) str = str.substring(1); // eliminates '#'
594
- name = NokogiriHelpers.stringOrBlank(context.getRuntime(), str);
595
-
596
- return name;
600
+ return name = NokogiriHelpers.stringOrBlank(context.getRuntime(), str);
597
601
  }
598
602
 
599
603
  /**
@@ -696,10 +700,9 @@ public class XmlNode extends RubyObject {
696
700
  // a node is blank if if it is a Text or CDATA node consisting of whitespace only
697
701
  if (node.getNodeType() == Node.TEXT_NODE || node.getNodeType() == Node.CDATA_SECTION_NODE) {
698
702
  String data = node.getTextContent();
699
- if (data == null) return context.getRuntime().getTrue();
700
- if ("".equals(data.trim())) return context.getRuntime().getTrue();
703
+ return context.runtime.newBoolean(data == null || data.isEmpty() || data.trim().isEmpty());
701
704
  }
702
- return context.getRuntime().getFalse();
705
+ return context.runtime.getFalse();
703
706
  }
704
707
 
705
708
  @JRubyMethod
@@ -709,8 +712,16 @@ public class XmlNode extends RubyObject {
709
712
 
710
713
  @JRubyMethod
711
714
  public IRubyObject children(ThreadContext context) {
712
- XmlNodeSet xmlNodeSet = (XmlNodeSet) NokogiriService.XML_NODESET_ALLOCATOR.allocate(context.getRuntime(), getNokogiriClass(context.getRuntime(), "Nokogiri::XML::NodeSet"));
713
- xmlNodeSet.setNodeList(node.getChildNodes());
715
+ XmlNodeSet xmlNodeSet = XmlNodeSet.create(context.runtime);
716
+
717
+ NodeList nodeList = node.getChildNodes();
718
+ if (nodeList.getLength() > 0) {
719
+ xmlNodeSet.setNodeList(nodeList); // initializes @document from first node
720
+ }
721
+ else { // TODO this is very ripe for refactoring
722
+ setDocumentAndDecorate(context, xmlNodeSet, doc);
723
+ }
724
+
714
725
  return xmlNodeSet;
715
726
  }
716
727
 
@@ -815,13 +826,11 @@ public class XmlNode extends RubyObject {
815
826
  ctx = new HtmlDomParserContext(runtime, options);
816
827
  ((HtmlDomParserContext)ctx).enableDocumentFragment();
817
828
  istream = new ByteArrayInputStream((rubyStringToString(str)).getBytes());
818
- } else if (document instanceof XmlDocument) {
829
+ } else {
819
830
  klass = getNokogiriClass(runtime, "Nokogiri::XML::Document");
820
831
  ctx = new XmlDomParserContext(runtime, options);
821
832
  String input = rubyStringToString(str);
822
833
  istream = new ByteArrayInputStream(input.getBytes());
823
- } else {
824
- return runtime.getNil();
825
834
  }
826
835
 
827
836
  ctx.setInputSource(istream);
@@ -869,84 +878,86 @@ public class XmlNode extends RubyObject {
869
878
  }
870
879
 
871
880
  private boolean isErrorIncreased(RubyArray baseErrors, RubyArray createdErrors) {
872
- RubyFixnum length = ((RubyArray)createdErrors.op_diff(baseErrors)).length();
873
- int diff_in_length = (Integer)length.toJava(Integer.class);
874
- return diff_in_length > 0;
881
+ int length = ((RubyArray) createdErrors.op_diff(baseErrors)).size();
882
+ return length > 0;
875
883
  }
876
884
 
877
885
  @JRubyMethod(name = {"content", "text", "inner_text"})
878
886
  public IRubyObject content(ThreadContext context) {
887
+ return stringOrNil(context.getRuntime(), getContentImpl());
888
+ }
889
+
890
+ public CharSequence getContentImpl() {
879
891
  if (!node.hasChildNodes() && node.getNodeValue() == null &&
880
- (node.getNodeType() == Node.TEXT_NODE || node.getNodeType() == Node.CDATA_SECTION_NODE))
881
- return context.nil;
882
- String textContent;
892
+ (node.getNodeType() == Node.TEXT_NODE || node.getNodeType() == Node.CDATA_SECTION_NODE)) {
893
+ return null;
894
+ }
895
+ CharSequence textContent;
883
896
  if (this instanceof XmlDocument) {
884
897
  Node node = ((Document)this.node).getDocumentElement();
885
898
  if (node == null) {
886
899
  textContent = "";
887
900
  } else {
888
- Node documentElement = ((Document)this.node).getDocumentElement();
889
- StringBuffer buffer = new StringBuffer();
890
- getTextContentRecursively(context, buffer, documentElement);
891
- textContent = buffer.toString();
901
+ Node documentElement = ((Document) this.node).getDocumentElement();
902
+ textContent = getTextContentRecursively(new StringBuilder(), documentElement);
892
903
  }
893
904
  } else {
894
- StringBuffer buffer = new StringBuffer();
895
- getTextContentRecursively(context, buffer, node);
896
- textContent = buffer.toString();
897
- }
898
- NokogiriHelpers.convertEncodingByNKFIfNecessary(context.getRuntime(), (XmlDocument)document(context), textContent);
899
- return stringOrNil(context.getRuntime(), textContent);
900
- }
901
-
902
- private void getTextContentRecursively(ThreadContext context, StringBuffer buffer, Node currentNode) {
903
- String textContent = currentNode.getNodeValue();
904
- if (textContent != null && NokogiriHelpers.shouldDecode(currentNode))
905
- textContent = NokogiriHelpers.decodeJavaString(textContent);
906
- if (textContent != null)
907
- buffer.append(textContent);
908
- NodeList children = currentNode.getChildNodes();
909
- for (int i = 0; i < children.getLength(); i++) {
910
- Node child = children.item(i);
911
- if (hasTextContent(child))
912
- getTextContentRecursively(context, buffer, child);
913
- }
905
+ textContent = getTextContentRecursively(new StringBuilder(), node);
906
+ }
907
+ // textContent = NokogiriHelpers.convertEncodingByNKFIfNecessary(context, (XmlDocument) document(context), textContent);
908
+ return textContent;
909
+ }
910
+
911
+ private StringBuilder getTextContentRecursively(StringBuilder buffer, Node currentNode) {
912
+ CharSequence textContent = currentNode.getNodeValue();
913
+ if (textContent != null && NokogiriHelpers.shouldDecode(currentNode)) {
914
+ textContent = NokogiriHelpers.decodeJavaString(textContent);
915
+ }
916
+ if (textContent != null) buffer.append(textContent);
917
+ NodeList children = currentNode.getChildNodes();
918
+ for (int i = 0; i < children.getLength(); i++) {
919
+ Node child = children.item(i);
920
+ if (hasTextContent(child)) getTextContentRecursively(buffer, child);
921
+ }
922
+ return buffer;
914
923
  }
915
924
 
916
925
  private boolean hasTextContent(Node child) {
917
- return child.getNodeType() != Node.COMMENT_NODE &&
918
- child.getNodeType() != Node.PROCESSING_INSTRUCTION_NODE;
926
+ return child.getNodeType() != Node.COMMENT_NODE && child.getNodeType() != Node.PROCESSING_INSTRUCTION_NODE;
919
927
  }
920
928
 
921
929
  @JRubyMethod
922
- public IRubyObject document(ThreadContext context) {
930
+ public final IRubyObject document(ThreadContext context) {
931
+ return document(context.runtime);
932
+ }
933
+
934
+ IRubyObject document(final Ruby runtime) {
923
935
  if (doc == null) {
924
936
  doc = (XmlDocument) node.getOwnerDocument().getUserData(NokogiriHelpers.CACHED_NODE);
925
937
  }
926
938
  if (doc == null) {
927
- doc = getCachedNodeOrCreate(context.getRuntime(), node.getOwnerDocument());
939
+ doc = getCachedNodeOrCreate(runtime, node.getOwnerDocument());
928
940
  node.getOwnerDocument().setUserData(NokogiriHelpers.CACHED_NODE, doc, null);
929
941
  }
930
942
  return doc;
931
943
  }
932
944
 
933
945
  public IRubyObject dup() {
934
- return this.dup_implementation(getMetaClass().getClassRuntime(), true);
946
+ return dup_implementation(getMetaClass().getClassRuntime(), true);
935
947
  }
936
948
 
937
949
  @JRubyMethod
938
950
  public IRubyObject dup(ThreadContext context) {
939
- return this.dup_implementation(context, true);
951
+ return dup_implementation(context, true);
940
952
  }
941
953
 
942
954
  @JRubyMethod
943
955
  public IRubyObject dup(ThreadContext context, IRubyObject depth) {
944
- boolean deep = (Integer)depth.toJava(Integer.class) != 0;
945
-
946
- return this.dup_implementation(context, deep);
956
+ boolean deep = depth instanceof RubyInteger && RubyFixnum.fix2int(depth) != 0;
957
+ return dup_implementation(context, deep);
947
958
  }
948
959
 
949
- protected IRubyObject dup_implementation(ThreadContext context, boolean deep) {
960
+ protected final IRubyObject dup_implementation(ThreadContext context, boolean deep) {
950
961
  return dup_implementation(context.getRuntime(), deep);
951
962
  }
952
963
 
@@ -957,17 +968,14 @@ public class XmlNode extends RubyObject {
957
968
  } catch (CloneNotSupportedException e) {
958
969
  throw runtime.newRuntimeError(e.toString());
959
970
  }
960
- if (node == null) throw runtime.newRuntimeError("FFFFFFFFFUUUUUUU");
961
971
  Node newNode = node.cloneNode(deep);
962
972
  clone.node = newNode;
963
973
  return clone;
964
974
  }
965
975
 
966
- public static IRubyObject encode_special_chars(ThreadContext context,
967
- IRubyObject string) {
968
- String s = rubyStringToString(string);
969
- String enc = NokogiriHelpers.encodeJavaString(s);
970
- return context.getRuntime().newString(enc);
976
+ public static RubyString encode_special_chars(ThreadContext context, IRubyObject string) {
977
+ CharSequence str = NokogiriHelpers.encodeJavaString( rubyStringToString(string) );
978
+ return RubyString.newString(context.getRuntime(), str);
971
979
  }
972
980
 
973
981
  /**
@@ -1206,7 +1214,7 @@ public class XmlNode extends RubyObject {
1206
1214
  protected void setContent(IRubyObject content) {
1207
1215
  String javaContent = rubyStringToString(content);
1208
1216
  node.setTextContent(javaContent);
1209
- if (javaContent.length() == 0) return;
1217
+ if (javaContent == null || javaContent.length() == 0) return;
1210
1218
  if (node.getNodeType() == Node.TEXT_NODE || node.getNodeType() == Node.CDATA_SECTION_NODE) return;
1211
1219
  if (node.getFirstChild() != null) {
1212
1220
  node.getFirstChild().setUserData(NokogiriHelpers.ENCODED_STRING, true, null);
@@ -1260,19 +1268,16 @@ public class XmlNode extends RubyObject {
1260
1268
  String encString = encoding.isNil() ? null : rubyStringToString(encoding);
1261
1269
 
1262
1270
  SaveContextVisitor visitor =
1263
- new SaveContextVisitor((Integer) options.toJava(Integer.class), rubyStringToString(indentString), encString, isHtmlDoc(context), isFragment(), 0);
1271
+ new SaveContextVisitor(RubyFixnum.fix2int(options), rubyStringToString(indentString), encString, isHtmlDoc(context), isFragment(), 0);
1264
1272
  accept(context, visitor);
1265
1273
 
1266
- IRubyObject rubyString = null;
1274
+ final IRubyObject rubyString;
1267
1275
  if (NokogiriHelpers.isUTF8(encString)) {
1268
- rubyString = stringOrNil(context.getRuntime(), visitor.toString());
1276
+ rubyString = convertString(context.getRuntime(), visitor.getInternalBuffer());
1269
1277
  } else {
1270
- try {
1271
- byte[] bytes = NokogiriHelpers.convertEncoding(Charset.forName(encString), visitor.toString());
1272
- rubyString = stringOrNil(context.getRuntime(), bytes);
1273
- } catch (CharacterCodingException e) {
1274
- throw context.getRuntime().newRuntimeError(e.getMessage());
1275
- }
1278
+ ByteBuffer bytes = convertEncoding(Charset.forName(encString), visitor.getInternalBuffer());
1279
+ ByteList str = new ByteList(bytes.array(), bytes.arrayOffset(), bytes.remaining());
1280
+ rubyString = RubyString.newString(context.getRuntime(), str);
1276
1281
  }
1277
1282
  RuntimeHelpers.invoke(context, io, "write", rubyString);
1278
1283
 
@@ -1432,9 +1437,10 @@ public class XmlNode extends RubyObject {
1432
1437
 
1433
1438
  @JRubyMethod(name = {"unlink", "remove"})
1434
1439
  public IRubyObject unlink(ThreadContext context) {
1435
- if (node.getParentNode() != null) {
1436
- clearXpathContext(node.getParentNode());
1437
- node.getParentNode().removeChild(node);
1440
+ final Node parent = node.getParentNode();
1441
+ if (parent != null) {
1442
+ parent.removeChild(node);
1443
+ clearXpathContext(parent);
1438
1444
  }
1439
1445
  return this;
1440
1446
  }
@@ -1532,7 +1538,7 @@ public class XmlNode extends RubyObject {
1532
1538
  }
1533
1539
 
1534
1540
  protected enum AdoptScheme {
1535
- CHILD, PREV_SIBLING, NEXT_SIBLING, REPLACEMENT;
1541
+ CHILD, PREV_SIBLING, NEXT_SIBLING, REPLACEMENT
1536
1542
  }
1537
1543
 
1538
1544
  /**
@@ -1682,10 +1688,6 @@ public class XmlNode extends RubyObject {
1682
1688
 
1683
1689
  Node nextSib = thisNode.getNextSibling();
1684
1690
 
1685
- if (nextSib != null &&
1686
- nextSib.getNodeType() == Node.TEXT_NODE &&
1687
- otherNode.getNodeType() == Node.TEXT_NODE) return;
1688
-
1689
1691
  if (nextSib != null) {
1690
1692
  parent.insertBefore(otherNode, nextSib);
1691
1693
  } else {