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