nokogiri 1.8.2-java → 1.8.3-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.
- checksums.yaml +5 -5
- data/.travis.yml +14 -14
- data/CHANGELOG.md +43 -1
- data/LICENSE.md +2 -1
- data/Manifest.txt +3 -0
- data/README.md +20 -21
- data/Rakefile +3 -9
- data/SECURITY.md +19 -0
- data/build_all +1 -1
- data/dependencies.yml +11 -11
- data/ext/java/nokogiri/HtmlSaxParserContext.java +7 -13
- data/ext/java/nokogiri/HtmlSaxPushParser.java +72 -90
- data/ext/java/nokogiri/NokogiriService.java +0 -19
- data/ext/java/nokogiri/XmlNode.java +2 -23
- data/ext/java/nokogiri/XmlSaxParserContext.java +81 -101
- data/ext/java/nokogiri/XmlSaxPushParser.java +117 -89
- data/ext/java/nokogiri/XmlSyntaxError.java +9 -17
- data/ext/java/nokogiri/internals/NokogiriHandler.java +100 -108
- data/ext/java/nokogiri/internals/NokogiriHelpers.java +11 -14
- data/ext/java/nokogiri/internals/ParserContext.java +34 -19
- data/ext/java/nokogiri/internals/ReaderNode.java +6 -10
- data/ext/java/nokogiri/internals/SaveContextVisitor.java +4 -3
- data/ext/java/nokogiri/internals/XmlDomParserContext.java +6 -3
- data/ext/java/nokogiri/internals/c14n/InclusiveNamespaces.java +4 -3
- data/ext/nokogiri/extconf.rb +1 -1
- data/ext/nokogiri/html_element_description.c +14 -14
- data/ext/nokogiri/xml_cdata.c +6 -4
- data/ext/nokogiri/xml_document.c +2 -3
- data/ext/nokogiri/xml_dtd.c +2 -2
- data/ext/nokogiri/xml_io.c +1 -0
- data/ext/nokogiri/xml_namespace.c +3 -9
- data/ext/nokogiri/xml_namespace.h +2 -0
- data/ext/nokogiri/xml_node.c +23 -15
- data/ext/nokogiri/xml_node_set.c +5 -4
- data/ext/nokogiri/xml_node_set.h +0 -1
- data/ext/nokogiri/xslt_stylesheet.c +2 -2
- data/lib/nokogiri/css/parser.rb +108 -90
- data/lib/nokogiri/css/parser.y +13 -2
- data/lib/nokogiri/css/tokenizer.rb +1 -1
- data/lib/nokogiri/css/tokenizer.rex +4 -4
- data/lib/nokogiri/css/xpath_visitor.rb +10 -3
- data/lib/nokogiri/html/document_fragment.rb +11 -1
- data/lib/nokogiri/nokogiri.jar +0 -0
- data/lib/nokogiri/version.rb +1 -1
- data/lib/nokogiri/xml/node.rb +58 -0
- data/lib/nokogiri/xml/node_set.rb +32 -18
- data/patches/libxml2/0001-Revert-Do-not-URI-escape-in-server-side-includes.patch +78 -0
- data/test/css/test_nthiness.rb +21 -21
- data/test/css/test_parser.rb +17 -0
- data/test/html/test_attributes.rb +85 -0
- data/test/html/test_document_fragment.rb +7 -1
- data/test/test_css_cache.rb +5 -3
- data/test/xml/sax/test_parser.rb +9 -1
- data/test/xml/sax/test_push_parser.rb +60 -0
- data/test/xml/test_cdata.rb +1 -1
- data/test/xml/test_document.rb +5 -5
- data/test/xml/test_dtd.rb +4 -4
- data/test/xml/test_node.rb +89 -6
- data/test/xml/test_node_attributes.rb +3 -3
- data/test/xml/test_node_reparenting.rb +18 -0
- data/test/xml/test_node_set.rb +31 -4
- data/test/xml/test_reader.rb +13 -1
- data/test/xml/test_syntax_error.rb +3 -3
- data/test/xml/test_xpath.rb +8 -0
- metadata +25 -4
@@ -37,29 +37,25 @@ import static org.jruby.javasupport.util.RuntimeHelpers.invoke;
|
|
37
37
|
|
38
38
|
import java.io.ByteArrayInputStream;
|
39
39
|
import java.io.IOException;
|
40
|
-
import java.
|
40
|
+
import java.io.InputStream;
|
41
|
+
import java.util.concurrent.ExecutionException;
|
41
42
|
import java.util.concurrent.ExecutorService;
|
42
43
|
import java.util.concurrent.Executors;
|
43
44
|
import java.util.concurrent.Future;
|
44
45
|
import java.util.concurrent.FutureTask;
|
45
46
|
import java.util.concurrent.ThreadFactory;
|
46
47
|
|
47
|
-
import nokogiri.internals
|
48
|
-
import nokogiri.internals.NokogiriBlockingQueueInputStream;
|
49
|
-
import nokogiri.internals.NokogiriHelpers;
|
50
|
-
import nokogiri.internals.ParserContext;
|
48
|
+
import nokogiri.internals.*;
|
51
49
|
|
52
50
|
import org.jruby.Ruby;
|
53
51
|
import org.jruby.RubyClass;
|
54
52
|
import org.jruby.RubyException;
|
55
53
|
import org.jruby.RubyObject;
|
56
|
-
import org.jruby.RubyString;
|
57
54
|
import org.jruby.anno.JRubyClass;
|
58
55
|
import org.jruby.anno.JRubyMethod;
|
59
56
|
import org.jruby.exceptions.RaiseException;
|
60
57
|
import org.jruby.runtime.ThreadContext;
|
61
58
|
import org.jruby.runtime.builtin.IRubyObject;
|
62
|
-
import org.jruby.util.ByteList;
|
63
59
|
import org.xml.sax.SAXException;
|
64
60
|
|
65
61
|
/**
|
@@ -71,12 +67,14 @@ import org.xml.sax.SAXException;
|
|
71
67
|
@JRubyClass(name="Nokogiri::XML::SAX::PushParser")
|
72
68
|
public class XmlSaxPushParser extends RubyObject {
|
73
69
|
ParserContext.Options options;
|
74
|
-
IRubyObject optionsRuby;
|
75
70
|
IRubyObject saxParser;
|
71
|
+
|
76
72
|
NokogiriBlockingQueueInputStream stream;
|
77
|
-
|
78
|
-
|
79
|
-
|
73
|
+
|
74
|
+
private ParserTask parserTask = null;
|
75
|
+
private FutureTask<XmlSaxParserContext> futureTask = null;
|
76
|
+
private ExecutorService executor = null;
|
77
|
+
RaiseException ex = null;
|
80
78
|
|
81
79
|
public XmlSaxPushParser(Ruby ruby, RubyClass rubyClass) {
|
82
80
|
super(ruby, rubyClass);
|
@@ -84,36 +82,37 @@ public class XmlSaxPushParser extends RubyObject {
|
|
84
82
|
|
85
83
|
@Override
|
86
84
|
public void finalize() {
|
87
|
-
|
85
|
+
try {
|
86
|
+
terminateImpl();
|
87
|
+
}
|
88
|
+
catch (Exception e) { /* ignored */ }
|
88
89
|
}
|
89
90
|
|
90
91
|
@JRubyMethod
|
91
|
-
public IRubyObject initialize_native(final ThreadContext context,
|
92
|
-
IRubyObject saxParser,
|
93
|
-
IRubyObject fileName) {
|
94
|
-
optionsRuby
|
95
|
-
= invoke(context, context.getRuntime().getClassFromPath("Nokogiri::XML::ParseOptions"), "new");
|
92
|
+
public IRubyObject initialize_native(final ThreadContext context, IRubyObject saxParser, IRubyObject fileName) {
|
96
93
|
options = new ParserContext.Options(0);
|
97
94
|
this.saxParser = saxParser;
|
98
95
|
return this;
|
99
96
|
}
|
100
97
|
|
101
|
-
|
102
|
-
|
103
|
-
|
98
|
+
private transient IRubyObject parse_options;
|
99
|
+
|
100
|
+
private IRubyObject parse_options(final ThreadContext context) {
|
101
|
+
if (parse_options == null) {
|
102
|
+
parse_options = invoke(context, context.runtime.getClassFromPath("Nokogiri::XML::ParseOptions"), "new");
|
103
|
+
}
|
104
|
+
return parse_options;
|
105
|
+
}
|
106
|
+
|
104
107
|
@JRubyMethod(name="options")
|
105
108
|
public IRubyObject getOptions(ThreadContext context) {
|
106
|
-
return invoke(context,
|
109
|
+
return invoke(context, parse_options(context), "options");
|
107
110
|
}
|
108
111
|
|
109
|
-
/**
|
110
|
-
* <code>val</code> is an integer.
|
111
|
-
*/
|
112
112
|
@JRubyMethod(name="options=")
|
113
|
-
public IRubyObject setOptions(ThreadContext context, IRubyObject
|
114
|
-
invoke(context,
|
115
|
-
options =
|
116
|
-
new ParserContext.Options(val.convertToInteger().getLongValue());
|
113
|
+
public IRubyObject setOptions(ThreadContext context, IRubyObject opts) {
|
114
|
+
invoke(context, parse_options(context), "options=", opts);
|
115
|
+
options = new ParserContext.Options(opts.convertToInteger().getLongValue());
|
117
116
|
return getOptions(context);
|
118
117
|
}
|
119
118
|
|
@@ -139,6 +138,11 @@ public class XmlSaxPushParser extends RubyObject {
|
|
139
138
|
@JRubyMethod
|
140
139
|
public IRubyObject native_write(ThreadContext context, IRubyObject chunk,
|
141
140
|
IRubyObject isLast) {
|
141
|
+
if (ex != null) {
|
142
|
+
// parser has already errored, rethrow the exception
|
143
|
+
throw ex;
|
144
|
+
}
|
145
|
+
|
142
146
|
try {
|
143
147
|
initialize_task(context);
|
144
148
|
} catch (IOException e) {
|
@@ -146,38 +150,28 @@ public class XmlSaxPushParser extends RubyObject {
|
|
146
150
|
}
|
147
151
|
final ByteArrayInputStream data = NokogiriHelpers.stringBytesToStream(chunk);
|
148
152
|
if (data == null) {
|
149
|
-
|
150
|
-
throw new RaiseException(XmlSyntaxError.createXMLSyntaxError(context.runtime)); // Nokogiri::XML::SyntaxError
|
153
|
+
return this;
|
151
154
|
}
|
152
155
|
|
153
156
|
int errorCount0 = parserTask.getErrorCount();
|
154
157
|
|
158
|
+
try {
|
159
|
+
Future<Void> task = stream.addChunk(data);
|
160
|
+
task.get();
|
161
|
+
} catch (ClosedStreamException ex) {
|
162
|
+
// this means the stream is closed, ignore this exception
|
163
|
+
} catch (Exception e) {
|
164
|
+
throw context.runtime.newRuntimeError(e.toString());
|
165
|
+
}
|
155
166
|
|
156
167
|
if (isLast.isTrue()) {
|
157
|
-
|
158
|
-
|
159
|
-
}
|
160
|
-
catch (SAXException e) {
|
161
|
-
throw context.runtime.newRuntimeError(e.getMessage());
|
162
|
-
}
|
163
|
-
terminateTask(context);
|
164
|
-
} else {
|
165
|
-
try {
|
166
|
-
Future<Void> task = stream.addChunk(data);
|
167
|
-
task.get();
|
168
|
-
}
|
169
|
-
catch (ClosedStreamException ex) {
|
170
|
-
// this means the stream is closed, ignore this exception
|
171
|
-
}
|
172
|
-
catch (Exception e) {
|
173
|
-
throw context.runtime.newRuntimeError(e.toString());
|
174
|
-
}
|
175
|
-
|
168
|
+
parserTask.getNokogiriHandler().endDocument();
|
169
|
+
terminateTask(context.runtime);
|
176
170
|
}
|
177
171
|
|
178
172
|
if (!options.recover && parserTask.getErrorCount() > errorCount0) {
|
179
|
-
terminateTask(context);
|
180
|
-
throw
|
173
|
+
terminateTask(context.runtime);
|
174
|
+
throw ex = parserTask.getLastError();
|
181
175
|
}
|
182
176
|
|
183
177
|
return this;
|
@@ -187,70 +181,104 @@ public class XmlSaxPushParser extends RubyObject {
|
|
187
181
|
if (futureTask == null || stream == null) {
|
188
182
|
stream = new NokogiriBlockingQueueInputStream();
|
189
183
|
|
190
|
-
|
184
|
+
assert saxParser != null : "saxParser null";
|
185
|
+
parserTask = new ParserTask(context, saxParser, stream);
|
191
186
|
futureTask = new FutureTask<XmlSaxParserContext>(parserTask);
|
192
187
|
executor = Executors.newSingleThreadExecutor(new ThreadFactory() {
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
188
|
+
@Override
|
189
|
+
public Thread newThread(Runnable r) {
|
190
|
+
Thread t = new Thread(r);
|
191
|
+
t.setName("XmlSaxPushParser");
|
192
|
+
t.setDaemon(true);
|
193
|
+
return t;
|
194
|
+
}
|
200
195
|
});
|
201
196
|
executor.submit(futureTask);
|
202
197
|
}
|
203
198
|
}
|
204
199
|
|
205
|
-
private
|
200
|
+
private void terminateTask(final Ruby runtime) {
|
201
|
+
if (executor == null) return;
|
202
|
+
|
206
203
|
try {
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
}
|
212
|
-
|
213
|
-
|
204
|
+
terminateImpl();
|
205
|
+
}
|
206
|
+
catch (InterruptedException e) {
|
207
|
+
throw runtime.newRuntimeError(e.toString());
|
208
|
+
}
|
209
|
+
catch (Exception e) {
|
210
|
+
throw runtime.newRuntimeError(e.toString());
|
211
|
+
}
|
212
|
+
}
|
213
|
+
|
214
|
+
private synchronized void terminateImpl() throws InterruptedException, ExecutionException {
|
215
|
+
terminateExecution(executor, stream, futureTask);
|
216
|
+
|
217
|
+
executor = null; stream = null; futureTask = null;
|
218
|
+
}
|
219
|
+
|
220
|
+
// SHARED for HtmlSaxPushParser
|
221
|
+
static void terminateExecution(final ExecutorService executor, final NokogiriBlockingQueueInputStream stream,
|
222
|
+
final FutureTask<?> futureTask)
|
223
|
+
throws InterruptedException, ExecutionException {
|
224
|
+
|
225
|
+
if (executor == null) return;
|
226
|
+
|
227
|
+
try {
|
228
|
+
Future<Void> task = stream.addChunk(NokogiriBlockingQueueInputStream.END);
|
229
|
+
task.get();
|
230
|
+
}
|
231
|
+
catch (ClosedStreamException ex) {
|
232
|
+
// ignore this exception, it means the stream was closed
|
214
233
|
}
|
215
234
|
futureTask.cancel(true);
|
216
235
|
executor.shutdown();
|
217
|
-
executor = null;
|
218
|
-
stream = null;
|
219
|
-
futureTask = null;
|
220
236
|
}
|
221
237
|
|
222
|
-
private
|
223
|
-
|
224
|
-
|
225
|
-
|
238
|
+
private static XmlSaxParserContext parse(final Ruby runtime, final InputStream stream) {
|
239
|
+
RubyClass klazz = getNokogiriClass(runtime, "Nokogiri::XML::SAX::ParserContext");
|
240
|
+
return XmlSaxParserContext.parse_stream(runtime, klazz, stream);
|
241
|
+
}
|
242
|
+
|
243
|
+
static class ParserTask extends ParserContext.ParserTask<XmlSaxParserContext> {
|
244
|
+
|
245
|
+
final InputStream stream;
|
246
|
+
|
247
|
+
private ParserTask(ThreadContext context, IRubyObject handler, InputStream stream) {
|
248
|
+
this(context, handler, parse(context.runtime, stream), stream);
|
249
|
+
}
|
226
250
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
this.
|
231
|
-
this.parser = (XmlSaxParserContext) XmlSaxParserContext.parse_stream(context, klazz, stream);
|
251
|
+
// IMPL with HtmlSaxPushParser
|
252
|
+
protected ParserTask(ThreadContext context, IRubyObject handler, XmlSaxParserContext parser, InputStream stream) {
|
253
|
+
super(context, handler, parser);
|
254
|
+
this.stream = stream;
|
232
255
|
}
|
233
256
|
|
234
257
|
@Override
|
235
258
|
public XmlSaxParserContext call() throws Exception {
|
236
|
-
|
237
|
-
|
238
|
-
|
259
|
+
try {
|
260
|
+
parser.parse_with(context, handler);
|
261
|
+
}
|
262
|
+
finally { stream.close(); }
|
239
263
|
// we have to close the stream before exiting, otherwise someone
|
240
264
|
// can add a chunk and block on task.get() forever.
|
241
|
-
|
242
|
-
|
243
|
-
|
265
|
+
return parser;
|
266
|
+
}
|
267
|
+
|
268
|
+
final NokogiriHandler getNokogiriHandler() {
|
269
|
+
return parser.getNokogiriHandler();
|
244
270
|
}
|
245
271
|
|
246
|
-
|
272
|
+
synchronized final int getErrorCount() {
|
247
273
|
// check for null because thread may not have started yet
|
248
274
|
if (parser.getNokogiriHandler() == null) return 0;
|
249
|
-
|
275
|
+
return parser.getNokogiriHandler().getErrorCount();
|
250
276
|
}
|
251
277
|
|
252
|
-
|
253
|
-
return
|
278
|
+
synchronized final RaiseException getLastError() {
|
279
|
+
return parser.getNokogiriHandler().getLastError();
|
254
280
|
}
|
281
|
+
|
255
282
|
}
|
283
|
+
|
256
284
|
}
|
@@ -34,10 +34,10 @@ package nokogiri;
|
|
34
34
|
|
35
35
|
import static nokogiri.internals.NokogiriHelpers.stringOrNil;
|
36
36
|
|
37
|
-
import org.jruby.CompatVersion;
|
38
37
|
import org.jruby.Ruby;
|
39
38
|
import org.jruby.RubyClass;
|
40
39
|
import org.jruby.RubyException;
|
40
|
+
import org.jruby.RubyString;
|
41
41
|
import org.jruby.anno.JRubyClass;
|
42
42
|
import org.jruby.anno.JRubyMethod;
|
43
43
|
import org.jruby.runtime.ThreadContext;
|
@@ -120,26 +120,18 @@ public class XmlSyntaxError extends RubyException {
|
|
120
120
|
setInstanceVariable("@file", stringOrNil(runtime, exception.getSystemId()));
|
121
121
|
}
|
122
122
|
|
123
|
-
|
124
|
-
|
125
|
-
// to support older version of JRuby, the annotation is commented out
|
123
|
+
// NOTE: special care - due JRuby 1.7.x
|
124
|
+
|
126
125
|
@Override
|
127
|
-
|
128
|
-
public IRubyObject to_s(ThreadContext context) {
|
129
|
-
IRubyObject msg = msg(context.runtime);
|
130
|
-
return msg != null ? msg : super.to_s(context);
|
131
|
-
}
|
126
|
+
public IRubyObject to_s(ThreadContext context) { return to_s19(context); }
|
132
127
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
public IRubyObject to_s19(ThreadContext context) {
|
138
|
-
IRubyObject msg = msg(context.runtime);
|
139
|
-
return msg != null ? msg : super.to_s19(context);
|
128
|
+
@JRubyMethod(name = "to_s")
|
129
|
+
public RubyString to_s19(ThreadContext context) {
|
130
|
+
RubyString msg = msg(context.runtime);
|
131
|
+
return msg != null ? msg : super.to_s(context).asString();
|
140
132
|
}
|
141
133
|
|
142
|
-
private
|
134
|
+
private RubyString msg(final Ruby runtime) {
|
143
135
|
if (exception != null && exception.getMessage() != null) {
|
144
136
|
if (messageSet) return null;
|
145
137
|
return runtime.newString( exception.getMessage() );
|
@@ -37,7 +37,10 @@ import static nokogiri.internals.NokogiriHelpers.getPrefix;
|
|
37
37
|
import static nokogiri.internals.NokogiriHelpers.isNamespace;
|
38
38
|
import static nokogiri.internals.NokogiriHelpers.stringOrNil;
|
39
39
|
|
40
|
+
import java.util.Arrays;
|
41
|
+
import java.util.HashSet;
|
40
42
|
import java.util.LinkedList;
|
43
|
+
import java.util.Set;
|
41
44
|
|
42
45
|
import nokogiri.XmlSyntaxError;
|
43
46
|
|
@@ -45,6 +48,7 @@ import org.jruby.Ruby;
|
|
45
48
|
import org.jruby.RubyArray;
|
46
49
|
import org.jruby.RubyClass;
|
47
50
|
import org.jruby.RubyObject;
|
51
|
+
import org.jruby.exceptions.RaiseException;
|
48
52
|
import org.jruby.javasupport.util.RuntimeHelpers;
|
49
53
|
import org.jruby.runtime.ThreadContext;
|
50
54
|
import org.jruby.runtime.builtin.IRubyObject;
|
@@ -61,8 +65,9 @@ import org.xml.sax.ext.DefaultHandler2;
|
|
61
65
|
* @author Yoko Harada <yokolet@gmail.com>
|
62
66
|
*/
|
63
67
|
public class NokogiriHandler extends DefaultHandler2 implements XmlDeclHandler {
|
68
|
+
|
64
69
|
StringBuilder charactersBuilder;
|
65
|
-
private final Ruby
|
70
|
+
private final Ruby runtime;
|
66
71
|
private final RubyClass attrClass;
|
67
72
|
private final IRubyObject object;
|
68
73
|
|
@@ -72,23 +77,24 @@ public class NokogiriHandler extends DefaultHandler2 implements XmlDeclHandler {
|
|
72
77
|
* TODO: should these be stored in the document 'errors' array?
|
73
78
|
* Currently only string messages are stored there.
|
74
79
|
*/
|
75
|
-
private final LinkedList<
|
80
|
+
private final LinkedList<RaiseException> errors = new LinkedList<RaiseException>();
|
76
81
|
|
77
82
|
private Locator locator;
|
78
|
-
private
|
79
|
-
private boolean needEmptyAttrCheck = false;
|
83
|
+
private boolean needEmptyAttrCheck;
|
80
84
|
|
81
85
|
public NokogiriHandler(Ruby runtime, IRubyObject object) {
|
82
|
-
|
86
|
+
assert object != null;
|
87
|
+
this.runtime = runtime;
|
83
88
|
this.attrClass = (RubyClass) runtime.getClassFromPath("Nokogiri::XML::SAX::Parser::Attribute");
|
84
89
|
this.object = object;
|
90
|
+
charactersBuilder = new StringBuilder();
|
85
91
|
String objectName = object.getMetaClass().getName();
|
86
|
-
if (
|
92
|
+
if ("Nokogiri::HTML::SAX::Parser".equals(objectName)) needEmptyAttrCheck = true;
|
87
93
|
}
|
88
94
|
|
89
95
|
@Override
|
90
96
|
public void skippedEntity(String skippedEntity) {
|
91
|
-
call("error",
|
97
|
+
call("error", runtime.newString("Entity '" + skippedEntity + "' not defined\n"));
|
92
98
|
}
|
93
99
|
|
94
100
|
@Override
|
@@ -97,27 +103,24 @@ public class NokogiriHandler extends DefaultHandler2 implements XmlDeclHandler {
|
|
97
103
|
}
|
98
104
|
|
99
105
|
@Override
|
100
|
-
public void startDocument()
|
106
|
+
public void startDocument() {
|
101
107
|
call("start_document");
|
102
|
-
charactersBuilder = new StringBuilder();
|
103
108
|
}
|
104
109
|
|
105
110
|
@Override
|
106
111
|
public void xmlDecl(String version, String encoding, String standalone) {
|
107
|
-
call("xmldecl", stringOrNil(
|
108
|
-
stringOrNil(ruby, encoding),
|
109
|
-
stringOrNil(ruby, standalone));
|
112
|
+
call("xmldecl", stringOrNil(runtime, version), stringOrNil(runtime, encoding), stringOrNil(runtime, standalone));
|
110
113
|
}
|
111
114
|
|
112
115
|
@Override
|
113
|
-
public void endDocument()
|
116
|
+
public void endDocument() {
|
114
117
|
populateCharacters();
|
115
118
|
call("end_document");
|
116
119
|
}
|
117
120
|
|
118
121
|
@Override
|
119
122
|
public void processingInstruction(String target, String data) {
|
120
|
-
|
123
|
+
call("processing_instruction", runtime.newString(target), runtime.newString(data));
|
121
124
|
}
|
122
125
|
|
123
126
|
/*
|
@@ -129,12 +132,14 @@ public class NokogiriHandler extends DefaultHandler2 implements XmlDeclHandler {
|
|
129
132
|
*/
|
130
133
|
@Override
|
131
134
|
public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException {
|
135
|
+
final Ruby runtime = this.runtime;
|
136
|
+
final ThreadContext context = runtime.getCurrentContext();
|
137
|
+
|
132
138
|
// for attributes other than namespace attrs
|
133
|
-
RubyArray rubyAttr = RubyArray.newArray(
|
139
|
+
RubyArray rubyAttr = RubyArray.newArray(runtime);
|
134
140
|
// for namespace defining attributes
|
135
|
-
RubyArray rubyNSAttr = RubyArray.newArray(
|
141
|
+
RubyArray rubyNSAttr = RubyArray.newArray(runtime);
|
136
142
|
|
137
|
-
ThreadContext context = ruby.getCurrentContext();
|
138
143
|
boolean fromFragmentHandler = false; // isFromFragmentHandler();
|
139
144
|
|
140
145
|
for (int i = 0; i < attrs.getLength(); i++) {
|
@@ -145,134 +150,132 @@ public class NokogiriHandler extends DefaultHandler2 implements XmlDeclHandler {
|
|
145
150
|
String pre;
|
146
151
|
|
147
152
|
pre = getPrefix(qn);
|
148
|
-
if (ln == null || ln.
|
153
|
+
if (ln == null || ln.isEmpty()) ln = getLocalPart(qn);
|
149
154
|
|
150
155
|
if (isNamespace(qn) && !fromFragmentHandler) {
|
151
156
|
// I haven't figured the reason out yet, but, in somewhere,
|
152
157
|
// namespace is converted to array in array and cause
|
153
158
|
// TypeError at line 45 in fragment_handler.rb
|
154
|
-
RubyArray ns = RubyArray.newArray(ruby, 2);
|
155
159
|
if (ln.equals("xmlns")) ln = null;
|
156
|
-
|
157
|
-
ns.add(ruby.newString(val));
|
158
|
-
rubyNSAttr.add(ns);
|
160
|
+
rubyNSAttr.append( runtime.newArray( stringOrNil(runtime, ln), runtime.newString(val) ) );
|
159
161
|
} else {
|
160
162
|
IRubyObject[] args = null;
|
161
163
|
if (needEmptyAttrCheck) {
|
162
164
|
if (isEmptyAttr(ln)) {
|
163
|
-
args = new IRubyObject[
|
164
|
-
|
165
|
-
|
166
|
-
|
165
|
+
args = new IRubyObject[] {
|
166
|
+
stringOrNil(runtime, ln),
|
167
|
+
stringOrNil(runtime, pre),
|
168
|
+
stringOrNil(runtime, u)
|
169
|
+
};
|
167
170
|
}
|
168
171
|
}
|
169
172
|
if (args == null) {
|
170
|
-
args = new IRubyObject[
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
173
|
+
args = new IRubyObject[] {
|
174
|
+
stringOrNil(runtime, ln),
|
175
|
+
stringOrNil(runtime, pre),
|
176
|
+
stringOrNil(runtime, u),
|
177
|
+
stringOrNil(runtime, val)
|
178
|
+
};
|
175
179
|
}
|
176
180
|
|
177
|
-
|
178
|
-
rubyAttr.add(attr);
|
181
|
+
rubyAttr.append( RuntimeHelpers.invoke(context, attrClass, "new", args) );
|
179
182
|
}
|
180
183
|
}
|
181
184
|
|
182
|
-
if (localName == null || localName.
|
185
|
+
if (localName == null || localName.isEmpty()) localName = getLocalPart(qName);
|
183
186
|
populateCharacters();
|
184
187
|
call("start_element_namespace",
|
185
|
-
stringOrNil(
|
188
|
+
stringOrNil(runtime, localName),
|
186
189
|
rubyAttr,
|
187
|
-
stringOrNil(
|
188
|
-
stringOrNil(
|
190
|
+
stringOrNil(runtime, getPrefix(qName)),
|
191
|
+
stringOrNil(runtime, uri),
|
189
192
|
rubyNSAttr);
|
190
193
|
}
|
191
194
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
}
|
200
|
-
return false;
|
195
|
+
static final Set<String> EMPTY_ATTRS;
|
196
|
+
static {
|
197
|
+
final String[] emptyAttrs = {
|
198
|
+
"checked", "compact", "declare", "defer", "disabled", "ismap", "multiple",
|
199
|
+
"noresize", "nohref", "noshade", "nowrap", "readonly", "selected"
|
200
|
+
};
|
201
|
+
EMPTY_ATTRS = new HashSet<String>(Arrays.asList(emptyAttrs));
|
201
202
|
}
|
202
|
-
|
203
|
-
|
204
|
-
return
|
203
|
+
|
204
|
+
private static boolean isEmptyAttr(String name) {
|
205
|
+
return EMPTY_ATTRS.contains(name);
|
205
206
|
}
|
206
|
-
|
207
|
-
public Integer
|
208
|
-
|
207
|
+
|
208
|
+
public final Integer getLine() { // -1 if none is available
|
209
|
+
final int line = locator.getLineNumber();
|
210
|
+
return line == -1 ? null : line;
|
209
211
|
}
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
IRubyObject document = rubyObj.getInstanceVariable("@document");
|
215
|
-
if (document != null) {
|
216
|
-
String name = document.getMetaClass().getName();
|
217
|
-
if ("Nokogiri::XML::FragmentHandler".equals(name)) {
|
218
|
-
return true;
|
219
|
-
}
|
220
|
-
}
|
221
|
-
}
|
222
|
-
return false;
|
212
|
+
|
213
|
+
public final Integer getColumn() { // -1 if none is available
|
214
|
+
final int column = locator.getColumnNumber();
|
215
|
+
return column == -1 ? null : column - 1;
|
223
216
|
}
|
224
217
|
|
225
218
|
@Override
|
226
|
-
public void endElement(String uri, String localName, String qName)
|
219
|
+
public void endElement(String uri, String localName, String qName) {
|
227
220
|
populateCharacters();
|
228
221
|
call("end_element_namespace",
|
229
|
-
stringOrNil(
|
230
|
-
stringOrNil(
|
231
|
-
stringOrNil(
|
222
|
+
stringOrNil(runtime, localName),
|
223
|
+
stringOrNil(runtime, getPrefix(qName)),
|
224
|
+
stringOrNil(runtime, uri));
|
232
225
|
}
|
233
226
|
|
234
227
|
@Override
|
235
|
-
public void characters(char[] ch, int start, int length)
|
228
|
+
public void characters(char[] ch, int start, int length) {
|
236
229
|
charactersBuilder.append(ch, start, length);
|
237
230
|
}
|
238
231
|
|
239
232
|
@Override
|
240
|
-
public void comment(char[] ch, int start, int length)
|
233
|
+
public void comment(char[] ch, int start, int length) {
|
241
234
|
populateCharacters();
|
242
|
-
call("comment",
|
235
|
+
call("comment", runtime.newString(new String(ch, start, length)));
|
243
236
|
}
|
244
237
|
|
245
238
|
@Override
|
246
|
-
public void startCDATA()
|
239
|
+
public void startCDATA() {
|
247
240
|
populateCharacters();
|
248
241
|
}
|
249
242
|
|
250
243
|
@Override
|
251
|
-
public void endCDATA()
|
252
|
-
call("cdata_block",
|
244
|
+
public void endCDATA() {
|
245
|
+
call("cdata_block", runtime.newString(charactersBuilder.toString()));
|
253
246
|
charactersBuilder.setLength(0);
|
254
247
|
}
|
255
248
|
|
256
249
|
@Override
|
257
|
-
public void error(SAXParseException
|
258
|
-
|
259
|
-
|
250
|
+
public void error(SAXParseException ex) {
|
251
|
+
try {
|
252
|
+
final String msg = ex.getMessage();
|
253
|
+
call("error", runtime.newString(msg == null ? "" : msg));
|
254
|
+
addError(new RaiseException(XmlSyntaxError.createError(runtime, ex), true));
|
255
|
+
} catch(RaiseException e) {
|
256
|
+
addError(e);
|
257
|
+
}
|
260
258
|
}
|
261
259
|
|
262
260
|
@Override
|
263
|
-
public void fatalError(SAXParseException
|
264
|
-
|
265
|
-
|
266
|
-
|
261
|
+
public void fatalError(SAXParseException ex) {
|
262
|
+
try {
|
263
|
+
final String msg = ex.getMessage();
|
264
|
+
call("error", runtime.newString(msg == null ? "" : msg));
|
265
|
+
addError(new RaiseException(XmlSyntaxError.createFatalError(runtime, ex), true));
|
266
|
+
|
267
|
+
} catch(RaiseException e) {
|
268
|
+
addError(e);
|
269
|
+
}
|
267
270
|
}
|
268
271
|
|
269
272
|
@Override
|
270
|
-
public void warning(SAXParseException
|
271
|
-
|
272
|
-
call("warning",
|
273
|
+
public void warning(SAXParseException ex) {
|
274
|
+
final String msg = ex.getMessage();
|
275
|
+
call("warning", runtime.newString(msg == null ? "" : msg));
|
273
276
|
}
|
274
277
|
|
275
|
-
protected synchronized void addError(
|
278
|
+
protected synchronized void addError(RaiseException e) {
|
276
279
|
errors.add(e);
|
277
280
|
}
|
278
281
|
|
@@ -280,30 +283,28 @@ public class NokogiriHandler extends DefaultHandler2 implements XmlDeclHandler {
|
|
280
283
|
return errors.size();
|
281
284
|
}
|
282
285
|
|
283
|
-
public synchronized
|
286
|
+
public synchronized RaiseException getLastError() {
|
284
287
|
return errors.getLast();
|
285
288
|
}
|
286
289
|
|
287
290
|
private void call(String methodName) {
|
288
|
-
ThreadContext context =
|
291
|
+
ThreadContext context = runtime.getCurrentContext();
|
289
292
|
RuntimeHelpers.invoke(context, document(context), methodName);
|
290
293
|
}
|
291
294
|
|
292
295
|
private void call(String methodName, IRubyObject argument) {
|
293
|
-
ThreadContext context =
|
296
|
+
ThreadContext context = runtime.getCurrentContext();
|
294
297
|
RuntimeHelpers.invoke(context, document(context), methodName, argument);
|
295
298
|
}
|
296
299
|
|
297
300
|
private void call(String methodName, IRubyObject arg1, IRubyObject arg2) {
|
298
|
-
ThreadContext context =
|
301
|
+
ThreadContext context = runtime.getCurrentContext();
|
299
302
|
RuntimeHelpers.invoke(context, document(context), methodName, arg1, arg2);
|
300
303
|
}
|
301
304
|
|
302
|
-
private void call(String methodName, IRubyObject arg1, IRubyObject arg2,
|
303
|
-
|
304
|
-
|
305
|
-
RuntimeHelpers.invoke(context, document(context), methodName,
|
306
|
-
arg1, arg2, arg3);
|
305
|
+
private void call(String methodName, IRubyObject arg1, IRubyObject arg2, IRubyObject arg3) {
|
306
|
+
ThreadContext context = runtime.getCurrentContext();
|
307
|
+
RuntimeHelpers.invoke(context, document(context), methodName, arg1, arg2, arg3);
|
307
308
|
}
|
308
309
|
|
309
310
|
private void call(String methodName,
|
@@ -312,26 +313,17 @@ public class NokogiriHandler extends DefaultHandler2 implements XmlDeclHandler {
|
|
312
313
|
IRubyObject arg2,
|
313
314
|
IRubyObject arg3,
|
314
315
|
IRubyObject arg4) {
|
315
|
-
|
316
|
-
|
317
|
-
args[1] = arg1;
|
318
|
-
args[2] = arg2;
|
319
|
-
args[3] = arg3;
|
320
|
-
args[4] = arg4;
|
321
|
-
ThreadContext context = ruby.getCurrentContext();
|
322
|
-
RuntimeHelpers.invoke(context, document(context), methodName, args);
|
316
|
+
ThreadContext context = runtime.getCurrentContext();
|
317
|
+
RuntimeHelpers.invoke(context, document(context), methodName, arg0, arg1, arg2, arg3, arg4);
|
323
318
|
}
|
324
319
|
|
325
320
|
private IRubyObject document(ThreadContext context) {
|
326
|
-
|
327
|
-
return ((RubyObject)object).fastGetInstanceVariable("@document");
|
328
|
-
}
|
329
|
-
return context.getRuntime().getNil();
|
321
|
+
return object.getInstanceVariables().getInstanceVariable("@document");
|
330
322
|
}
|
331
323
|
|
332
324
|
protected void populateCharacters() {
|
333
325
|
if (charactersBuilder.length() > 0) {
|
334
|
-
call("characters",
|
326
|
+
call("characters", runtime.newString(charactersBuilder.toString()));
|
335
327
|
charactersBuilder.setLength(0);
|
336
328
|
}
|
337
329
|
}
|