nokogiri 1.5.0.beta.1-java → 1.5.0.beta.2-java
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of nokogiri might be problematic. Click here for more details.
- data/CHANGELOG.ja.rdoc +28 -8
- data/CHANGELOG.rdoc +23 -0
- data/Manifest.txt +68 -1
- data/README.ja.rdoc +1 -1
- data/README.rdoc +22 -4
- data/Rakefile +6 -2
- data/ext/java/nokogiri/EncodingHandler.java +92 -0
- data/ext/java/nokogiri/HtmlDocument.java +116 -0
- data/ext/java/nokogiri/HtmlElementDescription.java +111 -0
- data/ext/java/nokogiri/HtmlEntityLookup.java +45 -0
- data/ext/java/nokogiri/HtmlSaxParserContext.java +218 -0
- data/ext/java/nokogiri/NokogiriService.java +370 -0
- data/ext/java/nokogiri/XmlAttr.java +147 -0
- data/ext/java/nokogiri/XmlAttributeDecl.java +98 -0
- data/ext/java/nokogiri/XmlCdata.java +50 -0
- data/ext/java/nokogiri/XmlComment.java +47 -0
- data/ext/java/nokogiri/XmlDocument.java +463 -0
- data/ext/java/nokogiri/XmlDocumentFragment.java +207 -0
- data/ext/java/nokogiri/XmlDtd.java +427 -0
- data/ext/java/nokogiri/XmlElement.java +172 -0
- data/ext/java/nokogiri/XmlElementContent.java +350 -0
- data/ext/java/nokogiri/XmlElementDecl.java +115 -0
- data/ext/java/nokogiri/XmlEntityDecl.java +129 -0
- data/ext/java/nokogiri/XmlEntityReference.java +42 -0
- data/ext/java/nokogiri/XmlNamespace.java +77 -0
- data/ext/java/nokogiri/XmlNode.java +1399 -0
- data/ext/java/nokogiri/XmlNodeSet.java +248 -0
- data/ext/java/nokogiri/XmlProcessingInstruction.java +70 -0
- data/ext/java/nokogiri/XmlReader.java +373 -0
- data/ext/java/nokogiri/XmlRelaxng.java +166 -0
- data/ext/java/nokogiri/XmlSaxParserContext.java +308 -0
- data/ext/java/nokogiri/XmlSaxPushParser.java +146 -0
- data/ext/java/nokogiri/XmlSchema.java +142 -0
- data/ext/java/nokogiri/XmlSyntaxError.java +84 -0
- data/ext/java/nokogiri/XmlText.java +96 -0
- data/ext/java/nokogiri/XmlXpathContext.java +130 -0
- data/ext/java/nokogiri/XsltStylesheet.java +126 -0
- data/ext/java/nokogiri/internals/HtmlDomParserContext.java +181 -0
- data/ext/java/nokogiri/internals/NokogiriDocumentCache.java +39 -0
- data/ext/java/nokogiri/internals/NokogiriErrorHandler.java +42 -0
- data/ext/java/nokogiri/internals/NokogiriHandler.java +251 -0
- data/ext/java/nokogiri/internals/NokogiriHelpers.java +526 -0
- data/ext/java/nokogiri/internals/NokogiriNamespaceCache.java +136 -0
- data/ext/java/nokogiri/internals/NokogiriNamespaceContext.java +80 -0
- data/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler.java +37 -0
- data/ext/java/nokogiri/internals/NokogiriNonStrictErrorHandler4NekoHtml.java +54 -0
- data/ext/java/nokogiri/internals/NokogiriStrictErrorHandler.java +49 -0
- data/ext/java/nokogiri/internals/NokogiriXPathFunction.java +88 -0
- data/ext/java/nokogiri/internals/NokogiriXPathFunctionResolver.java +23 -0
- data/ext/java/nokogiri/internals/ParserContext.java +235 -0
- data/ext/java/nokogiri/internals/PushInputStream.java +381 -0
- data/ext/java/nokogiri/internals/ReaderNode.java +431 -0
- data/ext/java/nokogiri/internals/SaveContext.java +249 -0
- data/ext/java/nokogiri/internals/SchemaErrorHandler.java +35 -0
- data/ext/java/nokogiri/internals/XmlDeclHandler.java +10 -0
- data/ext/java/nokogiri/internals/XmlDomParser.java +45 -0
- data/ext/java/nokogiri/internals/XmlDomParserContext.java +201 -0
- data/ext/java/nokogiri/internals/XmlSaxParser.java +33 -0
- data/ext/nokogiri/depend +32 -0
- data/ext/nokogiri/extconf.rb +61 -32
- data/ext/nokogiri/libcharset-1.dll +0 -0
- data/ext/nokogiri/libexslt.dll +0 -0
- data/ext/nokogiri/libiconv-2.dll +0 -0
- data/ext/nokogiri/libxml2.dll +0 -0
- data/ext/nokogiri/libxslt.dll +0 -0
- data/ext/nokogiri/nokogiri.c +0 -5
- data/ext/nokogiri/nokogiri.h +2 -2
- data/ext/nokogiri/xml_document.c +5 -0
- data/ext/nokogiri/xml_libxml2_hacks.c +112 -0
- data/ext/nokogiri/xml_libxml2_hacks.h +12 -0
- data/ext/nokogiri/xml_node.c +56 -16
- data/ext/nokogiri/xml_node_set.c +7 -7
- data/ext/nokogiri/xml_reader.c +20 -1
- data/ext/nokogiri/xml_relax_ng.c +0 -7
- data/ext/nokogiri/xml_xpath_context.c +2 -0
- data/ext/nokogiri/zlib1.dll +0 -0
- data/lib/nokogiri.rb +1 -2
- data/lib/nokogiri/css/generated_parser.rb +155 -148
- data/lib/nokogiri/css/generated_tokenizer.rb +2 -1
- data/lib/nokogiri/css/parser.y +3 -0
- data/lib/nokogiri/css/xpath_visitor.rb +1 -7
- data/lib/nokogiri/html.rb +2 -2
- data/lib/nokogiri/html/document_fragment.rb +7 -4
- data/lib/nokogiri/nokogiri.jar +0 -0
- data/lib/nokogiri/version.rb +3 -6
- data/lib/nokogiri/xml/builder.rb +1 -1
- data/lib/nokogiri/xml/document.rb +1 -2
- data/lib/nokogiri/xml/document_fragment.rb +7 -0
- data/lib/nokogiri/xml/node.rb +5 -3
- data/lib/nokogiri/xml/node_set.rb +25 -0
- data/lib/nokogiri/xml/reader.rb +2 -0
- data/lib/nokogiri/xml/sax/document.rb +3 -1
- data/spec/helper.rb +3 -0
- data/spec/xml/reader_spec.rb +307 -0
- data/tasks/test.rb +1 -1
- data/test/css/test_parser.rb +11 -1
- data/test/html/sax/test_parser_context.rb +2 -2
- data/test/html/test_document.rb +2 -2
- data/test/html/test_document_fragment.rb +34 -6
- data/test/test_memory_leak.rb +2 -2
- data/test/test_reader.rb +28 -6
- data/test/test_xslt_transforms.rb +2 -3
- data/test/xml/test_attr.rb +31 -4
- data/test/xml/test_builder.rb +5 -5
- data/test/xml/test_cdata.rb +3 -3
- data/test/xml/test_document.rb +8 -8
- data/test/xml/test_document_fragment.rb +4 -12
- data/test/xml/test_node.rb +1 -1
- data/test/xml/test_node_reparenting.rb +26 -11
- data/test/xml/test_node_set.rb +38 -2
- data/test/xml/test_text.rb +11 -2
- data/test/xml/test_unparented_node.rb +1 -1
- data/test/xml/test_xpath.rb +11 -7
- metadata +159 -100
- data.tar.gz.sig +0 -0
- data/lib/nokogiri/version_warning.rb +0 -14
- metadata.gz.sig +0 -0
@@ -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(" "); break;
|
82
|
+
case '\r': buffer.append(" "); break;
|
83
|
+
case '\t': buffer.append("	"); break;
|
84
|
+
//case '"': buffer.append("""); break;
|
85
|
+
// TODO: is replacing '"' with '%22' always correct?
|
86
|
+
case '"': buffer.append("%22"); break;
|
87
|
+
case '<': buffer.append("<"); break;
|
88
|
+
case '>': buffer.append(">"); break;
|
89
|
+
case '&': buffer.append("&"); 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
|
+
}
|