001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, Geotools Project Managment Committee (PMC)
005: * (C) 2001, Institut de Recherche pour le D�veloppement
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: */
017: package org.geotools.image.io;
018:
019: import java.io.*; // Many imports, including some for javadoc only.
020: import java.net.URI;
021: import java.net.URL;
022: import java.net.URLConnection;
023:
024: import javax.imageio.spi.ImageReaderSpi;
025: import javax.imageio.stream.ImageInputStream;
026:
027: import org.geotools.util.logging.Logging;
028: import org.geotools.resources.Utilities;
029: import org.geotools.resources.i18n.ErrorKeys;
030:
031: /**
032: * Base class for simple image decoders. This class provides a {@link #getInputStream} method,
033: * which returns the {@linkplain #input input} as an {@link InputStream} for convenience.
034: * Different kinds of input like {@linkplain File} or {@linkplain URL} are automatically
035: * handled.
036: *
037: * @since 2.4
038: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/unsupported/coverageio/src/main/java/org/geotools/image/io/StreamImageReader.java $
039: * @version $Id: StreamImageReader.java 27908 2007-11-15 16:29:28Z desruisseaux $
040: * @author Martin Desruisseaux
041: */
042: public abstract class StreamImageReader extends GeographicImageReader {
043: /**
044: * The stream to {@linkplain #close close} on {@link #setInput(Object,boolean,boolean)
045: * setInput(...)}, {@link #reset} or {@link #dispose} method invocation. This stream is
046: * typically an {@linkplain InputStream input stream} or a {@linkplain Reader reader}
047: * created by {@link #getInputStream} or similar methods in subclasses.
048: * <p>
049: * This field is never equals to the user-specified {@linkplain #input input}, since the
050: * usual {@link javax.imageio.ImageReader} contract is to <strong>not</strong> close the
051: * user-provided stream. It is set to a non-null value only if a stream has been created
052: * from an other user object like {@link File} or {@link URL}.
053: *
054: * @see #getInputStream
055: * @see org.geotools.image.io.text.TextImageReader#getReader
056: * @see #close
057: *
058: * @todo The field type will be changed to {@link Closeable} when we will be allowed
059: * to compile for J2SE 1.5.
060: */
061: protected Object closeOnReset;
062:
063: /**
064: * {@link #input} as an input stream, or {@code null} if none.
065: *
066: * @see #getInputStream
067: */
068: private InputStream stream;
069:
070: /**
071: * The stream position when {@link #setInput} is invoked.
072: */
073: private long streamOrigin;
074:
075: /**
076: * Constructs a new image reader.
077: *
078: * @param provider The {@link ImageReaderSpi} that is invoking this constructor,
079: * or {@code null} if none.
080: */
081: protected StreamImageReader(final ImageReaderSpi provider) {
082: super (provider);
083: }
084:
085: /**
086: * Sets the input source to use. Input may be one of the following object:
087: * {@link File}, {@link URL}, {@link Reader} (for ASCII data), {@link InputStream} or
088: * {@link ImageInputStream}. If {@code input} is {@code null}, then any currently
089: * set input source will be removed.
090: *
091: * @param input The input object to use for future decoding.
092: * @param seekForwardOnly If {@code true}, images and metadata may only be read
093: * in ascending order from this input source.
094: * @param ignoreMetadata If {@code true}, metadata may be ignored during reads.
095: *
096: * @see #getInput
097: * @see #getInputStream
098: */
099: //@Override
100: public void setInput(final Object input,
101: final boolean seekForwardOnly, final boolean ignoreMetadata) {
102: closeSilently();
103: super .setInput(input, seekForwardOnly, ignoreMetadata);
104: if (input instanceof ImageInputStream) {
105: try {
106: streamOrigin = ((ImageInputStream) input)
107: .getStreamPosition();
108: } catch (IOException exception) {
109: streamOrigin = 0;
110: Logging.unexpectedException(LOGGER.getName(),
111: StreamImageReader.class, "setInput", exception);
112: }
113: }
114: }
115:
116: /**
117: * Returns the stream length in bytes, or {@code -1} if unknown. This method checks the
118: * {@linkplain #input input} type and invokes one of {@link File#length()},
119: * {@link ImageInputStream#length()} ou {@link URLConnection#getContentLength()} method
120: * accordingly.
121: *
122: * @return The stream length, or -1 is unknown.
123: * @throws IOException if an I/O error occured.
124: */
125: protected long getStreamLength() throws IOException {
126: final Object input = getInput();
127: if (input instanceof ImageInputStream) {
128: long length = ((ImageInputStream) input).length();
129: if (length >= 0) {
130: length -= streamOrigin;
131: }
132: return length;
133: }
134: if (input instanceof File) {
135: return ((File) input).length();
136: }
137: if (input instanceof URL) {
138: return ((URL) input).openConnection().getContentLength();
139: }
140: if (input instanceof URLConnection) {
141: return ((URLConnection) input).getContentLength();
142: }
143: return -1;
144: }
145:
146: /**
147: * Returns the {@linkplain #input input} as an {@linkplain InputStream input stream} object.
148: * If the input is already an input stream, it is returned unchanged. Otherwise this method
149: * creates a new {@linkplain InputStream input stream} (usually <strong>not</strong>
150: * {@linkplain BufferedInputStream buffered}) from {@link File}, {@link URL},
151: * {@link URLConnection} or {@link ImageInputStream} inputs.
152: * <p>
153: * This method creates a new {@linkplain InputStream input stream} only when first invoked.
154: * All subsequent calls will returns the same instance. Consequently, the returned stream
155: * should never be closed by the caller. It may be {@linkplain #close closed} automatically
156: * when {@link #setInput setInput(...)}, {@link #reset()} or {@link #dispose()} methods are
157: * invoked.
158: *
159: * @return {@link #getInput} as an {@link InputStream}. This input stream is usually
160: * not {@linkplain BufferedInputStream buffered}.
161: * @throws IllegalStateException if the {@linkplain #input input} is not set.
162: * @throws IOException If the input stream can't be created for an other reason.
163: *
164: * @see #getInput
165: * @see org.geotools.image.io.text.TextImageReader#getReader
166: */
167: protected InputStream getInputStream()
168: throws IllegalStateException, IOException {
169: if (stream == null) {
170: final Object input = getInput();
171: if (input == null) {
172: throw new IllegalStateException(getErrorResources()
173: .getString(ErrorKeys.NO_IMAGE_INPUT));
174: }
175: if (input instanceof InputStream) {
176: stream = (InputStream) input;
177: closeOnReset = null; // We don't own the stream, so don't close it.
178: } else if (input instanceof ImageInputStream) {
179: stream = new InputStreamAdapter(
180: (ImageInputStream) input);
181: closeOnReset = null; // We don't own the ImageInputStream, so don't close it.
182: } else if (input instanceof String) {
183: stream = new FileInputStream((String) input);
184: closeOnReset = stream;
185: } else if (input instanceof File) {
186: stream = new FileInputStream((File) input);
187: closeOnReset = stream;
188: } else if (input instanceof URI) {
189: stream = ((URI) input).toURL().openStream();
190: closeOnReset = stream;
191: } else if (input instanceof URL) {
192: stream = ((URL) input).openStream();
193: closeOnReset = stream;
194: } else if (input instanceof URLConnection) {
195: stream = ((URLConnection) input).getInputStream();
196: closeOnReset = stream;
197: } else {
198: throw new IllegalStateException(
199: getErrorResources()
200: .getString(
201: ErrorKeys.ILLEGAL_CLASS_$2,
202: Utilities
203: .getShortClassName(input),
204: Utilities
205: .getShortClassName(InputStream.class)));
206: }
207: }
208: return stream;
209: }
210:
211: /**
212: * Closes the input stream created by {@link #getInputStream()}. This method does nothing
213: * if the input stream is the {@linkplain #input input} instance given by the user rather
214: * than a stream created by this class from a {@link File} or {@link URL} input.
215: * <p>
216: * This method is invoked automatically by {@link #setInput(Object,boolean,boolean)
217: * setInput(...)}, {@link #reset}, {@link #dispose} or {@link #finalize} methods and
218: * doesn't need to be invoked explicitly. It has protected access only in order to
219: * allow overriding by subclasses.
220: *
221: * @throws IOException if an error occured while closing the stream.
222: *
223: * @see #closeOnReset
224: */
225: //@Override
226: protected void close() throws IOException {
227: if (closeOnReset != null) {
228: // TODO: replace the remaining of this block by the following line
229: // when we will be allowed to compile for J2SE 1.5.
230: //closeOnReset.close();
231: if (closeOnReset instanceof InputStream) {
232: ((InputStream) closeOnReset).close();
233: }
234: if (closeOnReset instanceof Reader) {
235: ((Reader) closeOnReset).close();
236: }
237: }
238: closeOnReset = null;
239: stream = null;
240: super .close();
241: }
242:
243: /**
244: * Invokes {@link #close} and log the exception if any. This method is invoked from
245: * methods that do not allow {@link IOException} to be thrown. Since we will not use
246: * the stream anymore after closing it, it should not be a big deal if an error occured.
247: */
248: private void closeSilently() {
249: try {
250: close();
251: } catch (IOException exception) {
252: Logging.unexpectedException(LOGGER.getName(), getClass(),
253: "close", exception);
254: }
255: }
256:
257: /**
258: * Restores the {@code StreamImageReader} to its initial state. If an input stream were
259: * created by a previous call to {@link #getInputStream}, it will be {@linkplain #close
260: * closed} before to reset this reader.
261: */
262: //@Override
263: public void reset() {
264: closeSilently();
265: super .reset();
266: }
267:
268: /**
269: * Allows any resources held by this reader to be released. If an input stream were created
270: * by a previous call to {@link #getInputStream}, it will be {@linkplain #close closed}
271: * before to dispose this reader.
272: */
273: //@Override
274: public void dispose() {
275: closeSilently();
276: super .dispose();
277: }
278:
279: /**
280: * Closes the streams. This method is automatically invoked by the garbage collector.
281: */
282: //@Override
283: protected void finalize() throws Throwable {
284: close();
285: super .finalize();
286: }
287:
288: /**
289: * Service provider interface (SPI) for {@link StreamImageReader}s.
290: *
291: * @since 2.4
292: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/unsupported/coverageio/src/main/java/org/geotools/image/io/StreamImageReader.java $
293: * @version $Id: StreamImageReader.java 27908 2007-11-15 16:29:28Z desruisseaux $
294: * @author Martin Desruisseaux
295: */
296: public static abstract class Spi extends ImageReaderSpi {
297: /**
298: * List of legal input types for {@link StreamImageReader}.
299: */
300: private static final Class[] INPUT_TYPES = new Class[] {
301: File.class, URI.class, URL.class, URLConnection.class,
302: InputStream.class, ImageInputStream.class, String.class // To be interpreted as file path.
303: };
304:
305: /**
306: * Constructs a quasi-blank {@code StreamImageReader.Spi}. It is up to the subclass to
307: * initialize instance variables in order to provide working versions of all methods.
308: * This constructor provides the following defaults:
309: *
310: * <ul>
311: * <li>{@link #inputTypes} = {{@link File}, {@link URL}, {@link URLConnection},
312: * {@link InputStream}, {@link ImageInputStream}, {@link String}}</li>
313: * </ul>
314: *
315: * For efficienty reasons, the above fields are initialized to shared arrays. Subclasses
316: * can assign new arrays, but should not modify the default array content.
317: */
318: public Spi() {
319: inputTypes = INPUT_TYPES;
320: }
321: }
322: }
|