use of org.apache.sis.internal.storage.io.ChannelDataInput in project sis by apache.
the class StorageConnector method createChannelDataInput.
/**
* Creates a view for the input as a {@link ChannelDataInput} if possible.
* This is also a starting point for {@link #createDataInput()} and {@link #createByteBuffer()}.
* This method is one of the {@link #OPENERS} methods and should be invoked at most once per
* {@code StorageConnector} instance.
*
* @param asImageInputStream whether the {@code ChannelDataInput} needs to be {@link ChannelImageInputStream} subclass.
* @throws IOException if an error occurred while opening a channel for the input.
*/
private ChannelDataInput createChannelDataInput(final boolean asImageInputStream) throws IOException, DataStoreException {
/*
* Before to try to wrap an InputStream, mark its position so we can rewind if the user asks for
* the InputStream directly. We need to reset because ChannelDataInput may have read some bytes.
* Note that if mark is unsupported, the default InputStream.mark(…) implementation does nothing.
*/
reset();
if (storage instanceof InputStream) {
((InputStream) storage).mark(DEFAULT_BUFFER_SIZE);
}
/*
* Following method call recognizes ReadableByteChannel, InputStream (with special case for FileInputStream),
* URL, URI, File, Path or other types that may be added in future Apache SIS versions.
* If the given storage is already a ReadableByteChannel, then the factory will return it as-is.
*/
final ChannelFactory factory = ChannelFactory.prepare(storage, getOption(OptionKey.URL_ENCODING), false, getOption(OptionKey.OPEN_OPTIONS));
if (factory == null) {
return null;
}
/*
* ChannelDataInput depends on ReadableByteChannel, which itself depends on storage
* (potentially an InputStream). We need to remember this chain in 'Coupled' objects.
*/
final String name = getStorageName();
final ReadableByteChannel channel = factory.readable(name, null);
addView(ReadableByteChannel.class, channel, null, factory.isCoupled() ? CASCADE_ON_RESET : 0);
// User-supplied buffer.
ByteBuffer buffer = getOption(OptionKey.BYTE_BUFFER);
if (buffer == null) {
// Default buffer if user did not specified any.
buffer = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE);
}
final ChannelDataInput asDataInput;
if (asImageInputStream) {
asDataInput = new ChannelImageInputStream(name, channel, buffer, false);
} else {
asDataInput = new ChannelDataInput(name, channel, buffer, false);
}
addView(ChannelDataInput.class, asDataInput, ReadableByteChannel.class, CASCADE_ON_RESET);
/*
* Following is an undocumented mechanism for allowing some Apache SIS implementations of DataStore
* to re-open the same channel or input stream another time, typically for re-reading the same data.
*/
if (factory.canOpen()) {
addView(ChannelFactory.class, factory);
}
return asDataInput;
}
use of org.apache.sis.internal.storage.io.ChannelDataInput in project sis by apache.
the class StorageConnector method createByteBuffer.
/**
* Creates a {@link ByteBuffer} from the {@link ChannelDataInput} if possible, or from the
* {@link ImageInputStream} otherwise. The buffer will be initialized with an arbitrary amount
* of bytes read from the input. If this amount is not sufficient, it can be increased by a call
* to {@link #prefetch()}.
*
* <p>This method is one of the {@link #OPENERS} methods and should be invoked at most once per
* {@code StorageConnector} instance.</p>
*
* @throws IOException if an error occurred while opening a stream for the input.
*/
private ByteBuffer createByteBuffer() throws IOException, DataStoreException {
/*
* First, try to create the ChannelDataInput if it does not already exists.
* If successful, this will create a ByteBuffer companion as a side effect.
*/
final ChannelDataInput c = getStorageAs(ChannelDataInput.class);
ByteBuffer asByteBuffer = null;
if (c != null) {
asByteBuffer = c.buffer.asReadOnlyBuffer();
} else {
/*
* If no ChannelDataInput has been created by the above code, get the input as an ImageInputStream and
* read an arbitrary amount of bytes. Read only a small amount of bytes because, at the contrary of the
* buffer created in createChannelDataInput(boolean), the buffer created here is unlikely to be used for
* the reading process after the recognition of the file format.
*/
final ImageInputStream in = getStorageAs(ImageInputStream.class);
if (in != null) {
in.mark();
final byte[] buffer = new byte[MINIMAL_BUFFER_SIZE];
final int n = in.read(buffer);
in.reset();
if (n >= 1) {
// Can not invoke asReadOnly() because 'prefetch()' need to be able to write in it.
asByteBuffer = ByteBuffer.wrap(buffer).order(in.getByteOrder());
asByteBuffer.limit(n);
}
}
}
addView(ByteBuffer.class, asByteBuffer);
return asByteBuffer;
}
use of org.apache.sis.internal.storage.io.ChannelDataInput in project sis by apache.
the class ChannelDecoderTest method createChannelDecoder.
/**
* Creates a new {@link ChannelDecoder} instance for dataset of the given name.
* The {@code name} parameter can be one of the following values:
*
* <ul>
* <li>{@link #NCEP} for a netCDF binary file.</li>
* <li>{@link #CIP} for a netCDF binary file.</li>
* </ul>
*
* @param name the file name as one of the above-cited constants.
* @return the decoder for the given name.
* @throws IOException if an I/O error occurred while opening the file.
* @throws DataStoreException if a logical error occurred.
*/
public static Decoder createChannelDecoder(final String name) throws IOException, DataStoreException {
final InputStream in = IOTestCase.class.getResourceAsStream(name);
assumeNotNull(name, in);
final ChannelDataInput input = new ChannelDataInput(name, Channels.newChannel(in), ByteBuffer.allocate(4096), false);
return new ChannelDecoder(input, null, GeometryLibrary.JAVA2D, LISTENERS);
}
use of org.apache.sis.internal.storage.io.ChannelDataInput in project sis by apache.
the class NetcdfStoreProvider method decoder.
/**
* Creates a decoder for the given input. This method invokes
* {@link StorageConnector#closeAllExcept(Object)} after the decoder has been created.
*
* @param listeners where to send the warnings.
* @param connector information about the input (file, input stream, <i>etc.</i>)
* @return the decoder for the given input, or {@code null} if the input type is not recognized.
* @throws IOException if an error occurred while opening the netCDF file.
* @throws DataStoreException if a logical error (other than I/O) occurred.
*/
static Decoder decoder(final WarningListeners<DataStore> listeners, final StorageConnector connector) throws IOException, DataStoreException {
final GeometryLibrary geomlib = connector.getOption(OptionKey.GEOMETRY_LIBRARY);
Decoder decoder;
Object keepOpen;
final ChannelDataInput input = connector.getStorageAs(ChannelDataInput.class);
if (input != null)
try {
decoder = new ChannelDecoder(input, connector.getOption(OptionKey.ENCODING), geomlib, listeners);
keepOpen = input;
} catch (DataStoreException e) {
final String path = connector.getStorageAs(String.class);
if (path != null)
try {
decoder = createByReflection(path, false, geomlib, listeners);
keepOpen = path;
} catch (IOException | DataStoreException s) {
e.addSuppressed(s);
}
throw e;
}
else {
keepOpen = connector.getStorage();
decoder = createByReflection(keepOpen, true, geomlib, listeners);
}
connector.closeAllExcept(keepOpen);
return decoder;
}
use of org.apache.sis.internal.storage.io.ChannelDataInput in project sis by apache.
the class StorageConnector method createDataInput.
/**
* Creates a view for the input as a {@link DataInput} if possible. This method performs the choice
* documented in the {@link #getStorageAs(Class)} method for the {@code DataInput} case. Opening the
* data input may imply creating a {@link ByteBuffer}, in which case the buffer will be stored under
* the {@code ByteBuffer.class} key together with the {@code DataInput.class} case.
*
* <p>This method is one of the {@link #OPENERS} methods and should be invoked at most once per
* {@code StorageConnector} instance.</p>
*
* @throws IOException if an error occurred while opening a stream for the input.
*/
private DataInput createDataInput() throws IOException, DataStoreException {
/*
* Gets or creates a ChannelImageInputStream instance if possible. We really need that specific
* type because some SIS data stores will want to access directly the channel and the buffer.
* We will fallback on the ImageIO.createImageInputStream(Object) method only in last resort.
*/
Coupled c = getView(ChannelDataInput.class);
final ChannelDataInput in;
if (reset(c)) {
in = (ChannelDataInput) c.view;
} else {
// May be null.
in = createChannelDataInput(true);
}
final DataInput asDataInput;
if (in != null) {
// May have been added by createChannelDataInput(…).
c = getView(ChannelDataInput.class);
if (in instanceof DataInput) {
asDataInput = (DataInput) in;
} else {
// Upgrade existing instance.
asDataInput = new ChannelImageInputStream(in);
c.view = asDataInput;
}
// Share the same Coupled instance.
views.put(DataInput.class, c);
} else {
reset();
asDataInput = ImageIO.createImageInputStream(storage);
addView(DataInput.class, asDataInput, null, (byte) (CASCADE_ON_RESET | CASCADE_ON_CLOSE));
/*
* Note: Java Image I/O wrappers for Input/OutputStream do NOT close the underlying streams.
* This is a complication for us. We could mitigate the problem by subclassing the standard
* FileCacheImageInputStream and related classes, but we don't do that for now because this
* code should never be executed for InputStream storage. Instead getChannelDataInput(true)
* should have created a ChannelImageInputStream or ChannelDataInput.
*/
}
return asDataInput;
}
Aggregations