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 }