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.
- data/CHANGELOG.ja.rdoc +28 -8
- data/CHANGELOG.rdoc +23 -0
- data/Manifest.txt +63 -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/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/lib/isorelax.jar +0 -0
- data/lib/jing.jar +0 -0
- data/lib/nekodtd.jar +0 -0
- data/lib/nekohtml.jar +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/lib/xercesImpl.jar +0 -0
- 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 +68 -5
- data/lib/nokogiri/version_warning.rb +0 -14
@@ -0,0 +1,431 @@
|
|
1
|
+
package nokogiri.internals;
|
2
|
+
|
3
|
+
import static nokogiri.internals.NokogiriHelpers.getNokogiriClass;
|
4
|
+
import static nokogiri.internals.NokogiriHelpers.isNamespace;
|
5
|
+
import static nokogiri.internals.NokogiriHelpers.isXmlBase;
|
6
|
+
import static nokogiri.internals.NokogiriHelpers.stringOrBlank;
|
7
|
+
import static nokogiri.internals.NokogiriHelpers.stringOrNil;
|
8
|
+
|
9
|
+
import java.util.ArrayList;
|
10
|
+
import java.util.HashMap;
|
11
|
+
import java.util.List;
|
12
|
+
import java.util.Map;
|
13
|
+
import java.util.Set;
|
14
|
+
import java.util.Stack;
|
15
|
+
|
16
|
+
import nokogiri.XmlAttr;
|
17
|
+
import nokogiri.XmlDocument;
|
18
|
+
import nokogiri.XmlSyntaxError;
|
19
|
+
|
20
|
+
import org.jruby.Ruby;
|
21
|
+
import org.jruby.RubyArray;
|
22
|
+
import org.jruby.RubyBoolean;
|
23
|
+
import org.jruby.RubyHash;
|
24
|
+
import org.jruby.runtime.ThreadContext;
|
25
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
26
|
+
import org.w3c.dom.Attr;
|
27
|
+
import org.xml.sax.Attributes;
|
28
|
+
import org.xml.sax.SAXParseException;
|
29
|
+
|
30
|
+
public abstract class ReaderNode {
|
31
|
+
|
32
|
+
Ruby ruby;
|
33
|
+
public ReaderAttributeList attributeList;
|
34
|
+
public Map<String, String> namespaces;
|
35
|
+
public int depth, nodeType;
|
36
|
+
public String lang, localName, xmlBase, prefix, name, uri, value, xmlVersion = "1.0";
|
37
|
+
public boolean hasChildren = false;
|
38
|
+
public abstract String getString();
|
39
|
+
|
40
|
+
public IRubyObject getAttributeByIndex(IRubyObject index){
|
41
|
+
if(index.isNil()) return index;
|
42
|
+
|
43
|
+
long i = index.convertToInteger().getLongValue();
|
44
|
+
if(i > Integer.MAX_VALUE) {
|
45
|
+
throw ruby.newArgumentError("value too long to be an array index");
|
46
|
+
}
|
47
|
+
|
48
|
+
if (attributeList == null) return ruby.getNil();
|
49
|
+
if (i<0 || attributeList.length <= i) return ruby.getNil();
|
50
|
+
return stringOrBlank(ruby, attributeList.values.get(((Long)i).intValue()));
|
51
|
+
}
|
52
|
+
|
53
|
+
public IRubyObject getAttributeByName(IRubyObject name){
|
54
|
+
if(attributeList == null) return ruby.getNil();
|
55
|
+
String value = attributeList.getByName((String)name.toJava(String.class));
|
56
|
+
return stringOrNil(ruby, value);
|
57
|
+
}
|
58
|
+
|
59
|
+
public IRubyObject getAttributeByName(String name){
|
60
|
+
if(attributeList == null) return ruby.getNil();
|
61
|
+
String value = attributeList.getByName(name);
|
62
|
+
return stringOrNil(ruby, value);
|
63
|
+
}
|
64
|
+
|
65
|
+
public IRubyObject getAttributeCount(){
|
66
|
+
if(attributeList == null) return ruby.newFixnum(0);
|
67
|
+
return ruby.newFixnum(attributeList.length);
|
68
|
+
}
|
69
|
+
|
70
|
+
public IRubyObject getAttributesNodes() {
|
71
|
+
RubyArray array = RubyArray.newArray(ruby);
|
72
|
+
if (attributeList != null && attributeList.length > 0) {
|
73
|
+
// XmlDocument is used to create XmlAttr type of attribute nodes. Since the attribute nodes
|
74
|
+
// should have name and to_s methods, using XmlAttr would be convenient.
|
75
|
+
XmlDocument xmlDoc =
|
76
|
+
(XmlDocument)XmlDocument.rbNew(ruby.getCurrentContext(), getNokogiriClass(ruby, "Nokogiri::XML::Document"), new IRubyObject[0]);
|
77
|
+
for (int i=0; i<attributeList.length; i++) {
|
78
|
+
if (!isNamespace(attributeList.names.get(i))) {
|
79
|
+
Attr attr = xmlDoc.getDocument().createAttributeNS(attributeList.namespaces.get(i), attributeList.names.get(i));
|
80
|
+
attr.setValue(attributeList.values.get(i));
|
81
|
+
array.append(new XmlAttr(ruby, attr));
|
82
|
+
}
|
83
|
+
}
|
84
|
+
}
|
85
|
+
return array;
|
86
|
+
}
|
87
|
+
|
88
|
+
public IRubyObject getAttributes(ThreadContext context) {
|
89
|
+
if(attributeList == null) return context.getRuntime().getNil();
|
90
|
+
RubyHash hash = RubyHash.newHash(context.getRuntime());
|
91
|
+
for (int i=0; i<attributeList.length; i++) {
|
92
|
+
IRubyObject k = stringOrBlank(context.getRuntime(), attributeList.names.get(i));
|
93
|
+
IRubyObject v = stringOrBlank(context.getRuntime(), attributeList.values.get(i));
|
94
|
+
if (context.getRuntime().is1_9()) hash.op_aset19(context, k, v);
|
95
|
+
else hash.op_aset(context, k, v);
|
96
|
+
}
|
97
|
+
return hash;
|
98
|
+
}
|
99
|
+
|
100
|
+
public IRubyObject getDepth() {
|
101
|
+
return ruby.newFixnum(depth);
|
102
|
+
}
|
103
|
+
|
104
|
+
public IRubyObject getLang() {
|
105
|
+
return stringOrNil(ruby, lang);
|
106
|
+
}
|
107
|
+
|
108
|
+
public IRubyObject getLocalName() {
|
109
|
+
return stringOrNil(ruby, localName);
|
110
|
+
}
|
111
|
+
|
112
|
+
public IRubyObject getName() {
|
113
|
+
return stringOrNil(ruby, name);
|
114
|
+
}
|
115
|
+
|
116
|
+
public IRubyObject getNamespaces(ThreadContext context) {
|
117
|
+
if(namespaces == null) return ruby.getNil();
|
118
|
+
RubyHash hash = RubyHash.newHash(ruby);
|
119
|
+
Set<String> keys = namespaces.keySet();
|
120
|
+
for (String key : keys) {
|
121
|
+
String stringValue = namespaces.get(key);
|
122
|
+
IRubyObject k = stringOrBlank(context.getRuntime(), key);
|
123
|
+
IRubyObject v = stringOrBlank(context.getRuntime(), stringValue);
|
124
|
+
if (context.getRuntime().is1_9()) hash.op_aset19(context, k, v);
|
125
|
+
else hash.op_aset(context, k, v);
|
126
|
+
}
|
127
|
+
return hash;
|
128
|
+
}
|
129
|
+
|
130
|
+
public IRubyObject getXmlBase() {
|
131
|
+
return stringOrNil(ruby, xmlBase);
|
132
|
+
}
|
133
|
+
|
134
|
+
public IRubyObject getPrefix() {
|
135
|
+
return stringOrNil(ruby, prefix);
|
136
|
+
}
|
137
|
+
|
138
|
+
public IRubyObject getUri() {
|
139
|
+
return stringOrNil(ruby, uri);
|
140
|
+
}
|
141
|
+
|
142
|
+
public IRubyObject getValue() {
|
143
|
+
return stringOrNil(ruby, value);
|
144
|
+
}
|
145
|
+
|
146
|
+
public IRubyObject getXmlVersion() {
|
147
|
+
return ruby.newString(xmlVersion);
|
148
|
+
}
|
149
|
+
|
150
|
+
public RubyBoolean hasAttributes() {
|
151
|
+
if (attributeList == null || attributeList.length == 0) return ruby.getFalse();
|
152
|
+
return ruby.getTrue();
|
153
|
+
}
|
154
|
+
|
155
|
+
public abstract RubyBoolean hasValue();
|
156
|
+
|
157
|
+
public RubyBoolean isDefault(){
|
158
|
+
// TODO Implement.
|
159
|
+
return ruby.getFalse();
|
160
|
+
}
|
161
|
+
|
162
|
+
public boolean isError() { return false; }
|
163
|
+
|
164
|
+
protected void parsePrefix(String qName) {
|
165
|
+
int index = qName.indexOf(':');
|
166
|
+
if(index != -1) prefix = qName.substring(0, index);
|
167
|
+
}
|
168
|
+
|
169
|
+
public void setLang(String lang) {
|
170
|
+
lang = (lang != null) ? lang : null;
|
171
|
+
}
|
172
|
+
|
173
|
+
public IRubyObject toSyntaxError() { return ruby.getNil(); }
|
174
|
+
|
175
|
+
public IRubyObject getNodeType() { return ruby.newFixnum(nodeType); }
|
176
|
+
|
177
|
+
public static enum ReaderNodeType {
|
178
|
+
NODE(0),
|
179
|
+
ELEMENT(1),
|
180
|
+
ATTRIBUTE(2),
|
181
|
+
TEXT(3),
|
182
|
+
CDATA(4),
|
183
|
+
ENTITY_REFERENCE(5),
|
184
|
+
ENTITY(6),
|
185
|
+
PROCESSING_INSTRUCTION(7),
|
186
|
+
COMMENT(8),
|
187
|
+
DOCUMENT(9),
|
188
|
+
DOCUMENT_TYPE(10),
|
189
|
+
DOCUMENTFRAGMENT(11),
|
190
|
+
NOTATION(12),
|
191
|
+
WHITESPACE(13),
|
192
|
+
SIGNIFICANT_WHITESPACE(14),
|
193
|
+
END_ELEMENT(15),
|
194
|
+
END_ENTITY(16),
|
195
|
+
XML_DECLARATION(17);
|
196
|
+
|
197
|
+
private final int value;
|
198
|
+
ReaderNodeType(int value) {
|
199
|
+
this.value = value;
|
200
|
+
}
|
201
|
+
|
202
|
+
public int getValue() {
|
203
|
+
return value;
|
204
|
+
}
|
205
|
+
}
|
206
|
+
|
207
|
+
public static class ClosingNode extends ReaderNode {
|
208
|
+
|
209
|
+
public ClosingNode(Ruby ruby, String uri, String localName, String qName, int depth, Stack<String> langStack, Stack<String> xmlBaseStack) {
|
210
|
+
this.ruby = ruby;
|
211
|
+
nodeType = ReaderNodeType.END_ELEMENT.getValue();
|
212
|
+
this.uri = "".equals(uri) ? null : uri;
|
213
|
+
this.localName = localName.trim().length() > 0 ? localName : qName;
|
214
|
+
this.name = qName;
|
215
|
+
parsePrefix(qName);
|
216
|
+
this.depth = depth;
|
217
|
+
if (!langStack.isEmpty()) this.lang = langStack.peek();
|
218
|
+
if (!xmlBaseStack.isEmpty()) this.xmlBase = xmlBaseStack.peek();
|
219
|
+
}
|
220
|
+
|
221
|
+
@Override
|
222
|
+
public IRubyObject getAttributeCount() {
|
223
|
+
return ruby.newFixnum(0);
|
224
|
+
}
|
225
|
+
|
226
|
+
@Override
|
227
|
+
public RubyBoolean hasValue() {
|
228
|
+
return ruby.getFalse();
|
229
|
+
}
|
230
|
+
|
231
|
+
@Override
|
232
|
+
public String getString() {
|
233
|
+
StringBuffer sb = new StringBuffer();
|
234
|
+
sb.append("</").append(name).append(">");
|
235
|
+
return new String(sb);
|
236
|
+
}
|
237
|
+
}
|
238
|
+
|
239
|
+
public static class ElementNode extends ReaderNode {
|
240
|
+
private List<String> attributeStrings = new ArrayList<String>();
|
241
|
+
|
242
|
+
public ElementNode(Ruby ruby, String uri, String localName, String qName, Attributes attrs, int depth, Stack<String> langStack, Stack<String> xmlBaseStack) {
|
243
|
+
this.ruby = ruby;
|
244
|
+
this.nodeType = ReaderNodeType.ELEMENT.getValue();
|
245
|
+
this.uri = "".equals(uri) ? null : uri;
|
246
|
+
this.localName = localName.trim().length() > 0 ? localName : qName;
|
247
|
+
this.name = qName;
|
248
|
+
parsePrefix(qName);
|
249
|
+
this.depth = depth;
|
250
|
+
hasChildren = true;
|
251
|
+
parseAttributes(attrs, langStack, xmlBaseStack);
|
252
|
+
}
|
253
|
+
|
254
|
+
@Override
|
255
|
+
public RubyBoolean hasValue() {
|
256
|
+
return ruby.getFalse();
|
257
|
+
}
|
258
|
+
|
259
|
+
private void parseAttributes(Attributes attrs, Stack<String> langStack, Stack<String> xmlBaseStack) {
|
260
|
+
if (attrs.getLength() > 0) attributeList = new ReaderAttributeList();
|
261
|
+
String u, n, v;
|
262
|
+
for (int i = 0; i < attrs.getLength(); i++) {
|
263
|
+
u = attrs.getURI(i);
|
264
|
+
n = attrs.getQName(i);
|
265
|
+
v = attrs.getValue(i);
|
266
|
+
if (isNamespace(n)) {
|
267
|
+
if (namespaces == null) namespaces = new HashMap<String, String>();
|
268
|
+
namespaces.put(n, v);
|
269
|
+
} else {
|
270
|
+
if (lang == null) lang = resolveLang(n, v, langStack);
|
271
|
+
if (xmlBase == null) xmlBase = resolveXmlBase(n, v, xmlBaseStack);
|
272
|
+
}
|
273
|
+
attributeList.add(u, n, v);
|
274
|
+
attributeStrings.add(n + "=\"" + v + "\"");
|
275
|
+
}
|
276
|
+
}
|
277
|
+
|
278
|
+
private String resolveLang(String n, String v, Stack<String> langStack) {
|
279
|
+
if ("xml:lang".equals(n)) {
|
280
|
+
return v;
|
281
|
+
} else if (!langStack.isEmpty()) {
|
282
|
+
return langStack.peek();
|
283
|
+
} else {
|
284
|
+
return null;
|
285
|
+
}
|
286
|
+
}
|
287
|
+
|
288
|
+
private String resolveXmlBase(String n, String v, Stack<String> xmlBaseStack) {
|
289
|
+
if (isXmlBase(n)) {
|
290
|
+
return getXmlBaseUri(n, v, xmlBaseStack);
|
291
|
+
} else if (!xmlBaseStack.isEmpty()) {
|
292
|
+
return xmlBaseStack.peek();
|
293
|
+
} else {
|
294
|
+
return null;
|
295
|
+
}
|
296
|
+
}
|
297
|
+
|
298
|
+
private String getXmlBaseUri(String n, String v, Stack<String> xmlBaseStack) {
|
299
|
+
if ("xml:base".equals(n)) {
|
300
|
+
if (v.startsWith("http://")) {
|
301
|
+
return v;
|
302
|
+
} else if (v.startsWith("/") && v.endsWith("/")) {
|
303
|
+
String sub = v.substring(1, v.length() - 2);
|
304
|
+
String base = xmlBaseStack.peek();
|
305
|
+
if (base.endsWith("/")) {
|
306
|
+
base = base.substring(0, base.length() - 1);
|
307
|
+
}
|
308
|
+
int pos = base.lastIndexOf("/");
|
309
|
+
return base.substring(0, pos).concat(sub);
|
310
|
+
} else {
|
311
|
+
String base = xmlBaseStack.peek();
|
312
|
+
if (base.endsWith("/")) return base.concat(v);
|
313
|
+
else return base.concat("/").concat(v);
|
314
|
+
}
|
315
|
+
} else if ("xlink:href".equals(n)) {
|
316
|
+
String base = xmlBaseStack.peek();
|
317
|
+
if (base.endsWith("/")) return base.concat(v);
|
318
|
+
else return base.concat("/").concat(v);
|
319
|
+
}
|
320
|
+
return null;
|
321
|
+
}
|
322
|
+
|
323
|
+
@Override
|
324
|
+
public String getString() {
|
325
|
+
StringBuffer sb = new StringBuffer();
|
326
|
+
sb.append("<").append(name);
|
327
|
+
if (attributeList != null) {
|
328
|
+
for (int i=0; i<attributeList.length; i++) {
|
329
|
+
sb.append(" ").append(attributeStrings.get(i));
|
330
|
+
}
|
331
|
+
}
|
332
|
+
if (hasChildren) sb.append(">");
|
333
|
+
else sb.append("/>");
|
334
|
+
return new String(sb);
|
335
|
+
}
|
336
|
+
}
|
337
|
+
|
338
|
+
public static class ReaderAttributeList {
|
339
|
+
List<String> namespaces = new ArrayList<String>();
|
340
|
+
List<String> names = new ArrayList<String>();
|
341
|
+
List<String> values = new ArrayList<String>();
|
342
|
+
int length = 0;
|
343
|
+
|
344
|
+
void add(String namespace, String name, String value) {
|
345
|
+
namespace = namespace != null ? namespace : "";
|
346
|
+
namespaces.add(namespace);
|
347
|
+
name = name != null ? name : "";
|
348
|
+
names.add(name);
|
349
|
+
value = value != null ? value : "";
|
350
|
+
values.add(value);
|
351
|
+
length++;
|
352
|
+
}
|
353
|
+
|
354
|
+
String getByName(String name) {
|
355
|
+
for (int i=0; i<names.size(); i++) {
|
356
|
+
if (name.equals(names.get(i))) {
|
357
|
+
return values.get(i);
|
358
|
+
}
|
359
|
+
}
|
360
|
+
return null;
|
361
|
+
}
|
362
|
+
}
|
363
|
+
|
364
|
+
public static class EmptyNode extends ReaderNode {
|
365
|
+
|
366
|
+
public EmptyNode(Ruby ruby) {
|
367
|
+
this.ruby = ruby;
|
368
|
+
this.nodeType = ReaderNodeType.NODE.getValue();
|
369
|
+
}
|
370
|
+
|
371
|
+
@Override
|
372
|
+
public IRubyObject getXmlVersion() {
|
373
|
+
return this.ruby.getNil();
|
374
|
+
}
|
375
|
+
|
376
|
+
@Override
|
377
|
+
public RubyBoolean hasValue() {
|
378
|
+
return ruby.getFalse();
|
379
|
+
}
|
380
|
+
|
381
|
+
@Override
|
382
|
+
public String getString() {
|
383
|
+
return null;
|
384
|
+
}
|
385
|
+
}
|
386
|
+
|
387
|
+
public static class ExceptionNode extends EmptyNode {
|
388
|
+
private final XmlSyntaxError exception;
|
389
|
+
|
390
|
+
// Still don't know what to do with ex.
|
391
|
+
public ExceptionNode(Ruby ruby, SAXParseException ex) {
|
392
|
+
super(ruby);
|
393
|
+
this.exception = new XmlSyntaxError(ruby);
|
394
|
+
}
|
395
|
+
|
396
|
+
@Override
|
397
|
+
public boolean isError() {
|
398
|
+
return true;
|
399
|
+
}
|
400
|
+
|
401
|
+
@Override
|
402
|
+
public IRubyObject toSyntaxError() {
|
403
|
+
return this.exception;
|
404
|
+
}
|
405
|
+
}
|
406
|
+
|
407
|
+
public static class TextNode extends ReaderNode {
|
408
|
+
|
409
|
+
public TextNode(Ruby ruby, String content, int depth, Stack<String> langStack, Stack<String> xmlBaseStack) {
|
410
|
+
this.ruby = ruby;
|
411
|
+
this.value = content;
|
412
|
+
this.localName = "#text";
|
413
|
+
this.name = "#text";
|
414
|
+
this.depth = depth;
|
415
|
+
if (content.trim().length() > 0) nodeType = ReaderNodeType.TEXT.getValue();
|
416
|
+
else nodeType = ReaderNodeType.SIGNIFICANT_WHITESPACE.getValue();
|
417
|
+
if (!langStack.isEmpty()) this.lang = langStack.peek();
|
418
|
+
if (!xmlBaseStack.isEmpty()) this.xmlBase = xmlBaseStack.peek();
|
419
|
+
}
|
420
|
+
|
421
|
+
@Override
|
422
|
+
public RubyBoolean hasValue() {
|
423
|
+
return ruby.getTrue();
|
424
|
+
}
|
425
|
+
|
426
|
+
@Override
|
427
|
+
public String getString() {
|
428
|
+
return value;
|
429
|
+
}
|
430
|
+
}
|
431
|
+
}
|
@@ -0,0 +1,249 @@
|
|
1
|
+
package nokogiri.internals;
|
2
|
+
|
3
|
+
import java.lang.Character;
|
4
|
+
import org.jruby.Ruby;
|
5
|
+
import org.jruby.RubyClass;
|
6
|
+
import org.jruby.RubyString;
|
7
|
+
import org.jruby.javasupport.util.RuntimeHelpers;
|
8
|
+
import org.jruby.runtime.ThreadContext;
|
9
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
10
|
+
|
11
|
+
/**
|
12
|
+
*
|
13
|
+
* @author sergio
|
14
|
+
*/
|
15
|
+
public class SaveContext {
|
16
|
+
|
17
|
+
private final ThreadContext context;
|
18
|
+
private final RubyClass elementDescription;
|
19
|
+
private StringBuffer buffer;
|
20
|
+
private int options;
|
21
|
+
private int level=0;
|
22
|
+
private String encoding, indentString;
|
23
|
+
private boolean format, noDecl, noEmpty, noXhtml, xhtml, asXml, asHtml;
|
24
|
+
|
25
|
+
/*
|
26
|
+
* U can't touch this.
|
27
|
+
* http://www.youtube.com/watch?v=WJ2ZFVx6A4Q
|
28
|
+
*
|
29
|
+
* Taken from libxml save options.
|
30
|
+
*/
|
31
|
+
|
32
|
+
public static final int FORMAT = 1;
|
33
|
+
public static final int NO_DECL = 2;
|
34
|
+
public static final int NO_EMPTY = 4;
|
35
|
+
public static final int NO_XHTML = 8;
|
36
|
+
public static final int XHTML = 16;
|
37
|
+
public static final int AS_XML = 32;
|
38
|
+
public static final int AS_HTML = 64;
|
39
|
+
|
40
|
+
public SaveContext(ThreadContext context, int options, String indentString,
|
41
|
+
String encoding) {
|
42
|
+
this.context = context;
|
43
|
+
this.elementDescription =
|
44
|
+
(RubyClass) context.getRuntime().getClassFromPath(
|
45
|
+
"Nokogiri::HTML::ElementDescription");
|
46
|
+
this.options = options;
|
47
|
+
this.encoding = encoding;
|
48
|
+
this.indentString = indentString;
|
49
|
+
this.buffer = new StringBuffer();
|
50
|
+
format = (options & FORMAT) == FORMAT;
|
51
|
+
noDecl = (options & NO_DECL) == NO_DECL;
|
52
|
+
noEmpty = (options & NO_EMPTY) == NO_EMPTY;
|
53
|
+
noXhtml = (options & NO_XHTML) == NO_XHTML;
|
54
|
+
xhtml = (options & XHTML) == XHTML;
|
55
|
+
asXml = (options & AS_XML) == AS_XML;
|
56
|
+
asHtml = (options & AS_HTML) == AS_HTML;
|
57
|
+
}
|
58
|
+
|
59
|
+
|
60
|
+
public void append(String s) {
|
61
|
+
this.buffer.append(s);
|
62
|
+
}
|
63
|
+
|
64
|
+
public void append(char c) {
|
65
|
+
buffer.append(c);
|
66
|
+
}
|
67
|
+
|
68
|
+
public void append(StringBuffer sb) {
|
69
|
+
this.buffer.append(sb);
|
70
|
+
}
|
71
|
+
|
72
|
+
public void appendQuoted(String s) {
|
73
|
+
this.append("\"");
|
74
|
+
this.append(s);
|
75
|
+
this.append("\"");
|
76
|
+
}
|
77
|
+
|
78
|
+
public void appendQuoted(StringBuffer sb) {
|
79
|
+
this.append("\"");
|
80
|
+
this.append(sb);
|
81
|
+
this.append("\"");
|
82
|
+
}
|
83
|
+
|
84
|
+
public void emptyTag(String name) {
|
85
|
+
emptyTagStart(name);
|
86
|
+
emptyTagEnd(name);
|
87
|
+
}
|
88
|
+
|
89
|
+
public void emptyTagStart(String name) {
|
90
|
+
openTagInlineStart(name);
|
91
|
+
}
|
92
|
+
|
93
|
+
public void emptyTagEnd(String name) {
|
94
|
+
if (asHtml) {
|
95
|
+
if (isEmpty(name) && noEmpty()) {
|
96
|
+
append(">");
|
97
|
+
} else {
|
98
|
+
openTagInlineEnd();
|
99
|
+
closeTagInline(name);
|
100
|
+
}
|
101
|
+
} else if (xhtml) {
|
102
|
+
append(" />");
|
103
|
+
} else {
|
104
|
+
append("/>");
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
public void openTag(String name) {
|
109
|
+
openTagStart(name);
|
110
|
+
openTagEnd();
|
111
|
+
}
|
112
|
+
|
113
|
+
public void openTagStart(String name) {
|
114
|
+
maybeBreak();
|
115
|
+
indent();
|
116
|
+
append("<");
|
117
|
+
append(name);
|
118
|
+
}
|
119
|
+
|
120
|
+
public void openTagEnd() {
|
121
|
+
append(">");
|
122
|
+
maybeBreak();
|
123
|
+
increaseLevel();
|
124
|
+
}
|
125
|
+
|
126
|
+
public void closeTag(String name) {
|
127
|
+
decreaseLevel();
|
128
|
+
maybeBreak();
|
129
|
+
indent();
|
130
|
+
append("</");
|
131
|
+
append(name);
|
132
|
+
append(">");
|
133
|
+
maybeBreak();
|
134
|
+
}
|
135
|
+
|
136
|
+
public void openTagInline(String name) {
|
137
|
+
openTagInlineStart(name);
|
138
|
+
openTagInlineEnd();
|
139
|
+
}
|
140
|
+
|
141
|
+
public void openTagInlineStart(String name) {
|
142
|
+
maybeIndent();
|
143
|
+
append("<");
|
144
|
+
append(name);
|
145
|
+
}
|
146
|
+
|
147
|
+
public void openTagInlineEnd() {
|
148
|
+
append(">");
|
149
|
+
}
|
150
|
+
|
151
|
+
public void closeTagInline(String name) {
|
152
|
+
append("</");
|
153
|
+
append(name);
|
154
|
+
append(">");
|
155
|
+
}
|
156
|
+
|
157
|
+
public void maybeBreak() {
|
158
|
+
if (format && !endsInNewline()) append('\n');
|
159
|
+
}
|
160
|
+
|
161
|
+
public void maybeSpace() {
|
162
|
+
if (format && !endsInWhitespace()) append(' ');
|
163
|
+
}
|
164
|
+
|
165
|
+
/**
|
166
|
+
* Indent if this is the start of a fresh line.
|
167
|
+
*/
|
168
|
+
public void maybeIndent() {
|
169
|
+
if (format && endsInNewline()) indent();
|
170
|
+
}
|
171
|
+
|
172
|
+
public void indent() {
|
173
|
+
if (format) append(getCurrentIndentString());
|
174
|
+
}
|
175
|
+
|
176
|
+
public boolean endsInWhitespace() {
|
177
|
+
return (Character.isWhitespace(lastChar()));
|
178
|
+
}
|
179
|
+
|
180
|
+
public boolean endsInNewline() {
|
181
|
+
return (lastChar() == '\n');
|
182
|
+
}
|
183
|
+
|
184
|
+
public char lastChar() {
|
185
|
+
if (buffer.length() == 0) return '\n'; // logically, the char
|
186
|
+
// *before* a text file
|
187
|
+
// is a newline
|
188
|
+
return buffer.charAt(buffer.length() - 1);
|
189
|
+
}
|
190
|
+
|
191
|
+
public boolean asHtml() { return this.asHtml; }
|
192
|
+
|
193
|
+
public boolean asXml() { return this.asXml; }
|
194
|
+
|
195
|
+
public void decreaseLevel() {
|
196
|
+
if(this.level > 0) this.level--;
|
197
|
+
}
|
198
|
+
|
199
|
+
public String encoding() { return this.encoding; }
|
200
|
+
|
201
|
+
public boolean format() { return this.format; }
|
202
|
+
|
203
|
+
public String getCurrentIndentString() {
|
204
|
+
StringBuffer res = new StringBuffer();
|
205
|
+
for(int i = 0; i < this.level; i++) {
|
206
|
+
res.append(this.indentString());
|
207
|
+
}
|
208
|
+
return res.toString();
|
209
|
+
}
|
210
|
+
|
211
|
+
public String getEncoding() { return this.encoding; }
|
212
|
+
|
213
|
+
public void increaseLevel() {
|
214
|
+
if(this.level >= 0) this.level++;
|
215
|
+
}
|
216
|
+
|
217
|
+
public String indentString() { return this.indentString; }
|
218
|
+
|
219
|
+
public boolean noDecl() { return this.noDecl; }
|
220
|
+
|
221
|
+
public boolean noEmpty() { return this.noEmpty; }
|
222
|
+
|
223
|
+
public boolean noXhtml() { return this.noXhtml; }
|
224
|
+
|
225
|
+
public void setFormat(boolean format) { this.format = format; }
|
226
|
+
|
227
|
+
public void setLevel(int level) { this.level = level; }
|
228
|
+
|
229
|
+
@Override
|
230
|
+
public String toString() { return this.buffer.toString(); }
|
231
|
+
|
232
|
+
public RubyString toRubyString(Ruby runtime) {
|
233
|
+
return new RubyString(runtime, runtime.getString(), buffer);
|
234
|
+
}
|
235
|
+
|
236
|
+
public boolean Xhtml() { return this.xhtml; }
|
237
|
+
|
238
|
+
/**
|
239
|
+
* Looks up the HTML ElementDescription and tests if it is an
|
240
|
+
* empty element.
|
241
|
+
*/
|
242
|
+
protected boolean isEmpty(String name) {
|
243
|
+
IRubyObject desc =
|
244
|
+
RuntimeHelpers.invoke(context,
|
245
|
+
elementDescription, "[]",
|
246
|
+
context.getRuntime().newString(name));
|
247
|
+
return RuntimeHelpers.invoke(context, desc, "empty?").isTrue();
|
248
|
+
}
|
249
|
+
}
|