Save This Page
Home » openjdk-7 » javax » print » attribute » [javadoc | source]
    1   /*
    2    * Copyright 2000-2004 Sun Microsystems, Inc.  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.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   
   26   
   27   package javax.print.attribute;
   28   
   29   import java.io.Serializable;
   30   import java.util.Vector;
   31   
   32   /**
   33    * Class SetOfIntegerSyntax is an abstract base class providing the common
   34    * implementation of all attributes whose value is a set of nonnegative
   35    * integers. This includes attributes whose value is a single range of integers
   36    * and attributes whose value is a set of ranges of integers.
   37    * <P>
   38    * You can construct an instance of SetOfIntegerSyntax by giving it in "string
   39    * form." The string consists of zero or more comma-separated integer groups.
   40    * Each integer group consists of either one integer, two integers separated by
   41    * a hyphen (<CODE>-</CODE>), or two integers separated by a colon
   42    * (<CODE>:</CODE>). Each integer consists of one or more decimal digits
   43    * (<CODE>0</CODE> through <CODE>9</CODE>). Whitespace characters cannot
   44    * appear within an integer but are otherwise ignored. For example:
   45    * <CODE>""</CODE>, <CODE>"1"</CODE>, <CODE>"5-10"</CODE>, <CODE>"1:2,
   46    * 4"</CODE>.
   47    * <P>
   48    * You can also construct an instance of SetOfIntegerSyntax by giving it in
   49    * "array form." Array form consists of an array of zero or more integer groups
   50    * where each integer group is a length-1 or length-2 array of
   51    * <CODE>int</CODE>s; for example, <CODE>int[0][]</CODE>,
   52    * <CODE>int[][]{{1}}</CODE>, <CODE>int[][]{{5,10}}</CODE>,
   53    * <CODE>int[][]{{1,2},{4}}</CODE>.
   54    * <P>
   55    * In both string form and array form, each successive integer group gives a
   56    * range of integers to be included in the set. The first integer in each group
   57    * gives the lower bound of the range; the second integer in each group gives
   58    * the upper bound of the range; if there is only one integer in the group, the
   59    * upper bound is the same as the lower bound. If the upper bound is less than
   60    * the lower bound, it denotes a null range (no values). If the upper bound is
   61    * equal to the lower bound, it denotes a range consisting of a single value. If
   62    * the upper bound is greater than the lower bound, it denotes a range
   63    * consisting of more than one value. The ranges may appear in any order and are
   64    * allowed to overlap. The union of all the ranges gives the set's contents.
   65    * Once a SetOfIntegerSyntax instance is constructed, its value is immutable.
   66    * <P>
   67    * The SetOfIntegerSyntax object's value is actually stored in "<I>canonical</I>
   68    * array form." This is the same as array form, except there are no null ranges;
   69    * the members of the set are represented in as few ranges as possible (i.e.,
   70    * overlapping ranges are coalesced); the ranges appear in ascending order; and
   71    * each range is always represented as a length-two array of <CODE>int</CODE>s
   72    * in the form {lower bound, upper bound}. An empty set is represented as a
   73    * zero-length array.
   74    * <P>
   75    * Class SetOfIntegerSyntax has operations to return the set's members in
   76    * canonical array form, to test whether a given integer is a member of the
   77    * set, and to iterate through the members of the set.
   78    * <P>
   79    *
   80    * @author  David Mendenhall
   81    * @author  Alan Kaminsky
   82    */
   83   public abstract class SetOfIntegerSyntax implements Serializable, Cloneable {
   84   
   85       private static final long serialVersionUID = 3666874174847632203L;
   86   
   87       /**
   88        * This set's members in canonical array form.
   89        * @serial
   90        */
   91       private int[][] members;
   92   
   93   
   94       /**
   95        * Construct a new set-of-integer attribute with the given members in
   96        * string form.
   97        *
   98        * @param  members  Set members in string form. If null, an empty set is
   99        *                     constructed.
  100        *
  101        * @exception  IllegalArgumentException
  102        *     (Unchecked exception) Thrown if <CODE>members</CODE> does not
  103        *    obey  the proper syntax.
  104        */
  105       protected SetOfIntegerSyntax(String members) {
  106           this.members = parse (members);
  107       }
  108   
  109       /**
  110        * Parse the given string, returning canonical array form.
  111        */
  112       private static int[][] parse(String members) {
  113           // Create vector to hold int[] elements, each element being one range
  114           // parsed out of members.
  115           Vector theRanges = new Vector();
  116   
  117           // Run state machine over members.
  118           int n = (members == null ? 0 : members.length());
  119           int i = 0;
  120           int state = 0;
  121           int lb = 0;
  122           int ub = 0;
  123           char c;
  124           int digit;
  125           while (i < n) {
  126               c = members.charAt(i ++);
  127               switch (state) {
  128   
  129               case 0: // Before first integer in first group
  130                   if (Character.isWhitespace(c)) {
  131                       state = 0;
  132                   }
  133                   else if ((digit = Character.digit(c, 10)) != -1) {
  134                       lb = digit;
  135                       state = 1;
  136                   } else {
  137                       throw new IllegalArgumentException();
  138                   }
  139                   break;
  140   
  141               case 1: // In first integer in a group
  142                   if (Character.isWhitespace(c)){
  143                           state = 2;
  144                   } else if ((digit = Character.digit(c, 10)) != -1) {
  145                       lb = 10 * lb + digit;
  146                       state = 1;
  147                   } else if (c == '-' || c == ':') {
  148                       state = 3;
  149                   } else if (c == ',') {
  150                       accumulate (theRanges, lb, lb);
  151                       state = 6;
  152                   } else {
  153                       throw new IllegalArgumentException();
  154                   }
  155                   break;
  156   
  157               case 2: // After first integer in a group
  158                   if (Character.isWhitespace(c)) {
  159                       state = 2;
  160                   }
  161                   else if (c == '-' || c == ':') {
  162                       state = 3;
  163                   }
  164                   else if (c == ',') {
  165                       accumulate(theRanges, lb, lb);
  166                       state = 6;
  167                   } else {
  168                       throw new IllegalArgumentException();
  169                   }
  170                   break;
  171   
  172               case 3: // Before second integer in a group
  173                   if (Character.isWhitespace(c)) {
  174                       state = 3;
  175                   } else if ((digit = Character.digit(c, 10)) != -1) {
  176                       ub = digit;
  177                       state = 4;
  178                   } else {
  179                       throw new IllegalArgumentException();
  180                   }
  181                   break;
  182   
  183               case 4: // In second integer in a group
  184                   if (Character.isWhitespace(c)) {
  185                       state = 5;
  186                   } else if ((digit = Character.digit(c, 10)) != -1) {
  187                       ub = 10 * ub + digit;
  188                       state = 4;
  189                   } else if (c == ',') {
  190                       accumulate(theRanges, lb, ub);
  191                       state = 6;
  192                   } else {
  193                       throw new IllegalArgumentException();
  194                   }
  195                   break;
  196   
  197               case 5: // After second integer in a group
  198                   if (Character.isWhitespace(c)) {
  199                       state = 5;
  200                   } else if (c == ',') {
  201                       accumulate(theRanges, lb, ub);
  202                       state = 6;
  203                   } else {
  204                       throw new IllegalArgumentException();
  205                   }
  206                   break;
  207   
  208               case 6: // Before first integer in second or later group
  209                   if (Character.isWhitespace(c)) {
  210                       state = 6;
  211                   } else if ((digit = Character.digit(c, 10)) != -1) {
  212                       lb = digit;
  213                       state = 1;
  214                   } else {
  215                       throw new IllegalArgumentException();
  216                   }
  217                   break;
  218               }
  219           }
  220   
  221           // Finish off the state machine.
  222           switch (state) {
  223           case 0: // Before first integer in first group
  224               break;
  225           case 1: // In first integer in a group
  226           case 2: // After first integer in a group
  227               accumulate(theRanges, lb, lb);
  228               break;
  229           case 4: // In second integer in a group
  230           case 5: // After second integer in a group
  231               accumulate(theRanges, lb, ub);
  232               break;
  233           case 3: // Before second integer in a group
  234           case 6: // Before first integer in second or later group
  235               throw new IllegalArgumentException();
  236           }
  237   
  238           // Return canonical array form.
  239           return canonicalArrayForm (theRanges);
  240       }
  241   
  242       /**
  243        * Accumulate the given range (lb .. ub) into the canonical array form
  244        * into the given vector of int[] objects.
  245        */
  246       private static void accumulate(Vector ranges, int lb,int ub) {
  247           // Make sure range is non-null.
  248           if (lb <= ub) {
  249               // Stick range at the back of the vector.
  250               ranges.add(new int[] {lb, ub});
  251   
  252               // Work towards the front of the vector to integrate the new range
  253               // with the existing ranges.
  254               for (int j = ranges.size()-2; j >= 0; -- j) {
  255               // Get lower and upper bounds of the two ranges being compared.
  256                   int[] rangea = (int[]) ranges.elementAt (j);
  257                   int lba = rangea[0];
  258                   int uba = rangea[1];
  259                   int[] rangeb = (int[]) ranges.elementAt (j+1);
  260                   int lbb = rangeb[0];
  261                   int ubb = rangeb[1];
  262   
  263                   /* If the two ranges overlap or are adjacent, coalesce them.
  264                    * The two ranges overlap if the larger lower bound is less
  265                    * than or equal to the smaller upper bound. The two ranges
  266                    * are adjacent if the larger lower bound is one greater
  267                    * than the smaller upper bound.
  268                    */
  269                   if (Math.max(lba, lbb) - Math.min(uba, ubb) <= 1) {
  270                       // The coalesced range is from the smaller lower bound to
  271                       // the larger upper bound.
  272                       ranges.setElementAt(new int[]
  273                                              {Math.min(lba, lbb),
  274                                                   Math.max(uba, ubb)}, j);
  275                       ranges.remove (j+1);
  276                   } else if (lba > lbb) {
  277   
  278                       /* If the two ranges don't overlap and aren't adjacent but
  279                        * are out of order, swap them.
  280                        */
  281                       ranges.setElementAt (rangeb, j);
  282                       ranges.setElementAt (rangea, j+1);
  283                   } else {
  284                   /* If the two ranges don't overlap and aren't adjacent and
  285                    * aren't out of order, we're done early.
  286                    */
  287                       break;
  288                   }
  289               }
  290           }
  291       }
  292   
  293       /**
  294        * Convert the given vector of int[] objects to canonical array form.
  295        */
  296       private static int[][] canonicalArrayForm(Vector ranges) {
  297           return (int[][]) ranges.toArray (new int[ranges.size()][]);
  298       }
  299   
  300       /**
  301        * Construct a new set-of-integer attribute with the given members in
  302        * array form.
  303        *
  304        * @param  members  Set members in array form. If null, an empty set is
  305        *                     constructed.
  306        *
  307        * @exception  NullPointerException
  308        *     (Unchecked exception) Thrown if any element of
  309        *     <CODE>members</CODE> is null.
  310        * @exception  IllegalArgumentException
  311        *     (Unchecked exception) Thrown if any element of
  312        *     <CODE>members</CODE> is not a length-one or length-two array or if
  313        *     any non-null range in <CODE>members</CODE> has a lower bound less
  314        *     than zero.
  315        */
  316       protected SetOfIntegerSyntax(int[][] members) {
  317           this.members = parse (members);
  318       }
  319   
  320       /**
  321        * Parse the given array form, returning canonical array form.
  322        */
  323       private static int[][] parse(int[][] members) {
  324           // Create vector to hold int[] elements, each element being one range
  325           // parsed out of members.
  326           Vector ranges = new Vector();
  327   
  328           // Process all integer groups in members.
  329           int n = (members == null ? 0 : members.length);
  330           for (int i = 0; i < n; ++ i) {
  331               // Get lower and upper bounds of the range.
  332               int lb, ub;
  333               if (members[i].length == 1) {
  334                   lb = ub = members[i][0];
  335               } else if (members[i].length == 2) {
  336                   lb = members[i][0];
  337                   ub = members[i][1];
  338               } else {
  339                   throw new IllegalArgumentException();
  340               }
  341   
  342               // Verify valid bounds.
  343               if (lb <= ub && lb < 0) {
  344                   throw new IllegalArgumentException();
  345               }
  346   
  347               // Accumulate the range.
  348               accumulate(ranges, lb, ub);
  349           }
  350   
  351                   // Return canonical array form.
  352                   return canonicalArrayForm (ranges);
  353                   }
  354   
  355       /**
  356        * Construct a new set-of-integer attribute containing a single integer.
  357        *
  358        * @param  member  Set member.
  359        *
  360        * @exception  IllegalArgumentException
  361        *     (Unchecked exception) Thrown if <CODE>member</CODE> is less than
  362        *     zero.
  363        */
  364       protected SetOfIntegerSyntax(int member) {
  365           if (member < 0) {
  366               throw new IllegalArgumentException();
  367           }
  368           members = new int[][] {{member, member}};
  369       }
  370   
  371       /**
  372        * Construct a new set-of-integer attribute containing a single range of
  373        * integers. If the lower bound is greater than the upper bound (a null
  374        * range), an empty set is constructed.
  375        *
  376        * @param  lowerBound  Lower bound of the range.
  377        * @param  upperBound  Upper bound of the range.
  378        *
  379        * @exception  IllegalArgumentException
  380        *     (Unchecked exception) Thrown if the range is non-null and
  381        *     <CODE>lowerBound</CODE> is less than zero.
  382        */
  383       protected SetOfIntegerSyntax(int lowerBound, int upperBound) {
  384           if (lowerBound <= upperBound && lowerBound < 0) {
  385               throw new IllegalArgumentException();
  386           }
  387           members = lowerBound <=upperBound ?
  388               new int[][] {{lowerBound, upperBound}} :
  389               new int[0][];
  390       }
  391   
  392   
  393       /**
  394        * Obtain this set-of-integer attribute's members in canonical array form.
  395        * The returned array is "safe;" the client may alter it without affecting
  396        * this set-of-integer attribute.
  397        *
  398        * @return  This set-of-integer attribute's members in canonical array form.
  399        */
  400       public int[][] getMembers() {
  401           int n = members.length;
  402           int[][] result = new int[n][];
  403           for (int i = 0; i < n; ++ i) {
  404               result[i] = new int[] {members[i][0], members[i][1]};
  405           }
  406           return result;
  407       }
  408   
  409       /**
  410        * Determine if this set-of-integer attribute contains the given value.
  411        *
  412        * @param  x  Integer value.
  413        *
  414        * @return  True if this set-of-integer attribute contains the value
  415        *          <CODE>x</CODE>, false otherwise.
  416        */
  417       public boolean contains(int x) {
  418           // Do a linear search to find the range that contains x, if any.
  419           int n = members.length;
  420           for (int i = 0; i < n; ++ i) {
  421               if (x < members[i][0]) {
  422                   return false;
  423               } else if (x <= members[i][1]) {
  424                   return true;
  425               }
  426           }
  427           return false;
  428       }
  429   
  430       /**
  431        * Determine if this set-of-integer attribute contains the given integer
  432        * attribute's value.
  433        *
  434        * @param  attribute  Integer attribute.
  435        *
  436        * @return  True if this set-of-integer attribute contains
  437        *          <CODE>theAttribute</CODE>'s value, false otherwise.
  438        */
  439       public boolean contains(IntegerSyntax attribute) {
  440           return contains (attribute.getValue());
  441       }
  442   
  443       /**
  444        * Determine the smallest integer in this set-of-integer attribute that is
  445        * greater than the given value. If there are no integers in this
  446        * set-of-integer attribute greater than the given value, <CODE>-1</CODE> is
  447        * returned. (Since a set-of-integer attribute can only contain nonnegative
  448        * values, <CODE>-1</CODE> will never appear in the set.) You can use the
  449        * <CODE>next()</CODE> method to iterate through the integer values in a
  450        * set-of-integer attribute in ascending order, like this:
  451        * <PRE>
  452        *     SetOfIntegerSyntax attribute = . . .;
  453        *     int i = -1;
  454        *     while ((i = attribute.next (i)) != -1)
  455        *         {
  456        *         foo (i);
  457        *         }
  458        * </PRE>
  459        *
  460        * @param  x  Integer value.
  461        *
  462        * @return  The smallest integer in this set-of-integer attribute that is
  463        *          greater than <CODE>x</CODE>, or <CODE>-1</CODE> if no integer in
  464        *          this set-of-integer attribute is greater than <CODE>x</CODE>.
  465        */
  466       public int next(int x) {
  467           // Do a linear search to find the range that contains x, if any.
  468           int n = members.length;
  469           for (int i = 0; i < n; ++ i) {
  470               if (x < members[i][0]) {
  471                   return members[i][0];
  472               } else if (x < members[i][1]) {
  473                   return x + 1;
  474               }
  475           }
  476           return -1;
  477       }
  478   
  479       /**
  480        * Returns whether this set-of-integer attribute is equivalent to the passed
  481        * in object. To be equivalent, all of the following conditions must be
  482        * true:
  483        * <OL TYPE=1>
  484        * <LI>
  485        * <CODE>object</CODE> is not null.
  486        * <LI>
  487        * <CODE>object</CODE> is an instance of class SetOfIntegerSyntax.
  488        * <LI>
  489        * This set-of-integer attribute's members and <CODE>object</CODE>'s
  490        * members are the same.
  491        * </OL>
  492        *
  493        * @param  object  Object to compare to.
  494        *
  495        * @return  True if <CODE>object</CODE> is equivalent to this
  496        *          set-of-integer attribute, false otherwise.
  497        */
  498       public boolean equals(Object object) {
  499           if (object != null && object instanceof SetOfIntegerSyntax) {
  500               int[][] myMembers = this.members;
  501               int[][] otherMembers = ((SetOfIntegerSyntax) object).members;
  502               int m = myMembers.length;
  503               int n = otherMembers.length;
  504               if (m == n) {
  505                   for (int i = 0; i < m; ++ i) {
  506                       if (myMembers[i][0] != otherMembers[i][0] ||
  507                           myMembers[i][1] != otherMembers[i][1]) {
  508                           return false;
  509                       }
  510                   }
  511                   return true;
  512               } else {
  513                   return false;
  514               }
  515           } else {
  516               return false;
  517           }
  518       }
  519   
  520       /**
  521        * Returns a hash code value for this set-of-integer attribute. The hash
  522        * code is the sum of the lower and upper bounds of the ranges in the
  523        * canonical array form, or 0 for an empty set.
  524        */
  525       public int hashCode() {
  526           int result = 0;
  527           int n = members.length;
  528           for (int i = 0; i < n; ++ i) {
  529               result += members[i][0] + members[i][1];
  530           }
  531           return result;
  532       }
  533   
  534       /**
  535        * Returns a string value corresponding to this set-of-integer attribute.
  536        * The string value is a zero-length string if this set is empty. Otherwise,
  537        * the string value is a comma-separated list of the ranges in the canonical
  538        * array form, where each range is represented as <CODE>"<I>i</I>"</CODE> if
  539        * the lower bound equals the upper bound or
  540        * <CODE>"<I>i</I>-<I>j</I>"</CODE> otherwise.
  541        */
  542       public String toString() {
  543           StringBuffer result = new StringBuffer();
  544           int n = members.length;
  545           for (int i = 0; i < n; i++) {
  546               if (i > 0) {
  547                   result.append (',');
  548               }
  549               result.append (members[i][0]);
  550               if (members[i][0] != members[i][1]) {
  551                   result.append ('-');
  552                   result.append (members[i][1]);
  553               }
  554           }
  555           return result.toString();
  556       }
  557   
  558   }

Save This Page
Home » openjdk-7 » javax » print » attribute » [javadoc | source]