nokogiri 1.5.0.beta.4-java → 1.5.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 (87) hide show
  1. data/.gemtest +0 -0
  2. data/CHANGELOG.ja.rdoc +34 -0
  3. data/CHANGELOG.rdoc +40 -1
  4. data/Manifest.txt +11 -2
  5. data/README.rdoc +1 -1
  6. data/Rakefile +96 -105
  7. data/bin/nokogiri +1 -2
  8. data/ext/java/nokogiri/HtmlDocument.java +1 -31
  9. data/ext/java/nokogiri/HtmlSaxParserContext.java +1 -1
  10. data/ext/java/nokogiri/NokogiriService.java +77 -22
  11. data/ext/java/nokogiri/XmlAttr.java +5 -16
  12. data/ext/java/nokogiri/XmlCdata.java +4 -11
  13. data/ext/java/nokogiri/XmlComment.java +5 -5
  14. data/ext/java/nokogiri/XmlDocument.java +49 -59
  15. data/ext/java/nokogiri/XmlDocumentFragment.java +14 -8
  16. data/ext/java/nokogiri/XmlDtd.java +45 -43
  17. data/ext/java/nokogiri/XmlElement.java +19 -46
  18. data/ext/java/nokogiri/XmlElementDecl.java +9 -5
  19. data/ext/java/nokogiri/XmlEntityReference.java +24 -2
  20. data/ext/java/nokogiri/XmlNamespace.java +89 -34
  21. data/ext/java/nokogiri/XmlNode.java +31 -52
  22. data/ext/java/nokogiri/XmlNodeSet.java +42 -86
  23. data/ext/java/nokogiri/XmlProcessingInstruction.java +15 -19
  24. data/ext/java/nokogiri/XmlReader.java +40 -43
  25. data/ext/java/nokogiri/XmlSaxParserContext.java +2 -2
  26. data/ext/java/nokogiri/XmlSchema.java +14 -9
  27. data/ext/java/nokogiri/XmlText.java +18 -35
  28. data/ext/java/nokogiri/XmlXpathContext.java +43 -23
  29. data/ext/java/nokogiri/XsltStylesheet.java +17 -3
  30. data/ext/java/nokogiri/internals/HtmlDomParserContext.java +2 -4
  31. data/ext/java/nokogiri/internals/NokogiriHelpers.java +77 -20
  32. data/ext/java/nokogiri/internals/NokogiriNamespaceCache.java +13 -17
  33. data/ext/java/nokogiri/internals/NokogiriNamespaceContext.java +13 -1
  34. data/ext/java/nokogiri/internals/NokogiriXPathFunction.java +23 -8
  35. data/ext/java/nokogiri/internals/NokogiriXPathFunctionResolver.java +20 -3
  36. data/ext/java/nokogiri/internals/NokogiriXPathVariableResolver.java +67 -0
  37. data/ext/java/nokogiri/internals/NokogiriXsltErrorListener.java +86 -0
  38. data/ext/java/nokogiri/internals/ParserContext.java +25 -27
  39. data/ext/java/nokogiri/internals/ReaderNode.java +58 -1
  40. data/ext/java/nokogiri/internals/SaveContextVisitor.java +567 -0
  41. data/ext/java/nokogiri/internals/XmlDomParser.java +1 -2
  42. data/ext/java/nokogiri/internals/XmlDomParserContext.java +6 -0
  43. data/ext/nokogiri/nokogiri.c +24 -1
  44. data/ext/nokogiri/xml_io.c +32 -7
  45. data/ext/nokogiri/xml_node.c +14 -13
  46. data/ext/nokogiri/xml_sax_parser.c +9 -4
  47. data/ext/nokogiri/xslt_stylesheet.c +7 -1
  48. data/lib/nokogiri.rb +3 -22
  49. data/lib/nokogiri/css.rb +4 -0
  50. data/lib/nokogiri/html/document.rb +10 -14
  51. data/lib/nokogiri/nokogiri.jar +0 -0
  52. data/lib/nokogiri/version.rb +76 -23
  53. data/lib/nokogiri/xml/builder.rb +7 -0
  54. data/lib/nokogiri/xml/document.rb +17 -1
  55. data/lib/nokogiri/xml/document_fragment.rb +14 -0
  56. data/lib/nokogiri/xml/node.rb +36 -28
  57. data/lib/nokogiri/xml/node/save_options.rb +17 -1
  58. data/lib/nokogiri/xml/node_set.rb +7 -0
  59. data/lib/nokogiri/xml/parse_options.rb +8 -0
  60. data/lib/nokogiri/xml/reader.rb +6 -6
  61. data/lib/nokogiri/xml/schema.rb +7 -1
  62. data/lib/xercesImpl.jar +0 -0
  63. data/nokogiri_help_responses.md +40 -0
  64. data/tasks/cross_compile.rb +134 -159
  65. data/tasks/nokogiri.org.rb +18 -0
  66. data/tasks/test.rb +1 -1
  67. data/test/files/encoding.html +82 -0
  68. data/test/files/encoding.xhtml +84 -0
  69. data/test/files/metacharset.html +10 -0
  70. data/test/files/noencoding.html +47 -0
  71. data/test/helper.rb +2 -0
  72. data/test/html/test_document.rb +15 -0
  73. data/test/html/test_document_encoding.rb +13 -0
  74. data/test/test_memory_leak.rb +20 -0
  75. data/test/test_reader.rb +22 -0
  76. data/test/test_xslt_transforms.rb +6 -2
  77. data/test/xml/node/test_save_options.rb +10 -2
  78. data/test/xml/test_builder.rb +17 -0
  79. data/test/xml/test_document.rb +22 -0
  80. data/test/xml/test_node.rb +19 -1
  81. data/test/xml/test_node_reparenting.rb +16 -3
  82. data/test/xml/test_node_set.rb +34 -0
  83. data/test/xml/test_schema.rb +5 -0
  84. data/test/xslt/test_exception_handling.rb +37 -0
  85. metadata +141 -107
  86. data/deps.rip +0 -5
  87. data/ext/java/nokogiri/internals/SaveContext.java +0 -288
@@ -45,8 +45,6 @@ import java.util.Set;
45
45
  import java.util.regex.Matcher;
46
46
  import java.util.regex.Pattern;
47
47
 
48
- import nokogiri.internals.SaveContext;
49
-
50
48
  import org.jruby.Ruby;
51
49
  import org.jruby.RubyArray;
52
50
  import org.jruby.RubyClass;
@@ -57,6 +55,7 @@ import org.jruby.javasupport.util.RuntimeHelpers;
57
55
  import org.jruby.runtime.ThreadContext;
58
56
  import org.jruby.runtime.builtin.IRubyObject;
59
57
  import org.w3c.dom.Attr;
58
+ import org.w3c.dom.Document;
60
59
  import org.w3c.dom.NamedNodeMap;
61
60
 
62
61
  /**
@@ -92,6 +91,7 @@ public class XmlDocumentFragment extends XmlNode {
92
91
 
93
92
  // make wellformed fragment, ignore invalid namespace, or add appropriate namespace to parse
94
93
  if (args.length > 1 && args[1] instanceof RubyString) {
94
+ args[1] = trim(context, doc, (RubyString)args[1]);
95
95
  args[1] = RubyString.newString(context.getRuntime(), ignoreNamespaceIfNeeded(doc, rubyStringToString(args[1])));
96
96
  args[1] = RubyString.newString(context.getRuntime(), addNamespaceDeclIfNeeded(doc, rubyStringToString(args[1])));
97
97
  }
@@ -107,6 +107,18 @@ public class XmlDocumentFragment extends XmlNode {
107
107
  RuntimeHelpers.invoke(context, fragment, "initialize", args);
108
108
  return fragment;
109
109
  }
110
+
111
+ private static IRubyObject trim(ThreadContext context, XmlDocument xmlDocument, RubyString str) {
112
+ // checks whether schema is given. if exists, allows whitespace processing to a parser
113
+ Document document = (Document)xmlDocument.node;
114
+ if (document.getDoctype() != null) return str;
115
+ // strips trailing \n off forcefully
116
+ // not to return new object in case of no chomp needed, chomp! is used here.
117
+ IRubyObject result;
118
+ if (context.getRuntime().is1_9()) result = str.chomp_bang19(context);
119
+ else result = str.chomp_bang(context);
120
+ return result.isNil() ? str : result;
121
+ }
110
122
 
111
123
  private static Pattern qname_pattern = Pattern.compile("[^</:>\\s]+:[^</:>=\\s]+");
112
124
  private static Pattern starttag_pattern = Pattern.compile("<[^</>]+>");
@@ -208,10 +220,4 @@ public class XmlDocumentFragment extends XmlNode {
208
220
  public void relink_namespace(ThreadContext context) {
209
221
  ((XmlNodeSet) children(context)).relink_namespace(context);
210
222
  }
211
-
212
- @Override
213
- public void saveContent(ThreadContext context, SaveContext ctx) {
214
- saveNodeListContent(context, (XmlNodeSet) children(context), ctx);
215
- }
216
-
217
223
  }
@@ -37,7 +37,7 @@ import static nokogiri.internals.NokogiriHelpers.nonEmptyStringOrNil;
37
37
  import static nokogiri.internals.NokogiriHelpers.stringOrNil;
38
38
  import static org.jruby.javasupport.util.RuntimeHelpers.invoke;
39
39
  import nokogiri.internals.NokogiriHelpers;
40
- import nokogiri.internals.SaveContext;
40
+ import nokogiri.internals.SaveContextVisitor;
41
41
 
42
42
  import org.apache.xerces.xni.QName;
43
43
  import org.cyberneko.dtd.DTDConfiguration;
@@ -98,43 +98,40 @@ public class XmlDtd extends XmlNode {
98
98
  public XmlDtd(Ruby ruby, RubyClass rubyClass) {
99
99
  super(ruby, rubyClass);
100
100
  }
101
+
102
+ public void setNode(Ruby runtime, Node dtd) {
103
+ this.node = dtd;
104
+ notationClass = (RubyClass) runtime.getClassFromPath("Nokogiri::XML::Notation");
101
105
 
102
- public XmlDtd(Ruby ruby) {
103
- this(ruby, getNokogiriClass(ruby, "Nokogiri::XML::DTD"), null);
104
- }
105
-
106
- public XmlDtd(Ruby ruby, Node dtd) {
107
- this(ruby, getNokogiriClass(ruby, "Nokogiri::XML::DTD"), dtd);
108
- }
109
-
110
- public XmlDtd(Ruby ruby, RubyClass rubyClass, Node dtd) {
111
- super(ruby, rubyClass, dtd);
112
- notationClass = (RubyClass)
113
- ruby.getClassFromPath("Nokogiri::XML::Notation");
114
-
115
- name = pubId = sysId = ruby.getNil();
106
+ name = pubId = sysId = runtime.getNil();
116
107
  if (dtd == null) return;
117
108
 
118
109
  // This is the dtd declaration stored in the document; it
119
110
  // contains the DTD name (root element) and public and system
120
- // ids. The actual declarations are in the NekoDTD 'dtd'
111
+ // ids. The actual declarations are in the NekoDTD 'dtd'
121
112
  // variable. I don't know of a way to consolidate the two.
122
113
 
123
114
  DocumentType otherDtd = dtd.getOwnerDocument().getDoctype();
124
115
  if (otherDtd != null) {
125
- name = stringOrNil(ruby, otherDtd.getNodeName());
126
- pubId = nonEmptyStringOrNil(ruby, otherDtd.getPublicId());
127
- sysId = nonEmptyStringOrNil(ruby, otherDtd.getSystemId());
116
+ name = stringOrNil(runtime, otherDtd.getNodeName());
117
+ pubId = nonEmptyStringOrNil(runtime, otherDtd.getPublicId());
118
+ sysId = nonEmptyStringOrNil(runtime, otherDtd.getSystemId());
128
119
  }
129
120
  }
130
121
 
131
- public static XmlDtd newEmpty(Ruby ruby,
122
+ public XmlDtd(Ruby ruby, RubyClass rubyClass, Node dtd) {
123
+ super(ruby, rubyClass, dtd);
124
+ setNode(ruby, dtd);
125
+ }
126
+
127
+ public static XmlDtd newEmpty(Ruby runtime,
132
128
  Document doc,
133
129
  IRubyObject name,
134
130
  IRubyObject external_id,
135
131
  IRubyObject system_id) {
136
132
  Element placeHolder = doc.createElement("dtd_placeholder");
137
- XmlDtd dtd = new XmlDtd(ruby, placeHolder);
133
+ XmlDtd dtd = (XmlDtd) NokogiriService.XML_DTD_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::DTD"));
134
+ dtd.setNode(runtime, placeHolder);
138
135
  dtd.name = name;
139
136
  dtd.pubId = external_id;
140
137
  dtd.sysId = system_id;
@@ -155,38 +152,47 @@ public class XmlDtd extends XmlNode {
155
152
  * Document provided by NekoDTD.
156
153
  *
157
154
  */
158
- public static XmlDtd newFromInternalSubset(Ruby ruby, Document doc) {
155
+ public static XmlDtd newFromInternalSubset(Ruby runtime, Document doc) {
159
156
  Object dtdTree_ = doc.getUserData(XmlDocument.DTD_RAW_DOCUMENT);
160
- if (dtdTree_ == null)
161
- return new XmlDtd(ruby);
157
+ if (dtdTree_ == null) {
158
+ XmlDtd xmlDtd = (XmlDtd) NokogiriService.XML_DTD_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::DTD"));
159
+ xmlDtd.setNode(runtime, null);
160
+ return xmlDtd;
161
+ }
162
162
 
163
163
  Node dtdTree = (Node) dtdTree_;
164
164
  Node dtd = getInternalSubset(dtdTree);
165
165
  if (dtd == null) {
166
- return new XmlDtd(ruby);
166
+ XmlDtd xmlDtd = (XmlDtd) NokogiriService.XML_DTD_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::DTD"));
167
+ xmlDtd.setNode(runtime, null);
168
+ return xmlDtd;
167
169
  } else {
168
170
  // Import the node into doc so it has the correct owner document.
169
171
  dtd = doc.importNode(dtd, true);
170
- return new XmlDtd(ruby, dtd);
172
+ XmlDtd xmlDtd = (XmlDtd) NokogiriService.XML_DTD_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::DTD"));
173
+ xmlDtd.setNode(runtime, dtd);
174
+ return xmlDtd;
171
175
  }
172
176
  }
173
177
 
174
- public static IRubyObject newFromExternalSubset(Ruby ruby, Document doc) {
178
+ public static IRubyObject newFromExternalSubset(Ruby runtime, Document doc) {
175
179
  Object dtdTree_ = doc.getUserData(XmlDocument.DTD_RAW_DOCUMENT);
176
180
  if (dtdTree_ == null) {
177
- return ruby.getNil();
181
+ return runtime.getNil();
178
182
  }
179
183
 
180
184
  Node dtdTree = (Node) dtdTree_;
181
185
  Node dtd = getExternalSubset(dtdTree);
182
186
  if (dtd == null) {
183
- return ruby.getNil();
187
+ return runtime.getNil();
184
188
  } else if (!dtd.hasChildNodes()) {
185
- return ruby.getNil();
189
+ return runtime.getNil();
186
190
  } else {
187
191
  // Import the node into doc so it has the correct owner document.
188
192
  dtd = doc.importNode(dtd, true);
189
- return new XmlDtd(ruby, dtd);
193
+ XmlDtd xmlDtd = (XmlDtd) NokogiriService.XML_DTD_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::DTD"));
194
+ xmlDtd.setNode(runtime, dtd);
195
+ return xmlDtd;
190
196
  }
191
197
  }
192
198
 
@@ -365,10 +371,7 @@ public class XmlDtd extends XmlNode {
365
371
  extractDecls(context, node.getFirstChild());
366
372
 
367
373
  // convert allDecls to a NodeSet
368
- children =
369
- new XmlNodeSet(runtime,
370
- getNokogiriClass(runtime, "Nokogiri::XML::NodeSet"),
371
- allDecls);
374
+ children = XmlNodeSet.newXmlNodeSet(context, allDecls);
372
375
 
373
376
  // add attribute decls as attributes to the matching element decl
374
377
  RubyArray keys = attributes.keys();
@@ -455,13 +458,12 @@ public class XmlDtd extends XmlNode {
455
458
  }
456
459
  }
457
460
 
458
- public void saveContent(ThreadContext context, SaveContext ctx) {
459
- ctx.append("<!DOCTYPE " + name + " ");
460
- if (pubId != null) {
461
- ctx.append("PUBLIC \"" + pubId + "\" \"" + sysId + "\">");
462
- } else if (sysId != null) {
463
- ctx.append("SYSTEM " + sysId);
464
- }
461
+ @Override
462
+ public void accept(ThreadContext context, SaveContextVisitor visitor) {
463
+ // since we use nekoDTD to parse dtd, node might be ElementImpl type
464
+ // An external subset doesn't need to show up, so this method just see docType.
465
+ DocumentType docType = node.getOwnerDocument().getDoctype();
466
+ visitor.enter(docType);
467
+ visitor.leave(docType);
465
468
  }
466
-
467
469
  }
@@ -32,9 +32,8 @@
32
32
 
33
33
  package nokogiri;
34
34
 
35
- import static nokogiri.internals.NokogiriHelpers.namedNodeMapToRubyArray;
36
35
  import static nokogiri.internals.NokogiriHelpers.rubyStringToString;
37
- import nokogiri.internals.SaveContext;
36
+ import nokogiri.internals.SaveContextVisitor;
38
37
 
39
38
  import org.jruby.Ruby;
40
39
  import org.jruby.RubyArray;
@@ -94,8 +93,7 @@ public class XmlElement extends XmlNode {
94
93
  prefix.isNil() ? "xmlns" : "xmlns:" + rubyStringToString(prefix);
95
94
  element.setAttributeNS(uri, qName, rubyStringToString(href));
96
95
 
97
- XmlNamespace ns = (XmlNamespace)
98
- super.add_namespace_definition(context, prefix, href);
96
+ XmlNamespace ns = (XmlNamespace) super.add_namespace_definition(context, prefix, href);
99
97
  updateNodeNamespaceIfNecessary(context, ns);
100
98
 
101
99
  return ns;
@@ -174,49 +172,24 @@ public class XmlElement extends XmlNode {
174
172
  ((XmlNodeSet) children(context)).relink_namespace(context);
175
173
  }
176
174
  }
177
-
178
- /**
179
- * TODO: previous code handled elements with parent 'p' differently?.
180
- */
175
+
181
176
  @Override
182
- public void saveContent(ThreadContext context, SaveContext ctx) {
183
- Node firstChild = node.getFirstChild();
184
- boolean empty = (firstChild == null);
185
- short type = -1;
186
- if (!empty) type = firstChild.getNodeType();
187
- boolean inline = (!empty &&
188
- (type == Node.TEXT_NODE ||
189
- type == Node.CDATA_SECTION_NODE ||
190
- type == Node.ENTITY_REFERENCE_NODE));
191
-
192
- if (empty) {
193
- ctx.emptyTagStart(node.getNodeName());
194
- } else if (inline) {
195
- ctx.openTagInlineStart(node.getNodeName());
196
- } else {
197
- ctx.openTagStart(node.getNodeName());
198
- }
199
-
200
- RubyArray attr_list = namedNodeMapToRubyArray(context.getRuntime(), node.getAttributes());
201
- saveNodeListContent(context, attr_list, ctx);
202
-
203
- if (empty) {
204
- ctx.emptyTagEnd(node.getNodeName());
205
- return;
206
- } else if (inline) {
207
- ctx.openTagInlineEnd();
208
- } else {
209
- ctx.openTagEnd();
210
- }
211
-
212
- saveNodeListContent(context, (XmlNodeSet) children(context), ctx);
213
-
214
- if (inline) {
215
- ctx.closeTagInline(node.getNodeName());
216
- } else {
217
- ctx.closeTag(node.getNodeName());
177
+ public void accept(ThreadContext context, SaveContextVisitor visitor) {
178
+ visitor.enter((Element)node);
179
+ XmlNodeSet xmlNodeSet = (XmlNodeSet) children(context);
180
+ if (xmlNodeSet.length() > 0) {
181
+ RubyArray array = (RubyArray) xmlNodeSet.to_a(context);
182
+ for(int i = 0; i < array.getLength(); i++) {
183
+ Object item = array.get(i);
184
+ if (item instanceof XmlNode) {
185
+ XmlNode cur = (XmlNode) item;
186
+ cur.accept(context, visitor);
187
+ } else if (item instanceof XmlNamespace) {
188
+ XmlNamespace cur = (XmlNamespace)item;
189
+ cur.accept(context, visitor);
190
+ }
191
+ }
218
192
  }
219
-
193
+ visitor.leave((Element)node);
220
194
  }
221
-
222
195
  }
@@ -54,12 +54,16 @@ import org.w3c.dom.Node;
54
54
  @JRubyClass(name="Nokogiri::XML::ElementDecl", parent="Nokogiri::XML::Node")
55
55
  public class XmlElementDecl extends XmlNode {
56
56
  RubyArray attrDecls;
57
-
58
57
  IRubyObject contentModel;
59
-
60
- public XmlElementDecl(Ruby ruby, RubyClass klass) {
61
- super(ruby, klass);
62
- throw ruby.newRuntimeError("node required");
58
+
59
+ public XmlElementDecl(Ruby ruby, RubyClass klazz) {
60
+ super(ruby, klazz);
61
+ }
62
+
63
+ public void setNode(ThreadContext context, Node node) {
64
+ super.setNode(context, node);
65
+ attrDecls = RubyArray.newArray(context.getRuntime());
66
+ contentModel = context.getRuntime().getNil();
63
67
  }
64
68
 
65
69
  /**
@@ -32,14 +32,18 @@
32
32
 
33
33
  package nokogiri;
34
34
 
35
+ import static nokogiri.internals.NokogiriHelpers.getCachedNodeOrCreate;
35
36
  import static nokogiri.internals.NokogiriHelpers.rubyStringToString;
36
37
 
38
+ import nokogiri.internals.SaveContextVisitor;
39
+
37
40
  import org.jruby.Ruby;
38
41
  import org.jruby.RubyClass;
39
42
  import org.jruby.anno.JRubyClass;
40
43
  import org.jruby.runtime.ThreadContext;
41
44
  import org.jruby.runtime.builtin.IRubyObject;
42
45
  import org.w3c.dom.Document;
46
+ import org.w3c.dom.EntityReference;
43
47
  import org.w3c.dom.Node;
44
48
 
45
49
  /**
@@ -47,9 +51,10 @@ import org.w3c.dom.Node;
47
51
  *
48
52
  * @author sergio
49
53
  * @author Patrick Mahoney <pat@polycrystal.org>
54
+ * @author Yoko Harada <yokolet@gmail.com>
50
55
  */
51
56
  @JRubyClass(name="Nokogiri::XML::EntityReference", parent="Nokogiri::XML::Node")
52
- public class XmlEntityReference extends XmlNode{
57
+ public class XmlEntityReference extends XmlNode {
53
58
 
54
59
  public XmlEntityReference(Ruby ruby, RubyClass klazz) {
55
60
  super(ruby, klazz);
@@ -71,5 +76,22 @@ public class XmlEntityReference extends XmlNode{
71
76
  Node node = document.createEntityReference(rubyStringToString(name));
72
77
  setNode(context, node);
73
78
  }
74
-
79
+
80
+ @Override
81
+ public void accept(ThreadContext context, SaveContextVisitor visitor) {
82
+ visitor.enter((EntityReference)node);
83
+ Node child = node.getFirstChild();
84
+ while (child != null) {
85
+ IRubyObject nokoNode = getCachedNodeOrCreate(context.getRuntime(), child);
86
+ if (nokoNode instanceof XmlNode) {
87
+ XmlNode cur = (XmlNode) nokoNode;
88
+ cur.accept(context, visitor);
89
+ } else if (nokoNode instanceof XmlNamespace) {
90
+ XmlNamespace cur = (XmlNamespace) nokoNode;
91
+ cur.accept(context, visitor);
92
+ }
93
+ child = child.getNextSibling();
94
+ }
95
+ visitor.leave((EntityReference)node);
96
+ }
75
97
  }
@@ -32,9 +32,11 @@
32
32
 
33
33
  package nokogiri;
34
34
 
35
+ import static nokogiri.internals.NokogiriHelpers.CACHED_NODE;
36
+ import static nokogiri.internals.NokogiriHelpers.getCachedNodeOrCreate;
35
37
  import static nokogiri.internals.NokogiriHelpers.getLocalNameForNamespace;
36
38
  import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
37
- import nokogiri.internals.SaveContext;
39
+ import nokogiri.internals.SaveContextVisitor;
38
40
 
39
41
  import org.jruby.Ruby;
40
42
  import org.jruby.RubyClass;
@@ -44,6 +46,8 @@ import org.jruby.anno.JRubyClass;
44
46
  import org.jruby.anno.JRubyMethod;
45
47
  import org.jruby.runtime.ThreadContext;
46
48
  import org.jruby.runtime.builtin.IRubyObject;
49
+ import org.w3c.dom.Attr;
50
+ import org.w3c.dom.Document;
47
51
  import org.w3c.dom.Node;
48
52
 
49
53
  /**
@@ -54,37 +58,94 @@ import org.w3c.dom.Node;
54
58
  */
55
59
  @JRubyClass(name="Nokogiri::XML::Namespace")
56
60
  public class XmlNamespace extends RubyObject {
57
-
61
+ private Attr attr;
58
62
  private IRubyObject prefix;
59
63
  private IRubyObject href;
64
+ private String prefixString;
65
+ private String hrefString;
60
66
 
61
67
  public XmlNamespace(Ruby ruby, RubyClass klazz) {
62
68
  super(ruby, klazz);
63
69
  }
64
-
65
- public XmlNamespace(Ruby ruby, String prefix, String href) {
66
- this(ruby, getNokogiriClass(ruby, "Nokogiri::XML::Namespace"), prefix, href);
70
+
71
+ public Node getNode() {
72
+ return attr;
67
73
  }
68
-
69
- public XmlNamespace(Ruby ruby, RubyClass klazz, String prefix, String href) {
70
- super(ruby, klazz);
71
- this.prefix = (prefix == null) ? ruby.getNil() : RubyString.newString(ruby, prefix);
72
- this.href = (href == null) ? ruby.getNil() : RubyString.newString(ruby, href);
74
+
75
+ public String getPrefix() {
76
+ return prefixString;
73
77
  }
74
-
75
- public XmlNamespace(Ruby ruby, IRubyObject prefix, IRubyObject href) {
76
- this(ruby, getNokogiriClass(ruby, "Nokogiri::XML::Namespace"), prefix, href);
78
+
79
+ public String getHref() {
80
+ return hrefString;
77
81
  }
78
82
 
79
- public XmlNamespace(Ruby ruby, RubyClass klazz, IRubyObject prefix, IRubyObject href) {
80
- super(ruby, klazz);
83
+ public void init(Attr attr, IRubyObject prefix, IRubyObject href, IRubyObject xmlDocument) {
84
+ init(attr, prefix, href, (String) prefix.toJava(String.class), (String) href.toJava(String.class), xmlDocument);
85
+ }
86
+
87
+ public void init(Attr attr, IRubyObject prefix, IRubyObject href, String prefixString, String hrefString, IRubyObject xmlDocument) {
88
+ this.attr = attr;
81
89
  this.prefix = prefix;
82
90
  this.href = href;
91
+ this.prefixString = prefixString;
92
+ this.hrefString = hrefString;
93
+ this.attr.setUserData(CACHED_NODE, this, null);
94
+ setInstanceVariable("@document", xmlDocument);
83
95
  }
84
96
 
85
- public void setDefinition(Ruby runtime, String prefix, String href) {
86
- this.prefix = (prefix == null) ? runtime.getNil() : RubyString.newString(runtime, prefix);
87
- this.href = (href == null) ? runtime.getNil() : RubyString.newString(runtime, href);
97
+ public static XmlNamespace createFromAttr(Ruby runtime, Attr attr) {
98
+ String prefixValue = getLocalNameForNamespace(attr.getName());
99
+ IRubyObject prefix_value;
100
+ if (prefixValue == null) {
101
+ prefix_value = runtime.getNil();
102
+ prefixValue = "";
103
+ } else {
104
+ prefix_value = RubyString.newString(runtime, prefixValue);
105
+ }
106
+ String hrefValue = attr.getValue();
107
+ IRubyObject href_value = RubyString.newString(runtime, hrefValue);
108
+ // check namespace cache
109
+ XmlDocument xmlDocument = (XmlDocument)getCachedNodeOrCreate(runtime, attr.getOwnerDocument());
110
+ XmlNamespace xmlNamespace = xmlDocument.getNamespaceCache().get(prefixValue, hrefValue);
111
+ if (xmlNamespace != null) return xmlNamespace;
112
+
113
+ // creating XmlNamespace instance
114
+ XmlNamespace namespace =
115
+ (XmlNamespace) NokogiriService.XML_NAMESPACE_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::Namespace"));
116
+ namespace.init(attr, prefix_value, href_value, prefixValue, hrefValue, xmlDocument);
117
+
118
+ // updateing namespace cache
119
+ xmlDocument.getNamespaceCache().put(namespace, attr.getOwnerElement());
120
+ return namespace;
121
+ }
122
+
123
+ public static XmlNamespace createFromPrefixAndHref(Node owner, IRubyObject prefix, IRubyObject href) {
124
+ String prefixValue = prefix.isNil() ? "" : (String) prefix.toJava(String.class);
125
+ String hrefValue = (String) href.toJava(String.class);
126
+ Ruby runtime = prefix.getRuntime();
127
+ Document document = owner.getOwnerDocument();
128
+ // check namespace cache
129
+ XmlDocument xmlDocument = (XmlDocument)getCachedNodeOrCreate(runtime, document);
130
+ XmlNamespace xmlNamespace = xmlDocument.getNamespaceCache().get(prefixValue, hrefValue);
131
+ if (xmlNamespace != null) return xmlNamespace;
132
+
133
+ // creating XmlNamespace instance
134
+ XmlNamespace namespace =
135
+ (XmlNamespace) NokogiriService.XML_NAMESPACE_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::Namespace"));
136
+ String attrName = "xmlns";
137
+ if (!"".equals(prefixValue)) {
138
+ attrName = attrName + ":" + prefixValue;
139
+ }
140
+ Attr attrNode = document.createAttribute(attrName);
141
+ attrNode.setNodeValue(hrefValue);
142
+
143
+ // initialize XmlNamespace object
144
+ namespace.init(attrNode, prefix, href, prefixValue, hrefValue, xmlDocument);
145
+
146
+ // updating namespace cache
147
+ xmlDocument.getNamespaceCache().put(namespace, owner);
148
+ return namespace;
88
149
  }
89
150
 
90
151
  /**
@@ -97,32 +158,26 @@ public class XmlNamespace extends RubyObject {
97
158
  return super.clone();
98
159
  }
99
160
 
100
- public static XmlNamespace fromNode(Ruby ruby, Node node) {
101
- String localName = getLocalNameForNamespace(node.getNodeName());
102
- XmlNamespace namespace = (XmlNamespace) NokogiriService.XML_NAMESPACE_ALLOCATOR.allocate(ruby, getNokogiriClass(ruby, "Nokogiri::XML::Namespace"));
103
- namespace.setDefinition(ruby, localName, node.getNodeValue());
104
- return namespace;
105
- }
106
-
107
161
  public boolean isEmpty() {
108
- return this.prefix.isNil() && this.href.isNil();
109
- }
110
-
111
- public void setDocument(IRubyObject doc) {
112
- this.setInstanceVariable("@document", doc);
162
+ return prefix.isNil() && href.isNil();
113
163
  }
114
164
 
115
165
  @JRubyMethod
116
166
  public IRubyObject href(ThreadContext context) {
117
- return this.href;
167
+ return href;
118
168
  }
119
169
 
120
170
  @JRubyMethod
121
171
  public IRubyObject prefix(ThreadContext context) {
122
- return this.prefix;
172
+ return prefix;
123
173
  }
124
174
 
125
- public void saveContent(ThreadContext context, SaveContext ctx) {
126
- ctx.append(" " + prefix + "=\"" + href + "\"");
175
+ public void accept(ThreadContext context, SaveContextVisitor visitor) {
176
+ String string = " " + prefix + "=\"" + href + "\"";
177
+ visitor.enter(string);
178
+ visitor.leave(string);
179
+ // is below better?
180
+ //visitor.enter(attr);
181
+ //visitor.leave(attr);
127
182
  }
128
183
  }