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