/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2001-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.image.io.text;
import java.awt.image.BufferedImage;
import java.io.*; // Many imports, including some for javadoc only.
import java.text.ParseException;
import javax.imageio.ImageReadParam;
import org.geotools.io.LineFormat;
import org.geotools.resources.XArray;
/**
* A dummy implementation of {@link TextImageReader} used only by default implementation
* of {@link TextImageReader.Spi#canDecodeInput}. This class is more lightweight than
* loading the real image reader implementation.
*
* @source $URL$
* @version $Id$
* @author Martin Desruisseaux (IRD)
*/
final class TestReader extends TextImageReader {
/**
* The input stream to {@linkplain InputStream#reset reset} after
* {@linkplain InputStream#mark mark}.
*/
private InputStream marked;
/**
* Creates a new reader for the specified provider.
*/
public TestReader(final TextImageReader.Spi provider) {
super(provider);
}
/**
* Returns a null width.
*/
public int getWidth(int imageIndex) {
return 0;
}
/**
* Returns a null height.
*/
public int getHeight(int imageIndex) {
return 0;
}
/**
* Throws an {@link UnsupportedOperationException}.
*/
public BufferedImage read(final int imageIndex, final ImageReadParam param) {
throw new UnsupportedOperationException();
}
/**
* Returns the {@linkplain #input input} as a {@linkplain Reader reader}, which doesn't need to
* be {@linkplain BufferedReader buffered}. If the reader is an instance supplied explicitly by
* the user, then it will be {@linkplain Reader#mark marked} with the specified read ahead limit.
*
* @return {@link #getInput} as a {@link Reader}, or {@code null} if this method
* can't provide a reader suitable for {@code canDecode}.
* @throws IllegalStateException if the {@linkplain #input input} is not set.
* @throws IOException If the input stream can't be created for an other reason.
*/
private Reader getReader(final int readAheadLimit) throws IllegalStateException, IOException {
final Object input = getInput();
if (input instanceof Reader) {
final Reader reader = (Reader) input;
if (!reader.markSupported()) {
return null;
}
reader.mark(readAheadLimit);
return reader;
// Do not set 'closeOnReset' since we don't own the reader.
}
final InputStream stream = getInputStream();
if (closeOnReset == null) {
// If we are not allowed to close and reopen a new stream on ImageReader.read, then
// we must be able to mark the stream otherwise we will not support canDecode(...).
if (!stream.markSupported()) {
return null;
}
stream.mark(readAheadLimit);
}
final Reader reader = getInputStreamReader(stream);
if (closeOnReset == stream) {
closeOnReset = reader;
}
return reader;
}
/**
* Checks if the {@linkplain #getReader reader} seems to contains a readeable ASCII file.
* This method tries to read the first few lines. The caller is responsable for invoking
* {@link #close} after this method.
*
* @param readAheadLimit Maximum number of characters to read. If this amount is reached
* but this method still unable to make a choice, then it returns {@code null}.
* @return {@code true} if the source <em>seems</em> readable, {@code false} otherwise.
* @throws IOException If an error occured during reading.
*/
final boolean canDecode(final int readAheadLimit) throws IOException {
final Reader input = getReader(readAheadLimit);
if (input == null) {
return false;
}
final TextImageReader.Spi spi = (TextImageReader.Spi) originatingProvider;
final char[] buffer = new char[readAheadLimit];
final int length = input.read(buffer);
final LineFormat parser = getLineFormat(0);
double[][] rows = new double[16][];
int rowCount = 0;
int lower = 0;
scan: while (lower < readAheadLimit) {
// Skip line feeds at the begining of the line.
// They may be a rest from the previous line.
char c = buffer[lower];
if (c == '\r' || c == 'n') {
lower++;
continue;
}
// Search the end of line. If we reach the end of the buffer,
// do not attempt to parse that last line since it is incomplete.
int upper = lower;
while ((c = buffer[upper]) != '\r' && c != '\n') {
if (++upper >= readAheadLimit) {
break scan;
}
}
// Try to parse a line.
final String line = new String(buffer, lower, upper-lower);
if (!isComment(line)) {
try {
if (parser.setLine(line) != 0) {
if (rowCount == rows.length) {
rows = XArray.resize(rows, rows.length * 2);
}
rows[rowCount] = parser.getValues(rows[rowCount]);
rowCount++;
}
} catch (ParseException exception) {
return false;
}
}
lower = upper;
}
if (originatingProvider instanceof TextImageReader.Spi) {
rows = XArray.resize(rows, rowCount);
return ((TextImageReader.Spi) originatingProvider).isValidContent(rows);
}
return true;
}
/**
* Closes the reader created by this class, or {@linkplain Reader#reset reset} the
* user's reader to its original position.
*/
@Override
protected void close() throws IOException {
if (marked != null) {
marked.reset();
marked = null;
}
super.close();
}
}