1 /*
   2  * Copyright (c) 1999, 2011, 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 java.awt;
  27 
  28 /**
  29  * A set of attributes which control a print job.
  30  * <p>
  31  * Instances of this class control the number of copies, default selection,
  32  * destination, print dialog, file and printer names, page ranges, multiple
  33  * document handling (including collation), and multi-page imposition (such
  34  * as duplex) of every print job which uses the instance. Attribute names are
  35  * compliant with the Internet Printing Protocol (IPP) 1.1 where possible.
  36  * Attribute values are partially compliant where possible.
  37  * <p>
  38  * To use a method which takes an inner class type, pass a reference to
  39  * one of the constant fields of the inner class. Client code cannot create
  40  * new instances of the inner class types because none of those classes
  41  * has a public constructor. For example, to set the print dialog type to
  42  * the cross-platform, pure Java print dialog, use the following code:
  43  * <pre>
  44  * import java.awt.JobAttributes;
  45  *
  46  * public class PureJavaPrintDialogExample {
  47  *     public void setPureJavaPrintDialog(JobAttributes jobAttributes) {
  48  *         jobAttributes.setDialog(JobAttributes.DialogType.COMMON);
  49  *     }
  50  * }
  51  * </pre>
  52  * <p>
  53  * Every IPP attribute which supports an <i>attributeName</i>-default value
  54  * has a corresponding <code>set<i>attributeName</i>ToDefault</code> method.
  55  * Default value fields are not provided.
  56  *
  57  * @author      David Mendenhall
  58  * @since 1.3
  59  */
  60 public final class JobAttributes implements Cloneable {
  61     /**
  62      * A type-safe enumeration of possible default selection states.
  63      * @since 1.3
  64      */
  65     public static final class DefaultSelectionType extends AttributeValue {
  66         private static final int I_ALL = 0;
  67         private static final int I_RANGE = 1;
  68         private static final int I_SELECTION = 2;
  69 
  70         private static final String NAMES[] = {
  71             "all", "range", "selection"
  72         };
  73 
  74         /**
  75          * The {@code DefaultSelectionType} instance to use for
  76          * specifying that all pages of the job should be printed.
  77          */
  78         public static final DefaultSelectionType ALL =
  79            new DefaultSelectionType(I_ALL);
  80         /**
  81          * The {@code DefaultSelectionType} instance to use for
  82          * specifying that a range of pages of the job should be printed.
  83          */
  84         public static final DefaultSelectionType RANGE =
  85            new DefaultSelectionType(I_RANGE);
  86         /**
  87          * The {@code DefaultSelectionType} instance to use for
  88          * specifying that the current selection should be printed.
  89          */
  90         public static final DefaultSelectionType SELECTION =
  91            new DefaultSelectionType(I_SELECTION);
  92 
  93         private DefaultSelectionType(int type) {
  94             super(type, NAMES);
  95         }
  96     }
  97 
  98     /**
  99      * A type-safe enumeration of possible job destinations.
 100      * @since 1.3
 101      */
 102     public static final class DestinationType extends AttributeValue {
 103         private static final int I_FILE = 0;
 104         private static final int I_PRINTER = 1;
 105 
 106         private static final String NAMES[] = {
 107             "file", "printer"
 108         };
 109 
 110         /**
 111          * The {@code DestinationType} instance to use for
 112          * specifying print to file.
 113          */
 114         public static final DestinationType FILE =
 115             new DestinationType(I_FILE);
 116         /**
 117          * The {@code DestinationType} instance to use for
 118          * specifying print to printer.
 119          */
 120         public static final DestinationType PRINTER =
 121             new DestinationType(I_PRINTER);
 122 
 123         private DestinationType(int type) {
 124             super(type, NAMES);
 125         }
 126     }
 127 
 128     /**
 129      * A type-safe enumeration of possible dialogs to display to the user.
 130      * @since 1.3
 131      */
 132     public static final class DialogType extends AttributeValue {
 133         private static final int I_COMMON = 0;
 134         private static final int I_NATIVE = 1;
 135         private static final int I_NONE = 2;
 136 
 137         private static final String NAMES[] = {
 138             "common", "native", "none"
 139         };
 140 
 141         /**
 142          * The {@code DialogType} instance to use for
 143          * specifying the cross-platform, pure Java print dialog.
 144          */
 145         public static final DialogType COMMON = new DialogType(I_COMMON);
 146         /**
 147          * The {@code DialogType} instance to use for
 148          * specifying the platform's native print dialog.
 149          */
 150         public static final DialogType NATIVE = new DialogType(I_NATIVE);
 151         /**
 152          * The {@code DialogType} instance to use for
 153          * specifying no print dialog.
 154          */
 155         public static final DialogType NONE = new DialogType(I_NONE);
 156 
 157         private DialogType(int type) {
 158             super(type, NAMES);
 159         }
 160     }
 161 
 162     /**
 163      * A type-safe enumeration of possible multiple copy handling states.
 164      * It is used to control how the sheets of multiple copies of a single
 165      * document are collated.
 166      * @since 1.3
 167      */
 168     public static final class MultipleDocumentHandlingType extends
 169                                                                AttributeValue {
 170         private static final int I_SEPARATE_DOCUMENTS_COLLATED_COPIES = 0;
 171         private static final int I_SEPARATE_DOCUMENTS_UNCOLLATED_COPIES = 1;
 172 
 173         private static final String NAMES[] = {
 174             "separate-documents-collated-copies",
 175             "separate-documents-uncollated-copies"
 176         };
 177 
 178         /**
 179          * The {@code MultipleDocumentHandlingType} instance to use for specifying
 180          * that the job should be divided into separate, collated copies.
 181          */
 182         public static final MultipleDocumentHandlingType
 183             SEPARATE_DOCUMENTS_COLLATED_COPIES =
 184                 new MultipleDocumentHandlingType(
 185                     I_SEPARATE_DOCUMENTS_COLLATED_COPIES);
 186         /**
 187          * The {@code MultipleDocumentHandlingType} instance to use for specifying
 188          * that the job should be divided into separate, uncollated copies.
 189          */
 190         public static final MultipleDocumentHandlingType
 191             SEPARATE_DOCUMENTS_UNCOLLATED_COPIES =
 192                 new MultipleDocumentHandlingType(
 193                     I_SEPARATE_DOCUMENTS_UNCOLLATED_COPIES);
 194 
 195         private MultipleDocumentHandlingType(int type) {
 196             super(type, NAMES);
 197         }
 198     }
 199 
 200     /**
 201      * A type-safe enumeration of possible multi-page impositions. These
 202      * impositions are in compliance with IPP 1.1.
 203      * @since 1.3
 204      */
 205     public static final class SidesType extends AttributeValue {
 206         private static final int I_ONE_SIDED = 0;
 207         private static final int I_TWO_SIDED_LONG_EDGE = 1;
 208         private static final int I_TWO_SIDED_SHORT_EDGE = 2;
 209 
 210         private static final String NAMES[] = {
 211             "one-sided", "two-sided-long-edge", "two-sided-short-edge"
 212         };
 213 
 214         /**
 215          * The {@code SidesType} instance to use for specifying that
 216          * consecutive job pages should be printed upon the same side of
 217          * consecutive media sheets.
 218          */
 219         public static final SidesType ONE_SIDED = new SidesType(I_ONE_SIDED);
 220         /**
 221          * The {@code SidesType} instance to use for specifying that
 222          * consecutive job pages should be printed upon front and back sides
 223          * of consecutive media sheets, such that the orientation of each pair
 224          * of pages on the medium would be correct for the reader as if for
 225          * binding on the long edge.
 226          */
 227         public static final SidesType TWO_SIDED_LONG_EDGE =
 228             new SidesType(I_TWO_SIDED_LONG_EDGE);
 229         /**
 230          * The {@code SidesType} instance to use for specifying that
 231          * consecutive job pages should be printed upon front and back sides
 232          * of consecutive media sheets, such that the orientation of each pair
 233          * of pages on the medium would be correct for the reader as if for
 234          * binding on the short edge.
 235          */
 236         public static final SidesType TWO_SIDED_SHORT_EDGE =
 237             new SidesType(I_TWO_SIDED_SHORT_EDGE);
 238 
 239         private SidesType(int type) {
 240             super(type, NAMES);
 241         }
 242     }
 243 
 244     private int copies;
 245     private DefaultSelectionType defaultSelection;
 246     private DestinationType destination;
 247     private DialogType dialog;
 248     private String fileName;
 249     private int fromPage;
 250     private int maxPage;
 251     private int minPage;
 252     private MultipleDocumentHandlingType multipleDocumentHandling;
 253     private int[][] pageRanges;
 254     private int prFirst;
 255     private int prLast;
 256     private String printer;
 257     private SidesType sides;
 258     private int toPage;
 259 
 260     /**
 261      * Constructs a {@code JobAttributes} instance with default
 262      * values for every attribute.  The dialog defaults to
 263      * {@code DialogType.NATIVE}.  Min page defaults to
 264      * {@code 1}.  Max page defaults to {@code Integer.MAX_VALUE}.
 265      * Destination defaults to {@code DestinationType.PRINTER}.
 266      * Selection defaults to {@code DefaultSelectionType.ALL}.
 267      * Number of copies defaults to {@code 1}. Multiple document handling defaults
 268      * to {@code MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_UNCOLLATED_COPIES}.
 269      * Sides defaults to {@code SidesType.ONE_SIDED}. File name defaults
 270      * to {@code null}.
 271      */
 272     public JobAttributes() {
 273         setCopiesToDefault();
 274         setDefaultSelection(DefaultSelectionType.ALL);
 275         setDestination(DestinationType.PRINTER);
 276         setDialog(DialogType.NATIVE);
 277         setMaxPage(Integer.MAX_VALUE);
 278         setMinPage(1);
 279         setMultipleDocumentHandlingToDefault();
 280         setSidesToDefault();
 281     }
 282 
 283     /**
 284      * Constructs a {@code JobAttributes} instance which is a copy
 285      * of the supplied {@code JobAttributes}.
 286      *
 287      * @param   obj the {@code JobAttributes} to copy
 288      */
 289     public JobAttributes(JobAttributes obj) {
 290         set(obj);
 291     }
 292 
 293     /**
 294      * Constructs a {@code JobAttributes} instance with the
 295      * specified values for every attribute.
 296      *
 297      * @param   copies an integer greater than 0
 298      * @param   defaultSelection {@code DefaultSelectionType.ALL},
 299      *          {@code DefaultSelectionType.RANGE}, or
 300      *          {@code DefaultSelectionType.SELECTION}
 301      * @param   destination {@code DestinationType.FILE} or
 302      *          {@code DestinationType.PRINTER}
 303      * @param   dialog {@code DialogType.COMMON},
 304      *          {@code DialogType.NATIVE}, or
 305      *          {@code DialogType.NONE}
 306      * @param   fileName the possibly {@code null} file name
 307      * @param   maxPage an integer greater than zero and greater than or equal
 308      *          to <i>minPage</i>
 309      * @param   minPage an integer greater than zero and less than or equal
 310      *          to <i>maxPage</i>
 311      * @param   multipleDocumentHandling
 312      *     {@code MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_COLLATED_COPIES} or
 313      *     {@code MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_UNCOLLATED_COPIES}
 314      * @param   pageRanges an array of integer arrays of two elements; an array
 315      *          is interpreted as a range spanning all pages including and
 316      *          between the specified pages; ranges must be in ascending
 317      *          order and must not overlap; specified page numbers cannot be
 318      *          less than <i>minPage</i> nor greater than <i>maxPage</i>;
 319      *          for example:
 320      *          <pre>
 321      *          (new int[][] { new int[] { 1, 3 }, new int[] { 5, 5 },
 322      *                         new int[] { 15, 19 } }),
 323      *          </pre>
 324      *          specifies pages 1, 2, 3, 5, 15, 16, 17, 18, and 19. Note that
 325      *          ({@code new int[][] { new int[] { 1, 1 }, new int[] { 1, 2 } }}),
 326      *          is an invalid set of page ranges because the two ranges
 327      *          overlap
 328      * @param   printer the possibly {@code null} printer name
 329      * @param   sides {@code SidesType.ONE_SIDED},
 330      *          {@code SidesType.TWO_SIDED_LONG_EDGE}, or
 331      *          {@code SidesType.TWO_SIDED_SHORT_EDGE}
 332      * @throws  IllegalArgumentException if one or more of the above
 333      *          conditions is violated
 334      */
 335     public JobAttributes(int copies, DefaultSelectionType defaultSelection,
 336                          DestinationType destination, DialogType dialog,
 337                          String fileName, int maxPage, int minPage,
 338                          MultipleDocumentHandlingType multipleDocumentHandling,
 339                          int[][] pageRanges, String printer, SidesType sides) {
 340         setCopies(copies);
 341         setDefaultSelection(defaultSelection);
 342         setDestination(destination);
 343         setDialog(dialog);
 344         setFileName(fileName);
 345         setMaxPage(maxPage);
 346         setMinPage(minPage);
 347         setMultipleDocumentHandling(multipleDocumentHandling);
 348         setPageRanges(pageRanges);
 349         setPrinter(printer);
 350         setSides(sides);
 351     }
 352 
 353     /**
 354      * Creates and returns a copy of this {@code JobAttributes}.
 355      *
 356      * @return  the newly created copy; it is safe to cast this Object into
 357      *          a {@code JobAttributes}
 358      */
 359     public Object clone() {
 360         try {
 361             return super.clone();
 362         } catch (CloneNotSupportedException e) {
 363             // Since we implement Cloneable, this should never happen
 364             throw new InternalError(e);
 365         }
 366     }
 367 
 368     /**
 369      * Sets all of the attributes of this {@code JobAttributes} to
 370      * the same values as the attributes of obj.
 371      *
 372      * @param   obj the {@code JobAttributes} to copy
 373      */
 374     public void set(JobAttributes obj) {
 375         copies = obj.copies;
 376         defaultSelection = obj.defaultSelection;
 377         destination = obj.destination;
 378         dialog = obj.dialog;
 379         fileName = obj.fileName;
 380         fromPage = obj.fromPage;
 381         maxPage = obj.maxPage;
 382         minPage = obj.minPage;
 383         multipleDocumentHandling = obj.multipleDocumentHandling;
 384         // okay because we never modify the contents of pageRanges
 385         pageRanges = obj.pageRanges;
 386         prFirst = obj.prFirst;
 387         prLast = obj.prLast;
 388         printer = obj.printer;
 389         sides = obj.sides;
 390         toPage = obj.toPage;
 391     }
 392 
 393     /**
 394      * Returns the number of copies the application should render for jobs
 395      * using these attributes. This attribute is updated to the value chosen
 396      * by the user.
 397      *
 398      * @return  an integer greater than 0.
 399      */
 400     public int getCopies() {
 401         return copies;
 402     }
 403 
 404     /**
 405      * Specifies the number of copies the application should render for jobs
 406      * using these attributes. Not specifying this attribute is equivalent to
 407      * specifying {@code 1}.
 408      *
 409      * @param   copies an integer greater than 0
 410      * @throws  IllegalArgumentException if {@code copies} is less than
 411      *      or equal to 0
 412      */
 413     public void setCopies(int copies) {
 414         if (copies <= 0) {
 415             throw new IllegalArgumentException("Invalid value for attribute "+
 416                                                "copies");
 417         }
 418         this.copies = copies;
 419     }
 420 
 421     /**
 422      * Sets the number of copies the application should render for jobs using
 423      * these attributes to the default. The default number of copies is 1.
 424      */
 425     public void setCopiesToDefault() {
 426         setCopies(1);
 427     }
 428 
 429     /**
 430      * Specifies whether, for jobs using these attributes, the application
 431      * should print all pages, the range specified by the return value of
 432      * {@code getPageRanges}, or the current selection. This attribute
 433      * is updated to the value chosen by the user.
 434      *
 435      * @return  DefaultSelectionType.ALL, DefaultSelectionType.RANGE, or
 436      *          DefaultSelectionType.SELECTION
 437      */
 438     public DefaultSelectionType getDefaultSelection() {
 439         return defaultSelection;
 440     }
 441 
 442     /**
 443      * Specifies whether, for jobs using these attributes, the application
 444      * should print all pages, the range specified by the return value of
 445      * {@code getPageRanges}, or the current selection. Not specifying
 446      * this attribute is equivalent to specifying DefaultSelectionType.ALL.
 447      *
 448      * @param   defaultSelection DefaultSelectionType.ALL,
 449      *          DefaultSelectionType.RANGE, or DefaultSelectionType.SELECTION.
 450      * @throws  IllegalArgumentException if defaultSelection is {@code null}
 451      */
 452     public void setDefaultSelection(DefaultSelectionType defaultSelection) {
 453         if (defaultSelection == null) {
 454             throw new IllegalArgumentException("Invalid value for attribute "+
 455                                                "defaultSelection");
 456         }
 457         this.defaultSelection = defaultSelection;
 458     }
 459 
 460     /**
 461      * Specifies whether output will be to a printer or a file for jobs using
 462      * these attributes. This attribute is updated to the value chosen by the
 463      * user.
 464      *
 465      * @return  DestinationType.FILE or DestinationType.PRINTER
 466      */
 467     public DestinationType getDestination() {
 468         return destination;
 469     }
 470 
 471     /**
 472      * Specifies whether output will be to a printer or a file for jobs using
 473      * these attributes. Not specifying this attribute is equivalent to
 474      * specifying DestinationType.PRINTER.
 475      *
 476      * @param   destination DestinationType.FILE or DestinationType.PRINTER.
 477      * @throws  IllegalArgumentException if destination is null.
 478      */
 479     public void setDestination(DestinationType destination) {
 480         if (destination == null) {
 481             throw new IllegalArgumentException("Invalid value for attribute "+
 482                                                "destination");
 483         }
 484         this.destination = destination;
 485     }
 486 
 487     /**
 488      * Returns whether, for jobs using these attributes, the user should see
 489      * a print dialog in which to modify the print settings, and which type of
 490      * print dialog should be displayed. DialogType.COMMON denotes a cross-
 491      * platform, pure Java print dialog. DialogType.NATIVE denotes the
 492      * platform's native print dialog. If a platform does not support a native
 493      * print dialog, the pure Java print dialog is displayed instead.
 494      * DialogType.NONE specifies no print dialog (i.e., background printing).
 495      * This attribute cannot be modified by, and is not subject to any
 496      * limitations of, the implementation or the target printer.
 497      *
 498      * @return  {@code DialogType.COMMON}, {@code DialogType.NATIVE}, or
 499      *          {@code DialogType.NONE}
 500      */
 501     public DialogType getDialog() {
 502         return dialog;
 503     }
 504 
 505     /**
 506      * Specifies whether, for jobs using these attributes, the user should see
 507      * a print dialog in which to modify the print settings, and which type of
 508      * print dialog should be displayed. DialogType.COMMON denotes a cross-
 509      * platform, pure Java print dialog. DialogType.NATIVE denotes the
 510      * platform's native print dialog. If a platform does not support a native
 511      * print dialog, the pure Java print dialog is displayed instead.
 512      * DialogType.NONE specifies no print dialog (i.e., background printing).
 513      * Not specifying this attribute is equivalent to specifying
 514      * DialogType.NATIVE.
 515      *
 516      * @param   dialog DialogType.COMMON, DialogType.NATIVE, or
 517      *          DialogType.NONE.
 518      * @throws  IllegalArgumentException if dialog is null.
 519      */
 520     public void setDialog(DialogType dialog) {
 521         if (dialog == null) {
 522             throw new IllegalArgumentException("Invalid value for attribute "+
 523                                                "dialog");
 524         }
 525         this.dialog = dialog;
 526     }
 527 
 528     /**
 529      * Specifies the file name for the output file for jobs using these
 530      * attributes. This attribute is updated to the value chosen by the user.
 531      *
 532      * @return  the possibly {@code null} file name
 533      */
 534     public String getFileName() {
 535         return fileName;
 536     }
 537 
 538     /**
 539      * Specifies the file name for the output file for jobs using these
 540      * attributes. Default is platform-dependent and implementation-defined.
 541      *
 542      * @param   fileName the possibly null file name.
 543      */
 544     public void setFileName(String fileName) {
 545         this.fileName = fileName;
 546     }
 547 
 548     /**
 549      * Returns, for jobs using these attributes, the first page to be
 550      * printed, if a range of pages is to be printed. This attribute is
 551      * updated to the value chosen by the user. An application should ignore
 552      * this attribute on output, unless the return value of the
 553      * {@code getDefaultSelection} method is DefaultSelectionType.RANGE. An
 554      * application should honor the return value of {@code getPageRanges}
 555      * over the return value of this method, if possible.
 556      *
 557      * @return  an integer greater than zero and less than or equal to
 558      *          <i>toPage</i> and greater than or equal to <i>minPage</i> and
 559      *          less than or equal to <i>maxPage</i>.
 560      */
 561     public int getFromPage() {
 562         if (fromPage != 0) {
 563             return fromPage;
 564         } else if (toPage != 0) {
 565             return getMinPage();
 566         } else if (pageRanges != null) {
 567             return prFirst;
 568         } else {
 569             return getMinPage();
 570         }
 571     }
 572 
 573     /**
 574      * Specifies, for jobs using these attributes, the first page to be
 575      * printed, if a range of pages is to be printed. If this attribute is not
 576      * specified, then the values from the pageRanges attribute are used. If
 577      * pageRanges and either or both of fromPage and toPage are specified,
 578      * pageRanges takes precedence. Specifying none of pageRanges, fromPage,
 579      * or toPage is equivalent to calling
 580      * setPageRanges(new int[][] { new int[] { <i>minPage</i> } });
 581      *
 582      * @param   fromPage an integer greater than zero and less than or equal to
 583      *          <i>toPage</i> and greater than or equal to <i>minPage</i> and
 584      *          less than or equal to <i>maxPage</i>.
 585      * @throws  IllegalArgumentException if one or more of the above
 586      *          conditions is violated.
 587      */
 588     public void setFromPage(int fromPage) {
 589         if (fromPage <= 0 ||
 590             (toPage != 0 && fromPage > toPage) ||
 591             fromPage < minPage ||
 592             fromPage > maxPage) {
 593             throw new IllegalArgumentException("Invalid value for attribute "+
 594                                                "fromPage");
 595         }
 596         this.fromPage = fromPage;
 597     }
 598 
 599     /**
 600      * Specifies the maximum value the user can specify as the last page to
 601      * be printed for jobs using these attributes. This attribute cannot be
 602      * modified by, and is not subject to any limitations of, the
 603      * implementation or the target printer.
 604      *
 605      * @return  an integer greater than zero and greater than or equal
 606      *          to <i>minPage</i>.
 607      */
 608     public int getMaxPage() {
 609         return maxPage;
 610     }
 611 
 612     /**
 613      * Specifies the maximum value the user can specify as the last page to
 614      * be printed for jobs using these attributes. Not specifying this
 615      * attribute is equivalent to specifying {@code Integer.MAX_VALUE}.
 616      *
 617      * @param   maxPage an integer greater than zero and greater than or equal
 618      *          to <i>minPage</i>
 619      * @throws  IllegalArgumentException if one or more of the above
 620      *          conditions is violated
 621      */
 622     public void setMaxPage(int maxPage) {
 623         if (maxPage <= 0 || maxPage < minPage) {
 624             throw new IllegalArgumentException("Invalid value for attribute "+
 625                                                "maxPage");
 626         }
 627         this.maxPage = maxPage;
 628     }
 629 
 630     /**
 631      * Specifies the minimum value the user can specify as the first page to
 632      * be printed for jobs using these attributes. This attribute cannot be
 633      * modified by, and is not subject to any limitations of, the
 634      * implementation or the target printer.
 635      *
 636      * @return  an integer greater than zero and less than or equal
 637      *          to <i>maxPage</i>.
 638      */
 639     public int getMinPage() {
 640         return minPage;
 641     }
 642 
 643     /**
 644      * Specifies the minimum value the user can specify as the first page to
 645      * be printed for jobs using these attributes. Not specifying this
 646      * attribute is equivalent to specifying {@code 1}.
 647      *
 648      * @param   minPage an integer greater than zero and less than or equal
 649      *          to <i>maxPage</i>.
 650      * @throws  IllegalArgumentException if one or more of the above
 651      *          conditions is violated.
 652      */
 653     public void setMinPage(int minPage) {
 654         if (minPage <= 0 || minPage > maxPage) {
 655             throw new IllegalArgumentException("Invalid value for attribute "+
 656                                                "minPage");
 657         }
 658         this.minPage = minPage;
 659     }
 660 
 661     /**
 662      * Specifies the handling of multiple copies, including collation, for
 663      * jobs using these attributes. This attribute is updated to the value
 664      * chosen by the user.
 665      *
 666      * @return
 667      *     MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_COLLATED_COPIES or
 668      *     MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_UNCOLLATED_COPIES.
 669      */
 670     public MultipleDocumentHandlingType getMultipleDocumentHandling() {
 671         return multipleDocumentHandling;
 672     }
 673 
 674     /**
 675      * Specifies the handling of multiple copies, including collation, for
 676      * jobs using these attributes. Not specifying this attribute is equivalent
 677      * to specifying
 678      * MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_UNCOLLATED_COPIES.
 679      *
 680      * @param   multipleDocumentHandling
 681      *     MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_COLLATED_COPIES or
 682      *     MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_UNCOLLATED_COPIES.
 683      * @throws  IllegalArgumentException if multipleDocumentHandling is null.
 684      */
 685     public void setMultipleDocumentHandling(MultipleDocumentHandlingType
 686                                             multipleDocumentHandling) {
 687         if (multipleDocumentHandling == null) {
 688             throw new IllegalArgumentException("Invalid value for attribute "+
 689                                                "multipleDocumentHandling");
 690         }
 691         this.multipleDocumentHandling = multipleDocumentHandling;
 692     }
 693 
 694     /**
 695      * Sets the handling of multiple copies, including collation, for jobs
 696      * using these attributes to the default. The default handling is
 697      * MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_UNCOLLATED_COPIES.
 698      */
 699     public void setMultipleDocumentHandlingToDefault() {
 700         setMultipleDocumentHandling(
 701             MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_UNCOLLATED_COPIES);
 702     }
 703 
 704     /**
 705      * Specifies, for jobs using these attributes, the ranges of pages to be
 706      * printed, if a range of pages is to be printed. All range numbers are
 707      * inclusive. This attribute is updated to the value chosen by the user.
 708      * An application should ignore this attribute on output, unless the
 709      * return value of the {@code getDefaultSelection} method is
 710      * DefaultSelectionType.RANGE.
 711      *
 712      * @return  an array of integer arrays of 2 elements. An array
 713      *          is interpreted as a range spanning all pages including and
 714      *          between the specified pages. Ranges must be in ascending
 715      *          order and must not overlap. Specified page numbers cannot be
 716      *          less than <i>minPage</i> nor greater than <i>maxPage</i>.
 717      *          For example:
 718      *          (new int[][] { new int[] { 1, 3 }, new int[] { 5, 5 },
 719      *                         new int[] { 15, 19 } }),
 720      *          specifies pages 1, 2, 3, 5, 15, 16, 17, 18, and 19.
 721      */
 722     public int[][] getPageRanges() {
 723         if (pageRanges != null) {
 724             // Return a copy because otherwise client code could circumvent the
 725             // the checks made in setPageRanges by modifying the returned
 726             // array.
 727             int[][] copy = new int[pageRanges.length][2];
 728             for (int i = 0; i < pageRanges.length; i++) {
 729                 copy[i][0] = pageRanges[i][0];
 730                 copy[i][1] = pageRanges[i][1];
 731             }
 732             return copy;
 733         } else if (fromPage != 0 || toPage != 0) {
 734             int fromPage = getFromPage();
 735             int toPage = getToPage();
 736             return new int[][] { new int[] { fromPage, toPage } };
 737         } else {
 738             int minPage = getMinPage();
 739             return new int[][] { new int[] { minPage, minPage } };
 740         }
 741     }
 742 
 743     /**
 744      * Specifies, for jobs using these attributes, the ranges of pages to be
 745      * printed, if a range of pages is to be printed. All range numbers are
 746      * inclusive. If this attribute is not specified, then the values from the
 747      * fromPage and toPages attributes are used. If pageRanges and either or
 748      * both of fromPage and toPage are specified, pageRanges takes precedence.
 749      * Specifying none of pageRanges, fromPage, or toPage is equivalent to
 750      * calling setPageRanges(new int[][] { new int[] { <i>minPage</i>,
 751      *                                                 <i>minPage</i> } });
 752      *
 753      * @param   pageRanges an array of integer arrays of 2 elements. An array
 754      *          is interpreted as a range spanning all pages including and
 755      *          between the specified pages. Ranges must be in ascending
 756      *          order and must not overlap. Specified page numbers cannot be
 757      *          less than <i>minPage</i> nor greater than <i>maxPage</i>.
 758      *          For example:
 759      *          (new int[][] { new int[] { 1, 3 }, new int[] { 5, 5 },
 760      *                         new int[] { 15, 19 } }),
 761      *          specifies pages 1, 2, 3, 5, 15, 16, 17, 18, and 19. Note that
 762      *          (new int[][] { new int[] { 1, 1 }, new int[] { 1, 2 } }),
 763      *          is an invalid set of page ranges because the two ranges
 764      *          overlap.
 765      * @throws  IllegalArgumentException if one or more of the above
 766      *          conditions is violated.
 767      */
 768     public void setPageRanges(int[][] pageRanges) {
 769         String xcp = "Invalid value for attribute pageRanges";
 770         int first = 0;
 771         int last = 0;
 772 
 773         if (pageRanges == null) {
 774             throw new IllegalArgumentException(xcp);
 775         }
 776 
 777         for (int i = 0; i < pageRanges.length; i++) {
 778             if (pageRanges[i] == null ||
 779                 pageRanges[i].length != 2 ||
 780                 pageRanges[i][0] <= last ||
 781                 pageRanges[i][1] < pageRanges[i][0]) {
 782                     throw new IllegalArgumentException(xcp);
 783             }
 784             last = pageRanges[i][1];
 785             if (first == 0) {
 786                 first = pageRanges[i][0];
 787             }
 788         }
 789 
 790         if (first < minPage || last > maxPage) {
 791             throw new IllegalArgumentException(xcp);
 792         }
 793 
 794         // Store a copy because otherwise client code could circumvent the
 795         // the checks made above by holding a reference to the array and
 796         // modifying it after calling setPageRanges.
 797         int[][] copy = new int[pageRanges.length][2];
 798         for (int i = 0; i < pageRanges.length; i++) {
 799             copy[i][0] = pageRanges[i][0];
 800             copy[i][1] = pageRanges[i][1];
 801         }
 802         this.pageRanges = copy;
 803         this.prFirst = first;
 804         this.prLast = last;
 805     }
 806 
 807     /**
 808      * Returns the destination printer for jobs using these attributes. This
 809      * attribute is updated to the value chosen by the user.
 810      *
 811      * @return  the possibly null printer name.
 812      */
 813     public String getPrinter() {
 814         return printer;
 815     }
 816 
 817     /**
 818      * Specifies the destination printer for jobs using these attributes.
 819      * Default is platform-dependent and implementation-defined.
 820      *
 821      * @param   printer the possibly null printer name.
 822      */
 823     public void setPrinter(String printer) {
 824         this.printer = printer;
 825     }
 826 
 827     /**
 828      * Returns how consecutive pages should be imposed upon the sides of the
 829      * print medium for jobs using these attributes. SidesType.ONE_SIDED
 830      * imposes each consecutive page upon the same side of consecutive media
 831      * sheets. This imposition is sometimes called <i>simplex</i>.
 832      * SidesType.TWO_SIDED_LONG_EDGE imposes each consecutive pair of pages
 833      * upon front and back sides of consecutive media sheets, such that the
 834      * orientation of each pair of pages on the medium would be correct for
 835      * the reader as if for binding on the long edge. This imposition is
 836      * sometimes called <i>duplex</i>. SidesType.TWO_SIDED_SHORT_EDGE imposes
 837      * each consecutive pair of pages upon front and back sides of consecutive
 838      * media sheets, such that the orientation of each pair of pages on the
 839      * medium would be correct for the reader as if for binding on the short
 840      * edge. This imposition is sometimes called <i>tumble</i>. This attribute
 841      * is updated to the value chosen by the user.
 842      *
 843      * @return  SidesType.ONE_SIDED, SidesType.TWO_SIDED_LONG_EDGE, or
 844      *          SidesType.TWO_SIDED_SHORT_EDGE.
 845      */
 846     public SidesType getSides() {
 847         return sides;
 848     }
 849 
 850     /**
 851      * Specifies how consecutive pages should be imposed upon the sides of the
 852      * print medium for jobs using these attributes. SidesType.ONE_SIDED
 853      * imposes each consecutive page upon the same side of consecutive media
 854      * sheets. This imposition is sometimes called <i>simplex</i>.
 855      * SidesType.TWO_SIDED_LONG_EDGE imposes each consecutive pair of pages
 856      * upon front and back sides of consecutive media sheets, such that the
 857      * orientation of each pair of pages on the medium would be correct for
 858      * the reader as if for binding on the long edge. This imposition is
 859      * sometimes called <i>duplex</i>. SidesType.TWO_SIDED_SHORT_EDGE imposes
 860      * each consecutive pair of pages upon front and back sides of consecutive
 861      * media sheets, such that the orientation of each pair of pages on the
 862      * medium would be correct for the reader as if for binding on the short
 863      * edge. This imposition is sometimes called <i>tumble</i>. Not specifying
 864      * this attribute is equivalent to specifying SidesType.ONE_SIDED.
 865      *
 866      * @param   sides SidesType.ONE_SIDED, SidesType.TWO_SIDED_LONG_EDGE, or
 867      *          SidesType.TWO_SIDED_SHORT_EDGE.
 868      * @throws  IllegalArgumentException if sides is null.
 869      */
 870     public void setSides(SidesType sides) {
 871         if (sides == null) {
 872             throw new IllegalArgumentException("Invalid value for attribute "+
 873                                                "sides");
 874         }
 875         this.sides = sides;
 876     }
 877 
 878     /**
 879      * Sets how consecutive pages should be imposed upon the sides of the
 880      * print medium for jobs using these attributes to the default. The
 881      * default imposition is SidesType.ONE_SIDED.
 882      */
 883     public void setSidesToDefault() {
 884         setSides(SidesType.ONE_SIDED);
 885     }
 886 
 887     /**
 888      * Returns, for jobs using these attributes, the last page (inclusive)
 889      * to be printed, if a range of pages is to be printed. This attribute is
 890      * updated to the value chosen by the user. An application should ignore
 891      * this attribute on output, unless the return value of the
 892      * {@code getDefaultSelection} method is DefaultSelectionType.RANGE. An
 893      * application should honor the return value of {@code getPageRanges}
 894      * over the return value of this method, if possible.
 895      *
 896      * @return  an integer greater than zero and greater than or equal
 897      *          to <i>toPage</i> and greater than or equal to <i>minPage</i>
 898      *          and less than or equal to <i>maxPage</i>.
 899      */
 900     public int getToPage() {
 901         if (toPage != 0) {
 902             return toPage;
 903         } else if (fromPage != 0) {
 904             return fromPage;
 905         } else if (pageRanges != null) {
 906             return prLast;
 907         } else {
 908             return getMinPage();
 909         }
 910     }
 911 
 912     /**
 913      * Specifies, for jobs using these attributes, the last page (inclusive)
 914      * to be printed, if a range of pages is to be printed.
 915      * If this attribute is not specified, then the values from the pageRanges
 916      * attribute are used. If pageRanges and either or both of fromPage and
 917      * toPage are specified, pageRanges takes precedence. Specifying none of
 918      * pageRanges, fromPage, or toPage is equivalent to calling
 919      * setPageRanges(new int[][] { new int[] { <i>minPage</i> } });
 920      *
 921      * @param   toPage an integer greater than zero and greater than or equal
 922      *          to <i>fromPage</i> and greater than or equal to <i>minPage</i>
 923      *          and less than or equal to <i>maxPage</i>.
 924      * @throws  IllegalArgumentException if one or more of the above
 925      *          conditions is violated.
 926      */
 927     public void setToPage(int toPage) {
 928         if (toPage <= 0 ||
 929             (fromPage != 0 && toPage < fromPage) ||
 930             toPage < minPage ||
 931             toPage > maxPage) {
 932             throw new IllegalArgumentException("Invalid value for attribute "+
 933                                                "toPage");
 934         }
 935         this.toPage = toPage;
 936     }
 937 
 938     /**
 939      * Determines whether two JobAttributes are equal to each other.
 940      * <p>
 941      * Two JobAttributes are equal if and only if each of their attributes are
 942      * equal. Attributes of enumeration type are equal if and only if the
 943      * fields refer to the same unique enumeration object. A set of page
 944      * ranges is equal if and only if the sets are of equal length, each range
 945      * enumerates the same pages, and the ranges are in the same order.
 946      *
 947      * @param   obj the object whose equality will be checked.
 948      * @return  whether obj is equal to this JobAttribute according to the
 949      *          above criteria.
 950      */
 951     public boolean equals(Object obj) {
 952         if (!(obj instanceof JobAttributes)) {
 953             return false;
 954         }
 955         JobAttributes rhs = (JobAttributes)obj;
 956 
 957         if (fileName == null) {
 958             if (rhs.fileName != null) {
 959                 return false;
 960             }
 961         } else {
 962             if (!fileName.equals(rhs.fileName)) {
 963                 return false;
 964             }
 965         }
 966 
 967         if (pageRanges == null) {
 968             if (rhs.pageRanges != null) {
 969                 return false;
 970             }
 971         } else {
 972             if (rhs.pageRanges == null ||
 973                     pageRanges.length != rhs.pageRanges.length) {
 974                 return false;
 975             }
 976             for (int i = 0; i < pageRanges.length; i++) {
 977                 if (pageRanges[i][0] != rhs.pageRanges[i][0] ||
 978                     pageRanges[i][1] != rhs.pageRanges[i][1]) {
 979                     return false;
 980                 }
 981             }
 982         }
 983 
 984         if (printer == null) {
 985             if (rhs.printer != null) {
 986                 return false;
 987             }
 988         } else {
 989             if (!printer.equals(rhs.printer)) {
 990                 return false;
 991             }
 992         }
 993 
 994         return (copies == rhs.copies &&
 995                 defaultSelection == rhs.defaultSelection &&
 996                 destination == rhs.destination &&
 997                 dialog == rhs.dialog &&
 998                 fromPage == rhs.fromPage &&
 999                 maxPage == rhs.maxPage &&
1000                 minPage == rhs.minPage &&
1001                 multipleDocumentHandling == rhs.multipleDocumentHandling &&
1002                 prFirst == rhs.prFirst &&
1003                 prLast == rhs.prLast &&
1004                 sides == rhs.sides &&
1005                 toPage == rhs.toPage);
1006     }
1007 
1008     /**
1009      * Returns a hash code value for this JobAttributes.
1010      *
1011      * @return  the hash code.
1012      */
1013     public int hashCode() {
1014         int rest = ((copies + fromPage + maxPage + minPage + prFirst + prLast +
1015                      toPage) * 31) << 21;
1016         if (pageRanges != null) {
1017             int sum = 0;
1018             for (int i = 0; i < pageRanges.length; i++) {
1019                 sum += pageRanges[i][0] + pageRanges[i][1];
1020             }
1021             rest ^= (sum * 31) << 11;
1022         }
1023         if (fileName != null) {
1024             rest ^= fileName.hashCode();
1025         }
1026         if (printer != null) {
1027             rest ^= printer.hashCode();
1028         }
1029         return (defaultSelection.hashCode() << 6 ^
1030                 destination.hashCode() << 5 ^
1031                 dialog.hashCode() << 3 ^
1032                 multipleDocumentHandling.hashCode() << 2 ^
1033                 sides.hashCode() ^
1034                 rest);
1035     }
1036 
1037     /**
1038      * Returns a string representation of this JobAttributes.
1039      *
1040      * @return  the string representation.
1041      */
1042     public String toString() {
1043         int[][] pageRanges = getPageRanges();
1044         String prStr = "[";
1045         boolean first = true;
1046         for (int i = 0; i < pageRanges.length; i++) {
1047             if (first) {
1048                 first = false;
1049             } else {
1050                 prStr += ",";
1051             }
1052             prStr += pageRanges[i][0] + ":" + pageRanges[i][1];
1053         }
1054         prStr += "]";
1055 
1056         return "copies=" + getCopies() + ",defaultSelection=" +
1057             getDefaultSelection() + ",destination=" + getDestination() +
1058             ",dialog=" + getDialog() + ",fileName=" + getFileName() +
1059             ",fromPage=" + getFromPage() + ",maxPage=" + getMaxPage() +
1060             ",minPage=" + getMinPage() + ",multiple-document-handling=" +
1061             getMultipleDocumentHandling() + ",page-ranges=" + prStr +
1062             ",printer=" + getPrinter() + ",sides=" + getSides() + ",toPage=" +
1063             getToPage();
1064     }
1065 }