Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/javax/print/attribute/SetOfIntegerSyntax.java
38918 views
/*1* Copyright (c) 2000, 2004, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/242526package javax.print.attribute;2728import java.io.Serializable;29import java.util.Vector;3031/**32* Class SetOfIntegerSyntax is an abstract base class providing the common33* implementation of all attributes whose value is a set of nonnegative34* integers. This includes attributes whose value is a single range of integers35* and attributes whose value is a set of ranges of integers.36* <P>37* You can construct an instance of SetOfIntegerSyntax by giving it in "string38* form." The string consists of zero or more comma-separated integer groups.39* Each integer group consists of either one integer, two integers separated by40* a hyphen (<CODE>-</CODE>), or two integers separated by a colon41* (<CODE>:</CODE>). Each integer consists of one or more decimal digits42* (<CODE>0</CODE> through <CODE>9</CODE>). Whitespace characters cannot43* appear within an integer but are otherwise ignored. For example:44* <CODE>""</CODE>, <CODE>"1"</CODE>, <CODE>"5-10"</CODE>, <CODE>"1:2,45* 4"</CODE>.46* <P>47* You can also construct an instance of SetOfIntegerSyntax by giving it in48* "array form." Array form consists of an array of zero or more integer groups49* where each integer group is a length-1 or length-2 array of50* <CODE>int</CODE>s; for example, <CODE>int[0][]</CODE>,51* <CODE>int[][]{{1}}</CODE>, <CODE>int[][]{{5,10}}</CODE>,52* <CODE>int[][]{{1,2},{4}}</CODE>.53* <P>54* In both string form and array form, each successive integer group gives a55* range of integers to be included in the set. The first integer in each group56* gives the lower bound of the range; the second integer in each group gives57* the upper bound of the range; if there is only one integer in the group, the58* upper bound is the same as the lower bound. If the upper bound is less than59* the lower bound, it denotes a null range (no values). If the upper bound is60* equal to the lower bound, it denotes a range consisting of a single value. If61* the upper bound is greater than the lower bound, it denotes a range62* consisting of more than one value. The ranges may appear in any order and are63* allowed to overlap. The union of all the ranges gives the set's contents.64* Once a SetOfIntegerSyntax instance is constructed, its value is immutable.65* <P>66* The SetOfIntegerSyntax object's value is actually stored in "<I>canonical</I>67* array form." This is the same as array form, except there are no null ranges;68* the members of the set are represented in as few ranges as possible (i.e.,69* overlapping ranges are coalesced); the ranges appear in ascending order; and70* each range is always represented as a length-two array of <CODE>int</CODE>s71* in the form {lower bound, upper bound}. An empty set is represented as a72* zero-length array.73* <P>74* Class SetOfIntegerSyntax has operations to return the set's members in75* canonical array form, to test whether a given integer is a member of the76* set, and to iterate through the members of the set.77* <P>78*79* @author David Mendenhall80* @author Alan Kaminsky81*/82public abstract class SetOfIntegerSyntax implements Serializable, Cloneable {8384private static final long serialVersionUID = 3666874174847632203L;8586/**87* This set's members in canonical array form.88* @serial89*/90private int[][] members;919293/**94* Construct a new set-of-integer attribute with the given members in95* string form.96*97* @param members Set members in string form. If null, an empty set is98* constructed.99*100* @exception IllegalArgumentException101* (Unchecked exception) Thrown if <CODE>members</CODE> does not102* obey the proper syntax.103*/104protected SetOfIntegerSyntax(String members) {105this.members = parse (members);106}107108/**109* Parse the given string, returning canonical array form.110*/111private static int[][] parse(String members) {112// Create vector to hold int[] elements, each element being one range113// parsed out of members.114Vector theRanges = new Vector();115116// Run state machine over members.117int n = (members == null ? 0 : members.length());118int i = 0;119int state = 0;120int lb = 0;121int ub = 0;122char c;123int digit;124while (i < n) {125c = members.charAt(i ++);126switch (state) {127128case 0: // Before first integer in first group129if (Character.isWhitespace(c)) {130state = 0;131}132else if ((digit = Character.digit(c, 10)) != -1) {133lb = digit;134state = 1;135} else {136throw new IllegalArgumentException();137}138break;139140case 1: // In first integer in a group141if (Character.isWhitespace(c)){142state = 2;143} else if ((digit = Character.digit(c, 10)) != -1) {144lb = 10 * lb + digit;145state = 1;146} else if (c == '-' || c == ':') {147state = 3;148} else if (c == ',') {149accumulate (theRanges, lb, lb);150state = 6;151} else {152throw new IllegalArgumentException();153}154break;155156case 2: // After first integer in a group157if (Character.isWhitespace(c)) {158state = 2;159}160else if (c == '-' || c == ':') {161state = 3;162}163else if (c == ',') {164accumulate(theRanges, lb, lb);165state = 6;166} else {167throw new IllegalArgumentException();168}169break;170171case 3: // Before second integer in a group172if (Character.isWhitespace(c)) {173state = 3;174} else if ((digit = Character.digit(c, 10)) != -1) {175ub = digit;176state = 4;177} else {178throw new IllegalArgumentException();179}180break;181182case 4: // In second integer in a group183if (Character.isWhitespace(c)) {184state = 5;185} else if ((digit = Character.digit(c, 10)) != -1) {186ub = 10 * ub + digit;187state = 4;188} else if (c == ',') {189accumulate(theRanges, lb, ub);190state = 6;191} else {192throw new IllegalArgumentException();193}194break;195196case 5: // After second integer in a group197if (Character.isWhitespace(c)) {198state = 5;199} else if (c == ',') {200accumulate(theRanges, lb, ub);201state = 6;202} else {203throw new IllegalArgumentException();204}205break;206207case 6: // Before first integer in second or later group208if (Character.isWhitespace(c)) {209state = 6;210} else if ((digit = Character.digit(c, 10)) != -1) {211lb = digit;212state = 1;213} else {214throw new IllegalArgumentException();215}216break;217}218}219220// Finish off the state machine.221switch (state) {222case 0: // Before first integer in first group223break;224case 1: // In first integer in a group225case 2: // After first integer in a group226accumulate(theRanges, lb, lb);227break;228case 4: // In second integer in a group229case 5: // After second integer in a group230accumulate(theRanges, lb, ub);231break;232case 3: // Before second integer in a group233case 6: // Before first integer in second or later group234throw new IllegalArgumentException();235}236237// Return canonical array form.238return canonicalArrayForm (theRanges);239}240241/**242* Accumulate the given range (lb .. ub) into the canonical array form243* into the given vector of int[] objects.244*/245private static void accumulate(Vector ranges, int lb,int ub) {246// Make sure range is non-null.247if (lb <= ub) {248// Stick range at the back of the vector.249ranges.add(new int[] {lb, ub});250251// Work towards the front of the vector to integrate the new range252// with the existing ranges.253for (int j = ranges.size()-2; j >= 0; -- j) {254// Get lower and upper bounds of the two ranges being compared.255int[] rangea = (int[]) ranges.elementAt (j);256int lba = rangea[0];257int uba = rangea[1];258int[] rangeb = (int[]) ranges.elementAt (j+1);259int lbb = rangeb[0];260int ubb = rangeb[1];261262/* If the two ranges overlap or are adjacent, coalesce them.263* The two ranges overlap if the larger lower bound is less264* than or equal to the smaller upper bound. The two ranges265* are adjacent if the larger lower bound is one greater266* than the smaller upper bound.267*/268if (Math.max(lba, lbb) - Math.min(uba, ubb) <= 1) {269// The coalesced range is from the smaller lower bound to270// the larger upper bound.271ranges.setElementAt(new int[]272{Math.min(lba, lbb),273Math.max(uba, ubb)}, j);274ranges.remove (j+1);275} else if (lba > lbb) {276277/* If the two ranges don't overlap and aren't adjacent but278* are out of order, swap them.279*/280ranges.setElementAt (rangeb, j);281ranges.setElementAt (rangea, j+1);282} else {283/* If the two ranges don't overlap and aren't adjacent and284* aren't out of order, we're done early.285*/286break;287}288}289}290}291292/**293* Convert the given vector of int[] objects to canonical array form.294*/295private static int[][] canonicalArrayForm(Vector ranges) {296return (int[][]) ranges.toArray (new int[ranges.size()][]);297}298299/**300* Construct a new set-of-integer attribute with the given members in301* array form.302*303* @param members Set members in array form. If null, an empty set is304* constructed.305*306* @exception NullPointerException307* (Unchecked exception) Thrown if any element of308* <CODE>members</CODE> is null.309* @exception IllegalArgumentException310* (Unchecked exception) Thrown if any element of311* <CODE>members</CODE> is not a length-one or length-two array or if312* any non-null range in <CODE>members</CODE> has a lower bound less313* than zero.314*/315protected SetOfIntegerSyntax(int[][] members) {316this.members = parse (members);317}318319/**320* Parse the given array form, returning canonical array form.321*/322private static int[][] parse(int[][] members) {323// Create vector to hold int[] elements, each element being one range324// parsed out of members.325Vector ranges = new Vector();326327// Process all integer groups in members.328int n = (members == null ? 0 : members.length);329for (int i = 0; i < n; ++ i) {330// Get lower and upper bounds of the range.331int lb, ub;332if (members[i].length == 1) {333lb = ub = members[i][0];334} else if (members[i].length == 2) {335lb = members[i][0];336ub = members[i][1];337} else {338throw new IllegalArgumentException();339}340341// Verify valid bounds.342if (lb <= ub && lb < 0) {343throw new IllegalArgumentException();344}345346// Accumulate the range.347accumulate(ranges, lb, ub);348}349350// Return canonical array form.351return canonicalArrayForm (ranges);352}353354/**355* Construct a new set-of-integer attribute containing a single integer.356*357* @param member Set member.358*359* @exception IllegalArgumentException360* (Unchecked exception) Thrown if <CODE>member</CODE> is less than361* zero.362*/363protected SetOfIntegerSyntax(int member) {364if (member < 0) {365throw new IllegalArgumentException();366}367members = new int[][] {{member, member}};368}369370/**371* Construct a new set-of-integer attribute containing a single range of372* integers. If the lower bound is greater than the upper bound (a null373* 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 IllegalArgumentException379* (Unchecked exception) Thrown if the range is non-null and380* <CODE>lowerBound</CODE> is less than zero.381*/382protected SetOfIntegerSyntax(int lowerBound, int upperBound) {383if (lowerBound <= upperBound && lowerBound < 0) {384throw new IllegalArgumentException();385}386members = lowerBound <=upperBound ?387new int[][] {{lowerBound, upperBound}} :388new int[0][];389}390391392/**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 affecting395* this set-of-integer attribute.396*397* @return This set-of-integer attribute's members in canonical array form.398*/399public int[][] getMembers() {400int n = members.length;401int[][] result = new int[n][];402for (int i = 0; i < n; ++ i) {403result[i] = new int[] {members[i][0], members[i][1]};404}405return result;406}407408/**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 value414* <CODE>x</CODE>, false otherwise.415*/416public boolean contains(int x) {417// Do a linear search to find the range that contains x, if any.418int n = members.length;419for (int i = 0; i < n; ++ i) {420if (x < members[i][0]) {421return false;422} else if (x <= members[i][1]) {423return true;424}425}426return false;427}428429/**430* Determine if this set-of-integer attribute contains the given integer431* attribute's value.432*433* @param attribute Integer attribute.434*435* @return True if this set-of-integer attribute contains436* <CODE>theAttribute</CODE>'s value, false otherwise.437*/438public boolean contains(IntegerSyntax attribute) {439return contains (attribute.getValue());440}441442/**443* Determine the smallest integer in this set-of-integer attribute that is444* greater than the given value. If there are no integers in this445* set-of-integer attribute greater than the given value, <CODE>-1</CODE> is446* returned. (Since a set-of-integer attribute can only contain nonnegative447* values, <CODE>-1</CODE> will never appear in the set.) You can use the448* <CODE>next()</CODE> method to iterate through the integer values in a449* 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 is462* greater than <CODE>x</CODE>, or <CODE>-1</CODE> if no integer in463* this set-of-integer attribute is greater than <CODE>x</CODE>.464*/465public int next(int x) {466// Do a linear search to find the range that contains x, if any.467int n = members.length;468for (int i = 0; i < n; ++ i) {469if (x < members[i][0]) {470return members[i][0];471} else if (x < members[i][1]) {472return x + 1;473}474}475return -1;476}477478/**479* Returns whether this set-of-integer attribute is equivalent to the passed480* in object. To be equivalent, all of the following conditions must be481* 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>'s489* 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 this495* set-of-integer attribute, false otherwise.496*/497public boolean equals(Object object) {498if (object != null && object instanceof SetOfIntegerSyntax) {499int[][] myMembers = this.members;500int[][] otherMembers = ((SetOfIntegerSyntax) object).members;501int m = myMembers.length;502int n = otherMembers.length;503if (m == n) {504for (int i = 0; i < m; ++ i) {505if (myMembers[i][0] != otherMembers[i][0] ||506myMembers[i][1] != otherMembers[i][1]) {507return false;508}509}510return true;511} else {512return false;513}514} else {515return false;516}517}518519/**520* Returns a hash code value for this set-of-integer attribute. The hash521* code is the sum of the lower and upper bounds of the ranges in the522* canonical array form, or 0 for an empty set.523*/524public int hashCode() {525int result = 0;526int n = members.length;527for (int i = 0; i < n; ++ i) {528result += members[i][0] + members[i][1];529}530return result;531}532533/**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 canonical537* array form, where each range is represented as <CODE>"<I>i</I>"</CODE> if538* the lower bound equals the upper bound or539* <CODE>"<I>i</I>-<I>j</I>"</CODE> otherwise.540*/541public String toString() {542StringBuffer result = new StringBuffer();543int n = members.length;544for (int i = 0; i < n; i++) {545if (i > 0) {546result.append (',');547}548result.append (members[i][0]);549if (members[i][0] != members[i][1]) {550result.append ('-');551result.append (members[i][1]);552}553}554return result.toString();555}556557}558559560