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