1 /*
   2  * Copyright (c) 1999, 2006, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.imageio;
  27 
  28 import java.awt.Dimension;
  29 import java.awt.Rectangle;
  30 import java.awt.image.BufferedImage;
  31 import java.awt.image.RenderedImage;
  32 import java.awt.image.Raster;
  33 import java.io.IOException;
  34 import java.util.ArrayList;
  35 import java.util.List;
  36 import java.util.Locale;
  37 import java.util.MissingResourceException;
  38 import java.util.ResourceBundle;
  39 import javax.imageio.event.IIOWriteWarningListener;
  40 import javax.imageio.event.IIOWriteProgressListener;
  41 import javax.imageio.metadata.IIOMetadata;
  42 import javax.imageio.stream.ImageOutputStream;
  43 import javax.imageio.spi.ImageWriterSpi;
  44 
  45 /**
  46  * An abstract superclass for encoding and writing images.  This class
  47  * must be subclassed by classes that write out images in the context
  48  * of the Java Image I/O framework.
  49  *
  50  * <p> <code>ImageWriter</code> objects are normally instantiated by
  51  * the service provider class for the specific format.  Service
  52  * provider classes are registered with the <code>IIORegistry</code>,
  53  * which uses them for format recognition and presentation of
  54  * available format readers and writers.
  55  *
  56  * <p>
  57  *
  58  * @see ImageReader
  59  * @see ImageWriteParam
  60  * @see javax.imageio.spi.IIORegistry
  61  * @see javax.imageio.spi.ImageWriterSpi
  62  *
  63  */
  64 public abstract class ImageWriter implements ImageTranscoder {
  65 
  66     /**
  67      * The <code>ImageWriterSpi</code> that instantiated this object,
  68      * or <code>null</code> if its identity is not known or none
  69      * exists.  By default it is initialized to <code>null</code>.
  70      */
  71     protected ImageWriterSpi originatingProvider = null;
  72 
  73     /**
  74      * The <code>ImageOutputStream</code> or other <code>Object</code>
  75      * set by <code>setOutput</code> and retrieved by
  76      * <code>getOutput</code>.  By default it is initialized to
  77      * <code>null</code>.
  78      */
  79     protected Object output = null;
  80 
  81     /**
  82      * An array of <code>Locale</code>s that may be used to localize
  83      * warning messages and compression setting values, or
  84      * <code>null</code> if localization is not supported.  By default
  85      * it is initialized to <code>null</code>.
  86      */
  87     protected Locale[] availableLocales = null;
  88 
  89     /**
  90      * The current <code>Locale</code> to be used for localization, or
  91      * <code>null</code> if none has been set.  By default it is
  92      * initialized to <code>null</code>.
  93      */
  94     protected Locale locale = null;
  95 
  96     /**
  97      * A <code>List</code> of currently registered
  98      * <code>IIOWriteWarningListener</code>s, initialized by default to
  99      * <code>null</code>, which is synonymous with an empty
 100      * <code>List</code>.
 101      */
 102     protected List<IIOWriteWarningListener> warningListeners = null;
 103 
 104     /**
 105      * A <code>List</code> of <code>Locale</code>s, one for each
 106      * element of <code>warningListeners</code>, initialized by default
 107      * <code>null</code>, which is synonymous with an empty
 108      * <code>List</code>.
 109      */
 110     protected List<Locale> warningLocales = null;
 111 
 112     /**
 113      * A <code>List</code> of currently registered
 114      * <code>IIOWriteProgressListener</code>s, initialized by default
 115      * <code>null</code>, which is synonymous with an empty
 116      * <code>List</code>.
 117      */
 118     protected List<IIOWriteProgressListener> progressListeners = null;
 119 
 120     /**
 121      * If <code>true</code>, the current write operation should be
 122      * aborted.
 123      */
 124     private boolean abortFlag = false;
 125 
 126     /**
 127      * Constructs an <code>ImageWriter</code> and sets its
 128      * <code>originatingProvider</code> instance variable to the
 129      * supplied value.
 130      *
 131      * <p> Subclasses that make use of extensions should provide a
 132      * constructor with signature <code>(ImageWriterSpi,
 133      * Object)</code> in order to retrieve the extension object.  If
 134      * the extension object is unsuitable, an
 135      * <code>IllegalArgumentException</code> should be thrown.
 136      *
 137      * @param originatingProvider the <code>ImageWriterSpi</code> that
 138      * is constructing this object, or <code>null</code>.
 139      */
 140     protected ImageWriter(ImageWriterSpi originatingProvider) {
 141         this.originatingProvider = originatingProvider;
 142     }
 143 
 144     /**
 145      * Returns the <code>ImageWriterSpi</code> object that created
 146      * this <code>ImageWriter</code>, or <code>null</code> if this
 147      * object was not created through the <code>IIORegistry</code>.
 148      *
 149      * <p> The default implementation returns the value of the
 150      * <code>originatingProvider</code> instance variable.
 151      *
 152      * @return an <code>ImageWriterSpi</code>, or <code>null</code>.
 153      *
 154      * @see ImageWriterSpi
 155      */
 156     public ImageWriterSpi getOriginatingProvider() {
 157         return originatingProvider;
 158     }
 159 
 160     /**
 161      * Sets the destination to the given
 162      * <code>ImageOutputStream</code> or other <code>Object</code>.
 163      * The destination is assumed to be ready to accept data, and will
 164      * not be closed at the end of each write. This allows distributed
 165      * imaging applications to transmit a series of images over a
 166      * single network connection.  If <code>output</code> is
 167      * <code>null</code>, any currently set output will be removed.
 168      *
 169      * <p> If <code>output</code> is an
 170      * <code>ImageOutputStream</code>, calls to the
 171      * <code>write</code>, <code>writeToSequence</code>, and
 172      * <code>prepareWriteEmpty</code>/<code>endWriteEmpty</code>
 173      * methods will preserve the existing contents of the stream.
 174      * Other write methods, such as <code>writeInsert</code>,
 175      * <code>replaceStreamMetadata</code>,
 176      * <code>replaceImageMetadata</code>, <code>replacePixels</code>,
 177      * <code>prepareInsertEmpty</code>/<code>endInsertEmpty</code>,
 178      * and <code>endWriteSequence</code>, require the full contents
 179      * of the stream to be readable and writable, and may alter any
 180      * portion of the stream.
 181      *
 182      * <p> Use of a general <code>Object</code> other than an
 183      * <code>ImageOutputStream</code> is intended for writers that
 184      * interact directly with an output device or imaging protocol.
 185      * The set of legal classes is advertised by the writer's service
 186      * provider's <code>getOutputTypes</code> method; most writers
 187      * will return a single-element array containing only
 188      * <code>ImageOutputStream.class</code> to indicate that they
 189      * accept only an <code>ImageOutputStream</code>.
 190      *
 191      * <p> The default implementation sets the <code>output</code>
 192      * instance variable to the value of <code>output</code> after
 193      * checking <code>output</code> against the set of classes
 194      * advertised by the originating provider, if there is one.
 195      *
 196      * @param output the <code>ImageOutputStream</code> or other
 197      * <code>Object</code> to use for future writing.
 198      *
 199      * @exception IllegalArgumentException if <code>output</code> is
 200      * not an instance of one of the classes returned by the
 201      * originating service provider's <code>getOutputTypes</code>
 202      * method.
 203      *
 204      * @see #getOutput
 205      */
 206     public void setOutput(Object output) {
 207         if (output != null) {
 208             ImageWriterSpi provider = getOriginatingProvider();
 209             if (provider != null) {
 210                 Class[] classes = provider.getOutputTypes();
 211                 boolean found = false;
 212                 for (int i = 0; i < classes.length; i++) {
 213                     if (classes[i].isInstance(output)) {
 214                         found = true;
 215                         break;
 216                     }
 217                 }
 218                 if (!found) {
 219                     throw new IllegalArgumentException("Illegal output type!");
 220                 }
 221             }
 222         }
 223 
 224         this.output = output;
 225     }
 226 
 227     /**
 228      * Returns the <code>ImageOutputStream</code> or other
 229      * <code>Object</code> set by the most recent call to the
 230      * <code>setOutput</code> method.  If no destination has been
 231      * set, <code>null</code> is returned.
 232      *
 233      * <p> The default implementation returns the value of the
 234      * <code>output</code> instance variable.
 235      *
 236      * @return the <code>Object</code> that was specified using
 237      * <code>setOutput</code>, or <code>null</code>.
 238      *
 239      * @see #setOutput
 240      */
 241     public Object getOutput() {
 242         return output;
 243     }
 244 
 245     // Localization
 246 
 247     /**
 248      * Returns an array of <code>Locale</code>s that may be used to
 249      * localize warning listeners and compression settings.  A return
 250      * value of <code>null</code> indicates that localization is not
 251      * supported.
 252      *
 253      * <p> The default implementation returns a clone of the
 254      * <code>availableLocales</code> instance variable if it is
 255      * non-<code>null</code>, or else returns <code>null</code>.
 256      *
 257      * @return an array of <code>Locale</code>s that may be used as
 258      * arguments to <code>setLocale</code>, or <code>null</code>.
 259      */
 260     public Locale[] getAvailableLocales() {
 261         return (availableLocales == null) ?
 262             null : (Locale[])availableLocales.clone();
 263     }
 264 
 265     /**
 266      * Sets the current <code>Locale</code> of this
 267      * <code>ImageWriter</code> to the given value.  A value of
 268      * <code>null</code> removes any previous setting, and indicates
 269      * that the writer should localize as it sees fit.
 270      *
 271      * <p> The default implementation checks <code>locale</code>
 272      * against the values returned by
 273      * <code>getAvailableLocales</code>, and sets the
 274      * <code>locale</code> instance variable if it is found.  If
 275      * <code>locale</code> is <code>null</code>, the instance variable
 276      * is set to <code>null</code> without any checking.
 277      *
 278      * @param locale the desired <code>Locale</code>, or
 279      * <code>null</code>.
 280      *
 281      * @exception IllegalArgumentException if <code>locale</code> is
 282      * non-<code>null</code> but is not one of the values returned by
 283      * <code>getAvailableLocales</code>.
 284      *
 285      * @see #getLocale
 286      */
 287     public void setLocale(Locale locale) {
 288         if (locale != null) {
 289             Locale[] locales = getAvailableLocales();
 290             boolean found = false;
 291             if (locales != null) {
 292                 for (int i = 0; i < locales.length; i++) {
 293                     if (locale.equals(locales[i])) {
 294                         found = true;
 295                         break;
 296                     }
 297                 }
 298             }
 299             if (!found) {
 300                 throw new IllegalArgumentException("Invalid locale!");
 301             }
 302         }
 303         this.locale = locale;
 304     }
 305 
 306     /**
 307      * Returns the currently set <code>Locale</code>, or
 308      * <code>null</code> if none has been set.
 309      *
 310      * <p> The default implementation returns the value of the
 311      * <code>locale</code> instance variable.
 312      *
 313      * @return the current <code>Locale</code>, or <code>null</code>.
 314      *
 315      * @see #setLocale
 316      */
 317     public Locale getLocale() {
 318         return locale;
 319     }
 320 
 321     // Write params
 322 
 323     /**
 324      * Returns a new <code>ImageWriteParam</code> object of the
 325      * appropriate type for this file format containing default
 326      * values, that is, those values that would be used
 327      * if no <code>ImageWriteParam</code> object were specified.  This
 328      * is useful as a starting point for tweaking just a few parameters
 329      * and otherwise leaving the default settings alone.
 330      *
 331      * <p> The default implementation constructs and returns a new
 332      * <code>ImageWriteParam</code> object that does not allow tiling,
 333      * progressive encoding, or compression, and that will be
 334      * localized for the current <code>Locale</code> (<i>i.e.</i>,
 335      * what you would get by calling <code>new
 336      * ImageWriteParam(getLocale())</code>.
 337      *
 338      * <p> Individual plug-ins may return an instance of
 339      * <code>ImageWriteParam</code> with additional optional features
 340      * enabled, or they may return an instance of a plug-in specific
 341      * subclass of <code>ImageWriteParam</code>.
 342      *
 343      * @return a new <code>ImageWriteParam</code> object containing
 344      * default values.
 345      */
 346     public ImageWriteParam getDefaultWriteParam() {
 347         return new ImageWriteParam(getLocale());
 348     }
 349 
 350     // Metadata
 351 
 352     /**
 353      * Returns an <code>IIOMetadata</code> object containing default
 354      * values for encoding a stream of images.  The contents of the
 355      * object may be manipulated using either the XML tree structure
 356      * returned by the <code>IIOMetadata.getAsTree</code> method, an
 357      * <code>IIOMetadataController</code> object, or via plug-in
 358      * specific interfaces, and the resulting data supplied to one of
 359      * the <code>write</code> methods that take a stream metadata
 360      * parameter.
 361      *
 362      * <p> An optional <code>ImageWriteParam</code> may be supplied
 363      * for cases where it may affect the structure of the stream
 364      * metadata.
 365      *
 366      * <p> If the supplied <code>ImageWriteParam</code> contains
 367      * optional setting values not supported by this writer (<i>e.g.</i>
 368      * progressive encoding or any format-specific settings), they
 369      * will be ignored.
 370      *
 371      * <p> Writers that do not make use of stream metadata
 372      * (<i>e.g.</i>, writers for single-image formats) should return
 373      * <code>null</code>.
 374      *
 375      * @param param an <code>ImageWriteParam</code> that will be used to
 376      * encode the image, or <code>null</code>.
 377      *
 378      * @return an <code>IIOMetadata</code> object.
 379      */
 380     public abstract IIOMetadata
 381         getDefaultStreamMetadata(ImageWriteParam param);
 382 
 383     /**
 384      * Returns an <code>IIOMetadata</code> object containing default
 385      * values for encoding an image of the given type.  The contents
 386      * of the object may be manipulated using either the XML tree
 387      * structure returned by the <code>IIOMetadata.getAsTree</code>
 388      * method, an <code>IIOMetadataController</code> object, or via
 389      * plug-in specific interfaces, and the resulting data supplied to
 390      * one of the <code>write</code> methods that take a stream
 391      * metadata parameter.
 392      *
 393      * <p> An optional <code>ImageWriteParam</code> may be supplied
 394      * for cases where it may affect the structure of the image
 395      * metadata.
 396      *
 397      * <p> If the supplied <code>ImageWriteParam</code> contains
 398      * optional setting values not supported by this writer (<i>e.g.</i>
 399      * progressive encoding or any format-specific settings), they
 400      * will be ignored.
 401      *
 402      * @param imageType an <code>ImageTypeSpecifier</code> indicating the
 403      * format of the image to be written later.
 404      * @param param an <code>ImageWriteParam</code> that will be used to
 405      * encode the image, or <code>null</code>.
 406      *
 407      * @return an <code>IIOMetadata</code> object.
 408      */
 409     public abstract IIOMetadata
 410         getDefaultImageMetadata(ImageTypeSpecifier imageType,
 411                                 ImageWriteParam param);
 412 
 413     // comment inherited
 414     public abstract IIOMetadata convertStreamMetadata(IIOMetadata inData,
 415                                                       ImageWriteParam param);
 416 
 417     // comment inherited
 418     public abstract IIOMetadata
 419         convertImageMetadata(IIOMetadata inData,
 420                              ImageTypeSpecifier imageType,
 421                              ImageWriteParam param);
 422 
 423     // Thumbnails
 424 
 425     /**
 426      * Returns the number of thumbnails suported by the format being
 427      * written, given the image type and any additional write
 428      * parameters and metadata objects that will be used during
 429      * encoding.  A return value of <code>-1</code> indicates that
 430      * insufficient information is available.
 431      *
 432      * <p> An <code>ImageWriteParam</code> may optionally be supplied
 433      * for cases where it may affect thumbnail handling.
 434      *
 435      * <p> If the supplied <code>ImageWriteParam</code> contains
 436      * optional setting values not supported by this writer (<i>e.g.</i>
 437      * progressive encoding or any format-specific settings), they
 438      * will be ignored.
 439      *
 440      * <p> The default implementation returns 0.
 441      *
 442      * @param imageType an <code>ImageTypeSpecifier</code> indicating
 443      * the type of image to be written, or <code>null</code>.
 444      * @param param the <code>ImageWriteParam</code> that will be used for
 445      * writing, or <code>null</code>.
 446      * @param streamMetadata an <code>IIOMetadata</code> object that will
 447      * be used for writing, or <code>null</code>.
 448      * @param imageMetadata an <code>IIOMetadata</code> object that will
 449      * be used for writing, or <code>null</code>.
 450      *
 451      * @return the number of thumbnails that may be written given the
 452      * supplied parameters, or <code>-1</code> if insufficient
 453      * information is available.
 454      */
 455     public int getNumThumbnailsSupported(ImageTypeSpecifier imageType,
 456                                          ImageWriteParam param,
 457                                          IIOMetadata streamMetadata,
 458                                          IIOMetadata imageMetadata) {
 459         return 0;
 460     }
 461 
 462     /**
 463      * Returns an array of <code>Dimension</code>s indicating the
 464      * legal size ranges for thumbnail images as they will be encoded
 465      * in the output file or stream.  This information is merely
 466      * advisory; the writer will resize any supplied thumbnails as
 467      * necessary.
 468      *
 469      * <p> The information is returned as a set of pairs; the first
 470      * element of a pair contains an (inclusive) minimum width and
 471      * height, and the second element contains an (inclusive) maximum
 472      * width and height.  Together, each pair defines a valid range of
 473      * sizes.  To specify a fixed size, the same width and height will
 474      * appear for both elements.  A return value of <code>null</code>
 475      * indicates that the size is arbitrary or unknown.
 476      *
 477      * <p> An <code>ImageWriteParam</code> may optionally be supplied
 478      * for cases where it may affect thumbnail handling.
 479      *
 480      * <p> If the supplied <code>ImageWriteParam</code> contains
 481      * optional setting values not supported by this writer (<i>e.g.</i>
 482      * progressive encoding or any format-specific settings), they
 483      * will be ignored.
 484      *
 485      * <p> The default implementation returns <code>null</code>.
 486      *
 487      * @param imageType an <code>ImageTypeSpecifier</code> indicating the
 488      * type of image to be written, or <code>null</code>.
 489      * @param param the <code>ImageWriteParam</code> that will be used for
 490      * writing, or <code>null</code>.
 491      * @param streamMetadata an <code>IIOMetadata</code> object that will
 492      * be used for writing, or <code>null</code>.
 493      * @param imageMetadata an <code>IIOMetadata</code> object that will
 494      * be used for writing, or <code>null</code>.
 495      *
 496      * @return an array of <code>Dimension</code>s with an even length
 497      * of at least two, or <code>null</code>.
 498      */
 499     public Dimension[] getPreferredThumbnailSizes(ImageTypeSpecifier imageType,
 500                                                   ImageWriteParam param,
 501                                                   IIOMetadata streamMetadata,
 502                                                   IIOMetadata imageMetadata) {
 503         return null;
 504     }
 505 
 506     /**
 507      * Returns <code>true</code> if the methods that take an
 508      * <code>IIOImage</code> parameter are capable of dealing with a
 509      * <code>Raster</code> (as opposed to <code>RenderedImage</code>)
 510      * source image.  If this method returns <code>false</code>, then
 511      * those methods will throw an
 512      * <code>UnsupportedOperationException</code> if supplied with an
 513      * <code>IIOImage</code> containing a <code>Raster</code>.
 514      *
 515      * <p> The default implementation returns <code>false</code>.
 516      *
 517      * @return <code>true</code> if <code>Raster</code> sources are
 518      * supported.
 519      */
 520     public boolean canWriteRasters() {
 521         return false;
 522     }
 523 
 524     /**
 525      * Appends a complete image stream containing a single image and
 526      * associated stream and image metadata and thumbnails to the
 527      * output.  Any necessary header information is included.  If the
 528      * output is an <code>ImageOutputStream</code>, its existing
 529      * contents prior to the current seek position are not affected,
 530      * and need not be readable or writable.
 531      *
 532      * <p> The output must have been set beforehand using the
 533      * <code>setOutput</code> method.
 534      *
 535      * <p> Stream metadata may optionally be supplied; if it is
 536      * <code>null</code>, default stream metadata will be used.
 537      *
 538      * <p> If <code>canWriteRasters</code> returns <code>true</code>,
 539      * the <code>IIOImage</code> may contain a <code>Raster</code>
 540      * source.  Otherwise, it must contain a
 541      * <code>RenderedImage</code> source.
 542      *
 543      * <p> The supplied thumbnails will be resized if needed, and any
 544      * thumbnails in excess of the supported number will be ignored.
 545      * If the format requires additional thumbnails that are not
 546      * provided, the writer should generate them internally.
 547      *
 548      * <p>  An <code>ImageWriteParam</code> may
 549      * optionally be supplied to control the writing process.  If
 550      * <code>param</code> is <code>null</code>, a default write param
 551      * will be used.
 552      *
 553      * <p> If the supplied <code>ImageWriteParam</code> contains
 554      * optional setting values not supported by this writer (<i>e.g.</i>
 555      * progressive encoding or any format-specific settings), they
 556      * will be ignored.
 557      *
 558      * @param streamMetadata an <code>IIOMetadata</code> object representing
 559      * stream metadata, or <code>null</code> to use default values.
 560      * @param image an <code>IIOImage</code> object containing an
 561      * image, thumbnails, and metadata to be written.
 562      * @param param an <code>ImageWriteParam</code>, or
 563      * <code>null</code> to use a default
 564      * <code>ImageWriteParam</code>.
 565      *
 566      * @exception IllegalStateException if the output has not
 567      * been set.
 568      * @exception UnsupportedOperationException if <code>image</code>
 569      * contains a <code>Raster</code> and <code>canWriteRasters</code>
 570      * returns <code>false</code>.
 571      * @exception IllegalArgumentException if <code>image</code> is
 572      * <code>null</code>.
 573      * @exception IOException if an error occurs during writing.
 574      */
 575     public abstract void write(IIOMetadata streamMetadata,
 576                                IIOImage image,
 577                                ImageWriteParam param) throws IOException;
 578 
 579     /**
 580      * Appends a complete image stream containing a single image with
 581      * default metadata and thumbnails to the output.  This method is
 582      * a shorthand for <code>write(null, image, null)</code>.
 583      *
 584      * @param image an <code>IIOImage</code> object containing an
 585      * image, thumbnails, and metadata to be written.
 586      *
 587      * @exception IllegalStateException if the output has not
 588      * been set.
 589      * @exception IllegalArgumentException if <code>image</code> is
 590      * <code>null</code>.
 591      * @exception UnsupportedOperationException if <code>image</code>
 592      * contains a <code>Raster</code> and <code>canWriteRasters</code>
 593      * returns <code>false</code>.
 594      * @exception IOException if an error occurs during writing.
 595      */
 596     public void write(IIOImage image) throws IOException {
 597         write(null, image, null);
 598     }
 599 
 600     /**
 601      * Appends a complete image stream consisting of a single image
 602      * with default metadata and thumbnails to the output.  This
 603      * method is a shorthand for <code>write(null, new IIOImage(image,
 604      * null, null), null)</code>.
 605      *
 606      * @param image a <code>RenderedImage</code> to be written.
 607      *
 608      * @exception IllegalStateException if the output has not
 609      * been set.
 610      * @exception IllegalArgumentException if <code>image</code> is
 611      * <code>null</code>.
 612      * @exception IOException if an error occurs during writing.
 613      */
 614     public void write(RenderedImage image) throws IOException {
 615         write(null, new IIOImage(image, null, null), null);
 616     }
 617 
 618     // Check that the output has been set, then throw an
 619     // UnsupportedOperationException.
 620     private void unsupported() {
 621         if (getOutput() == null) {
 622             throw new IllegalStateException("getOutput() == null!");
 623         }
 624         throw new UnsupportedOperationException("Unsupported write variant!");
 625     }
 626 
 627     // Sequence writes
 628 
 629     /**
 630      * Returns <code>true</code> if the writer is able to append an
 631      * image to an image stream that already contains header
 632      * information and possibly prior images.
 633      *
 634      * <p> If <code>canWriteSequence</code> returns <code>false</code>,
 635      * <code>writeToSequence</code> and <code>endWriteSequence</code>
 636      * will throw an <code>UnsupportedOperationException</code>.
 637      *
 638      * <p> The default implementation returns <code>false</code>.
 639      *
 640      * @return <code>true</code> if images may be appended sequentially.
 641      */
 642     public boolean canWriteSequence() {
 643         return false;
 644     }
 645 
 646     /**
 647      * Prepares a stream to accept a series of subsequent
 648      * <code>writeToSequence</code> calls, using the provided stream
 649      * metadata object.  The metadata will be written to the stream if
 650      * it should precede the image data.  If the argument is <code>null</code>,
 651      * default stream metadata is used.
 652      *
 653      * <p> If the output is an <code>ImageOutputStream</code>, the existing
 654      * contents of the output prior to the current seek position are
 655      * flushed, and need not be readable or writable.  If the format
 656      * requires that <code>endWriteSequence</code> be able to rewind to
 657      * patch up the header information, such as for a sequence of images
 658      * in a single TIFF file, then the metadata written by this method
 659      * must remain in a writable portion of the stream.  Other formats
 660      * may flush the stream after this method and after each image.
 661      *
 662      * <p> If <code>canWriteSequence</code> returns <code>false</code>,
 663      * this method will throw an
 664      * <code>UnsupportedOperationException</code>.
 665      *
 666      * <p> The output must have been set beforehand using either
 667      * the <code>setOutput</code> method.
 668      *
 669      * <p> The default implementation throws an
 670      * <code>IllegalStateException</code> if the output is
 671      * <code>null</code>, and otherwise throws an
 672      * <code>UnsupportedOperationException</code>.
 673      *
 674      * @param streamMetadata A stream metadata object, or <code>null</code>.
 675      *
 676      * @exception IllegalStateException if the output has not
 677      * been set.
 678      * @exception UnsupportedOperationException if
 679      * <code>canWriteSequence</code> returns <code>false</code>.
 680      * @exception IOException if an error occurs writing the stream
 681      * metadata.
 682      */
 683     public void prepareWriteSequence(IIOMetadata streamMetadata)
 684         throws IOException {
 685         unsupported();
 686     }
 687 
 688     /**
 689      * Appends a single image and possibly associated metadata and
 690      * thumbnails, to the output.  If the output is an
 691      * <code>ImageOutputStream</code>, the existing contents of the
 692      * output prior to the current seek position may be flushed, and
 693      * need not be readable or writable, unless the plug-in needs to
 694      * be able to patch up the header information when
 695      * <code>endWriteSequence</code> is called (<italic>e.g.</italic> TIFF).
 696      *
 697      * <p> If <code>canWriteSequence</code> returns <code>false</code>,
 698      * this method will throw an
 699      * <code>UnsupportedOperationException</code>.
 700      *
 701      * <p> The output must have been set beforehand using
 702      * the <code>setOutput</code> method.
 703      *
 704      * <p> <code>prepareWriteSequence</code> must have been called
 705      * beforehand, or an <code>IllegalStateException</code> is thrown.
 706      *
 707      * <p> If <code>canWriteRasters</code> returns <code>true</code>,
 708      * the <code>IIOImage</code> may contain a <code>Raster</code>
 709      * source.  Otherwise, it must contain a
 710      * <code>RenderedImage</code> source.
 711      *
 712      * <p> The supplied thumbnails will be resized if needed, and any
 713      * thumbnails in excess of the supported number will be ignored.
 714      * If the format requires additional thumbnails that are not
 715      * provided, the writer will generate them internally.
 716      *
 717      * <p> An <code>ImageWriteParam</code> may optionally be supplied
 718      * to control the writing process.  If <code>param</code> is
 719      * <code>null</code>, a default write param will be used.
 720      *
 721      * <p> If the supplied <code>ImageWriteParam</code> contains
 722      * optional setting values not supported by this writer (<i>e.g.</i>
 723      * progressive encoding or any format-specific settings), they
 724      * will be ignored.
 725      *
 726      * <p> The default implementation throws an
 727      * <code>IllegalStateException</code> if the output is
 728      * <code>null</code>, and otherwise throws an
 729      * <code>UnsupportedOperationException</code>.
 730      *
 731      * @param image an <code>IIOImage</code> object containing an
 732      * image, thumbnails, and metadata to be written.
 733      * @param param an <code>ImageWriteParam</code>, or
 734      * <code>null</code> to use a default
 735      * <code>ImageWriteParam</code>.
 736      *
 737      * @exception IllegalStateException if the output has not
 738      * been set, or <code>prepareWriteSequence</code> has not been called.
 739      * @exception UnsupportedOperationException if
 740      * <code>canWriteSequence</code> returns <code>false</code>.
 741      * @exception IllegalArgumentException if <code>image</code> is
 742      * <code>null</code>.
 743      * @exception UnsupportedOperationException if <code>image</code>
 744      * contains a <code>Raster</code> and <code>canWriteRasters</code>
 745      * returns <code>false</code>.
 746      * @exception IOException if an error occurs during writing.
 747      */
 748     public void writeToSequence(IIOImage image, ImageWriteParam param)
 749         throws IOException {
 750         unsupported();
 751     }
 752 
 753     /**
 754      * Completes the writing of a sequence of images begun with
 755      * <code>prepareWriteSequence</code>.  Any stream metadata that
 756      * should come at the end of the sequence of images is written out,
 757      * and any header information at the beginning of the sequence is
 758      * patched up if necessary.  If the output is an
 759      * <code>ImageOutputStream</code>, data through the stream metadata
 760      * at the end of the sequence are flushed and need not be readable
 761      * or writable.
 762      *
 763      * <p> If <code>canWriteSequence</code> returns <code>false</code>,
 764      * this method will throw an
 765      * <code>UnsupportedOperationException</code>.
 766      *
 767      * <p> The default implementation throws an
 768      * <code>IllegalStateException</code> if the output is
 769      * <code>null</code>, and otherwise throws an
 770      * <code>UnsupportedOperationException</code>.
 771      *
 772      * @exception IllegalStateException if the output has not
 773      * been set, or <code>prepareWriteSequence</code> has not been called.
 774      * @exception UnsupportedOperationException if
 775      * <code>canWriteSequence</code> returns <code>false</code>.
 776      * @exception IOException if an error occurs during writing.
 777      */
 778     public void endWriteSequence() throws IOException {
 779         unsupported();
 780     }
 781 
 782     // Metadata replacement
 783 
 784     /**
 785      * Returns <code>true</code> if it is possible to replace the
 786      * stream metadata already present in the output.
 787      *
 788      * <p> The default implementation throws an
 789      * <code>IllegalStateException</code> if the output is
 790      * <code>null</code>, and otherwise returns <code>false</code>.
 791      *
 792      * @return <code>true</code> if replacement of stream metadata is
 793      * allowed.
 794      *
 795      * @exception IllegalStateException if the output has not
 796      * been set.
 797      * @exception IOException if an I/O error occurs during the query.
 798      */
 799     public boolean canReplaceStreamMetadata() throws IOException {
 800         if (getOutput() == null) {
 801             throw new IllegalStateException("getOutput() == null!");
 802         }
 803         return false;
 804     }
 805 
 806     /**
 807      * Replaces the stream metadata in the output with new
 808      * information.  If the output is an
 809      * <code>ImageOutputStream</code>, the prior contents of the
 810      * stream are examined and possibly edited to make room for the
 811      * new data.  All of the prior contents of the output must be
 812      * available for reading and writing.
 813      *
 814      * <p> If <code>canReplaceStreamMetadata</code> returns
 815      * <code>false</code>, an
 816      * <code>UnsupportedOperationException</code> will be thrown.
 817      *
 818      * <p> The default implementation throws an
 819      * <code>IllegalStateException</code> if the output is
 820      * <code>null</code>, and otherwise throws an
 821      * <code>UnsupportedOperationException</code>.
 822      *
 823      * @param streamMetadata an <code>IIOMetadata</code> object representing
 824      * stream metadata, or <code>null</code> to use default values.
 825      *
 826      * @exception IllegalStateException if the output has not
 827      * been set.
 828      * @exception UnsupportedOperationException if the
 829      * <code>canReplaceStreamMetadata</code> returns
 830      * <code>false</code>.  modes do not include
 831      * @exception IOException if an error occurs during writing.
 832      */
 833     public void replaceStreamMetadata(IIOMetadata streamMetadata)
 834         throws IOException {
 835         unsupported();
 836     }
 837 
 838     /**
 839      * Returns <code>true</code> if it is possible to replace the
 840      * image metadata associated with an existing image with index
 841      * <code>imageIndex</code>.  If this method returns
 842      * <code>false</code>, a call to
 843      * <code>replaceImageMetadata(imageIndex)</code> will throw an
 844      * <code>UnsupportedOperationException</code>.
 845      *
 846      * <p> A writer that does not support any image metadata
 847      * replacement may return <code>false</code> without performing
 848      * bounds checking on the index.
 849      *
 850      * <p> The default implementation throws an
 851      * <code>IllegalStateException</code> if the output is
 852      * <code>null</code>, and otherwise returns <code>false</code>
 853      * without checking the value of <code>imageIndex</code>.
 854      *
 855      * @param imageIndex the index of the image whose metadata is to
 856      * be replaced.
 857      *
 858      * @return <code>true</code> if the image metadata of the given
 859      * image can be replaced.
 860      *
 861      * @exception IllegalStateException if the output has not
 862      * been set.
 863      * @exception IndexOutOfBoundsException if the writer supports
 864      * image metadata replacement in general, but
 865      * <code>imageIndex</code> is less than 0 or greater than the
 866      * largest available index.
 867      * @exception IOException if an I/O error occurs during the query.
 868      */
 869     public boolean canReplaceImageMetadata(int imageIndex)
 870         throws IOException {
 871         if (getOutput() == null) {
 872             throw new IllegalStateException("getOutput() == null!");
 873         }
 874         return false;
 875     }
 876 
 877     /**
 878      * Replaces the image metadata associated with an existing image.
 879      *
 880      * <p> If <code>canReplaceImageMetadata(imageIndex)</code> returns
 881      * <code>false</code>, an
 882      * <code>UnsupportedOperationException</code> will be thrown.
 883      *
 884      * <p> The default implementation throws an
 885      * <code>IllegalStateException</code> if the output is
 886      * <code>null</code>, and otherwise throws an
 887      * <code>UnsupportedOperationException</code>.
 888      *
 889      * @param imageIndex the index of the image whose metadata is to
 890      * be replaced.
 891      * @param imageMetadata an <code>IIOMetadata</code> object
 892      * representing image metadata, or <code>null</code>.
 893      *
 894      * @exception IllegalStateException if the output has not been
 895      * set.
 896      * @exception UnsupportedOperationException if
 897      * <code>canReplaceImageMetadata</code> returns
 898      * <code>false</code>.
 899      * @exception IndexOutOfBoundsException if <code>imageIndex</code>
 900      * is less than 0 or greater than the largest available index.
 901      * @exception IOException if an error occurs during writing.
 902      */
 903     public void replaceImageMetadata(int imageIndex,
 904                                      IIOMetadata imageMetadata)
 905         throws IOException {
 906         unsupported();
 907     }
 908 
 909     // Image insertion
 910 
 911     /**
 912      * Returns <code>true</code> if the writer supports the insertion
 913      * of a new image at the given index.  Existing images with
 914      * indices greater than or equal to the insertion index will have
 915      * their indices increased by 1.  A value for
 916      * <code>imageIndex</code> of <code>-1</code> may be used to
 917      * signify an index one larger than the current largest index.
 918      *
 919      * <p> A writer that does not support any image insertion may
 920      * return <code>false</code> without performing bounds checking on
 921      * the index.
 922      *
 923      * <p> The default implementation throws an
 924      * <code>IllegalStateException</code> if the output is
 925      * <code>null</code>, and otherwise returns <code>false</code>
 926      * withour checking the value of <code>imageIndex</code>.
 927      *
 928      * @param imageIndex the index at which the image is to be
 929      * inserted.
 930      *
 931      * @return <code>true</code> if an image may be inserted at the
 932      * given index.
 933      *
 934      * @exception IllegalStateException if the output has not
 935      * been set.
 936      * @exception IndexOutOfBoundsException if the writer supports
 937      * image insertion in general, but <code>imageIndex</code> is less
 938      * than -1 or greater than the largest available index.
 939      * @exception IOException if an I/O error occurs during the query.
 940      */
 941     public boolean canInsertImage(int imageIndex) throws IOException {
 942         if (getOutput() == null) {
 943             throw new IllegalStateException("getOutput() == null!");
 944         }
 945         return false;
 946     }
 947 
 948     /**
 949      * Inserts a new image into an existing image stream.  Existing
 950      * images with an index greater than <code>imageIndex</code> are
 951      * preserved, and their indices are each increased by 1.  A value
 952      * for <code>imageIndex</code> of -1 may be used to signify an
 953      * index one larger than the previous largest index; that is, it
 954      * will cause the image to be logically appended to the end of the
 955      * sequence.  If the output is an <code>ImageOutputStream</code>,
 956      * the entirety of the stream must be both readable and writeable.
 957      *
 958      * <p> If <code>canInsertImage(imageIndex)</code> returns
 959      * <code>false</code>, an
 960      * <code>UnsupportedOperationException</code> will be thrown.
 961      *
 962      * <p> An <code>ImageWriteParam</code> may optionally be supplied
 963      * to control the writing process.  If <code>param</code> is
 964      * <code>null</code>, a default write param will be used.
 965      *
 966      * <p> If the supplied <code>ImageWriteParam</code> contains
 967      * optional setting values not supported by this writer (<i>e.g.</i>
 968      * progressive encoding or any format-specific settings), they
 969      * will be ignored.
 970      *
 971      * <p> The default implementation throws an
 972      * <code>IllegalStateException</code> if the output is
 973      * <code>null</code>, and otherwise throws an
 974      * <code>UnsupportedOperationException</code>.
 975      *
 976      * @param imageIndex the index at which to write the image.
 977      * @param image an <code>IIOImage</code> object containing an
 978      * image, thumbnails, and metadata to be written.
 979      * @param param an <code>ImageWriteParam</code>, or
 980      * <code>null</code> to use a default
 981      * <code>ImageWriteParam</code>.
 982      *
 983      * @exception IllegalStateException if the output has not
 984      * been set.
 985      * @exception UnsupportedOperationException if
 986      * <code>canInsertImage(imageIndex)</code> returns <code>false</code>.
 987      * @exception IllegalArgumentException if <code>image</code> is
 988      * <code>null</code>.
 989      * @exception IndexOutOfBoundsException if <code>imageIndex</code>
 990      * is less than -1 or greater than the largest available index.
 991      * @exception UnsupportedOperationException if <code>image</code>
 992      * contains a <code>Raster</code> and <code>canWriteRasters</code>
 993      * returns <code>false</code>.
 994      * @exception IOException if an error occurs during writing.
 995      */
 996     public void writeInsert(int imageIndex,
 997                             IIOImage image,
 998                             ImageWriteParam param) throws IOException {
 999         unsupported();
1000     }
1001 
1002     // Image removal
1003 
1004     /**
1005      * Returns <code>true</code> if the writer supports the removal
1006      * of an existing image at the given index.  Existing images with
1007      * indices greater than the insertion index will have
1008      * their indices decreased by 1.
1009      *
1010      * <p> A writer that does not support any image removal may
1011      * return <code>false</code> without performing bounds checking on
1012      * the index.
1013      *
1014      * <p> The default implementation throws an
1015      * <code>IllegalStateException</code> if the output is
1016      * <code>null</code>, and otherwise returns <code>false</code>
1017      * without checking the value of <code>imageIndex</code>.
1018      *
1019      * @param imageIndex the index of the image to be removed.
1020      *
1021      * @return <code>true</code> if it is possible to remove the given
1022      * image.
1023      *
1024      * @exception IllegalStateException if the output has not
1025      * been set.
1026      * @exception IndexOutOfBoundsException if the writer supports
1027      * image removal in general, but <code>imageIndex</code> is less
1028      * than 0 or greater than the largest available index.
1029      * @exception IOException if an I/O error occurs during the
1030      * query.
1031      */
1032     public boolean canRemoveImage(int imageIndex) throws IOException {
1033         if (getOutput() == null) {
1034             throw new IllegalStateException("getOutput() == null!");
1035         }
1036         return false;
1037     }
1038 
1039     /**
1040      * Removes an image from the stream.
1041      *
1042      * <p> If <code>canRemoveImage(imageIndex)</code> returns false,
1043      * an <code>UnsupportedOperationException</code>will be thrown.
1044      *
1045      * <p> The removal may or may not cause a reduction in the actual
1046      * file size.
1047      *
1048      * <p> The default implementation throws an
1049      * <code>IllegalStateException</code> if the output is
1050      * <code>null</code>, and otherwise throws an
1051      * <code>UnsupportedOperationException</code>.
1052      *
1053      * @param imageIndex the index of the image to be removed.
1054      *
1055      * @exception IllegalStateException if the output has not
1056      * been set.
1057      * @exception UnsupportedOperationException if
1058      * <code>canRemoveImage(imageIndex)</code> returns <code>false</code>.
1059      * @exception IndexOutOfBoundsException if <code>imageIndex</code>
1060      * is less than 0 or greater than the largest available index.
1061      * @exception IOException if an I/O error occurs during the
1062      * removal.
1063      */
1064     public void removeImage(int imageIndex) throws IOException {
1065         unsupported();
1066     }
1067 
1068     // Empty images
1069 
1070     /**
1071      * Returns <code>true</code> if the writer supports the writing of
1072      * a complete image stream consisting of a single image with
1073      * undefined pixel values and associated metadata and thumbnails
1074      * to the output.  The pixel values may be defined by future
1075      * calls to the <code>replacePixels</code> methods.  If the output
1076      * is an <code>ImageOutputStream</code>, its existing contents
1077      * prior to the current seek position are not affected, and need
1078      * not be readable or writable.
1079      *
1080      * <p> The default implementation throws an
1081      * <code>IllegalStateException</code> if the output is
1082      * <code>null</code>, and otherwise returns <code>false</code>.
1083      *
1084      * @return <code>true</code> if the writing of complete image
1085      * stream with contents to be defined later is supported.
1086      *
1087      * @exception IllegalStateException if the output has not been
1088      * set.
1089      * @exception IOException if an I/O error occurs during the
1090      * query.
1091      */
1092     public boolean canWriteEmpty() throws IOException {
1093         if (getOutput() == null) {
1094             throw new IllegalStateException("getOutput() == null!");
1095         }
1096         return false;
1097     }
1098 
1099     /**
1100      * Begins the writing of a complete image stream, consisting of a
1101      * single image with undefined pixel values and associated
1102      * metadata and thumbnails, to the output.  The pixel values will
1103      * be defined by future calls to the <code>replacePixels</code>
1104      * methods.  If the output is an <code>ImageOutputStream</code>,
1105      * its existing contents prior to the current seek position are
1106      * not affected, and need not be readable or writable.
1107      *
1108      * <p> The writing is not complete until a call to
1109      * <code>endWriteEmpty</code> occurs.  Calls to
1110      * <code>prepareReplacePixels</code>, <code>replacePixels</code>,
1111      * and <code>endReplacePixels</code> may occur between calls to
1112      * <code>prepareWriteEmpty</code> and <code>endWriteEmpty</code>.
1113      * However, calls to <code>prepareWriteEmpty</code> cannot be
1114      * nested, and calls to <code>prepareWriteEmpty</code> and
1115      * <code>prepareInsertEmpty</code> may not be interspersed.
1116      *
1117      * <p> If <code>canWriteEmpty</code> returns <code>false</code>,
1118      * an <code>UnsupportedOperationException</code> will be thrown.
1119      *
1120      * <p> An <code>ImageWriteParam</code> may optionally be supplied
1121      * to control the writing process.  If <code>param</code> is
1122      * <code>null</code>, a default write param will be used.
1123      *
1124      * <p> If the supplied <code>ImageWriteParam</code> contains
1125      * optional setting values not supported by this writer (<i>e.g.</i>
1126      * progressive encoding or any format-specific settings), they
1127      * will be ignored.
1128      *
1129      * <p> The default implementation throws an
1130      * <code>IllegalStateException</code> if the output is
1131      * <code>null</code>, and otherwise throws an
1132      * <code>UnsupportedOperationException</code>.
1133      *
1134      * @param streamMetadata an <code>IIOMetadata</code> object representing
1135      * stream metadata, or <code>null</code> to use default values.
1136      * @param imageType an <code>ImageTypeSpecifier</code> describing
1137      * the layout of the image.
1138      * @param width the width of the image.
1139      * @param height the height of the image.
1140      * @param imageMetadata an <code>IIOMetadata</code> object
1141      * representing image metadata, or <code>null</code>.
1142      * @param thumbnails a <code>List</code> of
1143      * <code>BufferedImage</code> thumbnails for this image, or
1144      * <code>null</code>.
1145      * @param param an <code>ImageWriteParam</code>, or
1146      * <code>null</code> to use a default
1147      * <code>ImageWriteParam</code>.
1148      *
1149      * @exception IllegalStateException if the output has not
1150      * been set.
1151      * @exception UnsupportedOperationException if
1152      * <code>canWriteEmpty</code> returns <code>false</code>.
1153      * @exception IllegalStateException if a previous call to
1154      * <code>prepareWriteEmpty</code> has been made without a
1155      * corresponding call to <code>endWriteEmpty</code>.
1156      * @exception IllegalStateException if a previous call to
1157      * <code>prepareInsertEmpty</code> has been made without a
1158      * corresponding call to <code>endInsertEmpty</code>.
1159      * @exception IllegalArgumentException if <code>imageType</code>
1160      * is <code>null</code> or <code>thumbnails</code> contains
1161      * <code>null</code> references or objects other than
1162      * <code>BufferedImage</code>s.
1163      * @exception IllegalArgumentException if width or height are less
1164      * than 1.
1165      * @exception IOException if an I/O error occurs during writing.
1166      */
1167     public void prepareWriteEmpty(IIOMetadata streamMetadata,
1168                                   ImageTypeSpecifier imageType,
1169                                   int width, int height,
1170                                   IIOMetadata imageMetadata,
1171                                   List<? extends BufferedImage> thumbnails,
1172                                   ImageWriteParam param) throws IOException {
1173         unsupported();
1174     }
1175 
1176     /**
1177      * Completes the writing of a new image that was begun with a
1178      * prior call to <code>prepareWriteEmpty</code>.
1179      *
1180      * <p> If <code>canWriteEmpty()</code> returns <code>false</code>,
1181      * an <code>UnsupportedOperationException</code> will be thrown.
1182      *
1183      * <p> The default implementation throws an
1184      * <code>IllegalStateException</code> if the output is
1185      * <code>null</code>, and otherwise throws an
1186      * <code>UnsupportedOperationException</code>.
1187      *
1188      * @exception IllegalStateException if the output has not
1189      * been set.
1190      * @exception UnsupportedOperationException if
1191      * <code>canWriteEmpty(imageIndex)</code> returns
1192      * <code>false</code>.
1193      * @exception IllegalStateException if a previous call to
1194      * <code>prepareWriteEmpty</code> without a corresponding call to
1195      * <code>endWriteEmpty</code> has not been made.
1196      * @exception IllegalStateException if a previous call to
1197      * <code>prepareInsertEmpty</code> without a corresponding call to
1198      * <code>endInsertEmpty</code> has been made.
1199      * @exception IllegalStateException if a call to
1200      * <code>prepareReiplacePixels</code> has been made without a
1201      * matching call to <code>endReplacePixels</code>.
1202      * @exception IOException if an I/O error occurs during writing.
1203      */
1204     public void endWriteEmpty() throws IOException {
1205         if (getOutput() == null) {
1206             throw new IllegalStateException("getOutput() == null!");
1207         }
1208         throw new IllegalStateException("No call to prepareWriteEmpty!");
1209     }
1210 
1211     /**
1212      * Returns <code>true</code> if the writer supports the insertion
1213      * of a new, empty image at the given index.  The pixel values of
1214      * the image are undefined, and may be specified in pieces using
1215      * the <code>replacePixels</code> methods.  Existing images with
1216      * indices greater than or equal to the insertion index will have
1217      * their indices increased by 1.  A value for
1218      * <code>imageIndex</code> of <code>-1</code> may be used to
1219      * signify an index one larger than the current largest index.
1220      *
1221      * <p> A writer that does not support insertion of empty images
1222      * may return <code>false</code> without performing bounds
1223      * checking on the index.
1224      *
1225      * <p> The default implementation throws an
1226      * <code>IllegalStateException</code> if the output is
1227      * <code>null</code>, and otherwise returns <code>false</code>
1228      * without checking the value of <code>imageIndex</code>.
1229      *
1230      * @param imageIndex the index at which the image is to be
1231      * inserted.
1232      *
1233      * @return <code>true</code> if an empty image may be inserted at
1234      * the given index.
1235      *
1236      * @exception IllegalStateException if the output has not been
1237      * set.
1238      * @exception IndexOutOfBoundsException if the writer supports
1239      * empty image insertion in general, but <code>imageIndex</code>
1240      * is less than -1 or greater than the largest available index.
1241      * @exception IOException if an I/O error occurs during the
1242      * query.
1243      */
1244     public boolean canInsertEmpty(int imageIndex) throws IOException {
1245         if (getOutput() == null) {
1246             throw new IllegalStateException("getOutput() == null!");
1247         }
1248         return false;
1249     }
1250 
1251     /**
1252      * Begins the insertion of a new image with undefined pixel values
1253      * into an existing image stream.  Existing images with an index
1254      * greater than <code>imageIndex</code> are preserved, and their
1255      * indices are each increased by 1.  A value for
1256      * <code>imageIndex</code> of -1 may be used to signify an index
1257      * one larger than the previous largest index; that is, it will
1258      * cause the image to be logically appended to the end of the
1259      * sequence.  If the output is an <code>ImageOutputStream</code>,
1260      * the entirety of the stream must be both readable and writeable.
1261      *
1262      * <p> The image contents may be
1263      * supplied later using the <code>replacePixels</code> method.
1264      * The insertion is not complete until a call to
1265      * <code>endInsertEmpty</code> occurs.  Calls to
1266      * <code>prepareReplacePixels</code>, <code>replacePixels</code>,
1267      * and <code>endReplacePixels</code> may occur between calls to
1268      * <code>prepareInsertEmpty</code> and
1269      * <code>endInsertEmpty</code>.  However, calls to
1270      * <code>prepareInsertEmpty</code> cannot be nested, and calls to
1271      * <code>prepareWriteEmpty</code> and
1272      * <code>prepareInsertEmpty</code> may not be interspersed.
1273      *
1274      * <p> If <code>canInsertEmpty(imageIndex)</code> returns
1275      * <code>false</code>, an
1276      * <code>UnsupportedOperationException</code> will be thrown.
1277      *
1278      * <p> An <code>ImageWriteParam</code> may optionally be supplied
1279      * to control the writing process.  If <code>param</code> is
1280      * <code>null</code>, a default write param will be used.
1281      *
1282      * <p> If the supplied <code>ImageWriteParam</code> contains
1283      * optional setting values not supported by this writer (<i>e.g.</i>
1284      * progressive encoding or any format-specific settings), they
1285      * will be ignored.
1286      *
1287      * <p> The default implementation throws an
1288      * <code>IllegalStateException</code> if the output is
1289      * <code>null</code>, and otherwise throws an
1290      * <code>UnsupportedOperationException</code>.
1291      *
1292      * @param imageIndex the index at which to write the image.
1293      * @param imageType an <code>ImageTypeSpecifier</code> describing
1294      * the layout of the image.
1295      * @param width the width of the image.
1296      * @param height the height of the image.
1297      * @param imageMetadata an <code>IIOMetadata</code> object
1298      * representing image metadata, or <code>null</code>.
1299      * @param thumbnails a <code>List</code> of
1300      * <code>BufferedImage</code> thumbnails for this image, or
1301      * <code>null</code>.
1302      * @param param an <code>ImageWriteParam</code>, or
1303      * <code>null</code> to use a default
1304      * <code>ImageWriteParam</code>.
1305      *
1306      * @exception IllegalStateException if the output has not
1307      * been set.
1308      * @exception UnsupportedOperationException if
1309      * <code>canInsertEmpty(imageIndex)</code> returns
1310      * <code>false</code>.
1311      * @exception IndexOutOfBoundsException if <code>imageIndex</code>
1312      * is less than -1 or greater than the largest available index.
1313      * @exception IllegalStateException if a previous call to
1314      * <code>prepareInsertEmpty</code> has been made without a
1315      * corresponding call to <code>endInsertEmpty</code>.
1316      * @exception IllegalStateException if a previous call to
1317      * <code>prepareWriteEmpty</code> has been made without a
1318      * corresponding call to <code>endWriteEmpty</code>.
1319      * @exception IllegalArgumentException if <code>imageType</code>
1320      * is <code>null</code> or <code>thumbnails</code> contains
1321      * <code>null</code> references or objects other than
1322      * <code>BufferedImage</code>s.
1323      * @exception IllegalArgumentException if width or height are less
1324      * than 1.
1325      * @exception IOException if an I/O error occurs during writing.
1326      */
1327     public void prepareInsertEmpty(int imageIndex,
1328                                    ImageTypeSpecifier imageType,
1329                                    int width, int height,
1330                                    IIOMetadata imageMetadata,
1331                                    List<? extends BufferedImage> thumbnails,
1332                                    ImageWriteParam param) throws IOException {
1333         unsupported();
1334     }
1335 
1336     /**
1337      * Completes the insertion of a new image that was begun with a
1338      * prior call to <code>prepareInsertEmpty</code>.
1339      *
1340      * <p> The default implementation throws an
1341      * <code>IllegalStateException</code> if the output is
1342      * <code>null</code>, and otherwise throws an
1343      * <code>UnsupportedOperationException</code>.
1344      *
1345      * @exception IllegalStateException if the output has not
1346      * been set.
1347      * @exception UnsupportedOperationException if
1348      * <code>canInsertEmpty(imageIndex)</code> returns
1349      * <code>false</code>.
1350      * @exception IllegalStateException if a previous call to
1351      * <code>prepareInsertEmpty</code> without a corresponding call to
1352      * <code>endInsertEmpty</code> has not been made.
1353      * @exception IllegalStateException if a previous call to
1354      * <code>prepareWriteEmpty</code> without a corresponding call to
1355      * <code>endWriteEmpty</code> has been made.
1356      * @exception IllegalStateException if a call to
1357      * <code>prepareReplacePixels</code> has been made without a
1358      * matching call to <code>endReplacePixels</code>.
1359      * @exception IOException if an I/O error occurs during writing.
1360      */
1361     public void endInsertEmpty() throws IOException {
1362         unsupported();
1363     }
1364 
1365     // Pixel replacement
1366 
1367     /**
1368      * Returns <code>true</code> if the writer allows pixels of the
1369      * given image to be replaced using the <code>replacePixels</code>
1370      * methods.
1371      *
1372      * <p> A writer that does not support any pixel replacement may
1373      * return <code>false</code> without performing bounds checking on
1374      * the index.
1375      *
1376      * <p> The default implementation throws an
1377      * <code>IllegalStateException</code> if the output is
1378      * <code>null</code>, and otherwise returns <code>false</code>
1379      * without checking the value of <code>imageIndex</code>.
1380      *
1381      * @param imageIndex the index of the image whose pixels are to be
1382      * replaced.
1383      *
1384      * @return <code>true</code> if the pixels of the given
1385      * image can be replaced.
1386      *
1387      * @exception IllegalStateException if the output has not been
1388      * set.
1389      * @exception IndexOutOfBoundsException if the writer supports
1390      * pixel replacement in general, but <code>imageIndex</code> is
1391      * less than 0 or greater than the largest available index.
1392      * @exception IOException if an I/O error occurs during the query.
1393      */
1394     public boolean canReplacePixels(int imageIndex) throws IOException {
1395         if (getOutput() == null) {
1396             throw new IllegalStateException("getOutput() == null!");
1397         }
1398         return false;
1399     }
1400 
1401     /**
1402      * Prepares the writer to handle a series of calls to the
1403      * <code>replacePixels</code> methods.  The affected pixel area
1404      * will be clipped against the supplied
1405      *
1406      * <p> If <code>canReplacePixels</code> returns
1407      * <code>false</code>, and
1408      * <code>UnsupportedOperationException</code> will be thrown.
1409      *
1410      * <p> The default implementation throws an
1411      * <code>IllegalStateException</code> if the output is
1412      * <code>null</code>, and otherwise throws an
1413      * <code>UnsupportedOperationException</code>.
1414      *
1415      * @param imageIndex the index of the image whose pixels are to be
1416      * replaced.
1417      * @param region a <code>Rectangle</code> that will be used to clip
1418      * future pixel regions.
1419      *
1420      * @exception IllegalStateException if the output has not
1421      * been set.
1422      * @exception UnsupportedOperationException if
1423      * <code>canReplacePixels(imageIndex)</code> returns
1424      * <code>false</code>.
1425      * @exception IndexOutOfBoundsException if <code>imageIndex</code>
1426      * is less than 0 or greater than the largest available index.
1427      * @exception IllegalStateException if there is a previous call to
1428      * <code>prepareReplacePixels</code> without a matching call to
1429      * <code>endReplacePixels</code> (<i>i.e.</i>, nesting is not
1430      * allowed).
1431      * @exception IllegalArgumentException if <code>region</code> is
1432      * <code>null</code> or has a width or height less than 1.
1433      * @exception IOException if an I/O error occurs during the
1434      * preparation.
1435      */
1436     public void prepareReplacePixels(int imageIndex,
1437                                      Rectangle region)  throws IOException {
1438         unsupported();
1439     }
1440 
1441     /**
1442      * Replaces a portion of an image already present in the output
1443      * with a portion of the given image.  The image data must match,
1444      * or be convertible to, the image layout of the existing image.
1445      *
1446      * <p> The destination region is specified in the
1447      * <code>param</code> argument, and will be clipped to the image
1448      * boundaries and the region supplied to
1449      * <code>prepareReplacePixels</code>.  At least one pixel of the
1450      * source must not be clipped, or an exception is thrown.
1451      *
1452      * <p> An <code>ImageWriteParam</code> may optionally be supplied
1453      * to control the writing process.  If <code>param</code> is
1454      * <code>null</code>, a default write param will be used.
1455      *
1456      * <p> If the supplied <code>ImageWriteParam</code> contains
1457      * optional setting values not supported by this writer (<i>e.g.</i>
1458      * progressive encoding or any format-specific settings), they
1459      * will be ignored.
1460      *
1461      * <p> This method may only be called after a call to
1462      * <code>prepareReplacePixels</code>, or else an
1463      * <code>IllegalStateException</code> will be thrown.
1464      *
1465      * <p> The default implementation throws an
1466      * <code>IllegalStateException</code> if the output is
1467      * <code>null</code>, and otherwise throws an
1468      * <code>UnsupportedOperationException</code>.
1469      *
1470      * @param image a <code>RenderedImage</code> containing source
1471      * pixels.
1472      * @param param an <code>ImageWriteParam</code>, or
1473      * <code>null</code> to use a default
1474      * <code>ImageWriteParam</code>.
1475      *
1476      * @exception IllegalStateException if the output has not
1477      * been set.
1478      * @exception UnsupportedOperationException if
1479      * <code>canReplacePixels(imageIndex)</code> returns
1480      * <code>false</code>.
1481      * @exception IllegalStateException if there is no previous call to
1482      * <code>prepareReplacePixels</code> without a matching call to
1483      * <code>endReplacePixels</code>.
1484      * @exception IllegalArgumentException if any of the following are true:
1485      * <ul>
1486      * <li> <code>image</code> is <code>null</code>.
1487      * <li> <code>param</code> is <code>null</code>.
1488      * <li> the intersected region does not contain at least one pixel.
1489      * <li> the layout of <code>image</code> does not match, or this
1490      * writer cannot convert it to, the existing image layout.
1491      * </ul>
1492      * @exception IOException if an I/O error occurs during writing.
1493      */
1494     public void replacePixels(RenderedImage image, ImageWriteParam param)
1495         throws IOException {
1496         unsupported();
1497     }
1498 
1499     /**
1500      * Replaces a portion of an image already present in the output
1501      * with a portion of the given <code>Raster</code>.  The image
1502      * data must match, or be convertible to, the image layout of the
1503      * existing image.
1504      *
1505      * <p> An <code>ImageWriteParam</code> may optionally be supplied
1506      * to control the writing process.  If <code>param</code> is
1507      * <code>null</code>, a default write param will be used.
1508      *
1509      * <p> The destination region is specified in the
1510      * <code>param</code> argument, and will be clipped to the image
1511      * boundaries and the region supplied to
1512      * <code>prepareReplacePixels</code>.  At least one pixel of the
1513      * source must not be clipped, or an exception is thrown.
1514      *
1515      * <p> If the supplied <code>ImageWriteParam</code> contains
1516      * optional setting values not supported by this writer (<i>e.g.</i>
1517      * progressive encoding or any format-specific settings), they
1518      * will be ignored.
1519      *
1520      * <p> This method may only be called after a call to
1521      * <code>prepareReplacePixels</code>, or else an
1522      * <code>IllegalStateException</code> will be thrown.
1523      *
1524      * <p> The default implementation throws an
1525      * <code>IllegalStateException</code> if the output is
1526      * <code>null</code>, and otherwise throws an
1527      * <code>UnsupportedOperationException</code>.
1528      *
1529      * @param raster a <code>Raster</code> containing source
1530      * pixels.
1531      * @param param an <code>ImageWriteParam</code>, or
1532      * <code>null</code> to use a default
1533      * <code>ImageWriteParam</code>.
1534      *
1535      * @exception IllegalStateException if the output has not
1536      * been set.
1537      * @exception UnsupportedOperationException if
1538      * <code>canReplacePixels(imageIndex)</code> returns
1539      * <code>false</code>.
1540      * @exception IllegalStateException if there is no previous call to
1541      * <code>prepareReplacePixels</code> without a matching call to
1542      * <code>endReplacePixels</code>.
1543      * @exception UnsupportedOperationException if
1544      * <code>canWriteRasters</code> returns <code>false</code>.
1545      * @exception IllegalArgumentException if any of the following are true:
1546      * <ul>
1547      * <li> <code>raster</code> is <code>null</code>.
1548      * <li> <code>param</code> is <code>null</code>.
1549      * <li> the intersected region does not contain at least one pixel.
1550      * <li> the layout of <code>raster</code> does not match, or this
1551      * writer cannot convert it to, the existing image layout.
1552      * </ul>
1553      * @exception IOException if an I/O error occurs during writing.
1554      */
1555     public void replacePixels(Raster raster, ImageWriteParam param)
1556         throws IOException {
1557         unsupported();
1558     }
1559 
1560     /**
1561      * Terminates a sequence of calls to <code>replacePixels</code>.
1562      *
1563      * <p> If <code>canReplacePixels</code> returns
1564      * <code>false</code>, and
1565      * <code>UnsupportedOperationException</code> will be thrown.
1566      *
1567      * <p> The default implementation throws an
1568      * <code>IllegalStateException</code> if the output is
1569      * <code>null</code>, and otherwise throws an
1570      * <code>UnsupportedOperationException</code>.
1571      *
1572      * @exception IllegalStateException if the output has not
1573      * been set.
1574      * @exception UnsupportedOperationException if
1575      * <code>canReplacePixels(imageIndex)</code> returns
1576      * <code>false</code>.
1577      * @exception IllegalStateException if there is no previous call
1578      * to <code>prepareReplacePixels</code> without a matching call to
1579      * <code>endReplacePixels</code>.
1580      * @exception IOException if an I/O error occurs during writing.
1581      */
1582     public void endReplacePixels() throws IOException {
1583         unsupported();
1584     }
1585 
1586     // Abort
1587 
1588     /**
1589      * Requests that any current write operation be aborted.  The
1590      * contents of the output following the abort will be undefined.
1591      *
1592      * <p> Writers should call <code>clearAbortRequest</code> at the
1593      * beginning of each write operation, and poll the value of
1594      * <code>abortRequested</code> regularly during the write.
1595      */
1596     public synchronized void abort() {
1597         this.abortFlag = true;
1598     }
1599 
1600     /**
1601      * Returns <code>true</code> if a request to abort the current
1602      * write operation has been made since the writer was instantiated or
1603      * <code>clearAbortRequest</code> was called.
1604      *
1605      * @return <code>true</code> if the current write operation should
1606      * be aborted.
1607      *
1608      * @see #abort
1609      * @see #clearAbortRequest
1610      */
1611     protected synchronized boolean abortRequested() {
1612         return this.abortFlag;
1613     }
1614 
1615     /**
1616      * Clears any previous abort request.  After this method has been
1617      * called, <code>abortRequested</code> will return
1618      * <code>false</code>.
1619      *
1620      * @see #abort
1621      * @see #abortRequested
1622      */
1623     protected synchronized void clearAbortRequest() {
1624         this.abortFlag = false;
1625     }
1626 
1627     // Listeners
1628 
1629     /**
1630      * Adds an <code>IIOWriteWarningListener</code> to the list of
1631      * registered warning listeners.  If <code>listener</code> is
1632      * <code>null</code>, no exception will be thrown and no action
1633      * will be taken.  Messages sent to the given listener will be
1634      * localized, if possible, to match the current
1635      * <code>Locale</code>.  If no <code>Locale</code> has been set,
1636      * warning messages may be localized as the writer sees fit.
1637      *
1638      * @param listener an <code>IIOWriteWarningListener</code> to be
1639      * registered.
1640      *
1641      * @see #removeIIOWriteWarningListener
1642      */
1643     public void addIIOWriteWarningListener(IIOWriteWarningListener listener) {
1644         if (listener == null) {
1645             return;
1646         }
1647         warningListeners = ImageReader.addToList(warningListeners, listener);
1648         warningLocales = ImageReader.addToList(warningLocales, getLocale());
1649     }
1650 
1651     /**
1652      * Removes an <code>IIOWriteWarningListener</code> from the list
1653      * of registered warning listeners.  If the listener was not
1654      * previously registered, or if <code>listener</code> is
1655      * <code>null</code>, no exception will be thrown and no action
1656      * will be taken.
1657      *
1658      * @param listener an <code>IIOWriteWarningListener</code> to be
1659      * deregistered.
1660      *
1661      * @see #addIIOWriteWarningListener
1662      */
1663     public
1664         void removeIIOWriteWarningListener(IIOWriteWarningListener listener) {
1665         if (listener == null || warningListeners == null) {
1666             return;
1667         }
1668         int index = warningListeners.indexOf(listener);
1669         if (index != -1) {
1670             warningListeners.remove(index);
1671             warningLocales.remove(index);
1672             if (warningListeners.size() == 0) {
1673                 warningListeners = null;
1674                 warningLocales = null;
1675             }
1676         }
1677     }
1678 
1679     /**
1680      * Removes all currently registered
1681      * <code>IIOWriteWarningListener</code> objects.
1682      *
1683      * <p> The default implementation sets the
1684      * <code>warningListeners</code> and <code>warningLocales</code>
1685      * instance variables to <code>null</code>.
1686      */
1687     public void removeAllIIOWriteWarningListeners() {
1688         this.warningListeners = null;
1689         this.warningLocales = null;
1690     }
1691 
1692     /**
1693      * Adds an <code>IIOWriteProgressListener</code> to the list of
1694      * registered progress listeners.  If <code>listener</code> is
1695      * <code>null</code>, no exception will be thrown and no action
1696      * will be taken.
1697      *
1698      * @param listener an <code>IIOWriteProgressListener</code> to be
1699      * registered.
1700      *
1701      * @see #removeIIOWriteProgressListener
1702      */
1703     public void
1704         addIIOWriteProgressListener(IIOWriteProgressListener listener) {
1705         if (listener == null) {
1706             return;
1707         }
1708         progressListeners = ImageReader.addToList(progressListeners, listener);
1709     }
1710 
1711     /**
1712      * Removes an <code>IIOWriteProgressListener</code> from the list
1713      * of registered progress listeners.  If the listener was not
1714      * previously registered, or if <code>listener</code> is
1715      * <code>null</code>, no exception will be thrown and no action
1716      * will be taken.
1717      *
1718      * @param listener an <code>IIOWriteProgressListener</code> to be
1719      * deregistered.
1720      *
1721      * @see #addIIOWriteProgressListener
1722      */
1723     public void
1724         removeIIOWriteProgressListener(IIOWriteProgressListener listener) {
1725         if (listener == null || progressListeners == null) {
1726             return;
1727         }
1728         progressListeners =
1729             ImageReader.removeFromList(progressListeners, listener);
1730     }
1731 
1732     /**
1733      * Removes all currently registered
1734      * <code>IIOWriteProgressListener</code> objects.
1735      *
1736      * <p> The default implementation sets the
1737      * <code>progressListeners</code> instance variable to
1738      * <code>null</code>.
1739      */
1740     public void removeAllIIOWriteProgressListeners() {
1741         this.progressListeners = null;
1742     }
1743 
1744     /**
1745      * Broadcasts the start of an image write to all registered
1746      * <code>IIOWriteProgressListener</code>s by calling their
1747      * <code>imageStarted</code> method.  Subclasses may use this
1748      * method as a convenience.
1749      *
1750      * @param imageIndex the index of the image about to be written.
1751      */
1752     protected void processImageStarted(int imageIndex) {
1753         if (progressListeners == null) {
1754             return;
1755         }
1756         int numListeners = progressListeners.size();
1757         for (int i = 0; i < numListeners; i++) {
1758             IIOWriteProgressListener listener =
1759                 (IIOWriteProgressListener)progressListeners.get(i);
1760             listener.imageStarted(this, imageIndex);
1761         }
1762     }
1763 
1764     /**
1765      * Broadcasts the current percentage of image completion to all
1766      * registered <code>IIOWriteProgressListener</code>s by calling
1767      * their <code>imageProgress</code> method.  Subclasses may use
1768      * this method as a convenience.
1769      *
1770      * @param percentageDone the current percentage of completion,
1771      * as a <code>float</code>.
1772      */
1773     protected void processImageProgress(float percentageDone) {
1774         if (progressListeners == null) {
1775             return;
1776         }
1777         int numListeners = progressListeners.size();
1778         for (int i = 0; i < numListeners; i++) {
1779             IIOWriteProgressListener listener =
1780                 (IIOWriteProgressListener)progressListeners.get(i);
1781             listener.imageProgress(this, percentageDone);
1782         }
1783     }
1784 
1785     /**
1786      * Broadcasts the completion of an image write to all registered
1787      * <code>IIOWriteProgressListener</code>s by calling their
1788      * <code>imageComplete</code> method.  Subclasses may use this
1789      * method as a convenience.
1790      */
1791     protected void processImageComplete() {
1792         if (progressListeners == null) {
1793             return;
1794         }
1795         int numListeners = progressListeners.size();
1796         for (int i = 0; i < numListeners; i++) {
1797             IIOWriteProgressListener listener =
1798                 (IIOWriteProgressListener)progressListeners.get(i);
1799             listener.imageComplete(this);
1800         }
1801     }
1802 
1803     /**
1804      * Broadcasts the start of a thumbnail write to all registered
1805      * <code>IIOWriteProgressListener</code>s by calling their
1806      * <code>thumbnailStarted</code> method.  Subclasses may use this
1807      * method as a convenience.
1808      *
1809      * @param imageIndex the index of the image associated with the
1810      * thumbnail.
1811      * @param thumbnailIndex the index of the thumbnail.
1812      */
1813     protected void processThumbnailStarted(int imageIndex,
1814                                            int thumbnailIndex) {
1815         if (progressListeners == null) {
1816             return;
1817         }
1818         int numListeners = progressListeners.size();
1819         for (int i = 0; i < numListeners; i++) {
1820             IIOWriteProgressListener listener =
1821                 (IIOWriteProgressListener)progressListeners.get(i);
1822             listener.thumbnailStarted(this, imageIndex, thumbnailIndex);
1823         }
1824     }
1825 
1826     /**
1827      * Broadcasts the current percentage of thumbnail completion to
1828      * all registered <code>IIOWriteProgressListener</code>s by calling
1829      * their <code>thumbnailProgress</code> method.  Subclasses may
1830      * use this method as a convenience.
1831      *
1832      * @param percentageDone the current percentage of completion,
1833      * as a <code>float</code>.
1834      */
1835     protected void processThumbnailProgress(float percentageDone) {
1836         if (progressListeners == null) {
1837             return;
1838         }
1839         int numListeners = progressListeners.size();
1840         for (int i = 0; i < numListeners; i++) {
1841             IIOWriteProgressListener listener =
1842                 (IIOWriteProgressListener)progressListeners.get(i);
1843             listener.thumbnailProgress(this, percentageDone);
1844         }
1845     }
1846 
1847     /**
1848      * Broadcasts the completion of a thumbnail write to all registered
1849      * <code>IIOWriteProgressListener</code>s by calling their
1850      * <code>thumbnailComplete</code> method.  Subclasses may use this
1851      * method as a convenience.
1852      */
1853     protected void processThumbnailComplete() {
1854         if (progressListeners == null) {
1855             return;
1856         }
1857         int numListeners = progressListeners.size();
1858         for (int i = 0; i < numListeners; i++) {
1859             IIOWriteProgressListener listener =
1860                 (IIOWriteProgressListener)progressListeners.get(i);
1861             listener.thumbnailComplete(this);
1862         }
1863     }
1864 
1865     /**
1866      * Broadcasts that the write has been aborted to all registered
1867      * <code>IIOWriteProgressListener</code>s by calling their
1868      * <code>writeAborted</code> method.  Subclasses may use this
1869      * method as a convenience.
1870      */
1871     protected void processWriteAborted() {
1872         if (progressListeners == null) {
1873             return;
1874         }
1875         int numListeners = progressListeners.size();
1876         for (int i = 0; i < numListeners; i++) {
1877             IIOWriteProgressListener listener =
1878                 (IIOWriteProgressListener)progressListeners.get(i);
1879             listener.writeAborted(this);
1880         }
1881     }
1882 
1883     /**
1884      * Broadcasts a warning message to all registered
1885      * <code>IIOWriteWarningListener</code>s by calling their
1886      * <code>warningOccurred</code> method.  Subclasses may use this
1887      * method as a convenience.
1888      *
1889      * @param imageIndex the index of the image on which the warning
1890      * occurred.
1891      * @param warning the warning message.
1892      *
1893      * @exception IllegalArgumentException if <code>warning</code>
1894      * is <code>null</code>.
1895      */
1896     protected void processWarningOccurred(int imageIndex,
1897                                           String warning) {
1898         if (warningListeners == null) {
1899             return;
1900         }
1901         if (warning == null) {
1902             throw new IllegalArgumentException("warning == null!");
1903         }
1904         int numListeners = warningListeners.size();
1905         for (int i = 0; i < numListeners; i++) {
1906             IIOWriteWarningListener listener =
1907                 (IIOWriteWarningListener)warningListeners.get(i);
1908 
1909             listener.warningOccurred(this, imageIndex, warning);
1910         }
1911     }
1912 
1913     /**
1914      * Broadcasts a localized warning message to all registered
1915      * <code>IIOWriteWarningListener</code>s by calling their
1916      * <code>warningOccurred</code> method with a string taken
1917      * from a <code>ResourceBundle</code>.  Subclasses may use this
1918      * method as a convenience.
1919      *
1920      * @param imageIndex the index of the image on which the warning
1921      * occurred.
1922      * @param baseName the base name of a set of
1923      * <code>ResourceBundle</code>s containing localized warning
1924      * messages.
1925      * @param keyword the keyword used to index the warning message
1926      * within the set of <code>ResourceBundle</code>s.
1927      *
1928      * @exception IllegalArgumentException if <code>baseName</code>
1929      * is <code>null</code>.
1930      * @exception IllegalArgumentException if <code>keyword</code>
1931      * is <code>null</code>.
1932      * @exception IllegalArgumentException if no appropriate
1933      * <code>ResourceBundle</code> may be located.
1934      * @exception IllegalArgumentException if the named resource is
1935      * not found in the located <code>ResourceBundle</code>.
1936      * @exception IllegalArgumentException if the object retrieved
1937      * from the <code>ResourceBundle</code> is not a
1938      * <code>String</code>.
1939      */
1940     protected void processWarningOccurred(int imageIndex,
1941                                           String baseName,
1942                                           String keyword) {
1943         if (warningListeners == null) {
1944             return;
1945         }
1946         if (baseName == null) {
1947             throw new IllegalArgumentException("baseName == null!");
1948         }
1949         if (keyword == null) {
1950             throw new IllegalArgumentException("keyword == null!");
1951         }
1952         int numListeners = warningListeners.size();
1953         for (int i = 0; i < numListeners; i++) {
1954             IIOWriteWarningListener listener =
1955                 (IIOWriteWarningListener)warningListeners.get(i);
1956             Locale locale = (Locale)warningLocales.get(i);
1957             if (locale == null) {
1958                 locale = Locale.getDefault();
1959             }
1960 
1961             /**
1962              * If an applet supplies an implementation of ImageWriter and
1963              * resource bundles, then the resource bundle will need to be
1964              * accessed via the applet class loader. So first try the context
1965              * class loader to locate the resource bundle.
1966              * If that throws MissingResourceException, then try the
1967              * system class loader.
1968              */
1969             ClassLoader loader = (ClassLoader)
1970                 java.security.AccessController.doPrivileged(
1971                    new java.security.PrivilegedAction() {
1972                       public Object run() {
1973                         return Thread.currentThread().getContextClassLoader();
1974                       }
1975                 });
1976 
1977             ResourceBundle bundle = null;
1978             try {
1979                 bundle = ResourceBundle.getBundle(baseName, locale, loader);
1980             } catch (MissingResourceException mre) {
1981                 try {
1982                     bundle = ResourceBundle.getBundle(baseName, locale);
1983                 } catch (MissingResourceException mre1) {
1984                     throw new IllegalArgumentException("Bundle not found!");
1985                 }
1986             }
1987 
1988             String warning = null;
1989             try {
1990                 warning = bundle.getString(keyword);
1991             } catch (ClassCastException cce) {
1992                 throw new IllegalArgumentException("Resource is not a String!");
1993             } catch (MissingResourceException mre) {
1994                 throw new IllegalArgumentException("Resource is missing!");
1995             }
1996 
1997             listener.warningOccurred(this, imageIndex, warning);
1998         }
1999     }
2000 
2001     // State management
2002 
2003     /**
2004      * Restores the <code>ImageWriter</code> to its initial state.
2005      *
2006      * <p> The default implementation calls
2007      * <code>setOutput(null)</code>, <code>setLocale(null)</code>,
2008      * <code>removeAllIIOWriteWarningListeners()</code>,
2009      * <code>removeAllIIOWriteProgressListeners()</code>, and
2010      * <code>clearAbortRequest</code>.
2011      */
2012     public void reset() {
2013         setOutput(null);
2014         setLocale(null);
2015         removeAllIIOWriteWarningListeners();
2016         removeAllIIOWriteProgressListeners();
2017         clearAbortRequest();
2018     }
2019 
2020     /**
2021      * Allows any resources held by this object to be released.  The
2022      * result of calling any other method (other than
2023      * <code>finalize</code>) subsequent to a call to this method
2024      * is undefined.
2025      *
2026      * <p>It is important for applications to call this method when they
2027      * know they will no longer be using this <code>ImageWriter</code>.
2028      * Otherwise, the writer may continue to hold on to resources
2029      * indefinitely.
2030      *
2031      * <p>The default implementation of this method in the superclass does
2032      * nothing.  Subclass implementations should ensure that all resources,
2033      * especially native resources, are released.
2034      */
2035     public void dispose() {
2036     }
2037 }