nokogiri 1.5.0-java → 1.5.1-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 +56 -12
- data/CHANGELOG.rdoc +45 -0
- data/C_CODING_STYLE.rdoc +27 -0
- data/Manifest.txt +4 -0
- data/README.rdoc +11 -7
- data/Rakefile +44 -26
- data/bin/nokogiri +10 -2
- data/ext/java/nokogiri/HtmlDocument.java +37 -2
- data/ext/java/nokogiri/NokogiriService.java +10 -2
- data/ext/java/nokogiri/XmlAttr.java +1 -1
- data/ext/java/nokogiri/XmlDocument.java +68 -11
- data/ext/java/nokogiri/XmlDocumentFragment.java +16 -5
- data/ext/java/nokogiri/XmlElement.java +0 -40
- data/ext/java/nokogiri/XmlNamespace.java +8 -1
- data/ext/java/nokogiri/XmlNode.java +131 -27
- data/ext/java/nokogiri/XmlNodeSet.java +4 -1
- data/ext/java/nokogiri/XmlSaxParserContext.java +2 -13
- data/ext/java/nokogiri/XmlXpathContext.java +4 -1
- data/ext/java/nokogiri/XsltStylesheet.java +198 -37
- data/ext/java/nokogiri/internals/HtmlDomParserContext.java +40 -2
- data/ext/java/nokogiri/internals/NokogiriHelpers.java +82 -9
- data/ext/java/nokogiri/internals/NokogiriXsltErrorListener.java +4 -3
- data/ext/java/nokogiri/internals/ParserContext.java +33 -3
- data/ext/java/nokogiri/internals/SaveContextVisitor.java +203 -12
- data/ext/java/nokogiri/internals/XmlDomParser.java +33 -2
- data/ext/java/nokogiri/internals/XmlDomParserContext.java +32 -12
- data/ext/nokogiri/extconf.rb +11 -3
- data/ext/nokogiri/html_document.c +16 -0
- data/ext/nokogiri/html_sax_parser_context.c +59 -37
- data/ext/nokogiri/html_sax_push_parser.c +87 -0
- data/ext/nokogiri/html_sax_push_parser.h +9 -0
- data/ext/nokogiri/nokogiri.c +6 -8
- data/ext/nokogiri/nokogiri.h +3 -0
- data/ext/nokogiri/xml_document.c +101 -3
- data/ext/nokogiri/xml_document.h +3 -3
- data/ext/nokogiri/xml_node.c +150 -58
- data/ext/nokogiri/xml_node_set.c +169 -120
- data/ext/nokogiri/xml_node_set.h +5 -0
- data/ext/nokogiri/xml_sax_parser_context.c +64 -41
- data/ext/nokogiri/xml_text.c +2 -0
- data/ext/nokogiri/xml_xpath_context.c +30 -24
- data/ext/nokogiri/xslt_stylesheet.c +62 -16
- data/ext/nokogiri/xslt_stylesheet.h +5 -0
- data/lib/nokogiri/css/parser.rb +163 -157
- data/lib/nokogiri/css/parser.y +6 -3
- data/lib/nokogiri/css/tokenizer.rb +1 -1
- data/lib/nokogiri/css/tokenizer.rex +1 -1
- data/lib/nokogiri/html.rb +1 -0
- data/lib/nokogiri/html/document.rb +82 -42
- data/lib/nokogiri/html/sax/push_parser.rb +16 -0
- data/lib/nokogiri/nokogiri.jar +0 -0
- data/lib/nokogiri/version.rb +1 -1
- data/lib/nokogiri/xml.rb +6 -0
- data/lib/nokogiri/xml/builder.rb +7 -1
- data/lib/nokogiri/xml/document.rb +32 -17
- data/lib/nokogiri/xml/document_fragment.rb +6 -1
- data/lib/nokogiri/xml/node.rb +40 -9
- data/lib/nokogiri/xslt.rb +5 -1
- data/tasks/cross_compile.rb +1 -0
- data/tasks/nokogiri.org.rb +6 -0
- data/tasks/test.rb +1 -0
- data/test/css/test_xpath_visitor.rb +6 -0
- data/test/helper.rb +1 -0
- data/test/html/test_document.rb +26 -0
- data/test/html/test_document_fragment.rb +1 -2
- data/test/test_memory_leak.rb +81 -1
- data/test/test_xslt_transforms.rb +152 -123
- data/test/xml/test_builder.rb +24 -2
- data/test/xml/test_c14n.rb +151 -0
- data/test/xml/test_document.rb +48 -0
- data/test/xml/test_namespace.rb +5 -0
- data/test/xml/test_node.rb +82 -1
- data/test/xml/test_node_attributes.rb +19 -0
- data/test/xml/test_node_inheritance.rb +32 -0
- data/test/xml/test_node_reparenting.rb +32 -0
- data/test/xml/test_node_set.rb +16 -8
- data/test/xml/test_reader_encoding.rb +16 -0
- data/test/xml/test_unparented_node.rb +32 -0
- data/test/xml/test_xinclude.rb +83 -0
- data/test/xml/test_xpath.rb +22 -0
- metadata +147 -123
@@ -582,9 +582,17 @@ public class NokogiriService implements BasicLibraryService {
|
|
582
582
|
}
|
583
583
|
};
|
584
584
|
|
585
|
-
|
585
|
+
public static ObjectAllocator XSLT_STYLESHEET_ALLOCATOR = new ObjectAllocator() {
|
586
|
+
private XsltStylesheet xsltStylesheet = null;
|
586
587
|
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
|
587
|
-
|
588
|
+
if (xsltStylesheet == null) xsltStylesheet = new XsltStylesheet(runtime, klazz);
|
589
|
+
try {
|
590
|
+
XsltStylesheet clone = (XsltStylesheet) xsltStylesheet.clone();
|
591
|
+
clone.setMetaClass(klazz);
|
592
|
+
return clone;
|
593
|
+
} catch (CloneNotSupportedException e) {
|
594
|
+
return new XmlText(runtime, klazz);
|
595
|
+
}
|
588
596
|
}
|
589
597
|
};
|
590
598
|
}
|
@@ -154,7 +154,7 @@ public class XmlAttr extends XmlNode{
|
|
154
154
|
protected IRubyObject getNodeName(ThreadContext context) {
|
155
155
|
if (name != null) return name;
|
156
156
|
String attrName = ((Attr)node).getName();
|
157
|
-
attrName = NokogiriHelpers.getLocalPart(attrName);
|
157
|
+
if (!(doc instanceof HtmlDocument)) attrName = NokogiriHelpers.getLocalPart(attrName);
|
158
158
|
return attrName == null ? context.getRuntime().getNil() : RubyString.newString(context.getRuntime(), attrName);
|
159
159
|
}
|
160
160
|
|
@@ -33,12 +33,13 @@
|
|
33
33
|
package nokogiri;
|
34
34
|
|
35
35
|
import static nokogiri.internals.NokogiriHelpers.getCachedNodeOrCreate;
|
36
|
-
import static nokogiri.internals.NokogiriHelpers.getLocalNameForNamespace;
|
37
36
|
import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
|
38
37
|
import static nokogiri.internals.NokogiriHelpers.isNamespace;
|
39
38
|
import static nokogiri.internals.NokogiriHelpers.rubyStringToString;
|
40
39
|
import static nokogiri.internals.NokogiriHelpers.stringOrNil;
|
41
40
|
|
41
|
+
import java.util.List;
|
42
|
+
|
42
43
|
import javax.xml.parsers.DocumentBuilderFactory;
|
43
44
|
import javax.xml.parsers.ParserConfigurationException;
|
44
45
|
|
@@ -48,14 +49,17 @@ import nokogiri.internals.SaveContextVisitor;
|
|
48
49
|
import nokogiri.internals.XmlDomParserContext;
|
49
50
|
|
50
51
|
import org.jruby.Ruby;
|
52
|
+
import org.jruby.RubyArray;
|
51
53
|
import org.jruby.RubyClass;
|
52
54
|
import org.jruby.RubyFixnum;
|
53
55
|
import org.jruby.RubyNil;
|
56
|
+
import org.jruby.RubyString;
|
54
57
|
import org.jruby.anno.JRubyClass;
|
55
58
|
import org.jruby.anno.JRubyMethod;
|
56
59
|
import org.jruby.javasupport.JavaUtil;
|
57
60
|
import org.jruby.javasupport.util.RuntimeHelpers;
|
58
61
|
import org.jruby.runtime.Arity;
|
62
|
+
import org.jruby.runtime.Block;
|
59
63
|
import org.jruby.runtime.ThreadContext;
|
60
64
|
import org.jruby.runtime.builtin.IRubyObject;
|
61
65
|
import org.w3c.dom.Attr;
|
@@ -108,8 +112,7 @@ public class XmlDocument extends XmlNode {
|
|
108
112
|
setInstanceVariable("@decorators", ruby.getNil());
|
109
113
|
}
|
110
114
|
|
111
|
-
|
112
|
-
public void setNode(ThreadContext context, Node node) {
|
115
|
+
public void setDocumentNode(ThreadContext context, Node node) {
|
113
116
|
super.setNode(context, node);
|
114
117
|
if (nsCache == null) nsCache = new NokogiriNamespaceCache();
|
115
118
|
Ruby runtime = context.getRuntime();
|
@@ -128,7 +131,7 @@ public class XmlDocument extends XmlNode {
|
|
128
131
|
// not sure, but like attribute values, text value will be lost
|
129
132
|
// unless it is referred once before this document is used.
|
130
133
|
// this seems to happen only when the fragment is parsed from Node#in_context.
|
131
|
-
|
134
|
+
protected void stabilizeTextContent(Document document) {
|
132
135
|
if (document.getDocumentElement() != null) document.getDocumentElement().getTextContent();
|
133
136
|
}
|
134
137
|
|
@@ -215,7 +218,7 @@ public class XmlDocument extends XmlNode {
|
|
215
218
|
return getUrl();
|
216
219
|
}
|
217
220
|
|
218
|
-
|
221
|
+
public static Document createNewDocument() {
|
219
222
|
try {
|
220
223
|
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(DOCUMENTBUILDERFACTORY_IMPLE_NAME, NokogiriService.class.getClassLoader());
|
221
224
|
return factory.newDocumentBuilder().newDocument();
|
@@ -237,11 +240,11 @@ public class XmlDocument extends XmlNode {
|
|
237
240
|
Document docNode = createNewDocument();
|
238
241
|
if ("Nokogiri::HTML::Document".equals(((RubyClass)klazz).getName())) {
|
239
242
|
xmlDocument = (XmlDocument) NokogiriService.HTML_DOCUMENT_ALLOCATOR.allocate(context.getRuntime(), (RubyClass) klazz);
|
240
|
-
xmlDocument.
|
243
|
+
xmlDocument.setDocumentNode(context, docNode);
|
241
244
|
} else {
|
242
245
|
// XML::Document and sublass
|
243
246
|
xmlDocument = (XmlDocument) NokogiriService.XML_DOCUMENT_ALLOCATOR.allocate(context.getRuntime(), (RubyClass) klazz);
|
244
|
-
xmlDocument.
|
247
|
+
xmlDocument.setDocumentNode(context, docNode);
|
245
248
|
}
|
246
249
|
} catch (Exception ex) {
|
247
250
|
throw context.getRuntime().newRuntimeError("couldn't create document: "+ex.toString());
|
@@ -349,9 +352,13 @@ public class XmlDocument extends XmlNode {
|
|
349
352
|
node.getOwnerDocument().renameNode(node, null, node.getLocalName());
|
350
353
|
NamedNodeMap attrs = node.getAttributes();
|
351
354
|
for (int i=0; i<attrs.getLength(); i++) {
|
352
|
-
|
353
|
-
attr.
|
354
|
-
|
355
|
+
Attr attr = (Attr) attrs.item(i);
|
356
|
+
if (isNamespace(attr.getNodeName())) {
|
357
|
+
((org.w3c.dom.Element)node).removeAttributeNode(attr);
|
358
|
+
} else {
|
359
|
+
attr.setPrefix(null);
|
360
|
+
attr.getOwnerDocument().renameNode(attr, null, attr.getLocalName());
|
361
|
+
}
|
355
362
|
}
|
356
363
|
}
|
357
364
|
XmlNodeSet nodeSet = (XmlNodeSet) xmlNode.children(context);
|
@@ -508,7 +515,7 @@ public class XmlDocument extends XmlNode {
|
|
508
515
|
public static IRubyObject wrapJavaDocument(ThreadContext context, IRubyObject klazz, IRubyObject arg) {
|
509
516
|
XmlDocument xmlDocument = (XmlDocument) NokogiriService.XML_DOCUMENT_ALLOCATOR.allocate(context.getRuntime(), getNokogiriClass(context.getRuntime(), "Nokogiri::XML::Document"));
|
510
517
|
Document document = (Document)arg.toJava(Document.class);
|
511
|
-
xmlDocument.
|
518
|
+
xmlDocument.setDocumentNode(context, document);
|
512
519
|
return xmlDocument;
|
513
520
|
}
|
514
521
|
|
@@ -516,4 +523,54 @@ public class XmlDocument extends XmlNode {
|
|
516
523
|
public IRubyObject toJavaDocument(ThreadContext context) {
|
517
524
|
return JavaUtil.convertJavaToUsableRubyObject(context.getRuntime(), (org.w3c.dom.Document)node);
|
518
525
|
}
|
526
|
+
|
527
|
+
/* call-seq:
|
528
|
+
* doc.canonicalize(mode=XML_C14N_1_0,inclusive_namespaces=nil,with_comments=false)
|
529
|
+
* doc.canonicalize { |obj, parent| ... }
|
530
|
+
*
|
531
|
+
* Canonicalize a document and return the results. Takes an optional block
|
532
|
+
* that takes two parameters: the +obj+ and that node's +parent+.
|
533
|
+
* The +obj+ will be either a Nokogiri::XML::Node, or a Nokogiri::XML::Namespace
|
534
|
+
* The block must return a non-nil, non-false value if the +obj+ passed in
|
535
|
+
* should be included in the canonicalized document.
|
536
|
+
*/
|
537
|
+
@JRubyMethod(optional=3)
|
538
|
+
public IRubyObject canonicalize(ThreadContext context, IRubyObject[] args, Block block) {
|
539
|
+
XmlNode startingNode = getStartingNode(block);
|
540
|
+
int canonicalOpts = 1;
|
541
|
+
int mode = 0;
|
542
|
+
if (args.length == 3) {
|
543
|
+
mode = (Integer)(args[0].isNil() ? 0 : args[0].toJava(Integer.class));
|
544
|
+
if (mode == 1) canonicalOpts = canonicalOpts | 16; // exclusive
|
545
|
+
// inclusive prefix list for exclusive c14n
|
546
|
+
canonicalOpts = args[2].isTrue() ? (canonicalOpts | 4) : canonicalOpts;
|
547
|
+
}
|
548
|
+
if (startingNode != this) canonicalOpts = canonicalOpts | 8; // subsets
|
549
|
+
// 38 = NO_DECL | NO_EMPTY | AS_XML
|
550
|
+
SaveContextVisitor visitor = new SaveContextVisitor(38, null, "UTF-8", false, false, canonicalOpts);
|
551
|
+
if (args.length == 3 && !args[1].isNil()) {
|
552
|
+
visitor.setC14nExclusiveInclusivePrefixes((List<String>)NokogiriHelpers.rubyStringArrayToJavaList((RubyArray)args[1]));
|
553
|
+
}
|
554
|
+
startingNode.accept(context, visitor);
|
555
|
+
Ruby runtime = context.getRuntime();
|
556
|
+
IRubyObject result = runtime.getTrue();
|
557
|
+
if (block.isGiven()) {
|
558
|
+
List<Node> list = visitor.getC14nNodeList();
|
559
|
+
for (Node n : list) {
|
560
|
+
IRubyObject currentNode = getCachedNodeOrCreate(runtime, n);
|
561
|
+
IRubyObject parentNode = getCachedNodeOrCreate(runtime, n.getParentNode());
|
562
|
+
result = block.call(context, currentNode, parentNode);
|
563
|
+
}
|
564
|
+
}
|
565
|
+
return result.isTrue() ? stringOrNil(runtime, visitor.toString()) : RubyString.newEmptyString(runtime);
|
566
|
+
}
|
567
|
+
|
568
|
+
private XmlNode getStartingNode(Block block) {
|
569
|
+
if (block.isGiven()) {
|
570
|
+
if (block.getBinding().getSelf() instanceof XmlNode) {
|
571
|
+
return (XmlNode)block.getBinding().getSelf();
|
572
|
+
}
|
573
|
+
}
|
574
|
+
return this;
|
575
|
+
}
|
519
576
|
}
|
@@ -92,8 +92,10 @@ public class XmlDocumentFragment extends XmlNode {
|
|
92
92
|
// make wellformed fragment, ignore invalid namespace, or add appropriate namespace to parse
|
93
93
|
if (args.length > 1 && args[1] instanceof RubyString) {
|
94
94
|
args[1] = trim(context, doc, (RubyString)args[1]);
|
95
|
-
|
96
|
-
|
95
|
+
if (XmlDocumentFragment.isTag((RubyString)args[1])) {
|
96
|
+
args[1] = RubyString.newString(context.getRuntime(), ignoreNamespaceIfNeeded(doc, rubyStringToString(args[1])));
|
97
|
+
args[1] = RubyString.newString(context.getRuntime(), addNamespaceDeclIfNeeded(doc, rubyStringToString(args[1])));
|
98
|
+
}
|
97
99
|
}
|
98
100
|
|
99
101
|
XmlDocumentFragment fragment = (XmlDocumentFragment) NokogiriService.XML_DOCUMENT_FRAGMENT_ALLOCATOR.allocate(context.getRuntime(), (RubyClass)cls);
|
@@ -120,18 +122,27 @@ public class XmlDocumentFragment extends XmlNode {
|
|
120
122
|
return result.isNil() ? str : result;
|
121
123
|
}
|
122
124
|
|
125
|
+
private static boolean isTag(RubyString ruby_string) {
|
126
|
+
String str = rubyStringToString(ruby_string);
|
127
|
+
if (str.startsWith("<") && str.endsWith(">")) return true;
|
128
|
+
return false;
|
129
|
+
}
|
130
|
+
|
123
131
|
private static Pattern qname_pattern = Pattern.compile("[^</:>\\s]+:[^</:>=\\s]+");
|
124
132
|
private static Pattern starttag_pattern = Pattern.compile("<[^</>]+>");
|
125
133
|
|
126
134
|
private static String ignoreNamespaceIfNeeded(XmlDocument doc, String tags) {
|
127
135
|
if (doc.getDocument() == null) return tags;
|
128
|
-
if (doc.getDocument().getDocumentElement() == null) return tags;
|
129
136
|
Matcher matcher = qname_pattern.matcher(tags);
|
130
137
|
Map<String, String> rewriteTable = new HashMap<String, String>();
|
131
138
|
while(matcher.find()) {
|
132
139
|
String qName = matcher.group();
|
133
|
-
|
134
|
-
|
140
|
+
if (doc.getDocument().getDocumentElement() != null) {
|
141
|
+
NamedNodeMap nodeMap = doc.getDocument().getDocumentElement().getAttributes();
|
142
|
+
if (!isNamespaceDefined(qName, nodeMap)) {
|
143
|
+
rewriteTable.put(qName, getLocalPart(qName));
|
144
|
+
}
|
145
|
+
} else {
|
135
146
|
rewriteTable.put(qName, getLocalPart(qName));
|
136
147
|
}
|
137
148
|
}
|
@@ -42,7 +42,6 @@ import org.jruby.anno.JRubyClass;
|
|
42
42
|
import org.jruby.anno.JRubyMethod;
|
43
43
|
import org.jruby.javasupport.util.RuntimeHelpers;
|
44
44
|
import org.jruby.runtime.ThreadContext;
|
45
|
-
import org.jruby.runtime.Visibility;
|
46
45
|
import org.jruby.runtime.builtin.IRubyObject;
|
47
46
|
import org.w3c.dom.Attr;
|
48
47
|
import org.w3c.dom.Element;
|
@@ -102,45 +101,6 @@ public class XmlElement extends XmlNode {
|
|
102
101
|
@Override
|
103
102
|
public boolean isElement() { return true; }
|
104
103
|
|
105
|
-
@Override
|
106
|
-
@JRubyMethod(visibility = Visibility.PRIVATE)
|
107
|
-
public IRubyObject get(ThreadContext context, IRubyObject rbkey) {
|
108
|
-
if (rbkey == null || rbkey.isNil()) context.getRuntime().getNil();
|
109
|
-
String key = rubyStringToString(rbkey);
|
110
|
-
Element element = (Element) node;
|
111
|
-
String value = element.getAttribute(key);
|
112
|
-
if(!value.equals("")){
|
113
|
-
return context.getRuntime().newString(value);
|
114
|
-
}
|
115
|
-
return context.getRuntime().getNil();
|
116
|
-
}
|
117
|
-
|
118
|
-
@Override
|
119
|
-
public IRubyObject key_p(ThreadContext context, IRubyObject rbkey) {
|
120
|
-
String key = rubyStringToString(rbkey);
|
121
|
-
Element element = (Element) node;
|
122
|
-
return context.getRuntime().newBoolean(element.hasAttribute(key));
|
123
|
-
}
|
124
|
-
|
125
|
-
@Override
|
126
|
-
public IRubyObject op_aset(ThreadContext context,
|
127
|
-
IRubyObject rbkey,
|
128
|
-
IRubyObject rbval) {
|
129
|
-
String key = rubyStringToString(rbkey);
|
130
|
-
String val = rubyStringToString(rbval);
|
131
|
-
Element element = (Element) node;
|
132
|
-
element.setAttribute(key, val);
|
133
|
-
return this;
|
134
|
-
}
|
135
|
-
|
136
|
-
@Override
|
137
|
-
public IRubyObject remove_attribute(ThreadContext context, IRubyObject name) {
|
138
|
-
String key = name.convertToString().asJavaString();
|
139
|
-
Element element = (Element) node;
|
140
|
-
element.removeAttribute(key);
|
141
|
-
return this;
|
142
|
-
}
|
143
|
-
|
144
104
|
@Override
|
145
105
|
public void relink_namespace(ThreadContext context) {
|
146
106
|
Element e = (Element) node;
|
@@ -1,7 +1,7 @@
|
|
1
1
|
/**
|
2
2
|
* (The MIT License)
|
3
3
|
*
|
4
|
-
* Copyright (c) 2008 -
|
4
|
+
* Copyright (c) 2008 - 2012:
|
5
5
|
*
|
6
6
|
* * {Aaron Patterson}[http://tenderlovemaking.com]
|
7
7
|
* * {Mike Dalessio}[http://mike.daless.io]
|
@@ -36,6 +36,7 @@ import static nokogiri.internals.NokogiriHelpers.CACHED_NODE;
|
|
36
36
|
import static nokogiri.internals.NokogiriHelpers.getCachedNodeOrCreate;
|
37
37
|
import static nokogiri.internals.NokogiriHelpers.getLocalNameForNamespace;
|
38
38
|
import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
|
39
|
+
import nokogiri.internals.NokogiriHelpers;
|
39
40
|
import nokogiri.internals.SaveContextVisitor;
|
40
41
|
|
41
42
|
import org.jruby.Ruby;
|
@@ -79,6 +80,12 @@ public class XmlNamespace extends RubyObject {
|
|
79
80
|
public String getHref() {
|
80
81
|
return hrefString;
|
81
82
|
}
|
83
|
+
|
84
|
+
void deleteHref() {
|
85
|
+
hrefString = "http://www.w3.org/XML/1998/namespace";
|
86
|
+
href = NokogiriHelpers.stringOrNil(getRuntime(), hrefString);
|
87
|
+
attr.getOwnerElement().removeAttributeNode(attr);
|
88
|
+
}
|
82
89
|
|
83
90
|
public void init(Attr attr, IRubyObject prefix, IRubyObject href, IRubyObject xmlDocument) {
|
84
91
|
init(attr, prefix, href, (String) prefix.toJava(String.class), (String) href.toJava(String.class), xmlDocument);
|
@@ -1,7 +1,7 @@
|
|
1
1
|
/**
|
2
2
|
* (The MIT License)
|
3
3
|
*
|
4
|
-
* Copyright (c) 2008 -
|
4
|
+
* Copyright (c) 2008 - 2012:
|
5
5
|
*
|
6
6
|
* * {Aaron Patterson}[http://tenderlovemaking.com]
|
7
7
|
* * {Mike Dalessio}[http://mike.daless.io]
|
@@ -42,6 +42,13 @@ import static nokogiri.internals.NokogiriHelpers.stringOrNil;
|
|
42
42
|
|
43
43
|
import java.io.ByteArrayInputStream;
|
44
44
|
import java.io.InputStream;
|
45
|
+
import java.io.UnsupportedEncodingException;
|
46
|
+
import java.nio.ByteBuffer;
|
47
|
+
import java.nio.CharBuffer;
|
48
|
+
import java.nio.charset.CharacterCodingException;
|
49
|
+
import java.nio.charset.Charset;
|
50
|
+
import java.nio.charset.CharsetDecoder;
|
51
|
+
import java.nio.charset.CharsetEncoder;
|
45
52
|
import java.util.ArrayList;
|
46
53
|
import java.util.List;
|
47
54
|
|
@@ -53,7 +60,6 @@ import nokogiri.internals.XmlDomParserContext;
|
|
53
60
|
|
54
61
|
import org.jruby.Ruby;
|
55
62
|
import org.jruby.RubyArray;
|
56
|
-
import org.jruby.RubyBoolean;
|
57
63
|
import org.jruby.RubyClass;
|
58
64
|
import org.jruby.RubyFixnum;
|
59
65
|
import org.jruby.RubyModule;
|
@@ -61,6 +67,7 @@ import org.jruby.RubyObject;
|
|
61
67
|
import org.jruby.RubyString;
|
62
68
|
import org.jruby.anno.JRubyClass;
|
63
69
|
import org.jruby.anno.JRubyMethod;
|
70
|
+
import org.jruby.exceptions.RaiseException;
|
64
71
|
import org.jruby.javasupport.util.RuntimeHelpers;
|
65
72
|
import org.jruby.runtime.Block;
|
66
73
|
import org.jruby.runtime.ThreadContext;
|
@@ -431,7 +438,25 @@ public class XmlNode extends RubyObject {
|
|
431
438
|
//this should delegate to subclasses' implementation
|
432
439
|
}
|
433
440
|
|
434
|
-
|
441
|
+
// Users might extend XmlNode. This method works for such a case.
|
442
|
+
public void accept(ThreadContext context, SaveContextVisitor visitor) {
|
443
|
+
visitor.enter(node);
|
444
|
+
XmlNodeSet xmlNodeSet = (XmlNodeSet) children(context);
|
445
|
+
if (xmlNodeSet.length() > 0) {
|
446
|
+
RubyArray array = (RubyArray) xmlNodeSet.to_a(context);
|
447
|
+
for(int i = 0; i < array.getLength(); i++) {
|
448
|
+
Object item = array.get(i);
|
449
|
+
if (item instanceof XmlNode) {
|
450
|
+
XmlNode cur = (XmlNode) item;
|
451
|
+
cur.accept(context, visitor);
|
452
|
+
} else if (item instanceof XmlNamespace) {
|
453
|
+
XmlNamespace cur = (XmlNamespace)item;
|
454
|
+
cur.accept(context, visitor);
|
455
|
+
}
|
456
|
+
}
|
457
|
+
}
|
458
|
+
visitor.leave(node);
|
459
|
+
}
|
435
460
|
|
436
461
|
public void setName(IRubyObject name) {
|
437
462
|
this.name = name;
|
@@ -502,6 +527,8 @@ public class XmlNode extends RubyObject {
|
|
502
527
|
if (node != namespaceOwner) {
|
503
528
|
node.getOwnerDocument().renameNode(node, ns.getHref(), ns.getPrefix() + node.getLocalName());
|
504
529
|
}
|
530
|
+
|
531
|
+
updateNodeNamespaceIfNecessary(context, ns);
|
505
532
|
|
506
533
|
return ns;
|
507
534
|
}
|
@@ -528,7 +555,7 @@ public class XmlNode extends RubyObject {
|
|
528
555
|
RubyArray attr = ruby.newArray();
|
529
556
|
|
530
557
|
for(int i = 0; i < nodeMap.getLength(); i++) {
|
531
|
-
if (!NokogiriHelpers.isNamespace(nodeMap.item(i))) {
|
558
|
+
if ((doc instanceof HtmlDocument) || !NokogiriHelpers.isNamespace(nodeMap.item(i))) {
|
532
559
|
attr.append(getCachedNodeOrCreate(context.getRuntime(), nodeMap.item(i)));
|
533
560
|
}
|
534
561
|
}
|
@@ -551,8 +578,13 @@ public class XmlNode extends RubyObject {
|
|
551
578
|
|
552
579
|
@JRubyMethod(name = "blank?")
|
553
580
|
public IRubyObject blank_p(ThreadContext context) {
|
554
|
-
|
555
|
-
if
|
581
|
+
// according to libxml doc,
|
582
|
+
// a node is blank if if it is a Text or CDATA node consisting of whitespace only
|
583
|
+
if (node.getNodeType() == Node.TEXT_NODE || node.getNodeType() == Node.CDATA_SECTION_NODE) {
|
584
|
+
String data = node.getTextContent();
|
585
|
+
if (data == null) return context.getRuntime().getTrue();
|
586
|
+
if ("".equals(data.trim())) return context.getRuntime().getTrue();
|
587
|
+
}
|
556
588
|
return context.getRuntime().getFalse();
|
557
589
|
}
|
558
590
|
|
@@ -683,7 +715,7 @@ public class XmlNode extends RubyObject {
|
|
683
715
|
|
684
716
|
RubyArray documentErrors = getErrorArray(document);
|
685
717
|
RubyArray docErrors = getErrorArray(doc);
|
686
|
-
if (
|
718
|
+
if (isErrorIncreased(documentErrors, docErrors)) {
|
687
719
|
for (int i = 0; i < docErrors.getLength(); i++) {
|
688
720
|
documentErrors.add(docErrors.get(i));
|
689
721
|
}
|
@@ -702,8 +734,7 @@ public class XmlNode extends RubyObject {
|
|
702
734
|
}
|
703
735
|
RubyArray nodeArray = RubyArray.newArray(runtime);
|
704
736
|
nodeArray.add(NokogiriHelpers.getCachedNodeOrCreate(runtime, first));
|
705
|
-
|
706
|
-
NokogiriHelpers.nodeListToRubyArray(runtime, first.getChildNodes(), nodeArray);
|
737
|
+
|
707
738
|
XmlNodeSet xmlNodeSet = XmlNodeSet.newXmlNodeSet(context, nodeArray);
|
708
739
|
return xmlNodeSet;
|
709
740
|
}
|
@@ -716,9 +747,10 @@ public class XmlNode extends RubyObject {
|
|
716
747
|
return RubyArray.newArray(document.getRuntime());
|
717
748
|
}
|
718
749
|
|
719
|
-
private boolean
|
720
|
-
|
721
|
-
|
750
|
+
private boolean isErrorIncreased(RubyArray baseErrors, RubyArray createdErrors) {
|
751
|
+
RubyFixnum length = ((RubyArray)createdErrors.op_diff(baseErrors)).length();
|
752
|
+
int diff_in_length = (Integer)length.toJava(Integer.class);
|
753
|
+
return diff_in_length > 0;
|
722
754
|
}
|
723
755
|
|
724
756
|
@JRubyMethod(name = {"content", "text", "inner_text"})
|
@@ -792,10 +824,19 @@ public class XmlNode extends RubyObject {
|
|
792
824
|
/**
|
793
825
|
* Get the attribute at the given key, <code>key</code>.
|
794
826
|
* Assumes that this node has attributes (i.e. that key? returned
|
795
|
-
* true).
|
827
|
+
* true).
|
796
828
|
*/
|
797
829
|
@JRubyMethod(visibility = Visibility.PRIVATE)
|
798
|
-
public IRubyObject get(ThreadContext context, IRubyObject
|
830
|
+
public IRubyObject get(ThreadContext context, IRubyObject rbkey) {
|
831
|
+
if (node instanceof Element) {
|
832
|
+
if (rbkey == null || rbkey.isNil()) context.getRuntime().getNil();
|
833
|
+
String key = rubyStringToString(rbkey);
|
834
|
+
Element element = (Element) node;
|
835
|
+
String value = element.getAttribute(key);
|
836
|
+
if (value != null) {
|
837
|
+
return context.getRuntime().newString(value);
|
838
|
+
}
|
839
|
+
}
|
799
840
|
return context.getRuntime().getNil();
|
800
841
|
}
|
801
842
|
|
@@ -888,11 +929,18 @@ public class XmlNode extends RubyObject {
|
|
888
929
|
*/
|
889
930
|
@JRubyMethod(name = {"key?", "has_attribute?"})
|
890
931
|
public IRubyObject key_p(ThreadContext context, IRubyObject rbkey) {
|
891
|
-
|
932
|
+
if (node instanceof Element) {
|
933
|
+
String key = rubyStringToString(rbkey);
|
934
|
+
Element element = (Element) node;
|
935
|
+
return context.getRuntime().newBoolean(element.hasAttribute(key));
|
936
|
+
} else {
|
937
|
+
return context.getRuntime().getNil();
|
938
|
+
}
|
892
939
|
}
|
893
940
|
|
894
941
|
@JRubyMethod
|
895
|
-
public IRubyObject namespace(ThreadContext context){
|
942
|
+
public IRubyObject namespace(ThreadContext context) {
|
943
|
+
if (doc instanceof HtmlDocument) return context.getRuntime().getNil();
|
896
944
|
XmlDocument xmlDocument = (XmlDocument) doc;
|
897
945
|
NokogiriNamespaceCache nsCache = xmlDocument.getNamespaceCache();
|
898
946
|
String prefix = node.getPrefix();
|
@@ -917,6 +965,7 @@ public class XmlNode extends RubyObject {
|
|
917
965
|
Ruby ruby = context.getRuntime();
|
918
966
|
RubyArray namespace_definitions = ruby.newArray();
|
919
967
|
if (doc == null) return namespace_definitions;
|
968
|
+
if (doc instanceof HtmlDocument) return namespace_definitions;
|
920
969
|
List<XmlNamespace> namespaces = ((XmlDocument)doc).getNamespaceCache().get(node);
|
921
970
|
for (XmlNamespace namespace : namespaces) {
|
922
971
|
((RubyArray)namespace_definitions).append(namespace);
|
@@ -953,7 +1002,11 @@ public class XmlNode extends RubyObject {
|
|
953
1002
|
|
954
1003
|
protected void setContent(IRubyObject content) {
|
955
1004
|
this.content = content;
|
956
|
-
|
1005
|
+
String javaContent = rubyStringToString(content);
|
1006
|
+
node.setTextContent(javaContent);
|
1007
|
+
if (javaContent.length() == 0) return;
|
1008
|
+
if (node.getNodeType() == Node.TEXT_NODE || node.getNodeType() == Node.CDATA_SECTION_NODE) return;
|
1009
|
+
node.getFirstChild().setUserData(NokogiriHelpers.ENCODED_STRING, true, null);
|
957
1010
|
}
|
958
1011
|
|
959
1012
|
private void setContent(String content) {
|
@@ -974,8 +1027,7 @@ public class XmlNode extends RubyObject {
|
|
974
1027
|
* IRubyObject options}
|
975
1028
|
*/
|
976
1029
|
@JRubyMethod(required=4, visibility=Visibility.PRIVATE)
|
977
|
-
public IRubyObject native_write_to(ThreadContext context,
|
978
|
-
IRubyObject[] args) {
|
1030
|
+
public IRubyObject native_write_to(ThreadContext context, IRubyObject[] args) {
|
979
1031
|
|
980
1032
|
IRubyObject io = args[0];
|
981
1033
|
IRubyObject encoding = args[1];
|
@@ -985,10 +1037,20 @@ public class XmlNode extends RubyObject {
|
|
985
1037
|
String encString = encoding.isNil() ? null : rubyStringToString(encoding);
|
986
1038
|
|
987
1039
|
SaveContextVisitor visitor =
|
988
|
-
new SaveContextVisitor((Integer)options.toJava(Integer.class), rubyStringToString(indentString), encString,
|
989
|
-
isHtmlDoc(context), isFragment());
|
1040
|
+
new SaveContextVisitor((Integer) options.toJava(Integer.class), rubyStringToString(indentString), encString, isHtmlDoc(context), isFragment(), 0);
|
990
1041
|
accept(context, visitor);
|
991
|
-
|
1042
|
+
|
1043
|
+
IRubyObject rubyString = null;
|
1044
|
+
if (NokogiriHelpers.isUTF8(encString)) {
|
1045
|
+
rubyString = stringOrNil(context.getRuntime(), visitor.toString());
|
1046
|
+
} else {
|
1047
|
+
try {
|
1048
|
+
byte[] bytes = NokogiriHelpers.convertEncoding(Charset.forName(encString), visitor.toString());
|
1049
|
+
rubyString = stringOrNil(context.getRuntime(), bytes);
|
1050
|
+
} catch (CharacterCodingException e) {
|
1051
|
+
throw context.getRuntime().newRuntimeError(e.getMessage());
|
1052
|
+
}
|
1053
|
+
}
|
992
1054
|
RuntimeHelpers.invoke(context, io, "write", rubyString);
|
993
1055
|
|
994
1056
|
return io;
|
@@ -1035,9 +1097,17 @@ public class XmlNode extends RubyObject {
|
|
1035
1097
|
return this;
|
1036
1098
|
}
|
1037
1099
|
|
1038
|
-
@JRubyMethod(
|
1039
|
-
public IRubyObject
|
1040
|
-
|
1100
|
+
@JRubyMethod(visibility = Visibility.PRIVATE)
|
1101
|
+
public IRubyObject set(ThreadContext context, IRubyObject rbkey, IRubyObject rbval) {
|
1102
|
+
if (node instanceof Element) {
|
1103
|
+
String key = rubyStringToString(rbkey);
|
1104
|
+
String val = rubyStringToString(rbval);
|
1105
|
+
Element element = (Element) node;
|
1106
|
+
element.setAttribute(key, val);
|
1107
|
+
return this;
|
1108
|
+
} else {
|
1109
|
+
return rbval;
|
1110
|
+
}
|
1041
1111
|
}
|
1042
1112
|
|
1043
1113
|
@JRubyMethod
|
@@ -1066,6 +1136,11 @@ public class XmlNode extends RubyObject {
|
|
1066
1136
|
|
1067
1137
|
@JRubyMethod(name = {"remove_attribute", "delete"})
|
1068
1138
|
public IRubyObject remove_attribute(ThreadContext context, IRubyObject name) {
|
1139
|
+
if (node instanceof Element) {
|
1140
|
+
String key = name.convertToString().asJavaString();
|
1141
|
+
Element element = (Element) node;
|
1142
|
+
element.removeAttribute(key);
|
1143
|
+
}
|
1069
1144
|
return this;
|
1070
1145
|
}
|
1071
1146
|
|
@@ -1297,10 +1372,12 @@ public class XmlNode extends RubyObject {
|
|
1297
1372
|
* otherNode parentless also. */
|
1298
1373
|
if (otherNode.getParentNode() != null)
|
1299
1374
|
otherNode.getParentNode().removeChild(otherNode);
|
1300
|
-
|
1301
1375
|
return;
|
1302
1376
|
}
|
1303
|
-
|
1377
|
+
if (thisNode.getPreviousSibling() != null &&
|
1378
|
+
thisNode.getPreviousSibling().getNodeType() == Node.TEXT_NODE &&
|
1379
|
+
otherNode.getNodeType() == Node.TEXT_NODE) return;
|
1380
|
+
|
1304
1381
|
parent.insertBefore(otherNode, thisNode);
|
1305
1382
|
}
|
1306
1383
|
|
@@ -1318,6 +1395,11 @@ public class XmlNode extends RubyObject {
|
|
1318
1395
|
}
|
1319
1396
|
|
1320
1397
|
Node nextSib = thisNode.getNextSibling();
|
1398
|
+
|
1399
|
+
if (nextSib != null &&
|
1400
|
+
nextSib.getNodeType() == Node.TEXT_NODE &&
|
1401
|
+
otherNode.getNodeType() == Node.TEXT_NODE) return;
|
1402
|
+
|
1321
1403
|
if (nextSib != null) {
|
1322
1404
|
parent.insertBefore(otherNode, nextSib);
|
1323
1405
|
} else {
|
@@ -1375,4 +1457,26 @@ public class XmlNode extends RubyObject {
|
|
1375
1457
|
public IRubyObject add_next_sibling_node(ThreadContext context, IRubyObject other) {
|
1376
1458
|
return adoptAs(context, AdoptScheme.NEXT_SIBLING, other);
|
1377
1459
|
}
|
1460
|
+
|
1461
|
+
/**
|
1462
|
+
* call-seq:
|
1463
|
+
* process_xincludes(options)
|
1464
|
+
*
|
1465
|
+
* Loads and substitutes all xinclude elements below the node. The
|
1466
|
+
* parser context will be initialized with +options+.
|
1467
|
+
*
|
1468
|
+
*/
|
1469
|
+
@JRubyMethod(visibility=Visibility.PRIVATE)
|
1470
|
+
public IRubyObject process_xincludes(ThreadContext context, IRubyObject options) {
|
1471
|
+
XmlDocument xmlDocument = (XmlDocument)document(context);
|
1472
|
+
RubyArray errors = (RubyArray)xmlDocument.getInstanceVariable("@errors");
|
1473
|
+
while(errors.getLength() > 0) {
|
1474
|
+
XmlSyntaxError error = (XmlSyntaxError)errors.shift(context);
|
1475
|
+
if (error.toString().contains("Include operation failed")) {
|
1476
|
+
throw new RaiseException(error);
|
1477
|
+
}
|
1478
|
+
}
|
1479
|
+
return this;
|
1480
|
+
}
|
1481
|
+
|
1378
1482
|
}
|