nokogiri 1.5.3.rc3-java → 1.5.3.rc4-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.

@@ -1,5 +1,9 @@
1
1
  == 1.5.3 / unreleased
2
2
 
3
+ * Features
4
+
5
+ * Support for "prefixless" CSS selectors ~, > and + like jQuery supports. #621, #623. (Thanks, David Lee!)
6
+
3
7
  * Bugfixes
4
8
 
5
9
  * Custom xpath functions with empty nodeset arguments cause a segfault. #634.
@@ -8,6 +12,11 @@
8
12
  namespace.
9
13
  * Fixed marshalling bugs around how arguments are passed to (and
10
14
  returned from) XSLT custom xpath functions. #640.
15
+ * Nokogiri::XML::Reader#outer_xml がJRubyで正しく動作しない #617
16
+ * Nokogiri::XML::Attribute が JRuby 上で nil namespace を返す #647
17
+ * Nokogiri::XML::Node#namespace= メソッドが JRuby 上で prefix が無い namespace を設定できない #648
18
+ * JRuby 1.9 モードで rake を実行するとデッドロックを引き起こす #571
19
+ * HTML::Document#meta_encoding does not raise exception on docs with malformed content-type. #655
11
20
 
12
21
 
13
22
  == 1.5.2 / 2012-03-09
@@ -1,5 +1,9 @@
1
1
  == 1.5.3 / unreleased
2
2
 
3
+ * Features
4
+
5
+ * Support for "prefixless" CSS selectors ~, > and + like jQuery supports. #621, #623. (Thanks, David Lee!)
6
+
3
7
  * Bugfixes
4
8
 
5
9
  * Custom xpath functions with empty nodeset arguments cause a segfault. #634.
@@ -8,6 +12,11 @@
8
12
  namespace.
9
13
  * Fixed marshalling bugs around how arguments are passed to (and
10
14
  returned from) XSLT custom xpath functions. #640.
15
+ * Nokogiri::XML::Reader#outer_xml is broken in JRuby #617
16
+ * Nokogiri::XML::Attribute on JRuby returns a nil namespace #647
17
+ * Nokogiri::XML::Node#namespace= cannot set a namespace without a prefix on JRuby #648
18
+ * JRuby 1.9 mode causes dead lock while running rake #571
19
+ * HTML::Document#meta_encoding does not raise exception on docs with malformed content-type. #655
11
20
 
12
21
 
13
22
  == 1.5.2 / 2012-03-09
@@ -439,7 +448,7 @@ Repackaging of 1.5.1 with a gemspec that is compatible with older Rubies. #631,
439
448
  * Nokogiri::HTML.fragment will properly handle text only nodes (LH #43)
440
449
  * Nokogiri::XML::Node#before will prepend text nodes (LH #44)
441
450
  * Nokogiri::XML::Node#after will append text nodes
442
- * Nokogiri::XML::Node#search automatically registers root namepsaces (LH #42)
451
+ * Nokogiri::XML::Node#search automatically registers root namespaces (LH #42)
443
452
  * Nokogiri::XML::NodeSet#search automatically registers namespaces
444
453
  * Nokogiri::HTML::NamedCharacters delegates to libxml2
445
454
  * Nokogiri::XML::Node#[] can take a symbol (LH #48)
@@ -559,7 +568,7 @@ Repackaging of 1.5.1 with a gemspec that is compatible with older Rubies. #631,
559
568
 
560
569
  * Bugfixes
561
570
 
562
- * Changed memory mangement from weak refs to document refs
571
+ * Changed memory management from weak refs to document refs
563
572
  * Plugged some memory leaks
564
573
  * Builder blocks can call methods from surrounding contexts
565
574
 
@@ -58,7 +58,6 @@ ext/java/nokogiri/internals/NokogiriXPathFunctionResolver.java
58
58
  ext/java/nokogiri/internals/NokogiriXPathVariableResolver.java
59
59
  ext/java/nokogiri/internals/NokogiriXsltErrorListener.java
60
60
  ext/java/nokogiri/internals/ParserContext.java
61
- ext/java/nokogiri/internals/PushInputStream.java
62
61
  ext/java/nokogiri/internals/ReaderNode.java
63
62
  ext/java/nokogiri/internals/SaveContextVisitor.java
64
63
  ext/java/nokogiri/internals/SchemaErrorHandler.java
data/ROADMAP.md CHANGED
@@ -26,6 +26,7 @@
26
26
  * https://github.com/tenderlove/nokogiri/issues/621
27
27
  * https://github.com/tenderlove/nokogiri/issues/342
28
28
  * https://github.com/tenderlove/nokogiri/issues/628
29
+ * https://github.com/tenderlove/nokogiri/issues/652
29
30
 
30
31
  * https://github.com/tenderlove/nokogiri/issues/394
31
32
  nth-of-type is wrong, and possibly other selectors as well
@@ -52,12 +53,22 @@
52
53
  * https://github.com/tenderlove/nokogiri/pull/464
53
54
 
54
55
 
56
+ ## Better Syntax around Node#xpath and NodeSet#xpath
57
+
58
+ * look at those methods, and use of Node#extract_params in Node#{css,search}
59
+ * we should standardize on a hash of options for these and other calls
60
+ * what should NodeSet#xpath return?
61
+ * https://github.com/tenderlove/nokogiri/issues/656
62
+
55
63
  ## Encoding
56
64
 
57
65
  We have a lot of issues open around encoding. Is this really an issue?
58
66
  Somebody who knows something about encoding, and cares, should point
59
67
  this one.
60
68
 
69
+ * Extract EncodingReader as a real object that can be injected
70
+ https://groups.google.com/forum/#!msg/nokogiri-talk/arJeAtMqvkg/tGihB-iBRSAJ
71
+
61
72
 
62
73
  ## Reader
63
74
 
@@ -33,8 +33,8 @@ Thank you so much!
33
33
 
34
34
  Hello!
35
35
 
36
- Thanks for asking this question! Your request for help will not go
37
- unanswered!
36
+ Thanks for asking this question! Your request for assistance using
37
+ Nokogiri will not go unanswered!
38
38
 
39
39
  However, Nokogiri's Github Issues is reserved for reporting bugs or
40
40
  submitting patches. If you ask your question on the mailing list, Team
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * (The MIT License)
3
3
  *
4
- * Copyright (c) 2008 - 2011:
4
+ * Copyright (c) 2008 - 2012:
5
5
  *
6
6
  * * {Aaron Patterson}[http://tenderlovemaking.com]
7
7
  * * {Mike Dalessio}[http://mike.daless.io]
@@ -96,6 +96,16 @@ public class XmlAttr extends XmlNode{
96
96
  Node attr = xmlDoc.getDocument().createAttribute(str);
97
97
  setNode(context, attr);
98
98
  }
99
+
100
+
101
+ // this method is called from XmlNode.setNode()
102
+ // if the node is attribute, and its name has prefix "xml"
103
+ // the default namespace should be registered for this attribute
104
+ void setNamespaceIfNecessary(Ruby runtime) {
105
+ if ("xml".equals(node.getPrefix())) {
106
+ XmlNamespace.createDefaultNamespace(runtime, node);
107
+ }
108
+ }
99
109
 
100
110
  private boolean isHtmlBooleanAttr() {
101
111
  String name = node.getNodeName().toLowerCase();
@@ -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 static nokogiri.internals.NokogiriHelpers.stringOrNil;
39
40
  import nokogiri.internals.NokogiriHelpers;
40
41
  import nokogiri.internals.SaveContextVisitor;
41
42
 
@@ -155,6 +156,30 @@ public class XmlNamespace extends RubyObject {
155
156
  return namespace;
156
157
  }
157
158
 
159
+ // owner should be an Attr node
160
+ public static XmlNamespace createDefaultNamespace(Ruby runtime, Node owner) {
161
+ String prefixValue = owner.getPrefix();
162
+ String hrefValue = owner.getNamespaceURI();
163
+ Document document = owner.getOwnerDocument();
164
+ // check namespace cache
165
+ XmlDocument xmlDocument = (XmlDocument)getCachedNodeOrCreate(runtime, document);
166
+ XmlNamespace xmlNamespace = xmlDocument.getNamespaceCache().get(prefixValue, hrefValue);
167
+ if (xmlNamespace != null) return xmlNamespace;
168
+
169
+ // creating XmlNamespace instance
170
+ XmlNamespace namespace =
171
+ (XmlNamespace) NokogiriService.XML_NAMESPACE_ALLOCATOR.allocate(runtime, getNokogiriClass(runtime, "Nokogiri::XML::Namespace"));
172
+
173
+ IRubyObject prefix = stringOrNil(runtime, prefixValue);
174
+ IRubyObject href = stringOrNil(runtime, hrefValue);
175
+ // initialize XmlNamespace object
176
+ namespace.init((Attr)owner, prefix, href, prefixValue, hrefValue, xmlDocument);
177
+
178
+ // updating namespace cache
179
+ xmlDocument.getNamespaceCache().put(namespace, owner);
180
+ return namespace;
181
+ }
182
+
158
183
  /**
159
184
  * Create and return a copy of this object.
160
185
  *
@@ -479,6 +479,10 @@ public class XmlNode extends RubyObject {
479
479
  doc = document(context);
480
480
  }
481
481
  }
482
+
483
+ if (this instanceof XmlAttr) {
484
+ ((XmlAttr)this).setNamespaceIfNecessary(context.getRuntime());
485
+ }
482
486
  }
483
487
 
484
488
  public void updateNodeNamespaceIfNecessary(ThreadContext context, XmlNamespace ns) {
@@ -1342,7 +1346,7 @@ public class XmlNode extends RubyObject {
1342
1346
  */
1343
1347
  if (parent.getNodeType() == Node.DOCUMENT_NODE &&
1344
1348
  otherNode.getNodeType() == Node.TEXT_NODE) {
1345
- Element e = ((Document)parent).createElement("text");
1349
+ Element e = ((Document)parent).createElement("nokogiri_text_wrapper");
1346
1350
  e.appendChild(otherNode);
1347
1351
  otherNode = e;
1348
1352
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * (The MIT License)
3
3
  *
4
- * Copyright (c) 2008 - 2011:
4
+ * Copyright (c) 2008 - 2012:
5
5
  *
6
6
  * * {Aaron Patterson}[http://tenderlovemaking.com]
7
7
  * * {Mike Dalessio}[http://mike.daless.io]
@@ -228,8 +228,15 @@ public class XmlReader extends RubyObject {
228
228
  if (!current.hasChildren) return null;
229
229
  StringBuffer sb = new StringBuffer();
230
230
  int currentDepth = (Integer)current.depth;
231
+ int inner = 0;
231
232
  for (ReaderNode node : nodeQueue) {
232
- if (((Integer)node.depth) > currentDepth) sb.append(node.getString());
233
+ if (((Integer)node.depth) == currentDepth && node.getName().equals(current.getName())) {
234
+ inner++;
235
+ }
236
+ if (((Integer)node.depth) > currentDepth) {
237
+ sb.append(node.getString());
238
+ }
239
+ if (inner == 2) break;
233
240
  }
234
241
  return new String(sb);
235
242
  }
@@ -242,9 +249,17 @@ public class XmlReader extends RubyObject {
242
249
  private String getOuterXml(ArrayDeque<ReaderNode> nodeQueue, ReaderNode current) {
243
250
  if (current.depth < 0) return null;
244
251
  StringBuffer sb = new StringBuffer();
245
- int initialDepth = (Integer)current.depth - 1;
252
+ int initialDepth = (Integer)current.depth;
253
+ int inner = 0;
246
254
  for (ReaderNode node : nodeQueue) {
247
- if (((Integer)node.depth) > initialDepth) sb.append(node.getString());
255
+ if (((Integer)node.depth) >= initialDepth) {
256
+ if (((Integer)node.depth) == initialDepth && node.getName().equals(current.getName())) {
257
+ inner++;
258
+ }
259
+
260
+ sb.append(node.getString());
261
+ }
262
+ if (inner == 2) break;
248
263
  }
249
264
  return new String(sb);
250
265
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * (The MIT License)
3
3
  *
4
- * Copyright (c) 2008 - 2011:
4
+ * Copyright (c) 2008 - 2012:
5
5
  *
6
6
  * * {Aaron Patterson}[http://tenderlovemaking.com]
7
7
  * * {Mike Dalessio}[http://mike.daless.io]
@@ -37,10 +37,15 @@ import static org.jruby.javasupport.util.RuntimeHelpers.invoke;
37
37
 
38
38
  import java.io.IOException;
39
39
  import java.io.InputStream;
40
- import java.nio.channels.ClosedChannelException;
40
+ import java.io.OutputStream;
41
+ import java.nio.channels.Channels;
42
+ import java.nio.channels.Pipe;
43
+ import java.util.concurrent.Callable;
44
+ import java.util.concurrent.ExecutorService;
45
+ import java.util.concurrent.Executors;
46
+ import java.util.concurrent.FutureTask;
41
47
 
42
48
  import nokogiri.internals.ParserContext;
43
- import nokogiri.internals.PushInputStream;
44
49
 
45
50
  import org.jruby.Ruby;
46
51
  import org.jruby.RubyClass;
@@ -63,9 +68,12 @@ import org.jruby.runtime.builtin.IRubyObject;
63
68
  public class XmlSaxPushParser extends RubyObject {
64
69
  ParserContext.Options options;
65
70
  IRubyObject optionsRuby;
66
- PushInputStream stream;
67
- Thread reader;
68
- Runner runner;
71
+ IRubyObject saxParser;
72
+ OutputStream ostream = null;
73
+ InputStream istream = null;
74
+ ParserTask parserTask = null;
75
+ FutureTask<XmlSaxParserContext> futureTask = null;
76
+ ExecutorService executor = null;
69
77
 
70
78
  public XmlSaxPushParser(Ruby ruby, RubyClass rubyClass) {
71
79
  super(ruby, rubyClass);
@@ -73,18 +81,12 @@ public class XmlSaxPushParser extends RubyObject {
73
81
 
74
82
  @JRubyMethod
75
83
  public IRubyObject initialize_native(final ThreadContext context,
76
- IRubyObject _saxParser,
84
+ IRubyObject saxParser,
77
85
  IRubyObject fileName) {
78
- optionsRuby = invoke(context,
79
- context.getRuntime().getClassFromPath("Nokogiri::XML::ParseOptions"),
80
- "new");
86
+ optionsRuby
87
+ = invoke(context, context.getRuntime().getClassFromPath("Nokogiri::XML::ParseOptions"), "new");
81
88
  options = new ParserContext.Options(0);
82
- stream = new PushInputStream();
83
-
84
- runner = new Runner(context, this, stream);
85
- reader = new Thread(runner);
86
- reader.start();
87
-
89
+ this.saxParser = saxParser;
88
90
  return this;
89
91
  }
90
92
 
@@ -110,74 +112,103 @@ public class XmlSaxPushParser extends RubyObject {
110
112
  @JRubyMethod
111
113
  public IRubyObject native_write(ThreadContext context, IRubyObject chunk,
112
114
  IRubyObject isLast) {
115
+ try {
116
+ initialize_task(context);
117
+ } catch (IOException e) {
118
+ throw context.getRuntime().newRuntimeError(e.getMessage());
119
+ }
113
120
  byte[] data = null;
114
121
  if (chunk instanceof RubyString || chunk.respondsTo("to_str")) {
115
122
  data = chunk.convertToString().getBytes();
116
- } else {
117
- XmlSyntaxError xmlSyntaxError = (XmlSyntaxError) NokogiriService.XML_SYNTAXERROR_ALLOCATOR.allocate(context.getRuntime(), getNokogiriClass(context.getRuntime(), "Nokogiri::XML::SyntaxError"));
123
+ } else {
124
+ try {
125
+ terminateTask();
126
+ } catch (IOException e) {
127
+ throw context.getRuntime().newRuntimeError(e.getMessage());
128
+ }
129
+ XmlSyntaxError xmlSyntaxError =
130
+ (XmlSyntaxError) NokogiriService.XML_SYNTAXERROR_ALLOCATOR.allocate(context.getRuntime(), getNokogiriClass(context.getRuntime(), "Nokogiri::XML::SyntaxError"));
118
131
  throw new RaiseException(xmlSyntaxError);
119
132
  }
120
133
 
121
- int errorCount0 = runner.getErrorCount();
122
-
134
+ int errorCount0 = parserTask.getErrorCount();;
135
+
123
136
  try {
124
- stream.writeAndWaitForRead(data);
125
- } catch (ClosedChannelException e) {
126
- // ignore
137
+ if (isLast.isTrue()) {
138
+ IRubyObject document = invoke(context, this, "document");
139
+ invoke(context, document, "end_document");
140
+ terminateTask();
141
+ } else {
142
+ ostream.write(data);
143
+ Thread.currentThread().sleep(10); // gives a reader a chance to work
144
+ }
127
145
  } catch (IOException e) {
128
- throw context.getRuntime().newRuntimeError(e.toString());
146
+ throw context.getRuntime().newRuntimeError(e.getMessage());
147
+ } catch (InterruptedException e) {
148
+ throw context.getRuntime().newRuntimeError(e.getMessage());
129
149
  }
130
150
 
131
- if (isLast.isTrue()) {
151
+ if (!options.recover && parserTask.getErrorCount() > errorCount0) {
132
152
  try {
133
- stream.close();
153
+ terminateTask();
134
154
  } catch (IOException e) {
135
- // ignore
136
- }
137
-
138
- for (;;) {
139
- try {
140
- reader.join();
141
- break;
142
- } catch (InterruptedException e) {
143
- // continue loop
144
- }
155
+ throw context.getRuntime().newRuntimeError(e.getMessage());
145
156
  }
146
- }
147
-
148
- if (!options.recover && runner.getErrorCount() > errorCount0) {
149
- throw new RaiseException(runner.getLastError(), true);
157
+ throw new RaiseException(parserTask.getLastError(), true);
150
158
  }
151
159
 
152
160
  return this;
153
161
  }
154
-
155
- protected static class Runner implements Runnable {
156
- protected ThreadContext context;
157
- protected IRubyObject handler;
158
- protected XmlSaxParserContext parser;
159
-
160
- public Runner(ThreadContext context,
161
- IRubyObject handler,
162
- InputStream stream) {
162
+
163
+ private void initialize_task(ThreadContext context) throws IOException {
164
+ if (futureTask == null || ostream == null || istream == null) {
165
+ Pipe pipe = Pipe.open();
166
+ Pipe.SinkChannel sink = pipe.sink();
167
+ ostream = Channels.newOutputStream(sink);
168
+ Pipe.SourceChannel source = pipe.source();
169
+ istream = Channels.newInputStream(source);
170
+
171
+ parserTask = new ParserTask(context, saxParser);
172
+ futureTask = new FutureTask<XmlSaxParserContext>(parserTask);
173
+ executor = Executors.newSingleThreadExecutor();
174
+ executor.submit(futureTask);
175
+ }
176
+ }
177
+
178
+ private synchronized void terminateTask() throws IOException {
179
+ futureTask.cancel(true);
180
+ executor.shutdown();
181
+ ostream.close();
182
+ istream.close();
183
+ ostream = null;
184
+ istream = null;
185
+ }
186
+
187
+ private class ParserTask implements Callable<XmlSaxParserContext> {
188
+ private ThreadContext context;
189
+ private IRubyObject handler;
190
+ private XmlSaxParserContext parser;
191
+
192
+ private ParserTask(ThreadContext context, IRubyObject handler) {
163
193
  RubyClass klazz = getNokogiriClass(context.getRuntime(), "Nokogiri::XML::SAX::ParserContext");
164
-
165
194
  this.context = context;
166
195
  this.handler = handler;
167
- this.parser = (XmlSaxParserContext) XmlSaxParserContext.parse_stream(context, klazz, stream);
196
+ this.parser = (XmlSaxParserContext) XmlSaxParserContext.parse_stream(context, klazz, istream);
168
197
  }
169
198
 
170
- public void run() {
199
+ @Override
200
+ public XmlSaxParserContext call() throws Exception {
171
201
  parser.parse_with(context, handler);
202
+ return parser;
172
203
  }
173
-
174
- public int getErrorCount() {
175
- // check for null because thread may nto have started yet
204
+
205
+ private synchronized int getErrorCount() {
206
+ // check for null because thread may not have started yet
176
207
  if (parser.getNokogiriHandler() == null) return 0;
177
208
  else return parser.getNokogiriHandler().getErrorCount();
178
209
  }
179
-
180
- public RubyException getLastError() {
210
+
211
+ private synchronized RubyException getLastError() {
181
212
  return (RubyException) parser.getNokogiriHandler().getLastError();
182
213
  }
183
214
  }