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  * <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<int[]> 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<int[]> 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 = ranges.elementAt (j);
 257                 int lba = rangea[0];
 258                 int uba = rangea[1];
 259                 int[] rangeb = 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<int[]> ranges) {
 297         return 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<int[]> 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 }