nokogiri 1.5.0.beta.1 → 1.5.0.beta.2

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 (114) hide show
  1. data/CHANGELOG.ja.rdoc +28 -8
  2. data/CHANGELOG.rdoc +23 -0
  3. data/Manifest.txt +63 -1
  4. data/README.ja.rdoc +1 -1
  5. data/README.rdoc +22 -4
  6. data/Rakefile +6 -2
  7. data/ext/java/nokogiri/EncodingHandler.java +92 -0
  8. data/ext/java/nokogiri/HtmlDocument.java +116 -0
  9. data/ext/java/nokogiri/HtmlElementDescription.java +111 -0
  10. data/ext/java/nokogiri/HtmlEntityLookup.java +45 -0
  11. data/ext/java/nokogiri/HtmlSaxParserContext.java +218 -0
  12. data/ext/java/nokogiri/NokogiriService.java +370 -0
  13. data/ext/java/nokogiri/XmlAttr.java +147 -0
  14. data/ext/java/nokogiri/XmlAttributeDecl.java +98 -0
  15. data/ext/java/nokogiri/XmlCdata.java +50 -0
  16. data/ext/java/nokogiri/XmlComment.java +47 -0
  17. data/ext/java/nokogiri/XmlDocument.java +463 -0
  18. data/ext/java/nokogiri/XmlDocumentFragment.java +207 -0
  19. data/ext/java/nokogiri/XmlDtd.java +427 -0
  20. data/ext/java/nokogiri/XmlElement.java +172 -0
  21. data/ext/java/nokogiri/XmlElementContent.java +350 -0
  22. data/ext/java/nokogiri/XmlElementDecl.java +115 -0
  23. data/ext/java/nokogiri/XmlEntityDecl.java +129 -0
  24. data/ext/java/nokogiri/XmlEntityReference.java +42 -0
  25. data/ext/java/nokogiri/XmlNamespace.java +77 -0
  26. data/ext/java/nokogiri/XmlNode.java +1399 -0
  27. data/ext/java/nokogiri/XmlNodeSet.java +248 -0
  28. data/ext/java/nokogiri/XmlProcessingInstruction.java +70 -0
  29. data/ext/java/nokogiri/XmlReader.java +373 -0
  30. data/ext/java/nokogiri/XmlRelaxng.java +166 -0
  31. data/ext/java/nokogiri/XmlSaxParserContext.java +308 -0
  32. data/ext/java/nokogiri/XmlSaxPushParser.java +146 -0
  33. data/ext/java/nokogiri/XmlSchema.java +142 -0
  34. data/ext/java/nokogiri/XmlSyntaxError.java +84 -0
  35. data/ext/java/nokogiri/XmlText.java +96 -0
  36. data/ext/java/nokogiri/XmlXpathContext.java +130 -0
  37. data/ext/java/nokogiri/XsltStylesheet.java +126 -0
  38. data/ext/java/nokogiri/internals/HtmlDomParserContext.java +181 -0
  39. data/ext/java/nokogiri/internals/NokogiriDocumentCache.java +39 -0
  40. data/ext/java/nokogiri/internals/NokogiriErrorHandler.java +42 -0
  41. data/ext/java/nokogiri/internals/NokogiriHandler.java +251 -0
  42. data/ext/java/nokogiri/internals/NokogiriHelpers.java +526 -0
  43. data/ext/java/nokogiri/internals/NokogiriNamespaceCache.java +136 -0
  44. data/ext/java/nokogiri/internals/NokogiriNamespaceContext.java +80 -0
  45. data/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler.java +37 -0
  46. data/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler4NekoHtml.java +54 -0
  47. data/ext/java/nokogiri/internals/NokogiriStrictErrorHandler.java +49 -0
  48. data/ext/java/nokogiri/internals/NokogiriXPathFunction.java +88 -0
  49. data/ext/java/nokogiri/internals/NokogiriXPathFunctionResolver.java +23 -0
  50. data/ext/java/nokogiri/internals/ParserContext.java +235 -0
  51. data/ext/java/nokogiri/internals/PushInputStream.java +381 -0
  52. data/ext/java/nokogiri/internals/ReaderNode.java +431 -0
  53. data/ext/java/nokogiri/internals/SaveContext.java +249 -0
  54. data/ext/java/nokogiri/internals/SchemaErrorHandler.java +35 -0
  55. data/ext/java/nokogiri/internals/XmlDeclHandler.java +10 -0
  56. data/ext/java/nokogiri/internals/XmlDomParser.java +45 -0
  57. data/ext/java/nokogiri/internals/XmlDomParserContext.java +201 -0
  58. data/ext/java/nokogiri/internals/XmlSaxParser.java +33 -0
  59. data/ext/nokogiri/depend +32 -0
  60. data/ext/nokogiri/extconf.rb +61 -32
  61. data/ext/nokogiri/nokogiri.c +0 -5
  62. data/ext/nokogiri/nokogiri.h +2 -2
  63. data/ext/nokogiri/xml_document.c +5 -0
  64. data/ext/nokogiri/xml_libxml2_hacks.c +112 -0
  65. data/ext/nokogiri/xml_libxml2_hacks.h +12 -0
  66. data/ext/nokogiri/xml_node.c +56 -16
  67. data/ext/nokogiri/xml_node_set.c +7 -7
  68. data/ext/nokogiri/xml_reader.c +20 -1
  69. data/ext/nokogiri/xml_relax_ng.c +0 -7
  70. data/ext/nokogiri/xml_xpath_context.c +2 -0
  71. data/lib/isorelax.jar +0 -0
  72. data/lib/jing.jar +0 -0
  73. data/lib/nekodtd.jar +0 -0
  74. data/lib/nekohtml.jar +0 -0
  75. data/lib/nokogiri.rb +1 -2
  76. data/lib/nokogiri/css/generated_parser.rb +155 -148
  77. data/lib/nokogiri/css/generated_tokenizer.rb +2 -1
  78. data/lib/nokogiri/css/parser.y +3 -0
  79. data/lib/nokogiri/css/xpath_visitor.rb +1 -7
  80. data/lib/nokogiri/html.rb +2 -2
  81. data/lib/nokogiri/html/document_fragment.rb +7 -4
  82. data/lib/nokogiri/nokogiri.jar +0 -0
  83. data/lib/nokogiri/version.rb +3 -6
  84. data/lib/nokogiri/xml/builder.rb +1 -1
  85. data/lib/nokogiri/xml/document.rb +1 -2
  86. data/lib/nokogiri/xml/document_fragment.rb +7 -0
  87. data/lib/nokogiri/xml/node.rb +5 -3
  88. data/lib/nokogiri/xml/node_set.rb +25 -0
  89. data/lib/nokogiri/xml/reader.rb +2 -0
  90. data/lib/nokogiri/xml/sax/document.rb +3 -1
  91. data/lib/xercesImpl.jar +0 -0
  92. data/spec/helper.rb +3 -0
  93. data/spec/xml/reader_spec.rb +307 -0
  94. data/tasks/test.rb +1 -1
  95. data/test/css/test_parser.rb +11 -1
  96. data/test/html/sax/test_parser_context.rb +2 -2
  97. data/test/html/test_document.rb +2 -2
  98. data/test/html/test_document_fragment.rb +34 -6
  99. data/test/test_memory_leak.rb +2 -2
  100. data/test/test_reader.rb +28 -6
  101. data/test/test_xslt_transforms.rb +2 -3
  102. data/test/xml/test_attr.rb +31 -4
  103. data/test/xml/test_builder.rb +5 -5
  104. data/test/xml/test_cdata.rb +3 -3
  105. data/test/xml/test_document.rb +8 -8
  106. data/test/xml/test_document_fragment.rb +4 -12
  107. data/test/xml/test_node.rb +1 -1
  108. data/test/xml/test_node_reparenting.rb +26 -11
  109. data/test/xml/test_node_set.rb +38 -2
  110. data/test/xml/test_text.rb +11 -2
  111. data/test/xml/test_unparented_node.rb +1 -1
  112. data/test/xml/test_xpath.rb +11 -7
  113. metadata +68 -5
  114. data/lib/nokogiri/version_warning.rb +0 -14
@@ -0,0 +1,147 @@
1
+ package nokogiri;
2
+
3
+ import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
4
+ import static nokogiri.internals.NokogiriHelpers.rubyStringToString;
5
+ import nokogiri.internals.NokogiriHelpers;
6
+ import nokogiri.internals.SaveContext;
7
+
8
+ import org.jruby.Ruby;
9
+ import org.jruby.RubyClass;
10
+ import org.jruby.RubyString;
11
+ import org.jruby.anno.JRubyClass;
12
+ import org.jruby.anno.JRubyMethod;
13
+ import org.jruby.javasupport.JavaUtil;
14
+ import org.jruby.runtime.ThreadContext;
15
+ import org.jruby.runtime.builtin.IRubyObject;
16
+ import org.w3c.dom.Attr;
17
+ import org.w3c.dom.Element;
18
+ import org.w3c.dom.Node;
19
+
20
+ @JRubyClass(name="Nokogiri::XML::Attr", parent="Nokogiri::XML::Node")
21
+ public class XmlAttr extends XmlNode{
22
+
23
+ public static final String[] HTML_BOOLEAN_ATTRS = {
24
+ "checked", "compact", "declare", "defer", "disabled", "ismap",
25
+ "multiple", "nohref", "noresize", "noshade", "nowrap", "readonly",
26
+ "selected"
27
+ };
28
+
29
+ public XmlAttr(Ruby ruby, Node attr){
30
+ super(ruby, getNokogiriClass(ruby, "Nokogiri::XML::Attr"), attr);
31
+ }
32
+
33
+ public XmlAttr(Ruby ruby, RubyClass rubyClass) {
34
+ super(ruby, rubyClass);
35
+ }
36
+
37
+ public XmlAttr(Ruby ruby, RubyClass rubyClass, Node attr){
38
+ super(ruby, rubyClass, attr);
39
+ }
40
+
41
+ @Override
42
+ protected void init(ThreadContext context, IRubyObject[] args) {
43
+ if (args.length < 2) {
44
+ throw getRuntime().newArgumentError(args.length, 2);
45
+ }
46
+
47
+ IRubyObject doc = args[0];
48
+ IRubyObject content = args[1];
49
+
50
+ if(!(doc instanceof XmlDocument)) {
51
+ final String msg =
52
+ "document must be an instance of Nokogiri::XML::Document";
53
+ throw getRuntime().newArgumentError(msg);
54
+ }
55
+
56
+ XmlDocument xmlDoc = (XmlDocument)doc;
57
+ String str = rubyStringToString(content);
58
+ Node attr = xmlDoc.getDocument().createAttribute(str);
59
+ setNode(attr);
60
+ }
61
+
62
+ public boolean isHtmlBooleanAttr() {
63
+ String name = node.getNodeName().toLowerCase();
64
+
65
+ for(String s : HTML_BOOLEAN_ATTRS) {
66
+ if(s.equals(name)) return true;
67
+ }
68
+
69
+ return false;
70
+ }
71
+
72
+
73
+ private String serializeAttrTextContent(String s) {
74
+ if (s == null) return "";
75
+
76
+ char[] c = s.toCharArray();
77
+ StringBuffer buffer = new StringBuffer(c.length);
78
+
79
+ for(int i = 0; i < c.length; i++) {
80
+ switch(c[i]){
81
+ case '\n': buffer.append("&#10;"); break;
82
+ case '\r': buffer.append("&#13;"); break;
83
+ case '\t': buffer.append("&#9;"); break;
84
+ //case '"': buffer.append("&quot;"); break;
85
+ // TODO: is replacing '"' with '%22' always correct?
86
+ case '"': buffer.append("%22"); break;
87
+ case '<': buffer.append("&lt;"); break;
88
+ case '>': buffer.append("&gt;"); break;
89
+ case '&': buffer.append("&amp;"); break;
90
+ default: buffer.append(c[i]);
91
+ }
92
+ }
93
+
94
+ return buffer.toString();
95
+ }
96
+
97
+ @Override
98
+ @JRubyMethod(name = {"content", "value", "to_s"})
99
+ public IRubyObject content(ThreadContext context) {
100
+ if (content != null && !content.isNil()) return content;
101
+ if (node == null) return context.getRuntime().getNil();
102
+ String attrValue = ((Attr)node).getValue();
103
+ if (attrValue == null) return context.getRuntime().getNil();
104
+ return JavaUtil.convertJavaToUsableRubyObject(context.getRuntime(), attrValue);
105
+ }
106
+
107
+ @JRubyMethod(name = {"value=", "content="})
108
+ public IRubyObject value_set(ThreadContext context, IRubyObject content){
109
+ Attr attr = (Attr) node;
110
+ attr.setValue((String)XmlNode.encode_special_chars(context, content).toJava(String.class));
111
+ setContent(content);
112
+ return content;
113
+ }
114
+
115
+ @Override
116
+ protected IRubyObject getNodeName(ThreadContext context) {
117
+ if (name != null) return name;
118
+ String attrName = ((Attr)node).getName();
119
+ attrName = NokogiriHelpers.getLocalPart(attrName);
120
+ return attrName == null ? context.getRuntime().getNil() : RubyString.newString(context.getRuntime(), attrName);
121
+ }
122
+
123
+ @Override
124
+ public void saveContent(ThreadContext context, SaveContext ctx) {
125
+ Attr attr = (Attr) node;
126
+
127
+ ctx.maybeSpace();
128
+ ctx.append(attr.getName());
129
+
130
+ if (!ctx.asHtml() || !isHtmlBooleanAttr()) {
131
+ ctx.append("=");
132
+ ctx.append("\"");
133
+ ctx.append(serializeAttrTextContent(attr.getValue()));
134
+ ctx.append("\"");
135
+ }
136
+ }
137
+
138
+ @Override
139
+ public IRubyObject unlink(ThreadContext context) {
140
+ Attr attr = (Attr) node;
141
+ Element parent = attr.getOwnerElement();
142
+ parent.removeAttributeNode(attr);
143
+
144
+ return this;
145
+ }
146
+
147
+ }
@@ -0,0 +1,98 @@
1
+ package nokogiri;
2
+
3
+ import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
4
+
5
+ import org.jruby.Ruby;
6
+ import org.jruby.RubyArray;
7
+ import org.jruby.RubyClass;
8
+ import org.jruby.anno.JRubyClass;
9
+ import org.jruby.anno.JRubyMethod;
10
+ import org.jruby.runtime.ThreadContext;
11
+ import org.jruby.runtime.builtin.IRubyObject;
12
+ import org.w3c.dom.Element;
13
+ import org.w3c.dom.Node;
14
+
15
+ /**
16
+ * DTD attribute declaration.
17
+ *
18
+ * @author Patrick Mahoney <pat@polycrystal.org>
19
+ */
20
+ @JRubyClass(name="Nokogiri::XML::AttributeDecl", parent="Nokogiri::XML::Node")
21
+ public class XmlAttributeDecl extends XmlNode {
22
+
23
+ public XmlAttributeDecl(Ruby ruby, RubyClass klass) {
24
+ super(ruby, klass);
25
+ throw ruby.newRuntimeError("node required");
26
+ }
27
+
28
+ /**
29
+ * Initialize based on an attributeDecl node from a NekoDTD parsed
30
+ * DTD.
31
+ *
32
+ * Internally, XmlAttributeDecl combines these into a single node.
33
+ */
34
+ public XmlAttributeDecl(Ruby ruby, RubyClass klass, Node attrDeclNode) {
35
+ super(ruby, klass, attrDeclNode);
36
+ }
37
+
38
+ public static IRubyObject create(ThreadContext context, Node attrDeclNode) {
39
+ XmlAttributeDecl self =
40
+ new XmlAttributeDecl(context.getRuntime(),
41
+ getNokogiriClass(context.getRuntime(), "Nokogiri::XML::AttributeDecl"),
42
+ attrDeclNode);
43
+ return self;
44
+ }
45
+
46
+ @Override
47
+ @JRubyMethod
48
+ public IRubyObject node_name(ThreadContext context) {
49
+ return attribute_name(context);
50
+ }
51
+
52
+ @Override
53
+ @JRubyMethod(name = "node_name=")
54
+ public IRubyObject node_name_set(ThreadContext context, IRubyObject name) {
55
+ throw context.getRuntime()
56
+ .newRuntimeError("cannot change name of DTD decl");
57
+ }
58
+
59
+ public IRubyObject element_name(ThreadContext context) {
60
+ return getAttribute(context, "ename");
61
+ }
62
+
63
+ public IRubyObject attribute_name(ThreadContext context) {
64
+ return getAttribute(context, "aname");
65
+ }
66
+
67
+ @JRubyMethod
68
+ public IRubyObject attribute_type(ThreadContext context) {
69
+ return getAttribute(context, "atype");
70
+ }
71
+
72
+ @JRubyMethod(name="default")
73
+ public IRubyObject default_value(ThreadContext context) {
74
+ return getAttribute(context, "default");
75
+ }
76
+
77
+ /**
78
+ * FIXME: will enumerations all be of the simple (val1|val2|val3)
79
+ * type string?
80
+ */
81
+ @JRubyMethod
82
+ public IRubyObject enumeration(ThreadContext context) {
83
+ RubyArray enumVals = RubyArray.newArray(context.getRuntime());
84
+ String atype = ((Element)node).getAttribute("atype");
85
+
86
+ if (atype != null && atype.length() != 0 && atype.charAt(0) == '(') {
87
+ // removed enclosing parens
88
+ String valueStr = atype.substring(1, atype.length() - 1);
89
+ String[] values = valueStr.split("\\|");
90
+ for (int i = 0; i < values.length; i++) {
91
+ enumVals.append(context.getRuntime().newString(values[i]));
92
+ }
93
+ }
94
+
95
+ return enumVals;
96
+ }
97
+
98
+ }
@@ -0,0 +1,50 @@
1
+ package nokogiri;
2
+
3
+ import nokogiri.internals.SaveContext;
4
+
5
+ import org.jruby.Ruby;
6
+ import org.jruby.RubyClass;
7
+ import org.jruby.anno.JRubyClass;
8
+ import org.jruby.runtime.ThreadContext;
9
+ import org.jruby.runtime.builtin.IRubyObject;
10
+ import org.w3c.dom.CDATASection;
11
+ import org.w3c.dom.Document;
12
+ import org.w3c.dom.Node;
13
+
14
+ @JRubyClass(name="Nokogiri::XML::CDATA", parent="Nokogiri::XML::Text")
15
+ public class XmlCdata extends XmlText {
16
+ public XmlCdata(Ruby ruby, RubyClass rubyClass) {
17
+ super(ruby, rubyClass);
18
+ }
19
+
20
+ public XmlCdata(Ruby ruby, RubyClass rubyClass, Node node) {
21
+ super(ruby, rubyClass, node);
22
+ }
23
+
24
+ @Override
25
+ protected void init(ThreadContext context, IRubyObject[] args) {
26
+ if (args.length < 2) {
27
+ throw getRuntime().newArgumentError(args.length, 2);
28
+ }
29
+ IRubyObject doc = args[0];
30
+ content = args[1];
31
+ XmlDocument xmlDoc =(XmlDocument) ((XmlNode) doc).document(context);
32
+ doc = xmlDoc;
33
+ Document document = xmlDoc.getDocument();
34
+ Node node = document.createCDATASection((content.isNil()) ? null : (String)content.toJava(String.class));
35
+ setNode(node);
36
+ }
37
+
38
+ @Override
39
+ public void saveContent(ThreadContext context, SaveContext ctx) {
40
+ CDATASection cdata = (CDATASection) node;
41
+
42
+ if(cdata.getData().length() == 0) {
43
+ ctx.append("<![CDATA[]]>");
44
+ } else {
45
+ ctx.append("<![CDATA[");
46
+ ctx.append(cdata.getData());
47
+ ctx.append("]]>");
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,47 @@
1
+ package nokogiri;
2
+
3
+ import static nokogiri.internals.NokogiriHelpers.rubyStringToString;
4
+ import nokogiri.internals.SaveContext;
5
+
6
+ import org.jruby.Ruby;
7
+ import org.jruby.RubyClass;
8
+ import org.jruby.anno.JRubyClass;
9
+ import org.jruby.runtime.ThreadContext;
10
+ import org.jruby.runtime.builtin.IRubyObject;
11
+ import org.w3c.dom.Document;
12
+ import org.w3c.dom.Node;
13
+
14
+ @JRubyClass(name="Nokogiri::XML::Comment", parent="Nokogiri::XML::CharacterData")
15
+ public class XmlComment extends XmlNode {
16
+ public XmlComment(Ruby ruby, RubyClass rubyClass, Node node) {
17
+ super(ruby, rubyClass, node);
18
+ }
19
+
20
+ public XmlComment(Ruby runtime, RubyClass klass) {
21
+ super(runtime, klass);
22
+ }
23
+
24
+ @Override
25
+ protected void init(ThreadContext context, IRubyObject[] args) {
26
+ if (args.length < 2)
27
+ throw getRuntime().newArgumentError(args.length, 2);
28
+
29
+ IRubyObject doc = args[0];
30
+ IRubyObject text = args[1];
31
+
32
+ XmlDocument xmlDoc = (XmlDocument) doc;
33
+ Document document = xmlDoc.getDocument();
34
+ Node node = document.createComment(rubyStringToString(text));
35
+ setNode(node);
36
+ }
37
+
38
+ @Override
39
+ public boolean isComment() { return true; }
40
+
41
+ @Override
42
+ public void saveContent(ThreadContext context, SaveContext ctx) {
43
+ ctx.append("<!--");
44
+ ctx.append(content(context).convertToString().asJavaString());
45
+ ctx.append("-->");
46
+ }
47
+ }
@@ -0,0 +1,463 @@
1
+ package nokogiri;
2
+
3
+ import static nokogiri.internals.NokogiriHelpers.getCachedNodeOrCreate;
4
+ import static nokogiri.internals.NokogiriHelpers.getLocalNameForNamespace;
5
+ import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
6
+ import static nokogiri.internals.NokogiriHelpers.isNamespace;
7
+ import static nokogiri.internals.NokogiriHelpers.stringOrNil;
8
+
9
+ import javax.xml.parsers.DocumentBuilderFactory;
10
+ import javax.xml.parsers.ParserConfigurationException;
11
+
12
+ import nokogiri.internals.NokogiriHelpers;
13
+ import nokogiri.internals.NokogiriNamespaceCache;
14
+ import nokogiri.internals.SaveContext;
15
+ import nokogiri.internals.XmlDomParserContext;
16
+
17
+ import org.jruby.Ruby;
18
+ import org.jruby.RubyClass;
19
+ import org.jruby.RubyFixnum;
20
+ import org.jruby.RubyNil;
21
+ import org.jruby.anno.JRubyClass;
22
+ import org.jruby.anno.JRubyMethod;
23
+ import org.jruby.javasupport.JavaUtil;
24
+ import org.jruby.javasupport.util.RuntimeHelpers;
25
+ import org.jruby.runtime.Arity;
26
+ import org.jruby.runtime.ThreadContext;
27
+ import org.jruby.runtime.builtin.IRubyObject;
28
+ import org.w3c.dom.Attr;
29
+ import org.w3c.dom.Document;
30
+ import org.w3c.dom.NamedNodeMap;
31
+ import org.w3c.dom.Node;
32
+ import org.w3c.dom.NodeList;
33
+
34
+ @JRubyClass(name="Nokogiri::XML::Document", parent="Nokogiri::XML::Node")
35
+ public class XmlDocument extends XmlNode {
36
+ private NokogiriNamespaceCache nsCache;
37
+
38
+ /* UserData keys for storing extra info in the document node. */
39
+ public final static String DTD_RAW_DOCUMENT = "DTD_RAW_DOCUMENT";
40
+ protected final static String DTD_INTERNAL_SUBSET = "DTD_INTERNAL_SUBSET";
41
+ protected final static String DTD_EXTERNAL_SUBSET = "DTD_EXTERNAL_SUBSET";
42
+
43
+ private static boolean substituteEntities = false;
44
+ private static boolean loadExternalSubset = false; // TODO: Verify this.
45
+
46
+ /** cache variables */
47
+ protected IRubyObject encoding = null;
48
+ protected IRubyObject url = null;
49
+
50
+ public XmlDocument(Ruby ruby, RubyClass klazz) {
51
+ super(ruby, klazz);
52
+ nsCache = new NokogiriNamespaceCache();
53
+ }
54
+
55
+ public XmlDocument(Ruby ruby, Document document) {
56
+ this(ruby, getNokogiriClass(ruby, "Nokogiri::XML::Document"), document);
57
+ }
58
+
59
+ public XmlDocument(Ruby ruby, RubyClass klass, Document document) {
60
+ super(ruby, klass, document);
61
+ nsCache = new NokogiriNamespaceCache();
62
+ createAndCacheNamespaces(ruby, document.getDocumentElement());
63
+ stabilizeTextContent(document);
64
+ setInstanceVariable("@decorators", ruby.getNil());
65
+ }
66
+
67
+ public void setEncoding(IRubyObject encoding) {
68
+ this.encoding = encoding;
69
+ }
70
+
71
+ // not sure, but like attribute values, text value will be lost
72
+ // unless it is referred once before this document is used.
73
+ // this seems to happen only when the fragment is parsed from Node#in_context.
74
+ private void stabilizeTextContent(Document document) {
75
+ if (document.getDocumentElement() != null) document.getDocumentElement().getTextContent();
76
+ }
77
+
78
+ private void createAndCacheNamespaces(Ruby ruby, Node node) {
79
+ if (node == null) return;
80
+ if (node.hasAttributes()) {
81
+ NamedNodeMap nodeMap = node.getAttributes();
82
+ for (int i=0; i<nodeMap.getLength(); i++) {
83
+ Node n = nodeMap.item(i);
84
+ if (n instanceof Attr) {
85
+ Attr attr = (Attr)n;
86
+ String attrName = attr.getName();
87
+ // not sure, but need to get value always before document is referred.
88
+ // or lose attribute value
89
+ String attrValue = attr.getValue();
90
+ if (isNamespace(attrName)) {
91
+ String prefix = getLocalNameForNamespace(attrName);
92
+ prefix = prefix != null ? prefix : "";
93
+ nsCache.put(ruby, prefix, attrValue, node, this);
94
+ }
95
+ }
96
+ }
97
+ }
98
+ NodeList children = node.getChildNodes();
99
+ for (int i=0; i<children.getLength(); i++) {
100
+ createAndCacheNamespaces(ruby, children.item(i));
101
+ }
102
+ }
103
+
104
+ // When a document is created from fragment with a context (reference) document,
105
+ // namespace should be resolved based on the context document.
106
+ public XmlDocument(Ruby ruby, RubyClass klass, Document document, XmlDocument contextDoc) {
107
+ super(ruby, klass, document);
108
+ nsCache = contextDoc.getNamespaceCache();
109
+ XmlNamespace default_ns = nsCache.getDefault();
110
+ String default_href = (String)(default_ns.href(ruby.getCurrentContext())).toJava(String.class);
111
+ resolveNamespaceIfNecessary(ruby.getCurrentContext(), document.getDocumentElement(), default_href);
112
+ }
113
+
114
+ private void resolveNamespaceIfNecessary(ThreadContext context, Node node, String default_href) {
115
+ if (node == null) return;
116
+ String nodePrefix = node.getPrefix();
117
+ if (nodePrefix == null) { // default namespace
118
+ node.getOwnerDocument().renameNode(node, default_href, node.getNodeName());
119
+ } else {
120
+ XmlNamespace xmlNamespace = nsCache.get(nodePrefix);
121
+ String href = (String)xmlNamespace.href(context).toJava(String.class);
122
+ node.getOwnerDocument().renameNode(node, href, node.getNodeName());
123
+ }
124
+ resolveNamespaceIfNecessary(context, node.getNextSibling(), default_href);
125
+ NodeList children = node.getChildNodes();
126
+ for (int i=0; i<children.getLength(); i++) {
127
+ resolveNamespaceIfNecessary(context, children.item(i), default_href);
128
+ }
129
+ }
130
+
131
+ public NokogiriNamespaceCache getNamespaceCache() {
132
+ return nsCache;
133
+ }
134
+
135
+ public void setNamespaceCache(NokogiriNamespaceCache nsCache) {
136
+ this.nsCache = nsCache;
137
+ }
138
+
139
+ public Document getDocument() {
140
+ return (Document) node;
141
+ }
142
+
143
+ @Override
144
+ protected IRubyObject getNodeName(ThreadContext context) {
145
+ return JavaUtil.convertJavaToUsableRubyObject(context.getRuntime(), "document");
146
+ }
147
+
148
+ public void setUrl(IRubyObject url) {
149
+ this.url = url;
150
+ }
151
+
152
+ protected IRubyObject getUrl() {
153
+ return this.url;
154
+ }
155
+
156
+ @JRubyMethod
157
+ public IRubyObject url(ThreadContext context) {
158
+ return getUrl();
159
+ }
160
+
161
+ protected static Document createNewDocument() {
162
+ try {
163
+ return DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
164
+ } catch (ParserConfigurationException e) {
165
+ return null; // this will end is disaster...
166
+ }
167
+ }
168
+
169
+ /*
170
+ * call-seq:
171
+ * new(version = default)
172
+ *
173
+ * Create a new document with +version+ (defaults to "1.0")
174
+ */
175
+ @JRubyMethod(name="new", meta = true, rest = true, required=0)
176
+ public static IRubyObject rbNew(ThreadContext context, IRubyObject cls, IRubyObject[] args) {
177
+ XmlDocument doc = null;
178
+ try {
179
+ Document docNode = createNewDocument();
180
+ doc = new XmlDocument(context.getRuntime(), (RubyClass) cls, docNode);
181
+ } catch (Exception ex) {
182
+ throw context.getRuntime().newRuntimeError("couldn't create document: "+ex.toString());
183
+ }
184
+
185
+ RuntimeHelpers.invoke(context, doc, "initialize", args);
186
+
187
+ return doc;
188
+ }
189
+
190
+ @JRubyMethod(required=1, optional=4)
191
+ public IRubyObject create_entity(ThreadContext context, IRubyObject[] argv) {
192
+ // FIXME: Entity node should be create by some right way.
193
+ // this impl passes tests, but entity doesn't exists in DTD, which
194
+ // would cause validation failure.
195
+ if (argv.length == 0) throw context.getRuntime().newRuntimeError("Could not create entity");
196
+ String tagName = (String) argv[0].toJava(String.class);
197
+ Node n = this.getOwnerDocument().createElement(tagName);
198
+ return XmlEntityDecl.create(context, n, argv);
199
+ }
200
+
201
+ @Override
202
+ @JRubyMethod
203
+ public IRubyObject document(ThreadContext context) {
204
+ return this;
205
+ }
206
+
207
+ @JRubyMethod(name="encoding=")
208
+ public IRubyObject encoding_set(ThreadContext context, IRubyObject encoding) {
209
+ this.encoding = encoding;
210
+ return this;
211
+ }
212
+
213
+ @JRubyMethod
214
+ public IRubyObject encoding(ThreadContext context) {
215
+ if (this.encoding == null) {
216
+ if (getDocument().getXmlEncoding() == null) {
217
+ this.encoding = context.getRuntime().getNil();
218
+ } else {
219
+ this.encoding = context.getRuntime().newString(getDocument().getXmlEncoding());
220
+ }
221
+ }
222
+
223
+ return this.encoding;
224
+ }
225
+
226
+ @JRubyMethod(meta = true)
227
+ public static IRubyObject load_external_subsets_set(ThreadContext context, IRubyObject cls, IRubyObject value) {
228
+ XmlDocument.loadExternalSubset = value.isTrue();
229
+ return context.getRuntime().getNil();
230
+ }
231
+
232
+ /**
233
+ * TODO: handle encoding?
234
+ *
235
+ * @param args[0] a Ruby IO or StringIO
236
+ * @param args[1] url or nil
237
+ * @param args[2] encoding
238
+ * @param args[3] bitset of parser options
239
+ */
240
+ public static IRubyObject newFromData(ThreadContext context,
241
+ IRubyObject klass,
242
+ IRubyObject[] args) {
243
+ Ruby ruby = context.getRuntime();
244
+ Arity.checkArgumentCount(ruby, args, 4, 4);
245
+ XmlDomParserContext ctx =
246
+ new XmlDomParserContext(ruby, args[2], args[3]);
247
+ ctx.setInputSource(context, args[0]);
248
+ return ctx.parse(context, klass, args[1]);
249
+ }
250
+
251
+ @JRubyMethod(meta = true, rest = true)
252
+ public static IRubyObject read_io(ThreadContext context,
253
+ IRubyObject klass,
254
+ IRubyObject[] args) {
255
+ return newFromData(context, klass, args);
256
+ }
257
+
258
+ @JRubyMethod(meta = true, rest = true)
259
+ public static IRubyObject read_memory(ThreadContext context,
260
+ IRubyObject klass,
261
+ IRubyObject[] args) {
262
+ return newFromData(context, klass, args);
263
+ }
264
+
265
+ /** not a JRubyMethod */
266
+ public static IRubyObject read_memory(ThreadContext context,
267
+ IRubyObject[] args) {
268
+ return read_memory(context,
269
+ getNokogiriClass(context.getRuntime(), "Nokogiri::XML::Document"),
270
+ args);
271
+ }
272
+
273
+ @JRubyMethod(name="remove_namespaces!")
274
+ public IRubyObject remove_namespaces(ThreadContext context) {
275
+ removeNamespceRecursively(context, this);
276
+ nsCache.clear();
277
+ return this;
278
+ }
279
+
280
+ private void removeNamespceRecursively(ThreadContext context, XmlNode xmlNode) {
281
+ Node node = xmlNode.node;
282
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
283
+ node.setPrefix(null);
284
+ node.getOwnerDocument().renameNode(node, null, node.getLocalName());
285
+ }
286
+ XmlNodeSet nodeSet = (XmlNodeSet) xmlNode.children(context);
287
+ for (long i=0; i < nodeSet.length(); i++) {
288
+ XmlNode childNode = (XmlNode)nodeSet.slice(context, RubyFixnum.newFixnum(context.getRuntime(), i));
289
+ removeNamespceRecursively(context, childNode);
290
+ }
291
+ }
292
+
293
+ @JRubyMethod
294
+ public IRubyObject root(ThreadContext context) {
295
+ Node rootNode = getDocument().getDocumentElement();
296
+ try {
297
+ Boolean isValid = (Boolean)rootNode.getUserData(NokogiriHelpers.VALID_ROOT_NODE);
298
+ if (!isValid) return context.getRuntime().getNil();
299
+ } catch (NullPointerException e) {
300
+ // does nothing since nil wasn't set to the root node before.
301
+ }
302
+ if (rootNode == null)
303
+ return context.getRuntime().getNil();
304
+ else
305
+ return getCachedNodeOrCreate(context.getRuntime(), rootNode);
306
+ }
307
+
308
+ @JRubyMethod(name="root=")
309
+ public IRubyObject root_set(ThreadContext context, IRubyObject newRoot_) {
310
+ // in case of document fragment, temporary root node should be deleted.
311
+
312
+ // Java can't have a root whose value is null. Instead of setting null,
313
+ // the method sets user data so that other methods are able to know the root
314
+ // should be nil.
315
+ if (newRoot_ instanceof RubyNil) {
316
+ getDocument().getDocumentElement().setUserData(NokogiriHelpers.VALID_ROOT_NODE, false, null);
317
+ return newRoot_;
318
+ }
319
+ XmlNode newRoot = asXmlNode(context, newRoot_);
320
+
321
+ IRubyObject root = root(context);
322
+ if (root.isNil()) {
323
+ Node newRootNode;
324
+ if (getDocument() == newRoot.getOwnerDocument()) {
325
+ newRootNode = newRoot.node;
326
+ } else {
327
+ // must copy otherwise newRoot may exist in two places
328
+ // with different owner document.
329
+ newRootNode = getDocument().importNode(newRoot.node, true);
330
+ }
331
+ add_child_node(context, getCachedNodeOrCreate(context.getRuntime(), newRootNode));
332
+ } else {
333
+ Node rootNode = asXmlNode(context, root).node;
334
+ ((XmlNode)getCachedNodeOrCreate(context.getRuntime(), rootNode)).replace_node(context, newRoot);
335
+ }
336
+
337
+ return newRoot;
338
+ }
339
+
340
+ @JRubyMethod
341
+ public IRubyObject version(ThreadContext context) {
342
+ return stringOrNil(context.getRuntime(), getDocument().getXmlVersion());
343
+ }
344
+
345
+ @JRubyMethod
346
+ public IRubyObject in_context(ThreadContext context) {
347
+ throw getRuntime().newNotImplementedError("not implemented");
348
+ }
349
+
350
+ @JRubyMethod(meta = true)
351
+ public static IRubyObject substitute_entities_set(ThreadContext context, IRubyObject cls, IRubyObject value) {
352
+ XmlDocument.substituteEntities = value.isTrue();
353
+ return context.getRuntime().getNil();
354
+ }
355
+
356
+ public IRubyObject getInternalSubset(ThreadContext context) {
357
+ IRubyObject dtd =
358
+ (IRubyObject) node.getUserData(DTD_INTERNAL_SUBSET);
359
+
360
+ if (dtd == null) {
361
+ if (getDocument().getDoctype() == null)
362
+ dtd = context.getRuntime().getNil();
363
+ else
364
+ dtd = XmlDtd.newFromInternalSubset(context.getRuntime(),
365
+ getDocument());
366
+
367
+ setInternalSubset(dtd);
368
+ }
369
+
370
+ return dtd;
371
+ }
372
+
373
+ /**
374
+ * Assumes XmlNode#internal_subset() has returned nil. (i.e. there
375
+ * is not already an internal subset).
376
+ */
377
+ public IRubyObject createInternalSubset(ThreadContext context,
378
+ IRubyObject name,
379
+ IRubyObject external_id,
380
+ IRubyObject system_id) {
381
+ XmlDtd dtd = XmlDtd.newEmpty(context.getRuntime(),
382
+ this.getDocument(),
383
+ name, external_id, system_id);
384
+ setInternalSubset(dtd);
385
+ return dtd;
386
+ }
387
+
388
+ protected void setInternalSubset(IRubyObject data) {
389
+ node.setUserData(DTD_INTERNAL_SUBSET, data, null);
390
+ }
391
+
392
+ public IRubyObject getExternalSubset(ThreadContext context) {
393
+ IRubyObject dtd = (IRubyObject)
394
+ node.getUserData(DTD_EXTERNAL_SUBSET);
395
+
396
+ if (dtd == null) {
397
+ dtd = XmlDtd.newFromExternalSubset(context.getRuntime(),
398
+ getDocument());
399
+ setExternalSubset(dtd);
400
+ }
401
+
402
+ return dtd;
403
+ }
404
+
405
+ /**
406
+ * Assumes XmlNode#external_subset() has returned nil. (i.e. there
407
+ * is not already an external subset).
408
+ */
409
+ public IRubyObject createExternalSubset(ThreadContext context,
410
+ IRubyObject name,
411
+ IRubyObject external_id,
412
+ IRubyObject system_id) {
413
+ XmlDtd dtd = XmlDtd.newEmpty(context.getRuntime(),
414
+ this.getDocument(),
415
+ name, external_id, system_id);
416
+ setExternalSubset(dtd);
417
+ return dtd;
418
+ }
419
+
420
+ protected void setExternalSubset(IRubyObject data) {
421
+ node.setUserData(DTD_EXTERNAL_SUBSET, data, null);
422
+ }
423
+
424
+ //public IRubyObject createE
425
+
426
+ @Override
427
+ public void saveContent(ThreadContext context, SaveContext ctx) {
428
+ if(!ctx.noDecl()) {
429
+ ctx.append("<?xml version=\"");
430
+ ctx.append(getDocument().getXmlVersion());
431
+ ctx.append("\"");
432
+ // if(!cur.encoding(context).isNil()) {
433
+ // ctx.append(" encoding=");
434
+ // ctx.append(cur.encoding(context).asJavaString());
435
+ // }
436
+
437
+ String encoding = ctx.getEncoding();
438
+
439
+ if(encoding == null &&
440
+ !encoding(context).isNil()) {
441
+ encoding = encoding(context).convertToString().asJavaString();
442
+ }
443
+
444
+ if(encoding != null) {
445
+ ctx.append(" encoding=\"");
446
+ ctx.append(encoding);
447
+ ctx.append("\"");
448
+ }
449
+
450
+ //ctx.append(" standalone=\"");
451
+ //ctx.append(getDocument().getXmlStandalone() ? "yes" : "no");
452
+ ctx.append("?>\n");
453
+ }
454
+
455
+ IRubyObject maybeRoot = root(context);
456
+ if (maybeRoot.isNil())
457
+ throw context.getRuntime().newRuntimeError("no root document");
458
+
459
+ XmlNode root = (XmlNode) maybeRoot;
460
+ root.saveContent(context, ctx);
461
+ ctx.append("\n");
462
+ }
463
+ }