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