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.

Files changed (65) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +14 -14
  3. data/CHANGELOG.md +43 -1
  4. data/LICENSE.md +2 -1
  5. data/Manifest.txt +3 -0
  6. data/README.md +20 -21
  7. data/Rakefile +3 -9
  8. data/SECURITY.md +19 -0
  9. data/build_all +1 -1
  10. data/dependencies.yml +11 -11
  11. data/ext/java/nokogiri/HtmlSaxParserContext.java +7 -13
  12. data/ext/java/nokogiri/HtmlSaxPushParser.java +72 -90
  13. data/ext/java/nokogiri/NokogiriService.java +0 -19
  14. data/ext/java/nokogiri/XmlNode.java +2 -23
  15. data/ext/java/nokogiri/XmlSaxParserContext.java +81 -101
  16. data/ext/java/nokogiri/XmlSaxPushParser.java +117 -89
  17. data/ext/java/nokogiri/XmlSyntaxError.java +9 -17
  18. data/ext/java/nokogiri/internals/NokogiriHandler.java +100 -108
  19. data/ext/java/nokogiri/internals/NokogiriHelpers.java +11 -14
  20. data/ext/java/nokogiri/internals/ParserContext.java +34 -19
  21. data/ext/java/nokogiri/internals/ReaderNode.java +6 -10
  22. data/ext/java/nokogiri/internals/SaveContextVisitor.java +4 -3
  23. data/ext/java/nokogiri/internals/XmlDomParserContext.java +6 -3
  24. data/ext/java/nokogiri/internals/c14n/InclusiveNamespaces.java +4 -3
  25. data/ext/nokogiri/extconf.rb +1 -1
  26. data/ext/nokogiri/html_element_description.c +14 -14
  27. data/ext/nokogiri/xml_cdata.c +6 -4
  28. data/ext/nokogiri/xml_document.c +2 -3
  29. data/ext/nokogiri/xml_dtd.c +2 -2
  30. data/ext/nokogiri/xml_io.c +1 -0
  31. data/ext/nokogiri/xml_namespace.c +3 -9
  32. data/ext/nokogiri/xml_namespace.h +2 -0
  33. data/ext/nokogiri/xml_node.c +23 -15
  34. data/ext/nokogiri/xml_node_set.c +5 -4
  35. data/ext/nokogiri/xml_node_set.h +0 -1
  36. data/ext/nokogiri/xslt_stylesheet.c +2 -2
  37. data/lib/nokogiri/css/parser.rb +108 -90
  38. data/lib/nokogiri/css/parser.y +13 -2
  39. data/lib/nokogiri/css/tokenizer.rb +1 -1
  40. data/lib/nokogiri/css/tokenizer.rex +4 -4
  41. data/lib/nokogiri/css/xpath_visitor.rb +10 -3
  42. data/lib/nokogiri/html/document_fragment.rb +11 -1
  43. data/lib/nokogiri/nokogiri.jar +0 -0
  44. data/lib/nokogiri/version.rb +1 -1
  45. data/lib/nokogiri/xml/node.rb +58 -0
  46. data/lib/nokogiri/xml/node_set.rb +32 -18
  47. data/patches/libxml2/0001-Revert-Do-not-URI-escape-in-server-side-includes.patch +78 -0
  48. data/test/css/test_nthiness.rb +21 -21
  49. data/test/css/test_parser.rb +17 -0
  50. data/test/html/test_attributes.rb +85 -0
  51. data/test/html/test_document_fragment.rb +7 -1
  52. data/test/test_css_cache.rb +5 -3
  53. data/test/xml/sax/test_parser.rb +9 -1
  54. data/test/xml/sax/test_push_parser.rb +60 -0
  55. data/test/xml/test_cdata.rb +1 -1
  56. data/test/xml/test_document.rb +5 -5
  57. data/test/xml/test_dtd.rb +4 -4
  58. data/test/xml/test_node.rb +89 -6
  59. data/test/xml/test_node_attributes.rb +3 -3
  60. data/test/xml/test_node_reparenting.rb +18 -0
  61. data/test/xml/test_node_set.rb +31 -4
  62. data/test/xml/test_reader.rb +13 -1
  63. data/test/xml/test_syntax_error.rb +3 -3
  64. data/test/xml/test_xpath.rb +8 -0
  65. 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.util.concurrent.Callable;
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.ClosedStreamException;
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
- ParserTask parserTask = null;
78
- FutureTask<XmlSaxParserContext> futureTask = null;
79
- ExecutorService executor = null;
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
- terminateTask(null);
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
- * Returns an integer.
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, optionsRuby, "options");
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 val) {
114
- invoke(context, optionsRuby, "options=", val);
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
- terminateTask(context);
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
- try {
158
- parserTask.parser.getNokogiriHandler().endDocument();
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 new RaiseException(parserTask.getLastError(), true);
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
- parserTask = new ParserTask(context, saxParser);
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
- @Override
194
- public Thread newThread(Runnable r) {
195
- Thread t = new Thread(r);
196
- t.setName("XmlSaxPushParser");
197
- t.setDaemon(true);
198
- return t;
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 synchronized void terminateTask(ThreadContext context) {
200
+ private void terminateTask(final Ruby runtime) {
201
+ if (executor == null) return;
202
+
206
203
  try {
207
- Future<Void> task = stream.addChunk(NokogiriBlockingQueueInputStream.END);
208
- task.get();
209
- } catch (ClosedStreamException ex) {
210
- // ignore this exception, it means the stream was closed
211
- } catch (Exception e) {
212
- if (context != null)
213
- throw context.getRuntime().newRuntimeError(e.getMessage());
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 class ParserTask implements Callable<XmlSaxParserContext> {
223
- private final ThreadContext context;
224
- private final IRubyObject handler;
225
- private final XmlSaxParserContext parser;
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
- private ParserTask(ThreadContext context, IRubyObject handler) {
228
- RubyClass klazz = getNokogiriClass(context.getRuntime(), "Nokogiri::XML::SAX::ParserContext");
229
- this.context = context;
230
- this.handler = handler;
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
- try {
237
- parser.parse_with(context, handler);
238
- } finally {
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
- stream.close();
242
- }
243
- return parser;
265
+ return parser;
266
+ }
267
+
268
+ final NokogiriHandler getNokogiriHandler() {
269
+ return parser.getNokogiriHandler();
244
270
  }
245
271
 
246
- private synchronized int getErrorCount() {
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
- else return parser.getNokogiriHandler().getErrorCount();
275
+ return parser.getNokogiriHandler().getErrorCount();
250
276
  }
251
277
 
252
- private synchronized RubyException getLastError() {
253
- return (RubyException) parser.getNokogiriHandler().getLastError();
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
- //@Override
124
- //"to_s" method was branched in 1.8 and 1.9 since JRuby 1.6.6
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
- @JRubyMethod(name = "to_s", compat = CompatVersion.RUBY1_8)
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
- //@Override
134
- //"to_s" method was branched in 1.8 and 1.9 since JRuby 1.6.6
135
- // to support older version of JRuby, the annotation is commented out
136
- @JRubyMethod(name = "to_s", compat = CompatVersion.RUBY1_9)
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 IRubyObject msg(final Ruby runtime) {
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 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<XmlSyntaxError> errors = new LinkedList<XmlSyntaxError>();
80
+ private final LinkedList<RaiseException> errors = new LinkedList<RaiseException>();
76
81
 
77
82
  private Locator locator;
78
- private static String htmlParserName = "Nokogiri::HTML::SAX::Parser";
79
- private boolean needEmptyAttrCheck = false;
83
+ private boolean needEmptyAttrCheck;
80
84
 
81
85
  public NokogiriHandler(Ruby runtime, IRubyObject object) {
82
- this.ruby = runtime;
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 (htmlParserName.equals(objectName)) needEmptyAttrCheck = true;
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", ruby.newString("Entity '" + skippedEntity + "' not defined\n"));
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() throws SAXException {
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(ruby, version),
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() throws SAXException {
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
- call("processing_instruction", ruby.newString(target), ruby.newString(data));
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(ruby);
139
+ RubyArray rubyAttr = RubyArray.newArray(runtime);
134
140
  // for namespace defining attributes
135
- RubyArray rubyNSAttr = RubyArray.newArray(ruby);
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.equals("")) ln = getLocalPart(qn);
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
- ns.add(stringOrNil(ruby, ln));
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[3];
164
- args[0] = stringOrNil(ruby, ln);
165
- args[1] = stringOrNil(ruby, pre);
166
- args[2] = stringOrNil(ruby, u);
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[4];
171
- args[0] = stringOrNil(ruby, ln);
172
- args[1] = stringOrNil(ruby, pre);
173
- args[2] = stringOrNil(ruby, u);
174
- args[3] = stringOrNil(ruby, val);
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
- IRubyObject attr = RuntimeHelpers.invoke(context, attrClass, "new", args);
178
- rubyAttr.add(attr);
181
+ rubyAttr.append( RuntimeHelpers.invoke(context, attrClass, "new", args) );
179
182
  }
180
183
  }
181
184
 
182
- if (localName == null || localName.equals("")) localName = getLocalPart(qName);
185
+ if (localName == null || localName.isEmpty()) localName = getLocalPart(qName);
183
186
  populateCharacters();
184
187
  call("start_element_namespace",
185
- stringOrNil(ruby, localName),
188
+ stringOrNil(runtime, localName),
186
189
  rubyAttr,
187
- stringOrNil(ruby, getPrefix(qName)),
188
- stringOrNil(ruby, uri),
190
+ stringOrNil(runtime, getPrefix(qName)),
191
+ stringOrNil(runtime, uri),
189
192
  rubyNSAttr);
190
193
  }
191
194
 
192
- private static String[] emptyAttrs =
193
- {"checked", "compact", "declare", "defer", "disabled", "ismap", "multiple",
194
- "noresize", "nohref", "noshade", "nowrap", "readonly", "selected"};
195
-
196
- private boolean isEmptyAttr(String name) {
197
- for (String emptyAttr : emptyAttrs) {
198
- if (emptyAttr.equals(name)) return true;
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
- public Integer getLine() {
204
- return locator.getLineNumber();
203
+
204
+ private static boolean isEmptyAttr(String name) {
205
+ return EMPTY_ATTRS.contains(name);
205
206
  }
206
-
207
- public Integer getColumn() {
208
- return locator.getColumnNumber() - 1;
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
- private boolean isFromFragmentHandler() {
212
- if (object != null && object instanceof RubyObject) {
213
- RubyObject rubyObj = (RubyObject)object;
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) throws SAXException {
219
+ public void endElement(String uri, String localName, String qName) {
227
220
  populateCharacters();
228
221
  call("end_element_namespace",
229
- stringOrNil(ruby, localName),
230
- stringOrNil(ruby, getPrefix(qName)),
231
- stringOrNil(ruby, uri));
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) throws SAXException {
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) throws SAXException {
233
+ public void comment(char[] ch, int start, int length) {
241
234
  populateCharacters();
242
- call("comment", ruby.newString(new String(ch, start, length)));
235
+ call("comment", runtime.newString(new String(ch, start, length)));
243
236
  }
244
237
 
245
238
  @Override
246
- public void startCDATA() throws SAXException {
239
+ public void startCDATA() {
247
240
  populateCharacters();
248
241
  }
249
242
 
250
243
  @Override
251
- public void endCDATA() throws SAXException {
252
- call("cdata_block", ruby.newString(charactersBuilder.toString()));
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 saxpe) {
258
- addError(XmlSyntaxError.createError(ruby, saxpe));
259
- call("error", ruby.newString(saxpe.getMessage()));
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 saxpe) throws SAXException
264
- {
265
- addError(XmlSyntaxError.createFatalError(ruby, saxpe));
266
- call("error", ruby.newString(saxpe.getMessage()));
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 saxpe) {
271
- //System.out.println("warning: " + saxpe);
272
- call("warning", ruby.newString(saxpe.getMessage()));
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(XmlSyntaxError e) {
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 IRubyObject getLastError() {
286
+ public synchronized RaiseException getLastError() {
284
287
  return errors.getLast();
285
288
  }
286
289
 
287
290
  private void call(String methodName) {
288
- ThreadContext context = ruby.getCurrentContext();
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 = ruby.getCurrentContext();
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 = ruby.getCurrentContext();
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
- IRubyObject arg3) {
304
- ThreadContext context = ruby.getCurrentContext();
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
- IRubyObject[] args = new IRubyObject[5];
316
- args[0] = arg0;
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
- if (object instanceof RubyObject) {
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", ruby.newString(charactersBuilder.toString()));
326
+ call("characters", runtime.newString(charactersBuilder.toString()));
335
327
  charactersBuilder.setLength(0);
336
328
  }
337
329
  }