nokogiri-backupify 1.5.0.beta.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (275) hide show
  1. data/.autotest +26 -0
  2. data/CHANGELOG.ja.rdoc +509 -0
  3. data/CHANGELOG.rdoc +490 -0
  4. data/Manifest.txt +274 -0
  5. data/README.ja.rdoc +106 -0
  6. data/README.rdoc +150 -0
  7. data/Rakefile +217 -0
  8. data/bin/nokogiri +54 -0
  9. data/deps.rip +5 -0
  10. data/ext/java/nokogiri/EncodingHandler.java +124 -0
  11. data/ext/java/nokogiri/HtmlDocument.java +146 -0
  12. data/ext/java/nokogiri/HtmlElementDescription.java +145 -0
  13. data/ext/java/nokogiri/HtmlEntityLookup.java +79 -0
  14. data/ext/java/nokogiri/HtmlSaxParserContext.java +256 -0
  15. data/ext/java/nokogiri/NokogiriService.java +466 -0
  16. data/ext/java/nokogiri/XmlAttr.java +183 -0
  17. data/ext/java/nokogiri/XmlAttributeDecl.java +130 -0
  18. data/ext/java/nokogiri/XmlCdata.java +89 -0
  19. data/ext/java/nokogiri/XmlComment.java +84 -0
  20. data/ext/java/nokogiri/XmlDocument.java +514 -0
  21. data/ext/java/nokogiri/XmlDocumentFragment.java +216 -0
  22. data/ext/java/nokogiri/XmlDtd.java +464 -0
  23. data/ext/java/nokogiri/XmlElement.java +221 -0
  24. data/ext/java/nokogiri/XmlElementContent.java +382 -0
  25. data/ext/java/nokogiri/XmlElementDecl.java +147 -0
  26. data/ext/java/nokogiri/XmlEntityDecl.java +161 -0
  27. data/ext/java/nokogiri/XmlEntityReference.java +75 -0
  28. data/ext/java/nokogiri/XmlNamespace.java +127 -0
  29. data/ext/java/nokogiri/XmlNode.java +1392 -0
  30. data/ext/java/nokogiri/XmlNodeSet.java +284 -0
  31. data/ext/java/nokogiri/XmlProcessingInstruction.java +103 -0
  32. data/ext/java/nokogiri/XmlReader.java +409 -0
  33. data/ext/java/nokogiri/XmlRelaxng.java +199 -0
  34. data/ext/java/nokogiri/XmlSaxParserContext.java +353 -0
  35. data/ext/java/nokogiri/XmlSaxPushParser.java +182 -0
  36. data/ext/java/nokogiri/XmlSchema.java +175 -0
  37. data/ext/java/nokogiri/XmlSyntaxError.java +114 -0
  38. data/ext/java/nokogiri/XmlText.java +135 -0
  39. data/ext/java/nokogiri/XmlXpathContext.java +175 -0
  40. data/ext/java/nokogiri/XsltStylesheet.java +181 -0
  41. data/ext/java/nokogiri/internals/HtmlDomParserContext.java +205 -0
  42. data/ext/java/nokogiri/internals/NokogiriDocumentCache.java +73 -0
  43. data/ext/java/nokogiri/internals/NokogiriErrorHandler.java +80 -0
  44. data/ext/java/nokogiri/internals/NokogiriHandler.java +326 -0
  45. data/ext/java/nokogiri/internals/NokogiriHelpers.java +583 -0
  46. data/ext/java/nokogiri/internals/NokogiriNamespaceCache.java +170 -0
  47. data/ext/java/nokogiri/internals/NokogiriNamespaceContext.java +118 -0
  48. data/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler.java +73 -0
  49. data/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler4NekoHtml.java +121 -0
  50. data/ext/java/nokogiri/internals/NokogiriStrictErrorHandler.java +78 -0
  51. data/ext/java/nokogiri/internals/NokogiriXPathFunction.java +120 -0
  52. data/ext/java/nokogiri/internals/NokogiriXPathFunctionResolver.java +56 -0
  53. data/ext/java/nokogiri/internals/ParserContext.java +278 -0
  54. data/ext/java/nokogiri/internals/PushInputStream.java +411 -0
  55. data/ext/java/nokogiri/internals/ReaderNode.java +473 -0
  56. data/ext/java/nokogiri/internals/SaveContext.java +282 -0
  57. data/ext/java/nokogiri/internals/SchemaErrorHandler.java +68 -0
  58. data/ext/java/nokogiri/internals/XmlDeclHandler.java +42 -0
  59. data/ext/java/nokogiri/internals/XmlDomParser.java +77 -0
  60. data/ext/java/nokogiri/internals/XmlDomParserContext.java +233 -0
  61. data/ext/java/nokogiri/internals/XmlSaxParser.java +65 -0
  62. data/ext/java/nokogiri/internals/XsltExtensionFunction.java +72 -0
  63. data/ext/nokogiri/depend +358 -0
  64. data/ext/nokogiri/extconf.rb +124 -0
  65. data/ext/nokogiri/html_document.c +154 -0
  66. data/ext/nokogiri/html_document.h +10 -0
  67. data/ext/nokogiri/html_element_description.c +276 -0
  68. data/ext/nokogiri/html_element_description.h +10 -0
  69. data/ext/nokogiri/html_entity_lookup.c +32 -0
  70. data/ext/nokogiri/html_entity_lookup.h +8 -0
  71. data/ext/nokogiri/html_sax_parser_context.c +94 -0
  72. data/ext/nokogiri/html_sax_parser_context.h +11 -0
  73. data/ext/nokogiri/nokogiri.c +92 -0
  74. data/ext/nokogiri/nokogiri.h +160 -0
  75. data/ext/nokogiri/xml_attr.c +94 -0
  76. data/ext/nokogiri/xml_attr.h +9 -0
  77. data/ext/nokogiri/xml_attribute_decl.c +70 -0
  78. data/ext/nokogiri/xml_attribute_decl.h +9 -0
  79. data/ext/nokogiri/xml_cdata.c +56 -0
  80. data/ext/nokogiri/xml_cdata.h +9 -0
  81. data/ext/nokogiri/xml_comment.c +54 -0
  82. data/ext/nokogiri/xml_comment.h +9 -0
  83. data/ext/nokogiri/xml_document.c +478 -0
  84. data/ext/nokogiri/xml_document.h +23 -0
  85. data/ext/nokogiri/xml_document_fragment.c +48 -0
  86. data/ext/nokogiri/xml_document_fragment.h +10 -0
  87. data/ext/nokogiri/xml_dtd.c +202 -0
  88. data/ext/nokogiri/xml_dtd.h +10 -0
  89. data/ext/nokogiri/xml_element_content.c +123 -0
  90. data/ext/nokogiri/xml_element_content.h +10 -0
  91. data/ext/nokogiri/xml_element_decl.c +69 -0
  92. data/ext/nokogiri/xml_element_decl.h +9 -0
  93. data/ext/nokogiri/xml_encoding_handler.c +79 -0
  94. data/ext/nokogiri/xml_encoding_handler.h +8 -0
  95. data/ext/nokogiri/xml_entity_decl.c +110 -0
  96. data/ext/nokogiri/xml_entity_decl.h +10 -0
  97. data/ext/nokogiri/xml_entity_reference.c +52 -0
  98. data/ext/nokogiri/xml_entity_reference.h +9 -0
  99. data/ext/nokogiri/xml_io.c +31 -0
  100. data/ext/nokogiri/xml_io.h +11 -0
  101. data/ext/nokogiri/xml_libxml2_hacks.c +112 -0
  102. data/ext/nokogiri/xml_libxml2_hacks.h +12 -0
  103. data/ext/nokogiri/xml_namespace.c +84 -0
  104. data/ext/nokogiri/xml_namespace.h +13 -0
  105. data/ext/nokogiri/xml_node.c +1384 -0
  106. data/ext/nokogiri/xml_node.h +13 -0
  107. data/ext/nokogiri/xml_node_set.c +418 -0
  108. data/ext/nokogiri/xml_node_set.h +9 -0
  109. data/ext/nokogiri/xml_processing_instruction.c +56 -0
  110. data/ext/nokogiri/xml_processing_instruction.h +9 -0
  111. data/ext/nokogiri/xml_reader.c +684 -0
  112. data/ext/nokogiri/xml_reader.h +10 -0
  113. data/ext/nokogiri/xml_relax_ng.c +161 -0
  114. data/ext/nokogiri/xml_relax_ng.h +9 -0
  115. data/ext/nokogiri/xml_sax_parser.c +288 -0
  116. data/ext/nokogiri/xml_sax_parser.h +39 -0
  117. data/ext/nokogiri/xml_sax_parser_context.c +199 -0
  118. data/ext/nokogiri/xml_sax_parser_context.h +10 -0
  119. data/ext/nokogiri/xml_sax_push_parser.c +115 -0
  120. data/ext/nokogiri/xml_sax_push_parser.h +9 -0
  121. data/ext/nokogiri/xml_schema.c +205 -0
  122. data/ext/nokogiri/xml_schema.h +9 -0
  123. data/ext/nokogiri/xml_syntax_error.c +58 -0
  124. data/ext/nokogiri/xml_syntax_error.h +13 -0
  125. data/ext/nokogiri/xml_text.c +50 -0
  126. data/ext/nokogiri/xml_text.h +9 -0
  127. data/ext/nokogiri/xml_xpath_context.c +309 -0
  128. data/ext/nokogiri/xml_xpath_context.h +9 -0
  129. data/ext/nokogiri/xslt_stylesheet.c +258 -0
  130. data/ext/nokogiri/xslt_stylesheet.h +9 -0
  131. data/lib/isorelax.jar +0 -0
  132. data/lib/jing.jar +0 -0
  133. data/lib/nekodtd.jar +0 -0
  134. data/lib/nekohtml.jar +0 -0
  135. data/lib/nokogiri.rb +143 -0
  136. data/lib/nokogiri/css.rb +23 -0
  137. data/lib/nokogiri/css/node.rb +99 -0
  138. data/lib/nokogiri/css/parser.rb +677 -0
  139. data/lib/nokogiri/css/parser.y +237 -0
  140. data/lib/nokogiri/css/parser_extras.rb +91 -0
  141. data/lib/nokogiri/css/syntax_error.rb +7 -0
  142. data/lib/nokogiri/css/tokenizer.rb +152 -0
  143. data/lib/nokogiri/css/tokenizer.rex +55 -0
  144. data/lib/nokogiri/css/xpath_visitor.rb +171 -0
  145. data/lib/nokogiri/decorators/slop.rb +35 -0
  146. data/lib/nokogiri/html.rb +36 -0
  147. data/lib/nokogiri/html/builder.rb +35 -0
  148. data/lib/nokogiri/html/document.rb +221 -0
  149. data/lib/nokogiri/html/document_fragment.rb +41 -0
  150. data/lib/nokogiri/html/element_description.rb +23 -0
  151. data/lib/nokogiri/html/element_description_defaults.rb +671 -0
  152. data/lib/nokogiri/html/entity_lookup.rb +13 -0
  153. data/lib/nokogiri/html/sax/parser.rb +52 -0
  154. data/lib/nokogiri/html/sax/parser_context.rb +16 -0
  155. data/lib/nokogiri/syntax_error.rb +4 -0
  156. data/lib/nokogiri/version.rb +35 -0
  157. data/lib/nokogiri/xml.rb +67 -0
  158. data/lib/nokogiri/xml/attr.rb +14 -0
  159. data/lib/nokogiri/xml/attribute_decl.rb +18 -0
  160. data/lib/nokogiri/xml/builder.rb +418 -0
  161. data/lib/nokogiri/xml/cdata.rb +11 -0
  162. data/lib/nokogiri/xml/character_data.rb +7 -0
  163. data/lib/nokogiri/xml/document.rb +218 -0
  164. data/lib/nokogiri/xml/document_fragment.rb +84 -0
  165. data/lib/nokogiri/xml/dtd.rb +22 -0
  166. data/lib/nokogiri/xml/element_content.rb +36 -0
  167. data/lib/nokogiri/xml/element_decl.rb +13 -0
  168. data/lib/nokogiri/xml/entity_decl.rb +19 -0
  169. data/lib/nokogiri/xml/namespace.rb +13 -0
  170. data/lib/nokogiri/xml/node.rb +907 -0
  171. data/lib/nokogiri/xml/node/save_options.rb +45 -0
  172. data/lib/nokogiri/xml/node_set.rb +350 -0
  173. data/lib/nokogiri/xml/notation.rb +6 -0
  174. data/lib/nokogiri/xml/parse_options.rb +85 -0
  175. data/lib/nokogiri/xml/pp.rb +2 -0
  176. data/lib/nokogiri/xml/pp/character_data.rb +18 -0
  177. data/lib/nokogiri/xml/pp/node.rb +56 -0
  178. data/lib/nokogiri/xml/processing_instruction.rb +8 -0
  179. data/lib/nokogiri/xml/reader.rb +112 -0
  180. data/lib/nokogiri/xml/relax_ng.rb +32 -0
  181. data/lib/nokogiri/xml/sax.rb +4 -0
  182. data/lib/nokogiri/xml/sax/document.rb +164 -0
  183. data/lib/nokogiri/xml/sax/parser.rb +115 -0
  184. data/lib/nokogiri/xml/sax/parser_context.rb +16 -0
  185. data/lib/nokogiri/xml/sax/push_parser.rb +60 -0
  186. data/lib/nokogiri/xml/schema.rb +57 -0
  187. data/lib/nokogiri/xml/syntax_error.rb +47 -0
  188. data/lib/nokogiri/xml/text.rb +9 -0
  189. data/lib/nokogiri/xml/xpath.rb +10 -0
  190. data/lib/nokogiri/xml/xpath/syntax_error.rb +11 -0
  191. data/lib/nokogiri/xml/xpath_context.rb +16 -0
  192. data/lib/nokogiri/xslt.rb +52 -0
  193. data/lib/nokogiri/xslt/stylesheet.rb +25 -0
  194. data/lib/xercesImpl.jar +0 -0
  195. data/lib/xsd/xmlparser/nokogiri.rb +90 -0
  196. data/tasks/cross_compile.rb +177 -0
  197. data/tasks/test.rb +94 -0
  198. data/test/css/test_nthiness.rb +159 -0
  199. data/test/css/test_parser.rb +303 -0
  200. data/test/css/test_tokenizer.rb +198 -0
  201. data/test/css/test_xpath_visitor.rb +85 -0
  202. data/test/decorators/test_slop.rb +16 -0
  203. data/test/files/2ch.html +108 -0
  204. data/test/files/address_book.rlx +12 -0
  205. data/test/files/address_book.xml +10 -0
  206. data/test/files/bar/bar.xsd +4 -0
  207. data/test/files/dont_hurt_em_why.xml +422 -0
  208. data/test/files/exslt.xml +8 -0
  209. data/test/files/exslt.xslt +35 -0
  210. data/test/files/foo/foo.xsd +4 -0
  211. data/test/files/po.xml +32 -0
  212. data/test/files/po.xsd +66 -0
  213. data/test/files/shift_jis.html +10 -0
  214. data/test/files/shift_jis.xml +5 -0
  215. data/test/files/snuggles.xml +3 -0
  216. data/test/files/staff.dtd +10 -0
  217. data/test/files/staff.xml +59 -0
  218. data/test/files/staff.xslt +32 -0
  219. data/test/files/tlm.html +850 -0
  220. data/test/files/valid_bar.xml +2 -0
  221. data/test/helper.rb +171 -0
  222. data/test/html/sax/test_parser.rb +136 -0
  223. data/test/html/sax/test_parser_context.rb +48 -0
  224. data/test/html/test_builder.rb +164 -0
  225. data/test/html/test_document.rb +457 -0
  226. data/test/html/test_document_encoding.rb +123 -0
  227. data/test/html/test_document_fragment.rb +255 -0
  228. data/test/html/test_element_description.rb +100 -0
  229. data/test/html/test_named_characters.rb +14 -0
  230. data/test/html/test_node.rb +190 -0
  231. data/test/html/test_node_encoding.rb +27 -0
  232. data/test/test_convert_xpath.rb +135 -0
  233. data/test/test_css_cache.rb +45 -0
  234. data/test/test_encoding_handler.rb +46 -0
  235. data/test/test_memory_leak.rb +52 -0
  236. data/test/test_nokogiri.rb +132 -0
  237. data/test/test_reader.rb +403 -0
  238. data/test/test_soap4r_sax.rb +52 -0
  239. data/test/test_xslt_transforms.rb +189 -0
  240. data/test/xml/node/test_save_options.rb +20 -0
  241. data/test/xml/node/test_subclass.rb +44 -0
  242. data/test/xml/sax/test_parser.rb +338 -0
  243. data/test/xml/sax/test_parser_context.rb +113 -0
  244. data/test/xml/sax/test_push_parser.rb +156 -0
  245. data/test/xml/test_attr.rb +65 -0
  246. data/test/xml/test_attribute_decl.rb +86 -0
  247. data/test/xml/test_builder.rb +210 -0
  248. data/test/xml/test_cdata.rb +50 -0
  249. data/test/xml/test_comment.rb +29 -0
  250. data/test/xml/test_document.rb +675 -0
  251. data/test/xml/test_document_encoding.rb +26 -0
  252. data/test/xml/test_document_fragment.rb +192 -0
  253. data/test/xml/test_dtd.rb +107 -0
  254. data/test/xml/test_dtd_encoding.rb +33 -0
  255. data/test/xml/test_element_content.rb +56 -0
  256. data/test/xml/test_element_decl.rb +73 -0
  257. data/test/xml/test_entity_decl.rb +122 -0
  258. data/test/xml/test_entity_reference.rb +21 -0
  259. data/test/xml/test_namespace.rb +70 -0
  260. data/test/xml/test_node.rb +899 -0
  261. data/test/xml/test_node_attributes.rb +34 -0
  262. data/test/xml/test_node_encoding.rb +107 -0
  263. data/test/xml/test_node_reparenting.rb +321 -0
  264. data/test/xml/test_node_set.rb +708 -0
  265. data/test/xml/test_parse_options.rb +52 -0
  266. data/test/xml/test_processing_instruction.rb +30 -0
  267. data/test/xml/test_reader_encoding.rb +126 -0
  268. data/test/xml/test_relax_ng.rb +60 -0
  269. data/test/xml/test_schema.rb +89 -0
  270. data/test/xml/test_syntax_error.rb +12 -0
  271. data/test/xml/test_text.rb +47 -0
  272. data/test/xml/test_unparented_node.rb +381 -0
  273. data/test/xml/test_xpath.rb +237 -0
  274. data/test/xslt/test_custom_functions.rb +94 -0
  275. metadata +525 -0
@@ -0,0 +1,1392 @@
1
+ /**
2
+ * (The MIT License)
3
+ *
4
+ * Copyright (c) 2008 - 2010:
5
+ *
6
+ * * {Aaron Patterson}[http://tenderlovemaking.com]
7
+ * * {Mike Dalessio}[http://mike.daless.io]
8
+ * * {Charles Nutter}[http://blog.headius.com]
9
+ * * {Sergio Arbeo}[http://www.serabe.com]
10
+ * * {Patrick Mahoney}[http://polycrystal.org]
11
+ * * {Yoko Harada}[http://yokolet.blogspot.com]
12
+ *
13
+ * Permission is hereby granted, free of charge, to any person obtaining
14
+ * a copy of this software and associated documentation files (the
15
+ * 'Software'), to deal in the Software without restriction, including
16
+ * without limitation the rights to use, copy, modify, merge, publish,
17
+ * distribute, sublicense, and/or sell copies of the Software, and to
18
+ * permit persons to whom the Software is furnished to do so, subject to
19
+ * the following conditions:
20
+ *
21
+ * The above copyright notice and this permission notice shall be
22
+ * included in all copies or substantial portions of the Software.
23
+ *
24
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
25
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
27
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
28
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
29
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
30
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31
+ */
32
+
33
+ package nokogiri;
34
+
35
+ import static java.lang.Math.max;
36
+ import static nokogiri.internals.NokogiriHelpers.getCachedNodeOrCreate;
37
+ import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
38
+ import static nokogiri.internals.NokogiriHelpers.nodeArrayToRubyArray;
39
+ import static nokogiri.internals.NokogiriHelpers.nonEmptyStringOrNil;
40
+ import static nokogiri.internals.NokogiriHelpers.rubyStringToString;
41
+ import static nokogiri.internals.NokogiriHelpers.stringOrNil;
42
+
43
+ import java.io.ByteArrayInputStream;
44
+ import java.io.InputStream;
45
+ import java.util.ArrayList;
46
+ import java.util.List;
47
+
48
+ import nokogiri.internals.HtmlDomParserContext;
49
+ import nokogiri.internals.NokogiriHelpers;
50
+ import nokogiri.internals.NokogiriNamespaceCache;
51
+ import nokogiri.internals.SaveContext;
52
+ import nokogiri.internals.XmlDomParserContext;
53
+
54
+ import org.jruby.Ruby;
55
+ import org.jruby.RubyArray;
56
+ import org.jruby.RubyBoolean;
57
+ import org.jruby.RubyClass;
58
+ import org.jruby.RubyFixnum;
59
+ import org.jruby.RubyModule;
60
+ import org.jruby.RubyObject;
61
+ import org.jruby.RubyString;
62
+ import org.jruby.anno.JRubyClass;
63
+ import org.jruby.anno.JRubyMethod;
64
+ import org.jruby.javasupport.util.RuntimeHelpers;
65
+ import org.jruby.runtime.Block;
66
+ import org.jruby.runtime.ThreadContext;
67
+ import org.jruby.runtime.Visibility;
68
+ import org.jruby.runtime.builtin.IRubyObject;
69
+ import org.w3c.dom.Attr;
70
+ import org.w3c.dom.Document;
71
+ import org.w3c.dom.Element;
72
+ import org.w3c.dom.NamedNodeMap;
73
+ import org.w3c.dom.Node;
74
+ import org.w3c.dom.NodeList;
75
+ import org.w3c.dom.Text;
76
+
77
+ /**
78
+ * Class for Nokogiri::XML::Node
79
+ *
80
+ */
81
+ @JRubyClass(name="Nokogiri::XML::Node")
82
+ public class XmlNode extends RubyObject {
83
+
84
+ /** The underlying Node object. */
85
+ protected Node node;
86
+
87
+ /* Cached objects */
88
+ protected IRubyObject content = null;
89
+ protected IRubyObject doc = null;
90
+ protected IRubyObject name = null;
91
+
92
+ /*
93
+ * Taken from http://ejohn.org/blog/comparing-document-position/
94
+ * Used for compareDocumentPosition.
95
+ * <ironic>Thanks to both java api and w3 doc for its helpful documentation</ironic>
96
+ */
97
+
98
+ protected static final int IDENTICAL_ELEMENTS = 0;
99
+ protected static final int IN_DIFFERENT_DOCUMENTS = 1;
100
+ protected static final int SECOND_PRECEDES_FIRST = 2;
101
+ protected static final int FIRST_PRECEDES_SECOND = 4;
102
+ protected static final int SECOND_CONTAINS_FIRST = 8;
103
+ protected static final int FIRST_CONTAINS_SECOND = 16;
104
+
105
+ /**
106
+ * Cast <code>node</code> to an XmlNode or raise a type error
107
+ * in <code>context</code>.
108
+ */
109
+ protected static XmlNode asXmlNode(ThreadContext context, IRubyObject node) {
110
+ if (node == null || !(node instanceof XmlNode)) {
111
+ Ruby ruby = context.getRuntime();
112
+ throw ruby.newTypeError(node, getNokogiriClass(ruby, "Nokogiri::XML::Node"));
113
+ } else {
114
+ return (XmlNode) node;
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Cast <code>node</code> to an XmlNode, or null if RubyNil, or
120
+ * raise a type error in <code>context</code>.
121
+ */
122
+ protected static XmlNode asXmlNodeOrNull(ThreadContext context, IRubyObject node) {
123
+ if (node == null || node.isNil()) {
124
+ return null;
125
+ } else {
126
+ return asXmlNode(context, node);
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Coalesce to adjacent TextNodes.
132
+ * @param context
133
+ * @param prev Previous node to cur.
134
+ * @param cur Next node to prev.
135
+ */
136
+ public static void coalesceTextNodes(ThreadContext context, IRubyObject prev, IRubyObject cur) {
137
+ XmlNode p = asXmlNode(context, prev);
138
+ XmlNode c = asXmlNode(context, cur);
139
+
140
+ Node pNode = p.node;
141
+ Node cNode = c.node;
142
+
143
+ pNode.setNodeValue(pNode.getNodeValue()+cNode.getNodeValue());
144
+ p.content = null; // clear cached content
145
+
146
+ c.assimilateXmlNode(context, p);
147
+ }
148
+
149
+ /**
150
+ * Coalesce text nodes around <code>anchorNode</code>. If
151
+ * <code>anchorNode</code> has siblings (previous or next) that
152
+ * are text nodes, the content will be merged into
153
+ * <code>anchorNode</code> and the redundant nodes will be removed
154
+ * from the DOM.
155
+ *
156
+ * To match libxml behavior (?) the final content of
157
+ * <code>anchorNode</code> and any removed nodes will be
158
+ * identical.
159
+ *
160
+ * @param context
161
+ * @param anchorNode
162
+ */
163
+ protected static void coalesceTextNodes(ThreadContext context,
164
+ IRubyObject anchorNode) {
165
+ XmlNode xa = asXmlNode(context, anchorNode);
166
+
167
+ XmlNode xp = asXmlNodeOrNull(context, xa.previous_sibling(context));
168
+ XmlNode xn = asXmlNodeOrNull(context, xa.next_sibling(context));
169
+
170
+ Node p = xp == null ? null : xp.node;
171
+ Node a = xa.node;
172
+ Node n = xn == null ? null : xn.node;
173
+
174
+ Node parent = a.getParentNode();
175
+
176
+ if (p != null && p.getNodeType() == Node.TEXT_NODE) {
177
+ xa.setContent(p.getNodeValue() + a.getNodeValue());
178
+ parent.removeChild(p);
179
+ xp.assimilateXmlNode(context, xa);
180
+ }
181
+ if (n != null && n.getNodeType() == Node.TEXT_NODE) {
182
+ xa.setContent(a.getNodeValue() + n.getNodeValue());
183
+ parent.removeChild(n);
184
+ xn.assimilateXmlNode(context, xa);
185
+ }
186
+ }
187
+
188
+ /**
189
+ * This is the allocator for XmlNode class. It should only be
190
+ * called from Ruby code.
191
+ */
192
+ public XmlNode(Ruby ruby, RubyClass cls) {
193
+ super(ruby, cls);
194
+ }
195
+
196
+ /**
197
+ * This is a constructor to create an XmlNode from an already
198
+ * existing node. It may be called by Java code.
199
+ */
200
+ public XmlNode(Ruby ruby, RubyClass cls, Node node) {
201
+ super(ruby, cls);
202
+ this.node = node;
203
+
204
+ if (node != null) {
205
+ resetCache();
206
+
207
+ if (node.getNodeType() != Node.DOCUMENT_NODE) {
208
+ doc = document(ruby.getCurrentContext());
209
+
210
+ if (doc != null) {
211
+ RuntimeHelpers.invoke(ruby.getCurrentContext(), doc, "decorate", this);
212
+ }
213
+ }
214
+ }
215
+
216
+ }
217
+
218
+ /**
219
+ * Create and return a copy of this object.
220
+ *
221
+ * @return a clone of this object
222
+ */
223
+ @Override
224
+ public Object clone() throws CloneNotSupportedException {
225
+ return super.clone();
226
+ }
227
+
228
+ protected void resetCache() {
229
+ node.setUserData(NokogiriHelpers.CACHED_NODE, this, null);
230
+ }
231
+
232
+ /**
233
+ * Allocate a new object, perform initialization, call that
234
+ * object's initialize method, and call any block passing the
235
+ * object as the only argument. If <code>cls</code> is
236
+ * Nokogiri::XML::Node, creates a new Nokogiri::XML::Element
237
+ * instead.
238
+ *
239
+ * This static method seems to be inherited, strangely enough.
240
+ * E.g. creating a new XmlAttr from Ruby code calls this method if
241
+ * XmlAttr does not define its own 'new' method.
242
+ *
243
+ * Since there is some Java bookkeeping that always needs to
244
+ * happen, we don't define the 'initialize' method in Java because
245
+ * we'd have to count on subclasses calling 'super'.
246
+ *
247
+ * The main consequence of this is that every subclass needs to
248
+ * define its own 'new' method.
249
+ *
250
+ * As a convenience, this method does the following:
251
+ *
252
+ * <ul>
253
+ *
254
+ * <li>allocates a new object using the allocator assigned to
255
+ * <code>cls</code></li>
256
+ *
257
+ * <li>calls the Java method init(); subclasses can override this,
258
+ * otherwise they should implement a specific 'new' method</li>
259
+ *
260
+ * <li>invokes the Ruby initializer</li>
261
+ *
262
+ * <li>if a block is given, calls the block with the new node as
263
+ * the argument</li>
264
+ *
265
+ * </ul>
266
+ *
267
+ * -pmahoney
268
+ */
269
+ @JRubyMethod(name = "new", meta = true, rest = true)
270
+ public static IRubyObject rbNew(ThreadContext context, IRubyObject cls,
271
+ IRubyObject[] args, Block block) {
272
+ Ruby ruby = context.getRuntime();
273
+ RubyClass klazz = (RubyClass) cls;
274
+
275
+ if (cls.equals(getNokogiriClass(ruby, "Nokogiri::XML::Node"))) {
276
+ klazz = getNokogiriClass(ruby, "Nokogiri::XML::Element");
277
+ }
278
+
279
+ XmlNode xmlNode = (XmlNode) klazz.allocate();
280
+ xmlNode.init(context, args);
281
+ xmlNode.callInit(args, block);
282
+ if (xmlNode.node == null) context.getRuntime().newRuntimeError("NODE IS NULL");
283
+ if (block.isGiven()) block.call(context, xmlNode);
284
+ return xmlNode;
285
+ }
286
+
287
+ /**
288
+ * Initialize the object from Ruby arguments. Should be
289
+ * overridden by subclasses. Should check for a minimum number of
290
+ * args but not for an exact number. Any extra args will then be
291
+ * passed to 'initialize'. The way 'new' and this 'init' function
292
+ * interact means that subclasses cannot arbitrarily change the
293
+ * require aruments by defining an 'initialize' method. This is
294
+ * how the C libxml wrapper works also.
295
+ *
296
+ * As written it performs initialization for a new Element with
297
+ * the given <code>name</code> within the document
298
+ * <code>doc</code>. So XmlElement need not override this. This
299
+ * implementation cannot be moved to XmlElement however, because
300
+ * subclassing XmlNode must result in something that behaves much
301
+ * like XmlElement.
302
+ */
303
+ protected void init(ThreadContext context, IRubyObject[] args) {
304
+ if (args.length < 2)
305
+ throw context.getRuntime().newArgumentError(args.length, 2);
306
+
307
+ IRubyObject name = args[0];
308
+ IRubyObject doc = args[1];
309
+
310
+ Document document = asXmlNode(context, doc).getOwnerDocument();
311
+ if (document == null) {
312
+ throw getRuntime().newArgumentError("node must have owner document");
313
+ }
314
+
315
+ Element element = document.createElementNS(null, rubyStringToString(name));
316
+ setNode(context, element);
317
+ }
318
+
319
+ /**
320
+ * Set the underlying node of this node to the underlying node of
321
+ * <code>otherNode</code>.
322
+ *
323
+ * FIXME: also update the cached node?
324
+ */
325
+ protected void assimilateXmlNode(ThreadContext context, IRubyObject otherNode) {
326
+ XmlNode toAssimilate = asXmlNode(context, otherNode);
327
+
328
+ this.node = toAssimilate.node;
329
+ content = null; // clear cache
330
+ }
331
+
332
+ /**
333
+ * See org.w3.dom.Node#normalize.
334
+ */
335
+ public void normalize() {
336
+ node.normalize();
337
+ }
338
+
339
+ public Node getNode() {
340
+ return node;
341
+ }
342
+
343
+ public static Node getNodeFromXmlNode(ThreadContext context, IRubyObject xmlNode) {
344
+ return asXmlNode(context, xmlNode).node;
345
+ }
346
+
347
+ protected String indentString(IRubyObject indentStringObject, String xml) {
348
+ String[] lines = xml.split("\n");
349
+
350
+ if(lines.length <= 1) return xml;
351
+
352
+ String[] resultLines = new String[lines.length];
353
+
354
+ String curLine;
355
+ boolean closingTag = false;
356
+ String indentString = rubyStringToString(indentStringObject);
357
+ int lengthInd = indentString.length();
358
+ StringBuffer curInd = new StringBuffer();
359
+
360
+ resultLines[0] = lines[0];
361
+
362
+ for(int i = 1; i < lines.length; i++) {
363
+
364
+ curLine = lines[i].trim();
365
+
366
+ if(curLine.length() == 0) continue;
367
+
368
+ if(curLine.startsWith("</")) {
369
+ closingTag = true;
370
+ curInd.setLength(max(0,curInd.length() - lengthInd));
371
+ }
372
+
373
+ resultLines[i] = curInd.toString() + curLine;
374
+
375
+ if(!curLine.endsWith("/>") && !closingTag) {
376
+ curInd.append(indentString);
377
+ }
378
+
379
+ closingTag = false;
380
+ }
381
+
382
+ StringBuffer result = new StringBuffer();
383
+ for(int i = 0; i < resultLines.length; i++) {
384
+ result.append(resultLines[i]);
385
+ result.append("\n");
386
+ }
387
+
388
+ return result.toString();
389
+ }
390
+
391
+ public boolean isComment() { return false; }
392
+
393
+ public boolean isElement() { return false; }
394
+
395
+ public boolean isProcessingInstruction() { return false; }
396
+
397
+ /**
398
+ * Return the string value of the attribute <code>key</code> or
399
+ * nil.
400
+ *
401
+ * Only applies where the underlying Node is an Element node, but
402
+ * implemented here in XmlNode because not all nodes with
403
+ * underlying Element nodes subclass XmlElement, such as the DTD
404
+ * declarations like XmlElementDecl.
405
+ */
406
+ protected IRubyObject getAttribute(ThreadContext context, String key) {
407
+ return getAttribute(context.getRuntime(), key);
408
+ }
409
+
410
+ protected IRubyObject getAttribute(Ruby runtime, String key) {
411
+ String value = getAttribute(key);
412
+ return nonEmptyStringOrNil(runtime, value);
413
+ }
414
+
415
+ protected String getAttribute(String key) {
416
+ if (node.getNodeType() != Node.ELEMENT_NODE) return null;
417
+
418
+ String value = ((Element)node).getAttribute(key);
419
+ return value.length() == 0 ? null : value;
420
+ }
421
+
422
+
423
+ public void post_add_child(ThreadContext context, XmlNode current, XmlNode child) {
424
+ }
425
+
426
+ public void relink_namespace(ThreadContext context) {
427
+ //this should delegate to subclasses' implementation
428
+ }
429
+
430
+ public void saveContent(ThreadContext context, SaveContext ctx) {
431
+ }
432
+
433
+ public void setName(IRubyObject name) {
434
+ this.name = name;
435
+ }
436
+
437
+ public void setDocument(ThreadContext context, IRubyObject doc) {
438
+ this.doc = doc;
439
+ setInstanceVariable("@document", doc);
440
+ if (doc != null) {
441
+ RuntimeHelpers.invoke(context, doc, "decorate", this);
442
+ }
443
+ }
444
+
445
+ public void setNode(ThreadContext context, Node node) {
446
+ this.node = node;
447
+
448
+ if (node != null) {
449
+ resetCache();
450
+ if (node.getNodeType() != Node.DOCUMENT_NODE) {
451
+ doc = document(context);
452
+ }
453
+ }
454
+ }
455
+
456
+ public void updateNodeNamespaceIfNecessary(ThreadContext context, XmlNamespace ns) {
457
+ String oldPrefix = this.node.getPrefix();
458
+ String uri = rubyStringToString(ns.href(context));
459
+
460
+ /*
461
+ * Update if both prefixes are null or equal
462
+ */
463
+ boolean update = (oldPrefix == null && ns.prefix(context).isNil()) ||
464
+ (oldPrefix != null && !ns.prefix(context).isNil()
465
+ && oldPrefix.equals(rubyStringToString(ns.prefix(context))));
466
+
467
+ if(update) {
468
+ this.node.getOwnerDocument().renameNode(this.node, uri, this.node.getNodeName());
469
+ }
470
+ }
471
+
472
+ protected IRubyObject getNodeName(ThreadContext context) {
473
+ if (name != null) return name;
474
+ String str = null;
475
+
476
+ if (this.name == null && node != null) {
477
+ str = node.getNodeName();
478
+ str = NokogiriHelpers.getLocalPart(str);
479
+ }
480
+ if (str == null) str = "";
481
+ name = context.getRuntime().newString(str);
482
+ return name;
483
+ }
484
+
485
+ protected void saveNodeListContent(ThreadContext context, XmlNodeSet list, SaveContext ctx) {
486
+ saveNodeListContent(context, (RubyArray) list.to_a(context), ctx);
487
+ }
488
+
489
+ protected void saveNodeListContent(ThreadContext context, RubyArray array, SaveContext ctx) {
490
+ int length = array.getLength();
491
+
492
+ boolean formatIndentation = ctx.format() && ctx.indentString()!=null;
493
+
494
+ for(int i = 0; i < length; i++) {
495
+ Object item = array.get(i);
496
+ if (item instanceof XmlNode) {
497
+ XmlNode cur = (XmlNode) item;
498
+
499
+ // if(formatIndentation &&
500
+ // (cur.isElement() || cur.isComment() || cur.isProcessingInstruction())) {
501
+ // ctx.append(ctx.getCurrentIndentString());
502
+ // }
503
+
504
+ cur.saveContent(context, ctx);
505
+ } else if (item instanceof XmlNamespace) {
506
+ XmlNamespace cur = (XmlNamespace)item;
507
+ cur.saveContent(context, ctx);
508
+ }
509
+
510
+ // if(ctx.format()) ctx.append("\n");
511
+ }
512
+ }
513
+
514
+ /**
515
+ * Add a namespace definition to this node. To the underlying
516
+ * node, add an attribute of the form
517
+ * <code>xmlns:prefix="uri"</code>.
518
+ */
519
+ @JRubyMethod(name = {"add_namespace_definition", "add_namespace"})
520
+ public IRubyObject add_namespace_definition(ThreadContext context,
521
+ IRubyObject prefix,
522
+ IRubyObject href) {
523
+ String prefixString = prefix.isNil() ? "" : rubyStringToString(prefix);
524
+ String hrefString = rubyStringToString(href);
525
+ XmlDocument xmlDocument = (XmlDocument) doc;
526
+ Node namespaceOwner;
527
+ if (node.getNodeType() == Node.ELEMENT_NODE) namespaceOwner = node;
528
+ else if (node.getNodeType() == Node.ATTRIBUTE_NODE) namespaceOwner = ((Attr)node).getOwnerElement();
529
+ else namespaceOwner = node.getParentNode();
530
+ XmlNamespace ns = xmlDocument.getNamespaceCache().put(context.getRuntime(), prefixString, hrefString, namespaceOwner, xmlDocument);
531
+ if (node != namespaceOwner) {
532
+ node.getOwnerDocument().renameNode(node, hrefString, prefixString + node.getLocalName());
533
+ }
534
+
535
+ return ns;
536
+ }
537
+
538
+ @JRubyMethod(name = {"attribute", "attr"})
539
+ public IRubyObject attribute(ThreadContext context, IRubyObject name){
540
+ NamedNodeMap attrs = this.node.getAttributes();
541
+ Node attr = attrs.getNamedItem(rubyStringToString(name));
542
+ if(attr == null) {
543
+ return context.getRuntime().newString(ERR_INSECURE_SET_INST_VAR);
544
+ }
545
+ return getCachedNodeOrCreate(context.getRuntime(), attr);
546
+ }
547
+
548
+ @JRubyMethod
549
+ public IRubyObject attribute_nodes(ThreadContext context) {
550
+ NamedNodeMap nodeMap = this.node.getAttributes();
551
+
552
+ Ruby ruby = context.getRuntime();
553
+ if(nodeMap == null){
554
+ return ruby.newEmptyArray();
555
+ }
556
+
557
+ RubyArray attr = ruby.newArray();
558
+
559
+ for(int i = 0; i < nodeMap.getLength(); i++) {
560
+ if (!NokogiriHelpers.isNamespace(nodeMap.item(i))) {
561
+ attr.append(getCachedNodeOrCreate(context.getRuntime(), nodeMap.item(i)));
562
+ }
563
+ }
564
+
565
+ return attr;
566
+ }
567
+
568
+ @JRubyMethod
569
+ public IRubyObject attribute_with_ns(ThreadContext context, IRubyObject name, IRubyObject namespace) {
570
+ String namej = rubyStringToString(name);
571
+ String nsj = (namespace.isNil()) ? null : rubyStringToString(namespace);
572
+
573
+ Node el = this.node.getAttributes().getNamedItemNS(nsj, namej);
574
+
575
+ if(el == null) {
576
+ return context.getRuntime().getNil();
577
+ }
578
+ return NokogiriHelpers.getCachedNodeOrCreate(context.getRuntime(), el);
579
+ }
580
+
581
+ @JRubyMethod(name = "blank?")
582
+ public IRubyObject blank_p(ThreadContext context) {
583
+ String data = node.getTextContent();
584
+ if ("".equals(data.trim())) return context.getRuntime().getTrue();
585
+ return context.getRuntime().getFalse();
586
+ }
587
+
588
+ @JRubyMethod
589
+ public IRubyObject child(ThreadContext context) {
590
+ return getCachedNodeOrCreate(context.getRuntime(), node.getFirstChild());
591
+ }
592
+
593
+ @JRubyMethod
594
+ public IRubyObject children(ThreadContext context) {
595
+ XmlNodeSet result = new XmlNodeSet(context.getRuntime(), node.getChildNodes());
596
+ return result;
597
+ }
598
+
599
+ @JRubyMethod
600
+ public IRubyObject first_element_child(ThreadContext context) {
601
+ List<Node> elementNodes = new ArrayList<Node>();
602
+ addElements(node, elementNodes, true);
603
+ if (elementNodes.size() == 0) return context.getRuntime().getNil();
604
+ return getCachedNodeOrCreate(context.getRuntime(), elementNodes.get(0));
605
+ }
606
+
607
+ @JRubyMethod
608
+ public IRubyObject last_element_child(ThreadContext context) {
609
+ List<Node> elementNodes = new ArrayList<Node>();
610
+ addElements(node, elementNodes, false);
611
+ if (elementNodes.size() == 0) return context.getRuntime().getNil();
612
+ return getCachedNodeOrCreate(context.getRuntime(), elementNodes.get(elementNodes.size()-1));
613
+ }
614
+
615
+ @JRubyMethod(name = {"element_children", "elements"})
616
+ public IRubyObject element_children(ThreadContext context) {
617
+ List<Node> elementNodes = new ArrayList<Node>();
618
+ addElements(node, elementNodes, false);
619
+ if (elementNodes.size() == 0) return XmlNodeSet.newEmptyNodeSet(context);
620
+ RubyArray array = NokogiriHelpers.nodeArrayToRubyArray(context.getRuntime(), elementNodes.toArray(new Node[0]));
621
+ XmlNodeSet result = new XmlNodeSet(context.getRuntime(), array);
622
+ return result;
623
+ }
624
+
625
+ private void addElements(Node n, List<Node> nodes, boolean isFirstOnly) {
626
+ NodeList children = n.getChildNodes();
627
+ if (children.getLength() == 0) return;
628
+ for (int i=0; i< children.getLength(); i++) {
629
+ Node child = children.item(i);
630
+ if (child.getNodeType() == Node.ELEMENT_NODE) {
631
+ nodes.add(child);
632
+ if (isFirstOnly) return;
633
+ }
634
+ }
635
+ }
636
+
637
+ /**
638
+ * call-seq:
639
+ * compare(other)
640
+ *
641
+ * Compare this Node to +other+ with respect to their Document
642
+ */
643
+ @JRubyMethod(visibility=Visibility.PRIVATE)
644
+ public IRubyObject compare(ThreadContext context, IRubyObject other) {
645
+ if (!(other instanceof XmlNode)) {
646
+ return context.getRuntime().newFixnum(-2);
647
+ }
648
+
649
+ Node otherNode = asXmlNode(context, other).node;
650
+
651
+ // Do not touch this if, if it's not for a good reason.
652
+ if (node.getNodeType() == Node.DOCUMENT_NODE ||
653
+ otherNode.getNodeType() == Node.DOCUMENT_NODE) {
654
+ return context.getRuntime().newFixnum(-1);
655
+ }
656
+
657
+ try{
658
+ int res = node.compareDocumentPosition(otherNode);
659
+ if ((res & FIRST_PRECEDES_SECOND) == FIRST_PRECEDES_SECOND) {
660
+ return context.getRuntime().newFixnum(-1);
661
+ } else if ((res & SECOND_PRECEDES_FIRST) == SECOND_PRECEDES_FIRST) {
662
+ return context.getRuntime().newFixnum(1);
663
+ } else if (res == IDENTICAL_ELEMENTS) {
664
+ return context.getRuntime().newFixnum(0);
665
+ }
666
+
667
+ return context.getRuntime().newFixnum(-2);
668
+ } catch (Exception ex) {
669
+ return context.getRuntime().newFixnum(-2);
670
+ }
671
+ }
672
+
673
+ /**
674
+ * TODO: this is a stub implementation. It's not clear what
675
+ * 'in_context' is supposed to do. Also should take
676
+ * <code>options</code> into account.
677
+ */
678
+ @JRubyMethod(required = 2, visibility = Visibility.PRIVATE)
679
+ public IRubyObject in_context(ThreadContext context,
680
+ IRubyObject str,
681
+ IRubyObject options) {
682
+ RubyModule klass;
683
+ XmlDomParserContext ctx;
684
+ InputStream istream;
685
+ XmlDocument document;
686
+
687
+ IRubyObject d = document(context);
688
+ Ruby runtime = context.getRuntime();
689
+ if (d != null && d instanceof XmlDocument) {
690
+ document = (XmlDocument)d;
691
+ } else {
692
+ return runtime.getNil();
693
+ }
694
+
695
+ if (document instanceof HtmlDocument) {
696
+ klass = getNokogiriClass(runtime, "Nokogiri::HTML::Document");
697
+ ctx = new HtmlDomParserContext(runtime, options);
698
+ ((HtmlDomParserContext)ctx).enableDocumentFragment();
699
+ istream = new ByteArrayInputStream((rubyStringToString(str)).getBytes());
700
+ } else if (document instanceof XmlDocument) {
701
+ klass = getNokogiriClass(runtime, "Nokogiri::XML::Document");
702
+ ctx = new XmlDomParserContext(runtime, options);
703
+ String input = rubyStringToString(str);
704
+ istream = new ByteArrayInputStream(input.getBytes());
705
+ } else {
706
+ return runtime.getNil();
707
+ }
708
+
709
+ ctx.setInputSource(istream);
710
+ XmlDocument doc = ctx.parse(context, klass, runtime.getNil());
711
+
712
+ RubyArray documentErrors = getErrorArray(document);
713
+ RubyArray docErrors = getErrorArray(doc);
714
+ if (isErrorIncreated(documentErrors, docErrors)) {
715
+ for (int i = 0; i < docErrors.getLength(); i++) {
716
+ documentErrors.add(docErrors.get(i));
717
+ }
718
+ document.setInstanceVariable("@errors", documentErrors);
719
+ return new XmlNodeSet(getRuntime(), RubyArray.newArray(runtime));
720
+ }
721
+
722
+ // The first child might be document type node (dtd declaration).
723
+ // XmlNodeSet to be return should not have dtd decl in its list.
724
+ Node first;
725
+ if (doc.node.getFirstChild().getNodeType() == Node.DOCUMENT_TYPE_NODE) {
726
+ first = doc.node.getFirstChild().getNextSibling();
727
+ } else {
728
+ first = doc.node.getFirstChild();
729
+ }
730
+ RubyArray nodeArray = RubyArray.newArray(runtime);
731
+ nodeArray.add(NokogiriHelpers.getCachedNodeOrCreate(runtime, first));
732
+
733
+ NokogiriHelpers.nodeListToRubyArray(runtime, first.getChildNodes(), nodeArray);
734
+ XmlNodeSet nodes = new XmlNodeSet(getRuntime(), nodeArray);
735
+ return nodes;
736
+ }
737
+
738
+ private RubyArray getErrorArray(XmlDocument document) {
739
+ IRubyObject obj = document.getInstanceVariable("@errors");
740
+ if (obj != null && obj instanceof RubyArray) {
741
+ return (RubyArray)obj;
742
+ }
743
+ return RubyArray.newArray(document.getRuntime());
744
+ }
745
+
746
+ private boolean isErrorIncreated(RubyArray baseErrors, RubyArray createdErrors) {
747
+ RubyBoolean result = baseErrors.compare(baseErrors.getRuntime().getCurrentContext(), "eql?", createdErrors, null);
748
+ return result.isFalse();
749
+ }
750
+
751
+ @JRubyMethod(name = {"content", "text", "inner_text"})
752
+ public IRubyObject content(ThreadContext context) {
753
+ if (content != null && content.isNil()) return content;
754
+ String textContent;
755
+ if (content != null) textContent = rubyStringToString(content);
756
+ else if (this instanceof XmlDocument) {
757
+ textContent = ((Document)this.node).getDocumentElement().getTextContent().trim();
758
+ } else {
759
+ textContent = this.node.getTextContent();
760
+ }
761
+ String decodedText = null;
762
+ if (textContent != null) decodedText = NokogiriHelpers.decodeJavaString(textContent);
763
+ return stringOrNil(context.getRuntime(), decodedText);
764
+ }
765
+
766
+ @JRubyMethod
767
+ public IRubyObject document(ThreadContext context) {
768
+ if(doc == null) {
769
+ doc = getCachedNodeOrCreate(context.getRuntime(), node.getOwnerDocument());
770
+ }
771
+ return doc;
772
+ }
773
+
774
+ @JRubyMethod
775
+ public IRubyObject dup(ThreadContext context) {
776
+ return this.dup_implementation(context, true);
777
+ }
778
+
779
+ @JRubyMethod
780
+ public IRubyObject dup(ThreadContext context, IRubyObject depth) {
781
+ boolean deep = (Integer)depth.toJava(Integer.class) != 0;
782
+
783
+ return this.dup_implementation(context, deep);
784
+ }
785
+
786
+ protected IRubyObject dup_implementation(ThreadContext context, boolean deep) {
787
+ XmlNode clone;
788
+ try {
789
+ clone = (XmlNode) clone();
790
+ } catch (CloneNotSupportedException e) {
791
+ throw context.getRuntime().newRuntimeError(e.toString());
792
+ }
793
+ if (node == null) throw context.getRuntime().newRuntimeError("FFFFFFFFFUUUUUUU");
794
+ Node newNode = node.cloneNode(deep);
795
+ clone.node = newNode;
796
+ return clone;
797
+ }
798
+
799
+ public static IRubyObject encode_special_chars(ThreadContext context,
800
+ IRubyObject string) {
801
+ String s = rubyStringToString(string);
802
+ String enc = NokogiriHelpers.encodeJavaString(s);
803
+ return context.getRuntime().newString(enc);
804
+ }
805
+
806
+ /**
807
+ * Instance method version of the above static method.
808
+ */
809
+ @JRubyMethod(name="encode_special_chars")
810
+ public IRubyObject i_encode_special_chars(ThreadContext context,
811
+ IRubyObject string) {
812
+ return encode_special_chars(context, string);
813
+ }
814
+
815
+ /**
816
+ * Get the attribute at the given key, <code>key</code>.
817
+ * Assumes that this node has attributes (i.e. that key? returned
818
+ * true). Overridden in XmlElement.
819
+ */
820
+ @JRubyMethod(visibility = Visibility.PRIVATE)
821
+ public IRubyObject get(ThreadContext context, IRubyObject key) {
822
+ return context.getRuntime().getNil();
823
+ }
824
+
825
+ /**
826
+ * Returns the owner document, checking if this node is the
827
+ * document, or returns null if there is no owner.
828
+ */
829
+ protected Document getOwnerDocument() {
830
+ if (node.getNodeType() == Node.DOCUMENT_NODE) {
831
+ return (Document) node;
832
+ } else {
833
+ return node.getOwnerDocument();
834
+ }
835
+ }
836
+
837
+ @JRubyMethod
838
+ public IRubyObject internal_subset(ThreadContext context) {
839
+ Document document = getOwnerDocument();
840
+
841
+ if(document == null) {
842
+ return context.getRuntime().getNil();
843
+ }
844
+
845
+ XmlDocument xdoc =
846
+ (XmlDocument) getCachedNodeOrCreate(context.getRuntime(), document);
847
+ IRubyObject xdtd = xdoc.getInternalSubset(context);
848
+ return xdtd;
849
+ }
850
+
851
+ @JRubyMethod
852
+ public IRubyObject create_internal_subset(ThreadContext context,
853
+ IRubyObject name,
854
+ IRubyObject external_id,
855
+ IRubyObject system_id) {
856
+ IRubyObject subset = internal_subset(context);
857
+ if (!subset.isNil()) {
858
+ throw context.getRuntime()
859
+ .newRuntimeError("Document already has internal subset");
860
+ }
861
+
862
+ Document document = getOwnerDocument();
863
+ if(document == null) {
864
+ return context.getRuntime().getNil();
865
+ }
866
+
867
+ XmlDocument xdoc =
868
+ (XmlDocument) getCachedNodeOrCreate(context.getRuntime(), document);
869
+ IRubyObject xdtd = xdoc.createInternalSubset(context, name,
870
+ external_id, system_id);
871
+ return xdtd;
872
+ }
873
+
874
+ @JRubyMethod
875
+ public IRubyObject external_subset(ThreadContext context) {
876
+ Document document = getOwnerDocument();
877
+
878
+ if(document == null) {
879
+ return context.getRuntime().getNil();
880
+ }
881
+
882
+ XmlDocument xdoc =
883
+ (XmlDocument) getCachedNodeOrCreate(context.getRuntime(), document);
884
+ IRubyObject xdtd = xdoc.getExternalSubset(context);
885
+ return xdtd;
886
+ }
887
+
888
+ @JRubyMethod
889
+ public IRubyObject create_external_subset(ThreadContext context,
890
+ IRubyObject name,
891
+ IRubyObject external_id,
892
+ IRubyObject system_id) {
893
+ IRubyObject subset = external_subset(context);
894
+ if (!subset.isNil()) {
895
+ throw context.getRuntime()
896
+ .newRuntimeError("Document already has external subset");
897
+ }
898
+
899
+ Document document = getOwnerDocument();
900
+ if(document == null) {
901
+ return context.getRuntime().getNil();
902
+ }
903
+ XmlDocument xdoc = (XmlDocument) getCachedNodeOrCreate(context.getRuntime(), document);
904
+ IRubyObject xdtd = xdoc.createExternalSubset(context, name, external_id, system_id);
905
+ return xdtd;
906
+ }
907
+
908
+ /**
909
+ * Test if this node has an attribute named <code>rbkey</code>.
910
+ * Overridden in XmlElement.
911
+ */
912
+ @JRubyMethod(name = {"key?", "has_attribute?"})
913
+ public IRubyObject key_p(ThreadContext context, IRubyObject rbkey) {
914
+ return context.getRuntime().getNil();
915
+ }
916
+
917
+ @JRubyMethod
918
+ public IRubyObject namespace(ThreadContext context){
919
+ XmlDocument xmlDocument = (XmlDocument) doc;
920
+ NokogiriNamespaceCache nsCache = xmlDocument.getNamespaceCache();
921
+ String prefix = node.getPrefix();
922
+ XmlNamespace namespace = nsCache.get(prefix == null ? "" : prefix, node.getNamespaceURI());
923
+ if (namespace == null || ((XmlNamespace) namespace).isEmpty()) {
924
+ return context.getRuntime().getNil();
925
+ }
926
+
927
+ return namespace;
928
+ }
929
+
930
+ /**
931
+ * Return an array of XmlNamespace nodes based on the attributes
932
+ * of this node.
933
+ */
934
+ @JRubyMethod
935
+ public IRubyObject namespace_definitions(ThreadContext context) {
936
+ // don't use namespace_definitions cache anymore since
937
+ // namespaces might be deleted. Reflecting the result of
938
+ // namesapce removals is complicated, so the cache might not be
939
+ // updated.
940
+ Ruby ruby = context.getRuntime();
941
+ RubyArray namespace_definitions = ruby.newArray();
942
+ if (doc == null) return namespace_definitions;
943
+ List<XmlNamespace> namespaces = ((XmlDocument)doc).getNamespaceCache().get(node);
944
+ for (XmlNamespace namespace : namespaces) {
945
+ ((RubyArray)namespace_definitions).append(namespace);
946
+ }
947
+
948
+ return (RubyArray) namespace_definitions;
949
+ }
950
+
951
+ /**
952
+ * Return an array of XmlNamespace nodes defined on this node and
953
+ * on any ancestor node.
954
+ */
955
+ @JRubyMethod
956
+ public IRubyObject namespace_scopes(ThreadContext context) {
957
+ RubyArray parentNamespaces;
958
+ RubyArray namespaces = (RubyArray) namespace_definitions(context);
959
+
960
+ IRubyObject parent = parent(context);
961
+ if (!parent.isNil()) {
962
+ parentNamespaces = (RubyArray)
963
+ ((XmlNode) parent).namespace_scopes(context);
964
+ } else {
965
+ parentNamespaces = getRuntime().newEmptyArray();
966
+ }
967
+
968
+ return parentNamespaces.op_plus(namespaces);
969
+ }
970
+
971
+ @JRubyMethod(name="namespaced_key?")
972
+ public IRubyObject namespaced_key_p(ThreadContext context, IRubyObject elementLName, IRubyObject namespaceUri) {
973
+ return this.attribute_with_ns(context, elementLName, namespaceUri).isNil() ?
974
+ context.getRuntime().getFalse() : context.getRuntime().getTrue();
975
+ }
976
+
977
+ protected void setContent(IRubyObject content) {
978
+ this.content = content;
979
+ this.node.setTextContent(rubyStringToString(content));
980
+ }
981
+
982
+ private void setContent(String content) {
983
+ node.setTextContent(content);
984
+ this.content = null; // clear cache
985
+ }
986
+
987
+ @JRubyMethod(name = "native_content=", visibility = Visibility.PRIVATE)
988
+ public IRubyObject native_content_set(ThreadContext context, IRubyObject content) {
989
+ setContent(content);
990
+ return content;
991
+ }
992
+
993
+ /**
994
+ * @param args {IRubyObject io,
995
+ * IRubyObject encoding,
996
+ * IRubyObject indentString,
997
+ * IRubyObject options}
998
+ */
999
+ @JRubyMethod(required=4, visibility=Visibility.PRIVATE)
1000
+ public IRubyObject native_write_to(ThreadContext context,
1001
+ IRubyObject[] args) {
1002
+
1003
+ IRubyObject io = args[0];
1004
+ IRubyObject encoding = args[1];
1005
+ IRubyObject indentString = args[2];
1006
+ IRubyObject options = args[3];
1007
+
1008
+ String encString = encoding.isNil() ? null : rubyStringToString(encoding);
1009
+
1010
+ SaveContext ctx = new SaveContext(context, (Integer)options.toJava(Integer.class),
1011
+ rubyStringToString(indentString),
1012
+ encString);
1013
+
1014
+ saveContent(context, ctx);
1015
+
1016
+ RuntimeHelpers.invoke(context, io, "write", ctx.toRubyString(context.getRuntime()));
1017
+
1018
+ return io;
1019
+ }
1020
+
1021
+ @JRubyMethod(name = {"next_sibling", "next"})
1022
+ public IRubyObject next_sibling(ThreadContext context) {
1023
+ return getCachedNodeOrCreate(context.getRuntime(), node.getNextSibling());
1024
+ }
1025
+
1026
+ @JRubyMethod(name = {"previous_sibling", "previous"})
1027
+ public IRubyObject previous_sibling(ThreadContext context) {
1028
+ return getCachedNodeOrCreate(context.getRuntime(), node.getPreviousSibling());
1029
+ }
1030
+
1031
+ @JRubyMethod(meta = true, rest = true)
1032
+ public static IRubyObject new_from_str(ThreadContext context,
1033
+ IRubyObject cls,
1034
+ IRubyObject[] args) {
1035
+ XmlDocument doc = (XmlDocument) XmlDocument.read_memory(context, args);
1036
+ return doc.root(context);
1037
+ }
1038
+
1039
+ @JRubyMethod(name = {"node_name", "name"})
1040
+ public IRubyObject node_name(ThreadContext context) {
1041
+ return getNodeName(context);
1042
+ }
1043
+
1044
+ @JRubyMethod(name = {"node_name=", "name="})
1045
+ public IRubyObject node_name_set(ThreadContext context, IRubyObject nodeName) {
1046
+ String newName = rubyStringToString(nodeName);
1047
+ getOwnerDocument().renameNode(node, null, newName);
1048
+ setName(nodeName);
1049
+ return this;
1050
+ }
1051
+
1052
+ @JRubyMethod(name = {"[]=", "set_attribute"})
1053
+ public IRubyObject op_aset(ThreadContext context, IRubyObject index, IRubyObject val) {
1054
+ return val;
1055
+ }
1056
+
1057
+ @JRubyMethod
1058
+ public IRubyObject parent(ThreadContext context) {
1059
+ /*
1060
+ * Check if this node is the root node of the document.
1061
+ * If so, parent is the document.
1062
+ */
1063
+ if (node.getOwnerDocument() != null &&
1064
+ node.getOwnerDocument().getDocumentElement() == node) {
1065
+ return document(context);
1066
+ } else {
1067
+ return getCachedNodeOrCreate(context.getRuntime(), node.getParentNode());
1068
+ }
1069
+ }
1070
+
1071
+ @JRubyMethod
1072
+ public IRubyObject path(ThreadContext context) {
1073
+ return RubyString.newString(context.getRuntime(), NokogiriHelpers.getNodeCompletePath(this.node));
1074
+ }
1075
+
1076
+ @JRubyMethod
1077
+ public IRubyObject pointer_id(ThreadContext context) {
1078
+ return RubyFixnum.newFixnum(context.getRuntime(), this.node.hashCode());
1079
+ }
1080
+
1081
+ @JRubyMethod(name = {"remove_attribute", "delete"})
1082
+ public IRubyObject remove_attribute(ThreadContext context, IRubyObject name) {
1083
+ return this;
1084
+ }
1085
+
1086
+ @JRubyMethod(visibility=Visibility.PRIVATE)
1087
+ public IRubyObject set_namespace(ThreadContext context, IRubyObject namespace) {
1088
+ if (namespace.isNil()) {
1089
+ if (doc != null) {
1090
+ Node n = node;
1091
+ String prefix = n.getPrefix();
1092
+ String href = n.getNamespaceURI();
1093
+ ((XmlDocument)doc).getNamespaceCache().remove(prefix == null ? "" : prefix, href);
1094
+ n.getOwnerDocument().renameNode(n, null, n.getNodeName());
1095
+ }
1096
+ } else {
1097
+ XmlNamespace ns = (XmlNamespace) namespace;
1098
+ String prefix = rubyStringToString(ns.prefix(context));
1099
+ String href = rubyStringToString(ns.href(context));
1100
+
1101
+ // Assigning node = ...renameNode() or not seems to make no
1102
+ // difference. Why not? -pmahoney
1103
+ node = node.getOwnerDocument().renameNode(node, href, NokogiriHelpers.newQName(prefix, node));
1104
+ }
1105
+
1106
+ return this;
1107
+ }
1108
+
1109
+ @JRubyMethod(name = {"unlink", "remove"})
1110
+ public IRubyObject unlink(ThreadContext context) {
1111
+ if(node.getParentNode() == null) {
1112
+ throw context.getRuntime().newRuntimeError("TYPE: " + node.getNodeType()+ " PARENT NULL");
1113
+ } else {
1114
+ node.getParentNode().removeChild(node);
1115
+ }
1116
+
1117
+ return this;
1118
+ }
1119
+
1120
+ /**
1121
+ * The C-library simply returns libxml2 magic numbers. Here we
1122
+ * convert Java Xml nodes to the appropriate constant defined in
1123
+ * xml/node.rb.
1124
+ */
1125
+ @JRubyMethod(name = {"node_type", "type"})
1126
+ public IRubyObject node_type(ThreadContext context) {
1127
+ String type;
1128
+ switch (node.getNodeType()) {
1129
+ case Node.ELEMENT_NODE:
1130
+ if (this instanceof XmlElementDecl)
1131
+ type = "ELEMENT_DECL";
1132
+ else if (this instanceof XmlAttributeDecl)
1133
+ type = "ATTRIBUTE_DECL";
1134
+ else if (this instanceof XmlEntityDecl)
1135
+ type = "ENTITY_DECL";
1136
+ else
1137
+ type = "ELEMENT_NODE";
1138
+ break;
1139
+ case Node.ATTRIBUTE_NODE: type = "ATTRIBUTE_NODE"; break;
1140
+ case Node.TEXT_NODE: type = "TEXT_NODE"; break;
1141
+ case Node.CDATA_SECTION_NODE: type = "CDATA_SECTION_NODE"; break;
1142
+ case Node.ENTITY_REFERENCE_NODE: type = "ENTITY_REF_NODE"; break;
1143
+ case Node.ENTITY_NODE: type = "ENTITY_NODE"; break;
1144
+ case Node.PROCESSING_INSTRUCTION_NODE: type = "PI_NODE"; break;
1145
+ case Node.COMMENT_NODE: type = "COMMENT_NODE"; break;
1146
+ case Node.DOCUMENT_NODE:
1147
+ if (this instanceof HtmlDocument)
1148
+ type = "HTML_DOCUMENT_NODE";
1149
+ else
1150
+ type = "DOCUMENT_NODE";
1151
+ break;
1152
+ case Node.DOCUMENT_TYPE_NODE: type = "DOCUMENT_TYPE_NODE"; break;
1153
+ case Node.DOCUMENT_FRAGMENT_NODE: type = "DOCUMENT_FRAG_NODE"; break;
1154
+ case Node.NOTATION_NODE: type = "NOTATION_NODE"; break;
1155
+ default:
1156
+ return context.getRuntime().newFixnum(0);
1157
+ }
1158
+
1159
+ return getNokogiriClass(context.getRuntime(), "Nokogiri::XML::Node").getConstant(type);
1160
+ }
1161
+
1162
+ @JRubyMethod
1163
+ public IRubyObject line(ThreadContext context) {
1164
+ Node root = getOwnerDocument();
1165
+ int[] counter = new int[1];
1166
+ count(root, counter);
1167
+ return RubyFixnum.newFixnum(context.getRuntime(), counter[0]+1);
1168
+ }
1169
+
1170
+ private boolean count(Node node, int[] counter) {
1171
+ if (node == this.node) {
1172
+ return true;
1173
+ }
1174
+ NodeList list = node.getChildNodes();
1175
+ for (int i=0; i<list.getLength(); i++) {
1176
+ Node n = list.item(i);
1177
+ if (n instanceof Text
1178
+ && ((Text)n).getData().contains("\n")) {
1179
+ counter[0] += 1;
1180
+ }
1181
+ if (count(n, counter)) return true;
1182
+ }
1183
+ return false;
1184
+ }
1185
+
1186
+ @JRubyMethod
1187
+ public IRubyObject next_element(ThreadContext context) {
1188
+ Node nextNode = node.getNextSibling();
1189
+ Ruby ruby = context.getRuntime();
1190
+ if (nextNode == null) return ruby.getNil();
1191
+ if (nextNode instanceof Element) {
1192
+ return getCachedNodeOrCreate(context.getRuntime(), nextNode);
1193
+ }
1194
+ Node deeper = nextNode.getNextSibling();
1195
+ if (deeper == null) return ruby.getNil();
1196
+ return getCachedNodeOrCreate(context.getRuntime(), deeper);
1197
+ }
1198
+
1199
+ @JRubyMethod
1200
+ public IRubyObject previous_element(ThreadContext context) {
1201
+ Node prevNode = node.getPreviousSibling();
1202
+ Ruby ruby = context.getRuntime();
1203
+ if (prevNode == null) return ruby.getNil();
1204
+ if (prevNode instanceof Element) {
1205
+ return getCachedNodeOrCreate(context.getRuntime(), prevNode);
1206
+ }
1207
+ Node shallower = prevNode.getPreviousSibling();
1208
+ if (shallower == null) return ruby.getNil();
1209
+ return getCachedNodeOrCreate(context.getRuntime(), shallower);
1210
+ }
1211
+
1212
+ protected enum AdoptScheme {
1213
+ CHILD, PREV_SIBLING, NEXT_SIBLING, REPLACEMENT;
1214
+ }
1215
+
1216
+ /**
1217
+ * Adopt XmlNode <code>other</code> into the document of
1218
+ * <code>this</code> using the specified scheme.
1219
+ */
1220
+ protected IRubyObject adoptAs(ThreadContext context, AdoptScheme scheme,
1221
+ IRubyObject other_) {
1222
+ XmlNode other = asXmlNode(context, other_);
1223
+ // this.doc might be null since this node can be empty node.
1224
+ if (this.doc != null) {
1225
+ other.setDocument(context, this.doc);
1226
+ }
1227
+ IRubyObject nodeOrTags = other;
1228
+ Node thisNode = node;
1229
+ Node otherNode = other.node;
1230
+
1231
+ try {
1232
+ Document doc = thisNode.getOwnerDocument();
1233
+ if (doc != null && doc != otherNode.getOwnerDocument()) {
1234
+ Node ret = doc.adoptNode(otherNode);
1235
+ if (ret == null) {
1236
+ throw context.getRuntime().newRuntimeError("Failed to take ownership of node");
1237
+ }
1238
+ }
1239
+
1240
+ Node parent = thisNode.getParentNode();
1241
+
1242
+ switch (scheme) {
1243
+ case CHILD:
1244
+ Node[] children = adoptAsChild(context, thisNode, otherNode);
1245
+ if (children.length == 1 && otherNode == children[0]) {
1246
+ break;
1247
+ } else {
1248
+ nodeOrTags = nodeArrayToRubyArray(context.getRuntime(), children);
1249
+ }
1250
+ break;
1251
+ case PREV_SIBLING:
1252
+ adoptAsPrevSibling(context, parent, thisNode, otherNode);
1253
+ break;
1254
+ case NEXT_SIBLING:
1255
+ adoptAsNextSibling(context, parent, thisNode, otherNode);
1256
+ break;
1257
+ case REPLACEMENT:
1258
+ adoptAsReplacement(context, parent, thisNode, otherNode);
1259
+ break;
1260
+ }
1261
+ } catch (Exception e) {
1262
+ throw context.getRuntime().newRuntimeError(e.toString());
1263
+ }
1264
+
1265
+ if (otherNode.getNodeType() == Node.TEXT_NODE) {
1266
+ coalesceTextNodes(context, other);
1267
+ }
1268
+
1269
+ relink_namespace(context);
1270
+ // post_add_child(context, this, other);
1271
+
1272
+ return nodeOrTags;
1273
+ }
1274
+
1275
+ protected Node[] adoptAsChild(ThreadContext context, Node parent,
1276
+ Node otherNode) {
1277
+ /*
1278
+ * This is a bit of a hack. C-Nokogiri allows adding a bare
1279
+ * text node as the root element. Java (and XML spec?) does
1280
+ * not. So we wrap the text node in an element.
1281
+ */
1282
+ if (parent.getNodeType() == Node.DOCUMENT_NODE &&
1283
+ otherNode.getNodeType() == Node.TEXT_NODE) {
1284
+ Element e = ((Document)parent).createElement("text");
1285
+ e.appendChild(otherNode);
1286
+ otherNode = e;
1287
+ }
1288
+ addNamespaceURIIfNeeded(otherNode);
1289
+ parent.appendChild(otherNode);
1290
+ Node[] nodes = new Node[1];
1291
+ nodes[0] = otherNode;
1292
+ return nodes;
1293
+ }
1294
+
1295
+ private void addNamespaceURIIfNeeded(Node child) {
1296
+ if (this instanceof XmlDocumentFragment && ((XmlDocumentFragment)this).getFragmentContext() != null) {
1297
+ XmlElement fragmentContext = ((XmlDocumentFragment)this).getFragmentContext();
1298
+ String namespace_uri = fragmentContext.node.getNamespaceURI();
1299
+ if (namespace_uri != null && namespace_uri.length() > 0) {
1300
+ node.getOwnerDocument().renameNode(child, namespace_uri, child.getNodeName());
1301
+ }
1302
+ }
1303
+ }
1304
+
1305
+ protected void adoptAsPrevSibling(ThreadContext context,
1306
+ Node parent,
1307
+ Node thisNode, Node otherNode) {
1308
+ if (parent == null) {
1309
+ /* I'm not sure what do do here... A node with no
1310
+ * parent can't exactly have a 'sibling', so we make
1311
+ * otherNode parentless also. */
1312
+ if (otherNode.getParentNode() != null)
1313
+ otherNode.getParentNode().removeChild(otherNode);
1314
+
1315
+ return;
1316
+ }
1317
+
1318
+ parent.insertBefore(otherNode, thisNode);
1319
+ }
1320
+
1321
+ protected void adoptAsNextSibling(ThreadContext context,
1322
+ Node parent,
1323
+ Node thisNode, Node otherNode) {
1324
+ if (parent == null) {
1325
+ /* I'm not sure what do do here... A node with no
1326
+ * parent can't exactly have a 'sibling', so we make
1327
+ * otherNode parentless also. */
1328
+ if (otherNode.getParentNode() != null)
1329
+ otherNode.getParentNode().removeChild(otherNode);
1330
+
1331
+ return;
1332
+ }
1333
+
1334
+ Node nextSib = thisNode.getNextSibling();
1335
+ if (nextSib != null) {
1336
+ parent.insertBefore(otherNode, nextSib);
1337
+ } else {
1338
+ parent.appendChild(otherNode);
1339
+ }
1340
+ }
1341
+
1342
+ protected void adoptAsReplacement(ThreadContext context,
1343
+ Node parentNode,
1344
+ Node thisNode, Node otherNode) {
1345
+ if (parentNode == null) {
1346
+ /* nothing to replace? */
1347
+ return;
1348
+ }
1349
+
1350
+ try {
1351
+ parentNode.replaceChild(otherNode, thisNode);
1352
+ if (otherNode.getNodeType() != Node.TEXT_NODE) {
1353
+ otherNode.getOwnerDocument().renameNode(otherNode, thisNode.getNamespaceURI(), otherNode.getNodeName());
1354
+ }
1355
+ } catch (Exception e) {
1356
+ String prefix = "could not replace child: ";
1357
+ throw context.getRuntime().newRuntimeError(prefix + e.toString());
1358
+ }
1359
+ }
1360
+
1361
+ /**
1362
+ * Add <code>other</code> as a child of <code>this</code>.
1363
+ */
1364
+ @JRubyMethod(visibility=Visibility.PRIVATE)
1365
+ public IRubyObject add_child_node(ThreadContext context, IRubyObject other) {
1366
+ return adoptAs(context, AdoptScheme.CHILD, other);
1367
+ }
1368
+
1369
+ /**
1370
+ * Replace <code>this</code> with <code>other</code>.
1371
+ */
1372
+ @JRubyMethod(visibility=Visibility.PRIVATE)
1373
+ public IRubyObject replace_node(ThreadContext context, IRubyObject other) {
1374
+ return adoptAs(context, AdoptScheme.REPLACEMENT, other);
1375
+ }
1376
+
1377
+ /**
1378
+ * Add <code>other</code> as a sibling before <code>this</code>.
1379
+ */
1380
+ @JRubyMethod(visibility=Visibility.PRIVATE)
1381
+ public IRubyObject add_previous_sibling_node(ThreadContext context, IRubyObject other) {
1382
+ return adoptAs(context, AdoptScheme.PREV_SIBLING, other);
1383
+ }
1384
+
1385
+ /**
1386
+ * Add <code>other</code> as a sibling after <code>this</code>.
1387
+ */
1388
+ @JRubyMethod(visibility=Visibility.PRIVATE)
1389
+ public IRubyObject add_next_sibling_node(ThreadContext context, IRubyObject other) {
1390
+ return adoptAs(context, AdoptScheme.NEXT_SIBLING, other);
1391
+ }
1392
+ }