1 /*
   2  * Copyright (c) 2000, 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.Point;
  29 import java.awt.Rectangle;
  30 
  31 /**
  32  * A superclass of all classes describing how streams should be
  33  * decoded or encoded.  This class contains all the variables and
  34  * methods that are shared by <code>ImageReadParam</code> and
  35  * <code>ImageWriteParam</code>.
  36  *
  37  * <p> This class provides mechanisms to specify a source region and a
  38  * destination region.  When reading, the source is the stream and
  39  * the in-memory image is the destination.  When writing, these are
  40  * reversed.  In the case of writing, destination regions may be used
  41  * only with a writer that supports pixel replacement.
  42  * <p>
  43  * Decimation subsampling may be specified for both readers
  44  * and writers, using a movable subsampling grid.
  45  * <p>
  46  * Subsets of the source and destination bands may be selected.
  47  *
  48  */
  49 public abstract class IIOParam {
  50 
  51     /**
  52      * The source region, on <code>null</code> if none is set.
  53      */
  54     protected Rectangle sourceRegion = null;
  55 
  56     /**
  57      * The decimation subsampling to be applied in the horizontal
  58      * direction.  By default, the value is <code>1</code>.
  59      * The value must not be negative or 0.
  60      */
  61     protected int sourceXSubsampling = 1;
  62 
  63     /**
  64      * The decimation subsampling to be applied in the vertical
  65      * direction.  By default, the value is <code>1</code>.
  66      * The value must not be negative or 0.
  67      */
  68     protected int sourceYSubsampling = 1;
  69 
  70     /**
  71      * A horizontal offset to be applied to the subsampling grid before
  72      * subsampling.  The first pixel to be used will be offset this
  73      * amount from the origin of the region, or of the image if no
  74      * region is specified.
  75      */
  76     protected int subsamplingXOffset = 0;
  77 
  78     /**
  79      * A vertical offset to be applied to the subsampling grid before
  80      * subsampling.  The first pixel to be used will be offset this
  81      * amount from the origin of the region, or of the image if no
  82      * region is specified.
  83      */
  84     protected int subsamplingYOffset = 0;
  85 
  86     /**
  87      * An array of <code>int</code>s indicating which source bands
  88      * will be used, or <code>null</code>.  If <code>null</code>, the
  89      * set of source bands to be used is as described in the comment
  90      * for the <code>setSourceBands</code> method.  No value should
  91      * be allowed to be negative.
  92      */
  93     protected int[] sourceBands = null;
  94 
  95     /**
  96      * An <code>ImageTypeSpecifier</code> to be used to generate a
  97      * destination image when reading, or to set the output color type
  98      * when writing.  If non has been set the value will be
  99      * <code>null</code>.  By default, the value is <code>null</code>.
 100      */
 101     protected ImageTypeSpecifier destinationType = null;
 102 
 103     /**
 104      * The offset in the destination where the upper-left decoded
 105      * pixel should be placed.  By default, the value is (0, 0).
 106      */
 107     protected Point destinationOffset = new Point(0, 0);
 108 
 109     /**
 110      * The default <code>IIOParamController</code> that will be
 111      * used to provide settings for this <code>IIOParam</code>
 112      * object when the <code>activateController</code> method
 113      * is called.  This default should be set by subclasses
 114      * that choose to provide their own default controller,
 115      * usually a GUI, for setting parameters.
 116      *
 117      * @see IIOParamController
 118      * @see #getDefaultController
 119      * @see #activateController
 120      */
 121     protected IIOParamController defaultController = null;
 122 
 123     /**
 124      * The <code>IIOParamController</code> that will be
 125      * used to provide settings for this <code>IIOParam</code>
 126      * object when the <code>activateController</code> method
 127      * is called.  This value overrides any default controller,
 128      * even when null.
 129      *
 130      * @see IIOParamController
 131      * @see #setController(IIOParamController)
 132      * @see #hasController()
 133      * @see #activateController()
 134      */
 135     protected IIOParamController controller = null;
 136 
 137     /**
 138      * Protected constructor may be called only by subclasses.
 139      */
 140     protected IIOParam() {
 141         controller = defaultController;
 142     }
 143 
 144     /**
 145      * Sets the source region of interest.  The region of interest is
 146      * described as a rectangle, with the upper-left corner of the
 147      * source image as pixel (0, 0) and increasing values down and to
 148      * the right.  The actual number of pixels used will depend on
 149      * the subsampling factors set by <code>setSourceSubsampling</code>.
 150      * If subsampling has been set such that this number is zero,
 151      * an <code>IllegalStateException</code> will be thrown.
 152      *
 153      * <p> The source region of interest specified by this method will
 154      * be clipped as needed to fit within the source bounds, as well
 155      * as the destination offsets, width, and height at the time of
 156      * actual I/O.
 157      *
 158      * <p> A value of <code>null</code> for <code>sourceRegion</code>
 159      * will remove any region specification, causing the entire image
 160      * to be used.
 161      *
 162      * @param sourceRegion a <code>Rectangle</code> specifying the
 163      * source region of interest, or <code>null</code>.
 164      *
 165      * @exception IllegalArgumentException if
 166      * <code>sourceRegion</code> is non-<code>null</code> and either
 167      * <code>sourceRegion.x</code> or <code>sourceRegion.y</code> is
 168      * negative.
 169      * @exception IllegalArgumentException if
 170      * <code>sourceRegion</code> is non-<code>null</code> and either
 171      * <code>sourceRegion.width</code> or
 172      * <code>sourceRegion.height</code> is negative or 0.
 173      * @exception IllegalStateException if subsampling is such that
 174      * this region will have a subsampled width or height of zero.
 175      *
 176      * @see #getSourceRegion
 177      * @see #setSourceSubsampling
 178      * @see ImageReadParam#setDestinationOffset
 179      * @see ImageReadParam#getDestinationOffset
 180      */
 181     public void setSourceRegion(Rectangle sourceRegion) {
 182         if (sourceRegion == null) {
 183             this.sourceRegion = null;
 184             return;
 185         }
 186 
 187         if (sourceRegion.x < 0) {
 188             throw new IllegalArgumentException("sourceRegion.x < 0!");
 189         }
 190         if (sourceRegion.y < 0){
 191             throw new IllegalArgumentException("sourceRegion.y < 0!");
 192         }
 193         if (sourceRegion.width <= 0) {
 194             throw new IllegalArgumentException("sourceRegion.width <= 0!");
 195         }
 196         if (sourceRegion.height <= 0) {
 197             throw new IllegalArgumentException("sourceRegion.height <= 0!");
 198         }
 199 
 200         // Throw an IllegalStateException if region falls between subsamples
 201         if (sourceRegion.width <= subsamplingXOffset) {
 202             throw new IllegalStateException
 203                 ("sourceRegion.width <= subsamplingXOffset!");
 204         }
 205         if (sourceRegion.height <= subsamplingYOffset) {
 206             throw new IllegalStateException
 207                 ("sourceRegion.height <= subsamplingYOffset!");
 208         }
 209 
 210         this.sourceRegion = (Rectangle)sourceRegion.clone();
 211     }
 212 
 213     /**
 214      * Returns the source region to be used.  The returned value is
 215      * that set by the most recent call to
 216      * <code>setSourceRegion</code>, and will be <code>null</code> if
 217      * there is no region set.
 218      *
 219      * @return the source region of interest as a
 220      * <code>Rectangle</code>, or <code>null</code>.
 221      *
 222      * @see #setSourceRegion
 223      */
 224     public Rectangle getSourceRegion() {
 225         if (sourceRegion == null) {
 226             return null;
 227         }
 228         return (Rectangle)sourceRegion.clone();
 229     }
 230 
 231     /**
 232      * Specifies a decimation subsampling to apply on I/O.  The
 233      * <code>sourceXSubsampling</code> and
 234      * <code>sourceYSubsampling</code> parameters specify the
 235      * subsampling period (<i>i.e.</i>, the number of rows and columns
 236      * to advance after every source pixel).  Specifically, a period of
 237      * 1 will use every row or column; a period of 2 will use every
 238      * other row or column.  The <code>subsamplingXOffset</code> and
 239      * <code>subsamplingYOffset</code> parameters specify an offset
 240      * from the region (or image) origin for the first subsampled pixel.
 241      * Adjusting the origin of the subsample grid is useful for avoiding
 242      * seams when subsampling a very large source image into destination
 243      * regions that will be assembled into a complete subsampled image.
 244      * Most users will want to simply leave these parameters at 0.
 245      *
 246      * <p> The number of pixels and scanlines to be used are calculated
 247      * as follows.
 248      * <p>
 249      * The number of subsampled pixels in a scanline is given by
 250      * <p>
 251      * <code>truncate[(width - subsamplingXOffset + sourceXSubsampling - 1)
 252      * / sourceXSubsampling]</code>.
 253      * <p>
 254      * If the region is such that this width is zero, an
 255      * <code>IllegalStateException</code> is thrown.
 256      * <p>
 257      * The number of scanlines to be used can be computed similarly.
 258      *
 259      * <p>The ability to set the subsampling grid to start somewhere
 260      * other than the source region origin is useful if the
 261      * region is being used to create subsampled tiles of a large image,
 262      * where the tile width and height are not multiples of the
 263      * subsampling periods.  If the subsampling grid does not remain
 264      * consistent from tile to tile, there will be artifacts at the tile
 265      * boundaries.  By adjusting the subsampling grid offset for each
 266      * tile to compensate, these artifacts can be avoided.  The tradeoff
 267      * is that in order to avoid these artifacts, the tiles are not all
 268      * the same size.  The grid offset to use in this case is given by:
 269      * <br>
 270      * grid offset = [period - (region offset modulo period)] modulo period)
 271      *
 272      * <p> If either <code>sourceXSubsampling</code> or
 273      * <code>sourceYSubsampling</code> is 0 or negative, an
 274      * <code>IllegalArgumentException</code> will be thrown.
 275      *
 276      * <p> If either <code>subsamplingXOffset</code> or
 277      * <code>subsamplingYOffset</code> is negative or greater than or
 278      * equal to the corresponding period, an
 279      * <code>IllegalArgumentException</code> will be thrown.
 280      *
 281      * <p> There is no <code>unsetSourceSubsampling</code> method;
 282      * simply call <code>setSourceSubsampling(1, 1, 0, 0)</code> to
 283      * restore default values.
 284      *
 285      * @param sourceXSubsampling the number of columns to advance
 286      * between pixels.
 287      * @param sourceYSubsampling the number of rows to advance between
 288      * pixels.
 289      * @param subsamplingXOffset the horizontal offset of the first subsample
 290      * within the region, or within the image if no region is set.
 291      * @param subsamplingYOffset the horizontal offset of the first subsample
 292      * within the region, or within the image if no region is set.
 293      * @exception IllegalArgumentException if either period is
 294      * negative or 0, or if either grid offset is negative or greater than
 295      * the corresponding period.
 296      * @exception IllegalStateException if the source region is such that
 297      * the subsampled output would contain no pixels.
 298      */
 299     public void setSourceSubsampling(int sourceXSubsampling,
 300                                      int sourceYSubsampling,
 301                                      int subsamplingXOffset,
 302                                      int subsamplingYOffset) {
 303         if (sourceXSubsampling <= 0) {
 304             throw new IllegalArgumentException("sourceXSubsampling <= 0!");
 305         }
 306         if (sourceYSubsampling <= 0) {
 307             throw new IllegalArgumentException("sourceYSubsampling <= 0!");
 308         }
 309         if (subsamplingXOffset < 0 ||
 310             subsamplingXOffset >= sourceXSubsampling) {
 311             throw new IllegalArgumentException
 312                 ("subsamplingXOffset out of range!");
 313         }
 314         if (subsamplingYOffset < 0 ||
 315             subsamplingYOffset >= sourceYSubsampling) {
 316             throw new IllegalArgumentException
 317                 ("subsamplingYOffset out of range!");
 318         }
 319 
 320         // Throw an IllegalStateException if region falls between subsamples
 321         if (sourceRegion != null) {
 322             if (subsamplingXOffset >= sourceRegion.width ||
 323                 subsamplingYOffset >= sourceRegion.height) {
 324                 throw new IllegalStateException("region contains no pixels!");
 325             }
 326         }
 327 
 328         this.sourceXSubsampling = sourceXSubsampling;
 329         this.sourceYSubsampling = sourceYSubsampling;
 330         this.subsamplingXOffset = subsamplingXOffset;
 331         this.subsamplingYOffset = subsamplingYOffset;
 332     }
 333 
 334     /**
 335      * Returns the number of source columns to advance for each pixel.
 336      *
 337      * <p>If <code>setSourceSubsampling</code> has not been called, 1
 338      * is returned (which is the correct value).
 339      *
 340      * @return the source subsampling X period.
 341      *
 342      * @see #setSourceSubsampling
 343      * @see #getSourceYSubsampling
 344      */
 345     public int getSourceXSubsampling() {
 346         return sourceXSubsampling;
 347     }
 348 
 349     /**
 350      * Returns the number of rows to advance for each pixel.
 351      *
 352      * <p>If <code>setSourceSubsampling</code> has not been called, 1
 353      * is returned (which is the correct value).
 354      *
 355      * @return the source subsampling Y period.
 356      *
 357      * @see #setSourceSubsampling
 358      * @see #getSourceXSubsampling
 359      */
 360     public int getSourceYSubsampling() {
 361         return sourceYSubsampling;
 362     }
 363 
 364     /**
 365      * Returns the horizontal offset of the subsampling grid.
 366      *
 367      * <p>If <code>setSourceSubsampling</code> has not been called, 0
 368      * is returned (which is the correct value).
 369      *
 370      * @return the source subsampling grid X offset.
 371      *
 372      * @see #setSourceSubsampling
 373      * @see #getSubsamplingYOffset
 374      */
 375     public int getSubsamplingXOffset() {
 376         return subsamplingXOffset;
 377     }
 378 
 379     /**
 380      * Returns the vertical offset of the subsampling grid.
 381      *
 382      * <p>If <code>setSourceSubsampling</code> has not been called, 0
 383      * is returned (which is the correct value).
 384      *
 385      * @return the source subsampling grid Y offset.
 386      *
 387      * @see #setSourceSubsampling
 388      * @see #getSubsamplingXOffset
 389      */
 390     public int getSubsamplingYOffset() {
 391         return subsamplingYOffset;
 392     }
 393 
 394     /**
 395      * Sets the indices of the source bands to be used.  Duplicate
 396      * indices are not allowed.
 397      *
 398      * <p> A <code>null</code> value indicates that all source bands
 399      * will be used.
 400      *
 401      * <p> At the time of reading, an
 402      * <code>IllegalArgumentException</code> will be thrown by the
 403      * reader or writer if a value larger than the largest available
 404      * source band index has been specified or if the number of source
 405      * bands and destination bands to be used differ.  The
 406      * <code>ImageReader.checkReadParamBandSettings</code> method may
 407      * be used to automate this test.
 408      *
 409      * <p> Semantically, a copy is made of the array; changes to the
 410      * array contents subsequent to this call have no effect on
 411      * this <code>IIOParam</code>.
 412      *
 413      * @param sourceBands an array of integer band indices to be
 414      * used.
 415      *
 416      * @exception IllegalArgumentException if <code>sourceBands</code>
 417      * contains a negative or duplicate value.
 418      *
 419      * @see #getSourceBands
 420      * @see ImageReadParam#setDestinationBands
 421      * @see ImageReader#checkReadParamBandSettings
 422      */
 423     public void setSourceBands(int[] sourceBands) {
 424         if (sourceBands == null) {
 425             this.sourceBands = null;
 426         } else {
 427             int numBands = sourceBands.length;
 428             for (int i = 0; i < numBands; i++) {
 429                 int band = sourceBands[i];
 430                 if (band < 0) {
 431                     throw new IllegalArgumentException("Band value < 0!");
 432                 }
 433                 for (int j = i + 1; j < numBands; j++) {
 434                     if (band == sourceBands[j]) {
 435                         throw new IllegalArgumentException("Duplicate band value!");
 436                     }
 437                 }
 438 
 439             }
 440             this.sourceBands = (sourceBands.clone());
 441         }
 442     }
 443 
 444     /**
 445      * Returns the set of of source bands to be used. The returned
 446      * value is that set by the most recent call to
 447      * <code>setSourceBands</code>, or <code>null</code> if there have
 448      * been no calls to <code>setSourceBands</code>.
 449      *
 450      * <p> Semantically, the array returned is a copy; changes to
 451      * array contents subsequent to this call have no effect on this
 452      * <code>IIOParam</code>.
 453      *
 454      * @return the set of source bands to be used, or
 455      * <code>null</code>.
 456      *
 457      * @see #setSourceBands
 458      */
 459     public int[] getSourceBands() {
 460         if (sourceBands == null) {
 461             return null;
 462         }
 463         return (sourceBands.clone());
 464     }
 465 
 466     /**
 467      * Sets the desired image type for the destination image, using an
 468      * <code>ImageTypeSpecifier</code>.
 469      *
 470      * <p> When reading, if the layout of the destination has been set
 471      * using this method, each call to an <code>ImageReader</code>
 472      * <code>read</code> method will return a new
 473      * <code>BufferedImage</code> using the format specified by the
 474      * supplied type specifier.  As a side effect, any destination
 475      * <code>BufferedImage</code> set by
 476      * <code>ImageReadParam.setDestination(BufferedImage)</code> will
 477      * no longer be set as the destination.  In other words, this
 478      * method may be thought of as calling
 479      * <code>setDestination((BufferedImage)null)</code>.
 480      *
 481      * <p> When writing, the destination type maybe used to determine
 482      * the color type of the image.  The <code>SampleModel</code>
 483      * information will be ignored, and may be <code>null</code>.  For
 484      * example, a 4-banded image could represent either CMYK or RGBA
 485      * data.  If a destination type is set, its
 486      * <code>ColorModel</code> will override any
 487      * <code>ColorModel</code> on the image itself.  This is crucial
 488      * when <code>setSourceBands</code> is used since the image's
 489      * <code>ColorModel</code> will refer to the entire image rather
 490      * than to the subset of bands being written.
 491      *
 492      * @param destinationType the <code>ImageTypeSpecifier</code> to
 493      * be used to determine the destination layout and color type.
 494      *
 495      * @see #getDestinationType
 496      */
 497     public void setDestinationType(ImageTypeSpecifier destinationType) {
 498         this.destinationType = destinationType;
 499     }
 500 
 501     /**
 502      * Returns the type of image to be returned by the read, if one
 503      * was set by a call to
 504      * <code>setDestination(ImageTypeSpecifier)</code>, as an
 505      * <code>ImageTypeSpecifier</code>.  If none was set,
 506      * <code>null</code> is returned.
 507      *
 508      * @return an <code>ImageTypeSpecifier</code> describing the
 509      * destination type, or <code>null</code>.
 510      *
 511      * @see #setDestinationType
 512      */
 513     public ImageTypeSpecifier getDestinationType() {
 514         return destinationType;
 515     }
 516 
 517     /**
 518      * Specifies the offset in the destination image at which future
 519      * decoded pixels are to be placed, when reading, or where a
 520      * region will be written, when writing.
 521      *
 522      * <p> When reading, the region to be written within the
 523      * destination <code>BufferedImage</code> will start at this
 524      * offset and have a width and height determined by the source
 525      * region of interest, the subsampling parameters, and the
 526      * destination bounds.
 527      *
 528      * <p> Normal writes are not affected by this method, only writes
 529      * performed using <code>ImageWriter.replacePixels</code>.  For
 530      * such writes, the offset specified is within the output stream
 531      * image whose pixels are being modified.
 532      *
 533      * <p> There is no <code>unsetDestinationOffset</code> method;
 534      * simply call <code>setDestinationOffset(new Point(0, 0))</code> to
 535      * restore default values.
 536      *
 537      * @param destinationOffset the offset in the destination, as a
 538      * <code>Point</code>.
 539      *
 540      * @exception IllegalArgumentException if
 541      * <code>destinationOffset</code> is <code>null</code>.
 542      *
 543      * @see #getDestinationOffset
 544      * @see ImageWriter#replacePixels
 545      */
 546     public void setDestinationOffset(Point destinationOffset) {
 547         if (destinationOffset == null) {
 548             throw new IllegalArgumentException("destinationOffset == null!");
 549         }
 550         this.destinationOffset = (Point)destinationOffset.clone();
 551     }
 552 
 553     /**
 554      * Returns the offset in the destination image at which pixels are
 555      * to be placed.
 556      *
 557      * <p> If <code>setDestinationOffsets</code> has not been called,
 558      * a <code>Point</code> with zero X and Y values is returned
 559      * (which is the correct value).
 560      *
 561      * @return the destination offset as a <code>Point</code>.
 562      *
 563      * @see #setDestinationOffset
 564      */
 565     public Point getDestinationOffset() {
 566         return (Point)destinationOffset.clone();
 567     }
 568 
 569     /**
 570      * Sets the <code>IIOParamController</code> to be used
 571      * to provide settings for this <code>IIOParam</code>
 572      * object when the <code>activateController</code> method
 573      * is called, overriding any default controller.  If the
 574      * argument is <code>null</code>, no controller will be
 575      * used, including any default.  To restore the default, use
 576      * <code>setController(getDefaultController())</code>.
 577      *
 578      * @param controller An appropriate
 579      * <code>IIOParamController</code>, or <code>null</code>.
 580      *
 581      * @see IIOParamController
 582      * @see #getController
 583      * @see #getDefaultController
 584      * @see #hasController
 585      * @see #activateController()
 586      */
 587     public void setController(IIOParamController controller) {
 588         this.controller = controller;
 589     }
 590 
 591     /**
 592      * Returns whatever <code>IIOParamController</code> is currently
 593      * installed.  This could be the default if there is one,
 594      * <code>null</code>, or the argument of the most recent call
 595      * to <code>setController</code>.
 596      *
 597      * @return the currently installed
 598      * <code>IIOParamController</code>, or <code>null</code>.
 599      *
 600      * @see IIOParamController
 601      * @see #setController
 602      * @see #getDefaultController
 603      * @see #hasController
 604      * @see #activateController()
 605      */
 606     public IIOParamController getController() {
 607         return controller;
 608     }
 609 
 610     /**
 611      * Returns the default <code>IIOParamController</code>, if there
 612      * is one, regardless of the currently installed controller.  If
 613      * there is no default controller, returns <code>null</code>.
 614      *
 615      * @return the default <code>IIOParamController</code>, or
 616      * <code>null</code>.
 617      *
 618      * @see IIOParamController
 619      * @see #setController(IIOParamController)
 620      * @see #getController
 621      * @see #hasController
 622      * @see #activateController()
 623      */
 624     public IIOParamController getDefaultController() {
 625         return defaultController;
 626     }
 627 
 628     /**
 629      * Returns <code>true</code> if there is a controller installed
 630      * for this <code>IIOParam</code> object.  This will return
 631      * <code>true</code> if <code>getController</code> would not
 632      * return <code>null</code>.
 633      *
 634      * @return <code>true</code> if a controller is installed.
 635      *
 636      * @see IIOParamController
 637      * @see #setController(IIOParamController)
 638      * @see #getController
 639      * @see #getDefaultController
 640      * @see #activateController()
 641      */
 642     public boolean hasController() {
 643         return (controller != null);
 644     }
 645 
 646     /**
 647      * Activates the installed <code>IIOParamController</code> for
 648      * this <code>IIOParam</code> object and returns the resulting
 649      * value.  When this method returns <code>true</code>, all values
 650      * for this <code>IIOParam</code> object will be ready for the
 651      * next read or write operation.  If <code>false</code> is
 652      * returned, no settings in this object will have been disturbed
 653      * (<i>i.e.</i>, the user canceled the operation).
 654      *
 655      * <p> Ordinarily, the controller will be a GUI providing a user
 656      * interface for a subclass of <code>IIOParam</code> for a
 657      * particular plug-in.  Controllers need not be GUIs, however.
 658      *
 659      * @return <code>true</code> if the controller completed normally.
 660      *
 661      * @exception IllegalStateException if there is no controller
 662      * currently installed.
 663      *
 664      * @see IIOParamController
 665      * @see #setController(IIOParamController)
 666      * @see #getController
 667      * @see #getDefaultController
 668      * @see #hasController
 669      */
 670     public boolean activateController() {
 671         if (!hasController()) {
 672             throw new IllegalStateException("hasController() == false!");
 673         }
 674         return getController().activate(this);
 675     }
 676 }