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 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 }