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.
- data/CHANGELOG.ja.rdoc +9 -0
- data/CHANGELOG.rdoc +11 -2
- data/Manifest.txt +0 -1
- data/ROADMAP.md +11 -0
- data/STANDARD_RESPONSES.md +2 -2
- data/ext/java/nokogiri/XmlAttr.java +11 -1
- data/ext/java/nokogiri/XmlNamespace.java +25 -0
- data/ext/java/nokogiri/XmlNode.java +5 -1
- data/ext/java/nokogiri/XmlReader.java +19 -4
- data/ext/java/nokogiri/XmlSaxPushParser.java +88 -57
- data/ext/java/nokogiri/internals/NokogiriHelpers.java +1 -0
- data/ext/nokogiri/xml_io.c +1 -1
- data/ext/nokogiri/xml_namespace.c +0 -6
- data/lib/nokogiri/css/node.rb +3 -0
- data/lib/nokogiri/css/parser.rb +229 -209
- data/lib/nokogiri/css/parser.y +7 -1
- data/lib/nokogiri/css/xpath_visitor.rb +2 -2
- data/lib/nokogiri/html/document.rb +2 -1
- data/lib/nokogiri/html/element_description_defaults.rb +1 -1
- data/lib/nokogiri/nokogiri.jar +0 -0
- data/lib/nokogiri/version.rb +1 -1
- data/lib/nokogiri/xml/document.rb +1 -1
- data/lib/nokogiri/xml/parse_options.rb +2 -2
- data/test/css/test_parser.rb +27 -0
- data/test/html/test_document.rb +18 -2
- data/test/test_reader.rb +63 -0
- data/test/xml/sax/test_push_parser.rb +1 -0
- data/test/xml/test_node.rb +21 -0
- metadata +2 -3
- data/ext/java/nokogiri/internals/PushInputStream.java +0 -411
data/lib/nokogiri/css/parser.y
CHANGED
@@ -9,12 +9,13 @@ rule
|
|
9
9
|
: selector COMMA simple_selector_1toN {
|
10
10
|
result = [val.first, val.last].flatten
|
11
11
|
}
|
12
|
+
| prefixless_combinator_selector { result = val.flatten }
|
12
13
|
| simple_selector_1toN { result = val.flatten }
|
13
14
|
;
|
14
15
|
combinator
|
15
16
|
: PLUS { result = :DIRECT_ADJACENT_SELECTOR }
|
16
17
|
| GREATER { result = :CHILD_SELECTOR }
|
17
|
-
| TILDE { result = :
|
18
|
+
| TILDE { result = :FOLLOWING_SELECTOR }
|
18
19
|
| S { result = :DESCENDANT_SELECTOR }
|
19
20
|
| DOUBLESLASH { result = :DESCENDANT_SELECTOR }
|
20
21
|
| SLASH { result = :CHILD_SELECTOR }
|
@@ -59,6 +60,11 @@ rule
|
|
59
60
|
)
|
60
61
|
}
|
61
62
|
;
|
63
|
+
prefixless_combinator_selector
|
64
|
+
: combinator simple_selector_1toN {
|
65
|
+
result = Node.new(val.first, [nil, val.last])
|
66
|
+
}
|
67
|
+
;
|
62
68
|
simple_selector_1toN
|
63
69
|
: simple_selector combinator simple_selector_1toN {
|
64
70
|
result = Node.new(val[1], [val.first, val.last])
|
@@ -126,13 +126,13 @@ module Nokogiri
|
|
126
126
|
{
|
127
127
|
'combinator' => ' and ',
|
128
128
|
'direct_adjacent_selector' => "/following-sibling::*[1]/self::",
|
129
|
-
'
|
129
|
+
'following_selector' => "/following-sibling::",
|
130
130
|
'descendant_selector' => '//',
|
131
131
|
'child_selector' => '/',
|
132
132
|
}.each do |k,v|
|
133
133
|
class_eval %{
|
134
134
|
def visit_#{k} node
|
135
|
-
"\#{node.value.first.accept(self)}#{v}\#{node.value.last.accept(self)}"
|
135
|
+
"\#{node.value.first.accept(self) if node.value.first}#{v}\#{node.value.last.accept(self)}"
|
136
136
|
end
|
137
137
|
}
|
138
138
|
end
|
@@ -3,7 +3,7 @@ module Nokogiri
|
|
3
3
|
class ElementDescription
|
4
4
|
|
5
5
|
# Methods are defined protected by method_defined? because at
|
6
|
-
# this point the C-library or Java library is
|
6
|
+
# this point the C-library or Java library is already loaded,
|
7
7
|
# and we don't want to clobber any methods that have been
|
8
8
|
# defined there.
|
9
9
|
|
data/lib/nokogiri/nokogiri.jar
CHANGED
Binary file
|
data/lib/nokogiri/version.rb
CHANGED
@@ -132,7 +132,7 @@ module Nokogiri
|
|
132
132
|
# in the hash.
|
133
133
|
#
|
134
134
|
# Note this is a very expensive operation in current implementation, as it
|
135
|
-
# traverses the entire graph, and also has to bring each node
|
135
|
+
# traverses the entire graph, and also has to bring each node across the
|
136
136
|
# libxml bridge into a ruby object.
|
137
137
|
def collect_namespaces
|
138
138
|
ns = {}
|
@@ -25,11 +25,11 @@ module Nokogiri
|
|
25
25
|
NOBLANKS = 1 << 8
|
26
26
|
# use the SAX1 interface internally
|
27
27
|
SAX1 = 1 << 9
|
28
|
-
# Implement XInclude
|
28
|
+
# Implement XInclude substitution
|
29
29
|
XINCLUDE = 1 << 10
|
30
30
|
# Forbid network access
|
31
31
|
NONET = 1 << 11
|
32
|
-
# Do not reuse the context
|
32
|
+
# Do not reuse the context dictionary
|
33
33
|
NODICT = 1 << 12
|
34
34
|
# remove redundant namespaces declarations
|
35
35
|
NSCLEAN = 1 << 13
|
data/test/css/test_parser.rb
CHANGED
@@ -218,6 +218,33 @@ module Nokogiri
|
|
218
218
|
@parser.parse("E + F G")
|
219
219
|
end
|
220
220
|
|
221
|
+
def test_child_selector
|
222
|
+
assert_xpath("//a//b/i", @parser.parse('a b>i'))
|
223
|
+
assert_xpath("//a//b/i", @parser.parse('a b > i'))
|
224
|
+
assert_xpath("//a/b/i", @parser.parse('a > b > i'))
|
225
|
+
end
|
226
|
+
|
227
|
+
def test_prefixless_child_selector
|
228
|
+
assert_xpath("./a", @parser.parse('>a'))
|
229
|
+
assert_xpath("./a", @parser.parse('> a'))
|
230
|
+
assert_xpath("./a//b/i", @parser.parse('>a b>i'))
|
231
|
+
assert_xpath("./a/b/i", @parser.parse('> a > b > i'))
|
232
|
+
end
|
233
|
+
|
234
|
+
def test_prefixless_preceding_sibling_selector
|
235
|
+
assert_xpath("./following-sibling::a", @parser.parse('~a'))
|
236
|
+
assert_xpath("./following-sibling::a", @parser.parse('~ a'))
|
237
|
+
assert_xpath("./following-sibling::a//b/following-sibling::i", @parser.parse('~a b~i'))
|
238
|
+
assert_xpath("./following-sibling::a//b/following-sibling::i", @parser.parse('~ a b ~ i'))
|
239
|
+
end
|
240
|
+
|
241
|
+
def test_prefixless_direct_adjacent_selector
|
242
|
+
assert_xpath("./following-sibling::*[1]/self::a", @parser.parse('+a'))
|
243
|
+
assert_xpath("./following-sibling::*[1]/self::a", @parser.parse('+ a'))
|
244
|
+
assert_xpath("./following-sibling::*[1]/self::a/following-sibling::*[1]/self::b", @parser.parse('+a+b'))
|
245
|
+
assert_xpath("./following-sibling::*[1]/self::a/following-sibling::*[1]/self::b", @parser.parse('+ a + b'))
|
246
|
+
end
|
247
|
+
|
221
248
|
def test_attribute
|
222
249
|
assert_xpath "//h1[@a = 'Tender Lovemaking']",
|
223
250
|
@parser.parse("h1[a='Tender Lovemaking']")
|
data/test/html/test_document.rb
CHANGED
@@ -125,8 +125,10 @@ module Nokogiri
|
|
125
125
|
|
126
126
|
def test_meta_encoding
|
127
127
|
assert_equal 'UTF-8', @html.meta_encoding
|
128
|
+
end
|
128
129
|
|
129
|
-
|
130
|
+
def test_meta_encoding_is_strict_about_http_equiv
|
131
|
+
doc = Nokogiri::HTML(<<-eohtml)
|
130
132
|
<html>
|
131
133
|
<head>
|
132
134
|
<meta http-equiv="X-Content-Type" content="text/html; charset=Shift_JIS">
|
@@ -136,7 +138,21 @@ module Nokogiri
|
|
136
138
|
</body>
|
137
139
|
</html>
|
138
140
|
eohtml
|
139
|
-
assert_nil
|
141
|
+
assert_nil doc.meta_encoding
|
142
|
+
end
|
143
|
+
|
144
|
+
def test_meta_encoding_handles_malformed_content_charset
|
145
|
+
doc = Nokogiri::HTML(<<EOHTML)
|
146
|
+
<html>
|
147
|
+
<head>
|
148
|
+
<meta http-equiv="Content-type" content="text/html; utf-8" />
|
149
|
+
</head>
|
150
|
+
<body>
|
151
|
+
foo
|
152
|
+
</body>
|
153
|
+
</html>
|
154
|
+
EOHTML
|
155
|
+
assert_nil doc.meta_encoding
|
140
156
|
end
|
141
157
|
|
142
158
|
def test_meta_encoding=
|
data/test/test_reader.rb
CHANGED
@@ -422,4 +422,67 @@ class TestReader < Nokogiri::TestCase
|
|
422
422
|
end
|
423
423
|
end
|
424
424
|
|
425
|
+
def test_correct_outer_xml_inclusion
|
426
|
+
xml = Nokogiri::XML::Reader.from_io(StringIO.new(<<-eoxml))
|
427
|
+
<root-element>
|
428
|
+
<children>
|
429
|
+
<child n="1">
|
430
|
+
<field>child-1</field>
|
431
|
+
</child>
|
432
|
+
<child n="2">
|
433
|
+
<field>child-2</field>
|
434
|
+
</child>
|
435
|
+
<child n="3">
|
436
|
+
<field>child-3</field>
|
437
|
+
</child>
|
438
|
+
</children>
|
439
|
+
</root-element>
|
440
|
+
eoxml
|
441
|
+
|
442
|
+
nodelengths = []
|
443
|
+
has_child2 = []
|
444
|
+
|
445
|
+
xml.each do |node|
|
446
|
+
if node.node_type == Nokogiri::XML::Reader::TYPE_ELEMENT and node.name == "child"
|
447
|
+
nodelengths << node.outer_xml.length
|
448
|
+
has_child2 << !!(node.outer_xml =~ /child-2/)
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
assert_equal(nodelengths[0], nodelengths[1])
|
453
|
+
assert(has_child2[1])
|
454
|
+
assert(!has_child2[0])
|
455
|
+
end
|
456
|
+
|
457
|
+
def test_correct_inner_xml_inclusion
|
458
|
+
xml = Nokogiri::XML::Reader.from_io(StringIO.new(<<-eoxml))
|
459
|
+
<root-element>
|
460
|
+
<children>
|
461
|
+
<child n="1">
|
462
|
+
<field>child-1</field>
|
463
|
+
</child>
|
464
|
+
<child n="2">
|
465
|
+
<field>child-2</field>
|
466
|
+
</child>
|
467
|
+
<child n="3">
|
468
|
+
<field>child-3</field>
|
469
|
+
</child>
|
470
|
+
</children>
|
471
|
+
</root-element>
|
472
|
+
eoxml
|
473
|
+
|
474
|
+
nodelengths = []
|
475
|
+
has_child2 = []
|
476
|
+
|
477
|
+
xml.each do |node|
|
478
|
+
if node.node_type == Nokogiri::XML::Reader::TYPE_ELEMENT and node.name == "child"
|
479
|
+
nodelengths << node.inner_xml.length
|
480
|
+
has_child2 << !!(node.inner_xml =~ /child-2/)
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
assert_equal(nodelengths[0], nodelengths[1])
|
485
|
+
assert(has_child2[1])
|
486
|
+
assert(!has_child2[0])
|
487
|
+
end
|
425
488
|
end
|
@@ -142,6 +142,7 @@ module Nokogiri
|
|
142
142
|
end
|
143
143
|
|
144
144
|
def test_broken_encoding
|
145
|
+
skip("ultra hard to fix for pure Java version") if Nokogiri.jruby?
|
145
146
|
@parser.options |= XML::ParseOptions::RECOVER
|
146
147
|
# This is ISO_8859-1:
|
147
148
|
@parser.<< "<?xml version='1.0' encoding='UTF-8'?><r>Gau\337</r>"
|
data/test/xml/test_node.rb
CHANGED
@@ -993,6 +993,27 @@ EOXML
|
|
993
993
|
assert_no_match("<ul>\n <li>", xml.to_xml(:save_with => XML::Node::SaveOptions::AS_XML))
|
994
994
|
assert_no_match("<ul>\n <li>", node.to_xml(:save_with => XML::Node::SaveOptions::AS_XML))
|
995
995
|
end
|
996
|
+
|
997
|
+
# issue 647
|
998
|
+
def test_default_namespace_should_be_created
|
999
|
+
subject = Nokogiri::XML.parse('<foo xml:bar="http://bar.com"/>').root
|
1000
|
+
ns = subject.attributes['bar'].namespace
|
1001
|
+
assert_not_nil ns
|
1002
|
+
assert_equal ns.class, Nokogiri::XML::Namespace
|
1003
|
+
assert_equal 'xml', ns.prefix
|
1004
|
+
assert_equal "http://www.w3.org/XML/1998/namespace", ns.href
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
# issue 648
|
1008
|
+
def test_namespace_without_prefix_should_be_set
|
1009
|
+
node = Nokogiri::XML.parse('<foo xmlns="http://bar.com"/>').root
|
1010
|
+
subject = Nokogiri::XML::Node.new 'foo', node.document
|
1011
|
+
subject.namespace = node.namespace
|
1012
|
+
ns = subject.namespace
|
1013
|
+
assert_equal ns.class, Nokogiri::XML::Namespace
|
1014
|
+
assert_nil ns.prefix
|
1015
|
+
assert_equal ns.href, "http://bar.com"
|
1016
|
+
end
|
996
1017
|
end
|
997
1018
|
end
|
998
1019
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: nokogiri
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease: 6
|
5
|
-
version: 1.5.3.
|
5
|
+
version: 1.5.3.rc4
|
6
6
|
platform: java
|
7
7
|
authors:
|
8
8
|
- Aaron Patterson
|
@@ -12,7 +12,7 @@ autorequire:
|
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
14
|
|
15
|
-
date: 2012-
|
15
|
+
date: 2012-04-27 00:00:00 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: hoe-bundler
|
@@ -249,7 +249,6 @@ files:
|
|
249
249
|
- ext/java/nokogiri/internals/NokogiriXPathVariableResolver.java
|
250
250
|
- ext/java/nokogiri/internals/NokogiriXsltErrorListener.java
|
251
251
|
- ext/java/nokogiri/internals/ParserContext.java
|
252
|
-
- ext/java/nokogiri/internals/PushInputStream.java
|
253
252
|
- ext/java/nokogiri/internals/ReaderNode.java
|
254
253
|
- ext/java/nokogiri/internals/SaveContextVisitor.java
|
255
254
|
- ext/java/nokogiri/internals/SchemaErrorHandler.java
|
@@ -1,411 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* (The MIT License)
|
3
|
-
*
|
4
|
-
* Copyright (c) 2008 - 2011:
|
5
|
-
*
|
6
|
-
* * {Aaron Patterson}[http://tenderlovemaking.com]
|
7
|
-
* * {Mike Dalessio}[http://mike.daless.io]
|
8
|
-
* * {Charles Nutter}[http://blog.headius.com/]
|
9
|
-
* * {Sergio Arbeo}[http://www.serabe.com/]
|
10
|
-
* * {Patrick Mahoney}[http://polycrystal.org/]
|
11
|
-
* * {Yoko Harada}[http://yokolet.blogspot.com/]
|
12
|
-
*
|
13
|
-
* Permission is hereby granted, free of charge, to any person obtaining
|
14
|
-
* a copy of this software and associated documentation files (the
|
15
|
-
* 'Software'), to deal in the Software without restriction, including
|
16
|
-
* without limitation the rights to use, copy, modify, merge, publish,
|
17
|
-
* distribute, sublicense, and/or sell copies of the Software, and to
|
18
|
-
* permit persons to whom the Software is furnished to do so, subject to
|
19
|
-
* the following conditions:
|
20
|
-
*
|
21
|
-
* The above copyright notice and this permission notice shall be
|
22
|
-
* included in all copies or substantial portions of the Software.
|
23
|
-
*
|
24
|
-
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
25
|
-
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
26
|
-
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
27
|
-
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
28
|
-
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
29
|
-
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
30
|
-
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
31
|
-
*/
|
32
|
-
|
33
|
-
package nokogiri.internals;
|
34
|
-
|
35
|
-
import java.io.IOException;
|
36
|
-
import java.io.InputStream;
|
37
|
-
import java.nio.channels.ClosedChannelException;
|
38
|
-
import java.util.ArrayList;
|
39
|
-
|
40
|
-
|
41
|
-
/**
|
42
|
-
* Implements a "push" InputStream. An owner thread create an
|
43
|
-
* InputStream and passes it to a second thread. The owner thread
|
44
|
-
* calls PushInputStream.write() to write data to the stream. The
|
45
|
-
* second thread calls PushInputStream.read() and other InputStream
|
46
|
-
* methods.
|
47
|
-
*
|
48
|
-
* You should ensure that only one thread write to, and only one
|
49
|
-
* thread reads to, this stream, though nothing enforces this
|
50
|
-
* strictly.
|
51
|
-
*/
|
52
|
-
public class PushInputStream extends InputStream {
|
53
|
-
/**
|
54
|
-
* Current position in the stream relative to the start of the
|
55
|
-
* buffer.
|
56
|
-
*/
|
57
|
-
protected int pos;
|
58
|
-
|
59
|
-
/**
|
60
|
-
* Current mark position, or -1 if there is no mark.
|
61
|
-
*/
|
62
|
-
protected int mark;
|
63
|
-
|
64
|
-
protected int readlimit;
|
65
|
-
|
66
|
-
/**
|
67
|
-
* State is open or closed.
|
68
|
-
*/
|
69
|
-
protected boolean isOpen;
|
70
|
-
|
71
|
-
protected Buffer buffer;
|
72
|
-
|
73
|
-
public PushInputStream() {
|
74
|
-
pos = 0;
|
75
|
-
mark = -1;
|
76
|
-
readlimit = -1;
|
77
|
-
isOpen = true;
|
78
|
-
|
79
|
-
buffer = new Buffer(512);
|
80
|
-
}
|
81
|
-
|
82
|
-
protected synchronized void ensureOpen() throws IOException {
|
83
|
-
if (!isOpen) {
|
84
|
-
throw new ClosedChannelException();
|
85
|
-
}
|
86
|
-
}
|
87
|
-
|
88
|
-
/**
|
89
|
-
* Write data that can be read from the stream.
|
90
|
-
*/
|
91
|
-
public synchronized void write(byte[] b) {
|
92
|
-
if (buffer == null) System.out.println("BUFFER IS NULL");
|
93
|
-
if (b == null) System.out.println("BYTE ARRAY IS NILL");
|
94
|
-
buffer.put(b);
|
95
|
-
notifyAll(); // notify readers waiting
|
96
|
-
}
|
97
|
-
|
98
|
-
/**
|
99
|
-
* Write data and then wait until all the data has been read
|
100
|
-
* (waits until the thread reading from this stream is blocked in
|
101
|
-
* a read()).
|
102
|
-
*/
|
103
|
-
public synchronized void writeAndWaitForRead(byte[] b) throws IOException {
|
104
|
-
ensureOpen();
|
105
|
-
write(b);
|
106
|
-
for (;;) {
|
107
|
-
try {
|
108
|
-
wait();
|
109
|
-
break;
|
110
|
-
} catch (InterruptedException e) {
|
111
|
-
// continue waiting
|
112
|
-
}
|
113
|
-
}
|
114
|
-
}
|
115
|
-
|
116
|
-
/*
|
117
|
-
*------------------------------------------------------------
|
118
|
-
* InputStream methods
|
119
|
-
*------------------------------------------------------------
|
120
|
-
*/
|
121
|
-
|
122
|
-
/**
|
123
|
-
* @see InputStream.available()
|
124
|
-
*/
|
125
|
-
@Override
|
126
|
-
public synchronized int available() throws IOException {
|
127
|
-
ensureOpen();
|
128
|
-
return buffer.size() - pos;
|
129
|
-
}
|
130
|
-
|
131
|
-
int nClose = 0;
|
132
|
-
/**
|
133
|
-
* @see InputStream.close()
|
134
|
-
*/
|
135
|
-
@Override
|
136
|
-
public synchronized void close() throws IOException {
|
137
|
-
if (!isOpen) return;
|
138
|
-
isOpen = false;
|
139
|
-
buffer = null;
|
140
|
-
notifyAll();
|
141
|
-
}
|
142
|
-
|
143
|
-
/**
|
144
|
-
* @see InputStream.mark()
|
145
|
-
*/
|
146
|
-
@Override
|
147
|
-
public synchronized void mark(int readlimit) {
|
148
|
-
this.mark = pos;
|
149
|
-
this.readlimit = readlimit;
|
150
|
-
}
|
151
|
-
|
152
|
-
/**
|
153
|
-
* Mark the current position in this stream. Supported by
|
154
|
-
* PushInputStream.
|
155
|
-
*
|
156
|
-
* @see InputStream.markSupported()
|
157
|
-
*/
|
158
|
-
@Override
|
159
|
-
public synchronized boolean markSupported() {
|
160
|
-
return true;
|
161
|
-
}
|
162
|
-
|
163
|
-
/**
|
164
|
-
* @see InputStream.read()
|
165
|
-
*/
|
166
|
-
@Override
|
167
|
-
public synchronized int read() throws IOException {
|
168
|
-
ensureOpen();
|
169
|
-
byte[] b = new byte[1];
|
170
|
-
read(b, 0, 1);
|
171
|
-
return (int) b[0];
|
172
|
-
}
|
173
|
-
|
174
|
-
/**
|
175
|
-
* @see InputStream.read(byte[])
|
176
|
-
*/
|
177
|
-
@Override
|
178
|
-
public synchronized int read(byte[] b) throws IOException {
|
179
|
-
ensureOpen();
|
180
|
-
return read(b, 0, b.length);
|
181
|
-
}
|
182
|
-
|
183
|
-
protected synchronized boolean markIsValid() {
|
184
|
-
return (mark >= 0 && pos < mark+readlimit);
|
185
|
-
}
|
186
|
-
|
187
|
-
/**
|
188
|
-
* @see InputStream.read(byte[], int, int)
|
189
|
-
*/
|
190
|
-
@Override
|
191
|
-
public synchronized int read(byte[] b, int off, int len) throws IOException {
|
192
|
-
while (isOpen && available() == 0) {
|
193
|
-
/* block until data available */
|
194
|
-
try {
|
195
|
-
notifyAll(); // notify writers waiting
|
196
|
-
wait();
|
197
|
-
} catch (InterruptedException e) {
|
198
|
-
// continue waiting
|
199
|
-
}
|
200
|
-
}
|
201
|
-
|
202
|
-
if (!isOpen) {
|
203
|
-
return -1;
|
204
|
-
}
|
205
|
-
|
206
|
-
int readLen = Math.min(available(), len);
|
207
|
-
|
208
|
-
buffer.get(pos, readLen, b, off);
|
209
|
-
pos += readLen;
|
210
|
-
|
211
|
-
int reduce;
|
212
|
-
|
213
|
-
if (markIsValid()) {
|
214
|
-
reduce = mark;
|
215
|
-
} else {
|
216
|
-
reduce = pos;
|
217
|
-
}
|
218
|
-
|
219
|
-
buffer.truncateFromStart(buffer.size - reduce);
|
220
|
-
pos -= reduce;
|
221
|
-
mark -= reduce;
|
222
|
-
if (mark < 0) mark = -1; // don't wrap mark around?
|
223
|
-
|
224
|
-
return readLen;
|
225
|
-
}
|
226
|
-
|
227
|
-
/**
|
228
|
-
* @see InputStream.reset()
|
229
|
-
*/
|
230
|
-
@Override
|
231
|
-
public synchronized void reset() throws IOException {
|
232
|
-
ensureOpen();
|
233
|
-
if (markIsValid())
|
234
|
-
pos = mark;
|
235
|
-
}
|
236
|
-
|
237
|
-
/**
|
238
|
-
* @see InputStream.skip()
|
239
|
-
*/
|
240
|
-
@Override
|
241
|
-
public synchronized long skip(long n) throws IOException {
|
242
|
-
ensureOpen();
|
243
|
-
pos += n;
|
244
|
-
return n;
|
245
|
-
}
|
246
|
-
|
247
|
-
/*
|
248
|
-
*------------------------------------------------------------
|
249
|
-
* Data Buffer
|
250
|
-
*------------------------------------------------------------
|
251
|
-
*/
|
252
|
-
|
253
|
-
public static class Block {
|
254
|
-
protected byte[] data;
|
255
|
-
|
256
|
-
public Block(int size) {
|
257
|
-
data = new byte[size];
|
258
|
-
}
|
259
|
-
|
260
|
-
public void copyIn(byte[] src, int srcPos, int destPos, int length) {
|
261
|
-
System.arraycopy(src, srcPos, data, destPos, length);
|
262
|
-
}
|
263
|
-
|
264
|
-
public void copyOut(int srcPos, byte[] dest, int destPos, int length) {
|
265
|
-
System.arraycopy(data, srcPos, dest, destPos, length);
|
266
|
-
}
|
267
|
-
}
|
268
|
-
|
269
|
-
public static class BlockList extends ArrayList<Block> {
|
270
|
-
public BlockList() {
|
271
|
-
super();
|
272
|
-
}
|
273
|
-
|
274
|
-
@Override
|
275
|
-
public void removeRange(int fromIndex, int toIndex) {
|
276
|
-
super.removeRange(fromIndex, toIndex);
|
277
|
-
}
|
278
|
-
}
|
279
|
-
|
280
|
-
public static class Buffer {
|
281
|
-
protected int blockSize;
|
282
|
-
protected BlockList blocks;
|
283
|
-
|
284
|
-
/**
|
285
|
-
* Offset (position) to the first logical byte in the buffer.
|
286
|
-
*/
|
287
|
-
protected int offset;
|
288
|
-
|
289
|
-
/**
|
290
|
-
* Logical size of the buffer.
|
291
|
-
*/
|
292
|
-
protected int size;
|
293
|
-
|
294
|
-
public Buffer(int blockSize) {
|
295
|
-
this.blockSize = blockSize;
|
296
|
-
this.blocks = new BlockList();
|
297
|
-
this.offset = 0;
|
298
|
-
this.size = 0;
|
299
|
-
}
|
300
|
-
|
301
|
-
public int size() {
|
302
|
-
return size;
|
303
|
-
}
|
304
|
-
|
305
|
-
protected class Segment {
|
306
|
-
/**
|
307
|
-
* Block index.
|
308
|
-
*/
|
309
|
-
protected int block;
|
310
|
-
|
311
|
-
/**
|
312
|
-
* Offset into the block.
|
313
|
-
*/
|
314
|
-
protected int off;
|
315
|
-
|
316
|
-
/**
|
317
|
-
* Length of segment.
|
318
|
-
*/
|
319
|
-
protected int len;
|
320
|
-
|
321
|
-
/**
|
322
|
-
* Calculate the block number and block offset given a position.
|
323
|
-
*/
|
324
|
-
protected Segment(int pos) {
|
325
|
-
int absPos = offset + pos;
|
326
|
-
block = (int) (absPos / blockSize);
|
327
|
-
off = (int) (absPos % blockSize);
|
328
|
-
len = -1;
|
329
|
-
}
|
330
|
-
}
|
331
|
-
|
332
|
-
protected Segment[] accessList(int pos, int size) {
|
333
|
-
Segment start = new Segment(pos);
|
334
|
-
Segment end = new Segment(pos + size);
|
335
|
-
int nBlocks = end.block - start.block + 1;
|
336
|
-
Segment[] segs = new Segment[nBlocks];
|
337
|
-
|
338
|
-
start.len = Math.min(size, blockSize - start.off);
|
339
|
-
segs[0] = start;
|
340
|
-
int currPos = pos + start.len;
|
341
|
-
int currSize = start.len;
|
342
|
-
for (int i = 1; i < nBlocks; i++) {
|
343
|
-
Segment seg = new Segment(currPos);
|
344
|
-
seg.len = Math.min(blockSize, size - currSize);
|
345
|
-
segs[i] = seg;
|
346
|
-
currPos += seg.len;
|
347
|
-
currSize += seg.len;
|
348
|
-
}
|
349
|
-
|
350
|
-
return segs;
|
351
|
-
}
|
352
|
-
|
353
|
-
protected void ensureCapacity(int pos) {
|
354
|
-
Segment seg = new Segment(pos-1);
|
355
|
-
|
356
|
-
while (blocks.size() < (seg.block + 1))
|
357
|
-
blocks.add(new Block(blockSize));
|
358
|
-
}
|
359
|
-
|
360
|
-
public void put(byte b) {
|
361
|
-
byte[] buf = new byte[1];
|
362
|
-
buf[0] = b;
|
363
|
-
put(buf);
|
364
|
-
}
|
365
|
-
|
366
|
-
public void put(byte[] b) {
|
367
|
-
ensureCapacity(size + b.length);
|
368
|
-
Segment[] segs = accessList(size, b.length);
|
369
|
-
|
370
|
-
int off = 0;
|
371
|
-
for (int i = 0; i < segs.length; i++) {
|
372
|
-
Block block = blocks.get(segs[i].block);
|
373
|
-
block.copyIn(b, off, segs[i].off, segs[i].len);
|
374
|
-
}
|
375
|
-
|
376
|
-
size += b.length;
|
377
|
-
}
|
378
|
-
|
379
|
-
public byte[] get(int pos, int len) {
|
380
|
-
byte[] b = new byte[len];
|
381
|
-
get(pos, len, b, 0);
|
382
|
-
return b;
|
383
|
-
}
|
384
|
-
|
385
|
-
/**
|
386
|
-
* Throws IndexOutOfBoundsException.
|
387
|
-
*/
|
388
|
-
public void get(int pos, int len, byte[] b, int off) {
|
389
|
-
Segment[] segs = accessList(pos, len);
|
390
|
-
for (int i = 0; i < segs.length; i++) {
|
391
|
-
Block block = blocks.get(segs[i].block);
|
392
|
-
block.copyOut(segs[i].off, b, off, segs[i].len);
|
393
|
-
}
|
394
|
-
}
|
395
|
-
|
396
|
-
/**
|
397
|
-
* Truncate the buffer to <code>newSize</code> by removing
|
398
|
-
* data from the start of the buffer.
|
399
|
-
*/
|
400
|
-
public void truncateFromStart(int newSize) {
|
401
|
-
if (newSize > size || newSize < 0)
|
402
|
-
throw new RuntimeException("invalid size");
|
403
|
-
|
404
|
-
Segment newStart = new Segment(size - newSize);
|
405
|
-
blocks.removeRange(0, newStart.block);
|
406
|
-
|
407
|
-
size = newSize;
|
408
|
-
offset = newStart.off;
|
409
|
-
}
|
410
|
-
}
|
411
|
-
}
|