Skip to content

Commit ae75cfa

Browse files
committed
Merge pull request #166 from scijava/robust-io
A robust general framework for I/O
2 parents fa24b15 + 98e490b commit ae75cfa

37 files changed

+3508
-48
lines changed

src/main/java/org/scijava/Typed.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,8 @@ public interface Typed<T> {
4242
/**
4343
* Gets whether this object is compatible with the given data object.
4444
* <p>
45-
* Typically, this will be the case when {@code data.getClass()} is assignable
46-
* to the type associated with the object (i.e., the one returned by
47-
* {@link #getType()}). But individual implementations may have other
45+
* By default, this method will return {@code true} always, since the type is
46+
* known to be compatible. But individual implementations may have other
4847
* requirements beyond class assignability.
4948
* </p>
5049
*/
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
/*
2+
* #%L
3+
* SciJava Common shared library for SciJava software.
4+
* %%
5+
* Copyright (C) 2009 - 2015 Board of Regents of the University of
6+
* Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
7+
* Institute of Molecular Cell Biology and Genetics.
8+
* %%
9+
* Redistribution and use in source and binary forms, with or without
10+
* modification, are permitted provided that the following conditions are met:
11+
*
12+
* 1. Redistributions of source code must retain the above copyright notice,
13+
* this list of conditions and the following disclaimer.
14+
* 2. Redistributions in binary form must reproduce the above copyright notice,
15+
* this list of conditions and the following disclaimer in the documentation
16+
* and/or other materials provided with the distribution.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28+
* POSSIBILITY OF SUCH DAMAGE.
29+
* #L%
30+
*/
31+
32+
package org.scijava.io;
33+
34+
import java.io.IOException;
35+
import java.io.InputStreamReader;
36+
import java.nio.ByteBuffer;
37+
import java.nio.ByteOrder;
38+
39+
import org.scijava.plugin.AbstractWrapperPlugin;
40+
41+
/**
42+
* Abstract base class for {@link DataHandle} plugins.
43+
*
44+
* @author Curtis Rueden
45+
*/
46+
public abstract class AbstractDataHandle<L extends Location> extends
47+
AbstractWrapperPlugin<L> implements DataHandle<L>
48+
{
49+
50+
// -- Constants --
51+
52+
/** Block size to use when searching through the stream. */
53+
private static final int DEFAULT_BLOCK_SIZE = 256 * 1024; // 256 KB
54+
55+
/** Maximum number of bytes to search when searching through the stream. */
56+
private static final int MAX_SEARCH_SIZE = 512 * 1024 * 1024; // 512 MB
57+
58+
// -- Fields --
59+
60+
private ByteOrder order = ByteOrder.BIG_ENDIAN;
61+
private String encoding = "UTF-8";
62+
63+
// -- DataHandle methods --
64+
65+
@Override
66+
public ByteOrder getOrder() {
67+
return order;
68+
}
69+
70+
@Override
71+
public boolean isLittleEndian() {
72+
return getOrder() == ByteOrder.LITTLE_ENDIAN;
73+
}
74+
75+
@Override
76+
public void setOrder(final ByteOrder order) {
77+
this.order = order;
78+
}
79+
80+
@Override
81+
public void setOrder(final boolean little) {
82+
setOrder(little ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);
83+
}
84+
85+
@Override
86+
public String getEncoding() {
87+
return encoding;
88+
}
89+
90+
@Override
91+
public void setEncoding(final String encoding) {
92+
this.encoding = encoding;
93+
}
94+
95+
@Override
96+
public int read(final ByteBuffer buf) throws IOException {
97+
return read(buf, buf.remaining());
98+
}
99+
100+
@Override
101+
public int read(final ByteBuffer buf, final int len)
102+
throws IOException
103+
{
104+
final int n;
105+
if (buf.hasArray()) {
106+
// read directly into the array
107+
n = read(buf.array(), buf.arrayOffset(), len);
108+
}
109+
else {
110+
// read into a temporary array, then copy
111+
final byte[] b = new byte[len];
112+
n = read(b);
113+
buf.put(b, 0, n);
114+
}
115+
return n;
116+
}
117+
118+
@Override
119+
public void write(final ByteBuffer buf) throws IOException {
120+
write(buf, buf.remaining());
121+
}
122+
123+
@Override
124+
public void write(final ByteBuffer buf, final int len)
125+
throws IOException
126+
{
127+
if (buf.hasArray()) {
128+
// write directly from the buffer's array
129+
write(buf.array(), buf.arrayOffset(), len);
130+
}
131+
else {
132+
// copy into a temporary array, then write
133+
final byte[] b = new byte[len];
134+
buf.get(b);
135+
write(b);
136+
}
137+
}
138+
139+
@Override
140+
public String readCString() throws IOException {
141+
final String line = findString("\0");
142+
return line.length() == 0 ? null : line;
143+
}
144+
145+
@Override
146+
public String readString(int n) throws IOException {
147+
final long avail = length() - offset();
148+
if (n > avail) n = (int) avail;
149+
final byte[] b = new byte[n];
150+
readFully(b);
151+
return new String(b, encoding);
152+
}
153+
154+
@Override
155+
public String readString(final String lastChars) throws IOException {
156+
if (lastChars.length() == 1) return findString(lastChars);
157+
final String[] terminators = new String[lastChars.length()];
158+
for (int i = 0; i < terminators.length; i++) {
159+
terminators[i] = lastChars.substring(i, i + 1);
160+
}
161+
return findString(terminators);
162+
}
163+
164+
@Override
165+
public String findString(final String... terminators) throws IOException {
166+
return findString(true, DEFAULT_BLOCK_SIZE, terminators);
167+
}
168+
169+
@Override
170+
public String findString(final boolean saveString,
171+
final String... terminators) throws IOException
172+
{
173+
return findString(saveString, DEFAULT_BLOCK_SIZE, terminators);
174+
}
175+
176+
@Override
177+
public String findString(final int blockSize, final String... terminators)
178+
throws IOException
179+
{
180+
return findString(true, blockSize, terminators);
181+
}
182+
183+
@Override
184+
public String findString(final boolean saveString, final int blockSize,
185+
final String... terminators) throws IOException
186+
{
187+
final StringBuilder out = new StringBuilder();
188+
final long startPos = offset();
189+
long bytesDropped = 0;
190+
final long inputLen = length();
191+
long maxLen = inputLen - startPos;
192+
final boolean tooLong = saveString && maxLen > MAX_SEARCH_SIZE;
193+
if (tooLong) maxLen = MAX_SEARCH_SIZE;
194+
boolean match = false;
195+
int maxTermLen = 0;
196+
for (final String term : terminators) {
197+
final int len = term.length();
198+
if (len > maxTermLen) maxTermLen = len;
199+
}
200+
201+
@SuppressWarnings("resource")
202+
final InputStreamReader in =
203+
new InputStreamReader(new DataHandleInputStream<L>(this), getEncoding());
204+
final char[] buf = new char[blockSize];
205+
long loc = 0;
206+
while (loc < maxLen && offset() < length() - 1) {
207+
// if we're not saving the string, drop any old, unnecessary output
208+
if (!saveString) {
209+
final int outLen = out.length();
210+
if (outLen >= maxTermLen) {
211+
final int dropIndex = outLen - maxTermLen + 1;
212+
final String last = out.substring(dropIndex, outLen);
213+
out.setLength(0);
214+
out.append(last);
215+
bytesDropped += dropIndex;
216+
}
217+
}
218+
219+
// read block from stream
220+
final int r = in.read(buf, 0, blockSize);
221+
if (r <= 0) throw new IOException("Cannot read from stream: " + r);
222+
223+
// append block to output
224+
out.append(buf, 0, r);
225+
226+
// check output, returning smallest possible string
227+
int min = Integer.MAX_VALUE, tagLen = 0;
228+
for (final String t : terminators) {
229+
final int len = t.length();
230+
final int start = (int) (loc - bytesDropped - len);
231+
final int value = out.indexOf(t, start < 0 ? 0 : start);
232+
if (value >= 0 && value < min) {
233+
match = true;
234+
min = value;
235+
tagLen = len;
236+
}
237+
}
238+
239+
if (match) {
240+
// reset stream to proper location
241+
seek(startPos + bytesDropped + min + tagLen);
242+
243+
// trim output string
244+
if (saveString) {
245+
out.setLength(min + tagLen);
246+
return out.toString();
247+
}
248+
return null;
249+
}
250+
251+
loc += r;
252+
}
253+
254+
// no match
255+
if (tooLong) throw new IOException("Maximum search length reached.");
256+
return saveString ? out.toString() : null;
257+
}
258+
259+
// -- InputStream look-alikes --
260+
261+
@Override
262+
public int read(byte[] b) throws IOException {
263+
return read(b, 0, b.length);
264+
}
265+
266+
@Override
267+
public long skip(final long n) throws IOException {
268+
if (n < 0) return 0;
269+
final long remain = length() - offset();
270+
final long num = n < remain ? n : remain;
271+
seek(offset() + num);
272+
return num;
273+
}
274+
275+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* #%L
3+
* SciJava Common shared library for SciJava software.
4+
* %%
5+
* Copyright (C) 2009 - 2015 Board of Regents of the University of
6+
* Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
7+
* Institute of Molecular Cell Biology and Genetics.
8+
* %%
9+
* Redistribution and use in source and binary forms, with or without
10+
* modification, are permitted provided that the following conditions are met:
11+
*
12+
* 1. Redistributions of source code must retain the above copyright notice,
13+
* this list of conditions and the following disclaimer.
14+
* 2. Redistributions in binary form must reproduce the above copyright notice,
15+
* this list of conditions and the following disclaimer in the documentation
16+
* and/or other materials provided with the distribution.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28+
* POSSIBILITY OF SUCH DAMAGE.
29+
* #L%
30+
*/
31+
32+
package org.scijava.io;
33+
34+
import java.net.URI;
35+
36+
/**
37+
* Abstract base class for {@link Location} implementations.
38+
*
39+
* @author Curtis Rueden
40+
*/
41+
public abstract class AbstractLocation implements Location {
42+
43+
// -- Location methods --
44+
45+
@Override
46+
public URI getURI() {
47+
return null;
48+
}
49+
50+
}

0 commit comments

Comments
 (0)