nokogiri 1.5.6.rc3-java → 1.5.7-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 (50) hide show
  1. data/CHANGELOG.ja.rdoc +87 -26
  2. data/CHANGELOG.rdoc +94 -32
  3. data/Manifest.txt +1 -0
  4. data/Rakefile +28 -15
  5. data/build_all +13 -5
  6. data/ext/java/nokogiri/NokogiriService.java +8 -1
  7. data/ext/java/nokogiri/XmlDocument.java +4 -4
  8. data/ext/java/nokogiri/XmlDtd.java +13 -2
  9. data/ext/java/nokogiri/XmlElement.java +3 -12
  10. data/ext/java/nokogiri/XmlEntityReference.java +11 -31
  11. data/ext/java/nokogiri/XmlNode.java +76 -32
  12. data/ext/java/nokogiri/XmlReader.java +257 -181
  13. data/ext/java/nokogiri/XmlSaxPushParser.java +17 -2
  14. data/ext/java/nokogiri/internals/NokogiriHelpers.java +23 -16
  15. data/ext/java/nokogiri/internals/NokogiriNamespaceCache.java +18 -1
  16. data/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler.java +9 -0
  17. data/ext/java/nokogiri/internals/ReaderNode.java +37 -37
  18. data/ext/java/nokogiri/internals/SaveContextVisitor.java +23 -16
  19. data/ext/java/nokogiri/internals/UncloseableInputStream.java +102 -0
  20. data/ext/java/nokogiri/internals/XmlDomParserContext.java +7 -4
  21. data/ext/nokogiri/extconf.rb +1 -0
  22. data/ext/nokogiri/nokogiri.h +4 -0
  23. data/ext/nokogiri/xml_node.c +33 -1
  24. data/ext/nokogiri/xml_reader.c +0 -3
  25. data/ext/nokogiri/xml_sax_parser.c +4 -1
  26. data/lib/nekodtd.jar +0 -0
  27. data/lib/nokogiri.rb +1 -0
  28. data/lib/nokogiri/css/xpath_visitor.rb +1 -1
  29. data/lib/nokogiri/nokogiri.jar +0 -0
  30. data/lib/nokogiri/version.rb +4 -1
  31. data/lib/nokogiri/xml/builder.rb +12 -2
  32. data/lib/nokogiri/xml/document.rb +3 -1
  33. data/lib/nokogiri/xml/sax/parser.rb +1 -0
  34. data/tasks/cross_compile.rb +15 -15
  35. data/test/css/test_parser.rb +9 -9
  36. data/test/css/test_xpath_visitor.rb +1 -1
  37. data/test/helper.rb +1 -0
  38. data/test/html/sax/test_parser.rb +5 -2
  39. data/test/html/test_document_fragment.rb +4 -2
  40. data/test/namespaces/test_namespaces_in_builder_doc.rb +60 -0
  41. data/test/namespaces/test_namespaces_in_created_doc.rb +62 -0
  42. data/test/namespaces/test_namespaces_in_parsed_doc.rb +60 -0
  43. data/test/test_reader.rb +38 -4
  44. data/test/xml/sax/test_parser.rb +10 -1
  45. data/test/xml/test_builder.rb +40 -1
  46. data/test/xml/test_document.rb +50 -2
  47. data/test/xml/test_entity_reference.rb +2 -4
  48. data/test/xml/test_node.rb +30 -1
  49. data/test_all +2 -2
  50. metadata +142 -232
data/Manifest.txt CHANGED
@@ -66,6 +66,7 @@ ext/java/nokogiri/internals/ParserContext.java
66
66
  ext/java/nokogiri/internals/ReaderNode.java
67
67
  ext/java/nokogiri/internals/SaveContextVisitor.java
68
68
  ext/java/nokogiri/internals/SchemaErrorHandler.java
69
+ ext/java/nokogiri/internals/UncloseableInputStream.java
69
70
  ext/java/nokogiri/internals/XmlDeclHandler.java
70
71
  ext/java/nokogiri/internals/XmlDomParserContext.java
71
72
  ext/java/nokogiri/internals/XmlSaxParser.java
data/Rakefile CHANGED
@@ -7,7 +7,7 @@ Hoe.plugin :debugging
7
7
  Hoe.plugin :git
8
8
  Hoe.plugin :gemspec
9
9
  Hoe.plugin :bundler
10
- Hoe.add_include_dirs '.' # for ruby 1.9.2
10
+ Hoe.add_include_dirs '.'
11
11
 
12
12
  GENERATED_PARSER = "lib/nokogiri/css/parser.rb"
13
13
  GENERATED_TOKENIZER = "lib/nokogiri/css/tokenizer.rb"
@@ -35,7 +35,7 @@ HOE = Hoe.spec 'nokogiri' do
35
35
  self.clean_globs += [
36
36
  'nokogiri.gemspec',
37
37
  'lib/nokogiri/nokogiri.{bundle,jar,rb,so}',
38
- 'lib/nokogiri/1.{8,9}',
38
+ 'lib/nokogiri/{1.8,1.9,2.0}',
39
39
  # GENERATED_PARSER,
40
40
  # GENERATED_TOKENIZER
41
41
  ]
@@ -48,7 +48,7 @@ HOE = Hoe.spec 'nokogiri' do
48
48
  ["mini_portile", ">= 0.2.2"],
49
49
  ["minitest", "~> 2.2.2"],
50
50
  ["rake", ">= 0.9"],
51
- ["rake-compiler", "= 0.8.0"],
51
+ ["rake-compiler", "~> 0.8.0"],
52
52
  ["racc", ">= 1.4.6"],
53
53
  ["rexical", ">= 1.0.5"]
54
54
  ]
@@ -67,6 +67,19 @@ end
67
67
 
68
68
  # ----------------------------------------
69
69
 
70
+ def add_file_to_gem relative_path
71
+ target_path = File.join gem_build_path, relative_path
72
+ target_dir = File.dirname(target_path)
73
+ mkdir_p target_dir unless File.directory?(target_dir)
74
+ rm_f target_path
75
+ ln relative_path, target_path
76
+ HOE.spec.files += [relative_path]
77
+ end
78
+
79
+ def gem_build_path
80
+ File.join 'pkg', HOE.spec.full_name
81
+ end
82
+
70
83
  if java?
71
84
  # TODO: clean this section up.
72
85
  require "rake/javaextensiontask"
@@ -78,17 +91,15 @@ if java?
78
91
  ext.classpath = jars.map { |x| File.expand_path x }.join ':'
79
92
  end
80
93
 
81
- gem_build_path = File.join 'pkg', HOE.spec.full_name
82
-
83
94
  task gem_build_path => [:compile] do
84
- cp 'lib/nokogiri/nokogiri.jar', File.join(gem_build_path, 'lib', 'nokogiri')
85
- HOE.spec.files += ['lib/nokogiri/nokogiri.jar']
95
+ add_file_to_gem 'lib/nokogiri/nokogiri.jar'
86
96
  end
87
97
  else
88
98
  mingw_available = true
89
99
  begin
90
100
  require 'tasks/cross_compile'
91
101
  rescue
102
+ puts "WARNING: cross compilation not available: #{$!}"
92
103
  mingw_available = false
93
104
  end
94
105
  require "rake/extensiontask"
@@ -101,10 +112,10 @@ else
101
112
  if mingw_available
102
113
  ext.cross_compile = true
103
114
  ext.cross_platform = ["x86-mswin32-60", "x86-mingw32"]
104
- ext.cross_config_options << "--with-xml2-include=#{File.join($recipes[:libxml2].path, 'include', 'libxml2')}"
105
- ext.cross_config_options << "--with-xml2-lib=#{File.join($recipes[:libxml2].path, 'lib')}"
106
- ext.cross_config_options << "--with-iconv-dir=#{$recipes[:libiconv].path}"
107
- ext.cross_config_options << "--with-xslt-dir=#{$recipes[:libxslt].path}"
115
+ ext.cross_config_options << "--with-xml2-include=#{File.join($recipes["libxml2"].path, 'include', 'libxml2')}"
116
+ ext.cross_config_options << "--with-xml2-lib=#{File.join($recipes["libxml2"].path, 'lib')}"
117
+ ext.cross_config_options << "--with-iconv-dir=#{$recipes["libiconv"].path}"
118
+ ext.cross_config_options << "--with-xslt-dir=#{$recipes["libxslt"].path}"
108
119
  ext.cross_config_options << "--with-zlib-dir=#{CROSS_DIR}"
109
120
  end
110
121
  end
@@ -183,8 +194,7 @@ end
183
194
 
184
195
  desc "build a windows gem without all the ceremony."
185
196
  task "gem:windows" => "gem" do
186
- # TODO: 1.8.7-p358, 1.9.3-p194
187
- cross_rubies = ["1.8.7-p330", "1.9.2-p136"]
197
+ cross_rubies = ["1.8.7-p358", "1.9.3-p194", "2.0.0-p0"]
188
198
  ruby_cc_version = cross_rubies.collect { |_| _.split("-").first }.join(":") # e.g., "1.8.7:1.9.2"
189
199
  rake_compiler_config_path = "#{ENV['HOME']}/.rake-compiler/config.yml"
190
200
 
@@ -205,10 +215,13 @@ task "gem:windows" => "gem" do
205
215
  end
206
216
 
207
217
  # verify that --export-all is in the 1.9 rbconfig. see #279,#374,#375.
208
- rbconfig_19 = rake_compiler_config["rbconfig-1.9.2"]
218
+ rbconfig_19 = rake_compiler_config["rbconfig-1.9.3"]
209
219
  raise "rbconfig #{rbconfig_19} needs --export-all in its DLDFLAGS value" if File.read(rbconfig_19).split("\n").grep(/CONFIG\["DLDFLAGS"\].*--export-all/).empty?
210
220
 
211
- pkg_config_path = [:libxslt, :libxml2].collect { |pkg| File.join($recipes[pkg].path, "lib/pkgconfig") }.join(":")
221
+ rbconfig_20 = rake_compiler_config["rbconfig-2.0.0"]
222
+ raise "rbconfig #{rbconfig_20} needs --export-all in its DLDFLAGS value" if File.read(rbconfig_20).split("\n").grep(/CONFIG\["DLDFLAGS"\].*--export-all/).empty?
223
+
224
+ pkg_config_path = %w[libxslt libxml2].collect { |pkg| File.join($recipes[pkg].path, "lib/pkgconfig") }.join(":")
212
225
  sh("env PKG_CONFIG_PATH=#{pkg_config_path} RUBY_CC_VERSION=#{ruby_cc_version} rake cross native gem") || raise("build failed!")
213
226
  end
214
227
 
data/build_all CHANGED
@@ -31,7 +31,7 @@
31
31
  # end
32
32
  #
33
33
  # - you may also have to hack rubygems.rb to eliminate a reference to
34
- # RUBY_ENGINE
34
+ # RUBY_ENGINE (just comment it out)
35
35
  #
36
36
 
37
37
  HOST=
@@ -71,11 +71,19 @@ if [[ $platform =~ "64" ]] ; then
71
71
  exit 1
72
72
  fi
73
73
  rvm_use 1.8.7
74
- if [[ ! -a ${HOME}/.rake-compiler/ruby/ruby-1.8.7-p330/lib/ruby/1.8.7/x86_64-linux/rbconfig.rb ]] ; then
75
- bundle exec rake-compiler cross-ruby VERSION=1.8.7-p330
74
+ if [[ ! -a ${HOME}/.rake-compiler/ruby/ruby-1.8.7-p358/lib/ruby/1.8.7/x86_64-linux/rbconfig.rb ]] ; then
75
+
76
+ # if this fails around the purelib.rb thing, try varying the ruby
77
+ # used to run this script, and whether the HOST env var is set
78
+ # below.
79
+
80
+ bundle exec rake-compiler cross-ruby VERSION=1.8.7-p358 # HOST=i386-mingw32
81
+ fi
82
+ if [[ ! -a ${HOME}/.rake-compiler/ruby/ruby-1.9.3-p194/lib/ruby/1.9.1/x86_64-linux/rbconfig.rb ]] ; then
83
+ bundle exec rake-compiler cross-ruby VERSION=1.9.3-p194
76
84
  fi
77
- if [[ ! -a ${HOME}/.rake-compiler/ruby/ruby-1.9.2-p136/lib/ruby/1.9.1/x86_64-linux/rbconfig.rb ]] ; then
78
- bundle exec rake-compiler cross-ruby VERSION=1.9.2-p136
85
+ if [[ ! -a ${HOME}/.rake-compiler/ruby/ruby-2.0.0-p0/lib/ruby/2.0.0/x86_64-linux/rbconfig.rb ]] ; then
86
+ bundle exec rake-compiler cross-ruby VERSION=2.0.0-p0
79
87
  fi
80
88
  bundle exec rake cross
81
89
  bundle exec rake gem:windows
@@ -90,6 +90,7 @@ public class NokogiriService implements BasicLibraryService {
90
90
  nokogiriClassCache.put("Nokogiri::XML::XPathContext", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::XPathContext"));
91
91
  nokogiriClassCache.put("Nokogiri::XML::AttributeDecl", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::AttributeDecl"));
92
92
  nokogiriClassCache.put("Nokogiri::XML::SAX::ParserContext", (RubyClass)ruby.getClassFromPath("Nokogiri::XML::SAX::ParserContext"));
93
+ nokogiriClassCache.put("StringIO", (RubyClass)ruby.getClassFromPath("StringIO"));
93
94
  }
94
95
 
95
96
  private void init(Ruby ruby) {
@@ -100,6 +101,7 @@ public class NokogiriService implements BasicLibraryService {
100
101
  RubyModule htmlSaxModule = htmlModule.defineModuleUnder("SAX");
101
102
  RubyModule xsltModule = nokogiri.defineModuleUnder("XSLT");
102
103
 
104
+ createJavaLibraryVersionConstants(ruby, nokogiri);
103
105
  createNokogiriModule(ruby, nokogiri);
104
106
  createSyntaxErrors(ruby, nokogiri, xmlModule);
105
107
  RubyClass xmlNode = createXmlModule(ruby, xmlModule);
@@ -108,7 +110,12 @@ public class NokogiriService implements BasicLibraryService {
108
110
  createSaxModule(ruby, xmlSaxModule, htmlSaxModule);
109
111
  createXsltModule(ruby, xsltModule);
110
112
  }
111
-
113
+
114
+ private void createJavaLibraryVersionConstants(Ruby ruby, RubyModule nokogiri) {
115
+ nokogiri.defineConstant("XERCES_VERSION", ruby.newString(org.apache.xerces.impl.Version.getVersion()));
116
+ nokogiri.defineConstant("NEKO_VERSION", ruby.newString(org.cyberneko.html.Version.getVersion()));
117
+ }
118
+
112
119
  private void createNokogiriModule(Ruby ruby, RubyModule nokogiri) {;
113
120
  RubyClass encHandler = nokogiri.defineClassUnder("EncodingHandler", ruby.getObject(), ENCODING_HANDLER_ALLOCATOR);
114
121
  encHandler.defineAnnotatedMethods(EncodingHandler.class);
@@ -179,11 +179,11 @@ public class XmlDocument extends XmlNode {
179
179
  if (node == null) return;
180
180
  String nodePrefix = node.getPrefix();
181
181
  if (nodePrefix == null) { // default namespace
182
- node.getOwnerDocument().renameNode(node, default_href, node.getNodeName());
182
+ NokogiriHelpers.renameNode(node, default_href, node.getNodeName());
183
183
  } else {
184
184
  XmlNamespace xmlNamespace = nsCache.get(nodePrefix);
185
185
  String href = rubyStringToString(xmlNamespace.href(context));
186
- node.getOwnerDocument().renameNode(node, href, node.getNodeName());
186
+ NokogiriHelpers.renameNode(node, href, node.getNodeName());
187
187
  }
188
188
  resolveNamespaceIfNecessary(context, node.getNextSibling(), default_href);
189
189
  NodeList children = node.getChildNodes();
@@ -358,7 +358,7 @@ public class XmlDocument extends XmlNode {
358
358
  Node node = xmlNode.node;
359
359
  if (node.getNodeType() == Node.ELEMENT_NODE) {
360
360
  node.setPrefix(null);
361
- node.getOwnerDocument().renameNode(node, null, node.getLocalName());
361
+ NokogiriHelpers.renameNode(node, null, node.getLocalName());
362
362
  NamedNodeMap attrs = node.getAttributes();
363
363
  for (int i=0; i<attrs.getLength(); i++) {
364
364
  Attr attr = (Attr) attrs.item(i);
@@ -366,7 +366,7 @@ public class XmlDocument extends XmlNode {
366
366
  ((org.w3c.dom.Element)node).removeAttributeNode(attr);
367
367
  } else {
368
368
  attr.setPrefix(null);
369
- attr.getOwnerDocument().renameNode(attr, null, attr.getLocalName());
369
+ NokogiriHelpers.renameNode(attr, null, attr.getLocalName());
370
370
  }
371
371
  }
372
372
  }
@@ -129,9 +129,20 @@ public class XmlDtd extends XmlNode {
129
129
  IRubyObject name,
130
130
  IRubyObject external_id,
131
131
  IRubyObject system_id) {
132
- Element placeHolder = doc.createElement("dtd_placeholder");
132
+
133
+ DocumentType placeholder = null;
134
+ if (doc.getDoctype() == null) {
135
+ String javaName = NokogiriHelpers.rubyStringToString(name);
136
+ String javaExternalId = NokogiriHelpers.rubyStringToString(external_id);
137
+ String javaSystemId = NokogiriHelpers.rubyStringToString(system_id);
138
+ placeholder = doc.getImplementation().createDocumentType(javaName, javaExternalId, javaSystemId);
139
+ doc.appendChild(placeholder);
140
+ } else {
141
+ placeholder = doc.getDoctype();
142
+ }
143
+ // FIXME: what if the document had a doc type, why are we here ?
133
144
  XmlDtd dtd = (XmlDtd) NokogiriService.XML_DTD_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::DTD"));
134
- dtd.setNode(runtime, placeHolder);
145
+ dtd.setNode(runtime, placeholder);
135
146
  dtd.name = name;
136
147
  dtd.pubId = external_id;
137
148
  dtd.sysId = system_id;
@@ -38,7 +38,6 @@ import org.jruby.Ruby;
38
38
  import org.jruby.RubyArray;
39
39
  import org.jruby.RubyClass;
40
40
  import org.jruby.anno.JRubyClass;
41
- import org.jruby.javasupport.util.RuntimeHelpers;
42
41
  import org.jruby.runtime.ThreadContext;
43
42
  import org.w3c.dom.Element;
44
43
  import org.w3c.dom.Node;
@@ -62,17 +61,9 @@ public class XmlElement extends XmlNode {
62
61
 
63
62
  @Override
64
63
  public void setNode(ThreadContext context, Node node) {
65
- this.node = node;
66
- if (node != null) {
67
- resetCache();
68
- if (node.getNodeType() != Node.DOCUMENT_NODE) {
69
- doc = document(context);
70
- setInstanceVariable("@document", doc);
71
- if (doc != null) {
72
- RuntimeHelpers.invoke(context, doc, "decorate", this);
73
- }
74
- }
75
- }
64
+ super.setNode(context, node);
65
+ if (doc != null)
66
+ setInstanceVariable("@document", doc);
76
67
  }
77
68
 
78
69
  @Override
@@ -36,6 +36,7 @@ import static nokogiri.internals.NokogiriHelpers.getCachedNodeOrCreate;
36
36
  import static nokogiri.internals.NokogiriHelpers.rubyStringToString;
37
37
  import nokogiri.internals.SaveContextVisitor;
38
38
 
39
+ import org.apache.xerces.dom.CoreDocumentImpl;
39
40
  import org.jruby.Ruby;
40
41
  import org.jruby.RubyClass;
41
42
  import org.jruby.anno.JRubyClass;
@@ -70,21 +71,19 @@ public class XmlEntityReference extends XmlNode {
70
71
  IRubyObject doc = args[0];
71
72
  IRubyObject name = args[1];
72
73
 
73
- Document owner = ((XmlNode) doc).getOwnerDocument();
74
- Node node = new NokogiriEntityReference(owner, rubyStringToString(name));
75
- super.setNode(context, node);
76
- }
77
-
78
- public void setNode(ThreadContext context, Node entityRef) {
79
- Document owner = entityRef.getOwnerDocument();
80
- String name = entityRef.getNodeName();
81
- Node node = new NokogiriEntityReference(owner, name);
82
- super.setNode(context, node);
74
+ Document document = ((XmlNode) doc).getOwnerDocument();
75
+ // FIXME: disable error checking as a workaround for #719. this depends on the internals of Xerces.
76
+ CoreDocumentImpl internalDocument = (CoreDocumentImpl) document;
77
+ boolean oldErrorChecking = internalDocument.getErrorChecking();
78
+ internalDocument.setErrorChecking(false);
79
+ Node node = document.createEntityReference(rubyStringToString(name));
80
+ internalDocument.setErrorChecking(oldErrorChecking);
81
+ setNode(context, node);
83
82
  }
84
83
 
85
84
  @Override
86
85
  public void accept(ThreadContext context, SaveContextVisitor visitor) {
87
- visitor.enterEntityReference((NokogiriEntityReference)node);
86
+ visitor.enter(node);
88
87
  Node child = node.getFirstChild();
89
88
  while (child != null) {
90
89
  IRubyObject nokoNode = getCachedNodeOrCreate(context.getRuntime(), child);
@@ -97,25 +96,6 @@ public class XmlEntityReference extends XmlNode {
97
96
  }
98
97
  child = child.getNextSibling();
99
98
  }
100
- visitor.leaveEntityReference((NokogiriEntityReference)node);
101
- }
102
-
103
- public class NokogiriEntityReference extends org.apache.xerces.dom.TextImpl {
104
- // Nokogiri's EntityReference should quack like a org.w3c.dom.Text node.
105
- // EntityReference node should not raise exception for names such as #xa.
106
- // This is reported bug in issue#719.
107
- // Also, EntityReference node should not bother xpath.
108
- // For this purpose, Node type should be Node.TEXT_NODE.
109
- public NokogiriEntityReference(Document owner, String name) {
110
- super((org.apache.xerces.dom.DocumentImpl)owner, name);
111
- }
112
-
113
- public short getNodeType() {
114
- return Node.TEXT_NODE;
115
- }
116
-
117
- public String getNodeName() {
118
- return getNodeValue();
119
- }
99
+ visitor.leave(node);
120
100
  }
121
101
  }
@@ -54,6 +54,7 @@ import nokogiri.internals.NokogiriNamespaceCache;
54
54
  import nokogiri.internals.SaveContextVisitor;
55
55
  import nokogiri.internals.XmlDomParserContext;
56
56
 
57
+ import org.apache.xerces.dom.CoreDocumentImpl;
57
58
  import org.jruby.Ruby;
58
59
  import org.jruby.RubyArray;
59
60
  import org.jruby.RubyClass;
@@ -211,22 +212,23 @@ public class XmlNode extends RubyObject {
211
212
  */
212
213
  public XmlNode(Ruby ruby, RubyClass cls, Node node) {
213
214
  super(ruby, cls);
214
- this.node = node;
215
+ setNode(ruby.getCurrentContext(), node);
216
+ }
215
217
 
218
+ protected void decorate(Ruby ruby) {
216
219
  if (node != null) {
217
220
  resetCache();
218
221
 
219
222
  if (node.getNodeType() != Node.DOCUMENT_NODE) {
220
223
  doc = document(ruby.getCurrentContext());
221
224
 
222
- if (doc != null) {
225
+ if (doc != null && doc.isTrue()) {
223
226
  RuntimeHelpers.invoke(ruby.getCurrentContext(), doc, "decorate", this);
224
227
  }
225
228
  }
226
229
  }
227
-
228
230
  }
229
-
231
+
230
232
  /**
231
233
  * Create and return a copy of this object.
232
234
  *
@@ -326,12 +328,15 @@ public class XmlNode extends RubyObject {
326
328
 
327
329
  Element element = null;
328
330
  String node_name = rubyStringToString(name);
329
- try {
330
- element = document.createElementNS(null, node_name);
331
- } catch (org.w3c.dom.DOMException e) {
332
- // issue#683 NAMESPACE_ERR is thrown from RDF::RDFXML::Writer.new
333
- // retry without namespace
331
+ String prefix = NokogiriHelpers.getPrefix(node_name);
332
+ if (prefix == null) {
334
333
  element = document.createElement(node_name);
334
+ } else {
335
+ String namespace_uri = null;
336
+ if (document.getDocumentElement() != null) {
337
+ namespace_uri = document.getDocumentElement().lookupNamespaceURI(prefix);
338
+ }
339
+ element = document.createElementNS(namespace_uri, node_name);
335
340
  }
336
341
  setNode(context, element);
337
342
  }
@@ -449,8 +454,16 @@ public class XmlNode extends RubyObject {
449
454
  public void relink_namespace(ThreadContext context) {
450
455
  if (node instanceof Element) {
451
456
  Element e = (Element) node;
457
+ String prefix = e.getPrefix();
458
+ String currentNS = e.getNamespaceURI();
459
+ if (prefix == null && currentNS == null) {
460
+ prefix = NokogiriHelpers.getPrefix(e.getNodeName());
461
+ } else if (currentNS != null) {
462
+ prefix = e.lookupPrefix(currentNS);
463
+ }
452
464
  e.getOwnerDocument().setStrictErrorChecking(false);
453
- e.getOwnerDocument().renameNode(e, e.lookupNamespaceURI(e.getPrefix()), e.getNodeName());
465
+ String nsURI = e.lookupNamespaceURI(prefix);
466
+ this.node = NokogiriHelpers.renameNode(e, nsURI, e.getNodeName());
454
467
 
455
468
  if (e.hasAttributes()) {
456
469
  NamedNodeMap attrs = e.getAttributes();
@@ -458,24 +471,28 @@ public class XmlNode extends RubyObject {
458
471
  for (int i = 0; i < attrs.getLength(); i++) {
459
472
  Attr attr = (Attr) attrs.item(i);
460
473
  String nsUri = "";
461
- String prefix = attr.getPrefix();
474
+ String attrPrefix = attr.getPrefix();
475
+ if (attrPrefix == null) {
476
+ attrPrefix = NokogiriHelpers.getPrefix(attr.getNodeName());
477
+ }
462
478
  String nodeName = attr.getNodeName();
463
479
  if ("xml".equals(prefix)) {
464
480
  nsUri = "http://www.w3.org/XML/1998/namespace";
465
- } else if ("xmlns".equals(prefix) || nodeName.equals("xmlns")) {
481
+ } else if ("xmlns".equals(attrPrefix) || nodeName.equals("xmlns")) {
466
482
  nsUri = "http://www.w3.org/2000/xmlns/";
467
483
  } else {
468
- nsUri = attr.getNamespaceURI();
484
+ nsUri = attr.lookupNamespaceURI(attrPrefix);
469
485
  }
470
486
  if (!(nsUri == null || "".equals(nsUri))) {
471
487
  XmlNamespace.createFromAttr(context.getRuntime(), attr);
472
488
  }
473
- e.getOwnerDocument().renameNode(attr, nsUri, nodeName);
489
+ NokogiriHelpers.renameNode(attr, nsUri, nodeName);
474
490
  }
475
491
  }
476
492
 
477
- if (e.hasChildNodes()) {
478
- ((XmlNodeSet) children(context)).relink_namespace(context);
493
+ if (this.node.hasChildNodes()) {
494
+ XmlNodeSet nodeSet = (XmlNodeSet)(children(context));
495
+ nodeSet.relink_namespace(context);
479
496
  }
480
497
  }
481
498
  }
@@ -514,13 +531,8 @@ public class XmlNode extends RubyObject {
514
531
 
515
532
  public void setNode(ThreadContext context, Node node) {
516
533
  this.node = node;
517
-
518
- if (node != null) {
519
- resetCache();
520
- if (node.getNodeType() != Node.DOCUMENT_NODE) {
521
- doc = document(context);
522
- }
523
- }
534
+
535
+ decorate(context.getRuntime());
524
536
 
525
537
  if (this instanceof XmlAttr) {
526
538
  ((XmlAttr)this).setNamespaceIfNecessary(context.getRuntime());
@@ -539,7 +551,7 @@ public class XmlNode extends RubyObject {
539
551
  && oldPrefix.equals(rubyStringToString(ns.prefix(context))));
540
552
 
541
553
  if(update) {
542
- this.node.getOwnerDocument().renameNode(this.node, uri, this.node.getNodeName());
554
+ this.node = NokogiriHelpers.renameNode(this.node, uri, this.node.getNodeName());
543
555
  }
544
556
  }
545
557
 
@@ -579,7 +591,7 @@ public class XmlNode extends RubyObject {
579
591
  else namespaceOwner = node.getParentNode();
580
592
  XmlNamespace ns = XmlNamespace.createFromPrefixAndHref(namespaceOwner, prefix, href);
581
593
  if (node != namespaceOwner) {
582
- node.getOwnerDocument().renameNode(node, ns.getHref(), ns.getPrefix() + node.getLocalName());
594
+ this.node = NokogiriHelpers.renameNode(node, ns.getHref(), ns.getPrefix() + node.getLocalName());
583
595
  }
584
596
 
585
597
  updateNodeNamespaceIfNecessary(context, ns);
@@ -915,8 +927,9 @@ public class XmlNode extends RubyObject {
915
927
  if (rbkey == null || rbkey.isNil()) context.getRuntime().getNil();
916
928
  String key = rubyStringToString(rbkey);
917
929
  Element element = (Element) node;
930
+ if (!element.hasAttribute(key)) return context.getRuntime().getNil();
918
931
  String value = element.getAttribute(key);
919
- return nonEmptyStringOrNil(context.getRuntime(), value);
932
+ return stringOrNil(context.getRuntime(), value);
920
933
  }
921
934
  return context.getRuntime().getNil();
922
935
  }
@@ -1174,7 +1187,7 @@ public class XmlNode extends RubyObject {
1174
1187
  @JRubyMethod(name = {"node_name=", "name="})
1175
1188
  public IRubyObject node_name_set(ThreadContext context, IRubyObject nodeName) {
1176
1189
  String newName = rubyStringToString(nodeName);
1177
- getOwnerDocument().renameNode(node, null, newName);
1190
+ this.node = NokogiriHelpers.renameNode(node, null, newName);
1178
1191
  setName(nodeName);
1179
1192
  return this;
1180
1193
  }
@@ -1192,6 +1205,8 @@ public class XmlNode extends RubyObject {
1192
1205
  String uri = null;
1193
1206
  if (prefix.equals("xml")) {
1194
1207
  uri = "http://www.w3.org/XML/1998/namespace";
1208
+ } else if (prefix.equals("xmlns")) {
1209
+ uri = "http://www.w3.org/2000/xmlns/";
1195
1210
  } else {
1196
1211
  uri = findNamespaceHref(context, prefix);
1197
1212
  }
@@ -1216,7 +1231,11 @@ public class XmlNode extends RubyObject {
1216
1231
  return namespace.getHref();
1217
1232
  }
1218
1233
  }
1219
- currentNode = (XmlNode) currentNode.parent(context);
1234
+ if (currentNode.parent(context).isNil()) {
1235
+ break;
1236
+ } else {
1237
+ currentNode = (XmlNode) currentNode.parent(context);
1238
+ }
1220
1239
  }
1221
1240
  return null;
1222
1241
  }
@@ -1263,7 +1282,7 @@ public class XmlNode extends RubyObject {
1263
1282
  String prefix = n.getPrefix();
1264
1283
  String href = n.getNamespaceURI();
1265
1284
  ((XmlDocument)doc).getNamespaceCache().remove(prefix == null ? "" : prefix, href);
1266
- n.getOwnerDocument().renameNode(n, null, n.getNodeName());
1285
+ this.node = NokogiriHelpers.renameNode(n, null, NokogiriHelpers.getLocalPart(n.getNodeName()));
1267
1286
  }
1268
1287
  } else {
1269
1288
  XmlNamespace ns = (XmlNamespace) namespace;
@@ -1272,7 +1291,14 @@ public class XmlNode extends RubyObject {
1272
1291
 
1273
1292
  // Assigning node = ...renameNode() or not seems to make no
1274
1293
  // difference. Why not? -pmahoney
1275
- node = node.getOwnerDocument().renameNode(node, href, NokogiriHelpers.newQName(prefix, node));
1294
+
1295
+ // It actually makes a great deal of difference. renameNode()
1296
+ // will operate in place if it can, but sometimes it can't.
1297
+ // The node you passed in *might* come back as you expect, but
1298
+ // it might not. It's much safer to throw away the original
1299
+ // and keep the return value. -mbklein
1300
+ String new_name = NokogiriHelpers.newQName(prefix, node);
1301
+ this.node = NokogiriHelpers.renameNode(node, href, new_name);
1276
1302
  }
1277
1303
 
1278
1304
  return this;
@@ -1401,12 +1427,16 @@ public class XmlNode extends RubyObject {
1401
1427
  Node otherNode = other.node;
1402
1428
 
1403
1429
  try {
1430
+ Document prev = otherNode.getOwnerDocument();
1404
1431
  Document doc = thisNode.getOwnerDocument();
1405
1432
  if (doc != null && doc != otherNode.getOwnerDocument()) {
1406
1433
  Node ret = doc.adoptNode(otherNode);
1434
+ // FIXME: this is really a hack, see documentation of fixUserData() for more details.
1435
+ fixUserData(prev, ret);
1407
1436
  if (ret == null) {
1408
1437
  throw context.getRuntime().newRuntimeError("Failed to take ownership of node");
1409
1438
  }
1439
+ otherNode = ret;
1410
1440
  }
1411
1441
 
1412
1442
  Node parent = thisNode.getParentNode();
@@ -1444,6 +1474,20 @@ public class XmlNode extends RubyObject {
1444
1474
  return nodeOrTags;
1445
1475
  }
1446
1476
 
1477
+ /**
1478
+ * This is a hack to fix #839. We should submit a patch to Xerces.
1479
+ * It looks like CoreDocumentImpl.adoptNode() doesn't copy
1480
+ * the user data associated with child nodes (recursively).
1481
+ */
1482
+ private void fixUserData(Document previous, Node ret) {
1483
+ String key = NokogiriHelpers.ENCODED_STRING;
1484
+ for (Node child = ret.getFirstChild(); child != null; child = child.getNextSibling()) {
1485
+ CoreDocumentImpl previousDocument = (CoreDocumentImpl) previous;
1486
+ child.setUserData(key, previousDocument.getUserData(child, key), null);
1487
+ fixUserData(previous, child);
1488
+ }
1489
+ }
1490
+
1447
1491
  protected Node[] adoptAsChild(ThreadContext context, Node parent,
1448
1492
  Node otherNode) {
1449
1493
  /*
@@ -1469,7 +1513,7 @@ public class XmlNode extends RubyObject {
1469
1513
  XmlElement fragmentContext = ((XmlDocumentFragment)this).getFragmentContext();
1470
1514
  String namespace_uri = fragmentContext.node.getNamespaceURI();
1471
1515
  if (namespace_uri != null && namespace_uri.length() > 0) {
1472
- node.getOwnerDocument().renameNode(child, namespace_uri, child.getNodeName());
1516
+ NokogiriHelpers.renameNode(child, namespace_uri, child.getNodeName());
1473
1517
  }
1474
1518
  }
1475
1519
  }
@@ -1526,7 +1570,7 @@ public class XmlNode extends RubyObject {
1526
1570
  try {
1527
1571
  parentNode.replaceChild(otherNode, thisNode);
1528
1572
  if (otherNode.getNodeType() != Node.TEXT_NODE) {
1529
- otherNode.getOwnerDocument().renameNode(otherNode, thisNode.getNamespaceURI(), otherNode.getNodeName());
1573
+ NokogiriHelpers.renameNode(otherNode, thisNode.getNamespaceURI(), otherNode.getNodeName());
1530
1574
  }
1531
1575
  } catch (Exception e) {
1532
1576
  String prefix = "could not replace child: ";