diff options
Diffstat (limited to 'libjava/classpath/javax/print/attribute/SetOfIntegerSyntax.java')
-rw-r--r-- | libjava/classpath/javax/print/attribute/SetOfIntegerSyntax.java | 436 |
1 files changed, 436 insertions, 0 deletions
diff --git a/libjava/classpath/javax/print/attribute/SetOfIntegerSyntax.java b/libjava/classpath/javax/print/attribute/SetOfIntegerSyntax.java new file mode 100644 index 000000000..b1e0af1a7 --- /dev/null +++ b/libjava/classpath/javax/print/attribute/SetOfIntegerSyntax.java @@ -0,0 +1,436 @@ +/* SetOfIntegerSyntax.java -- + Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package javax.print.attribute; + +import java.io.Serializable; +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; + +/** + * <code>SetOfIntegerSyntax</code> is the abstract base class of all attribute + * classes which provide a set of non-negative integers as value (e.g. the + * page ranges to print) represented as single values or ranges of values. + * <p> + * A <code>SetOfIntegerSyntax</code> instance consists of an integer array of + * ranges. Ranges may have the same lower and upper bound representing a single + * integer value. Ranges with a lower bound greater than the upper bound are + * null ranges and discarded. Ranges may overlap in their values. In no case + * negative integers are allowed. + * </p> + * <p> + * There are several constructors available: + * <ul> + * <li><code>SetOfIntegerSyntax(int member)</code><br> + * Constructor for an instance with only one integer value. + * </li><br> + * <li><code>SetOfIntegerSyntax(int lowerBound, int upperBound)</code><br> + * Constructor for an instance with one range of integer values. + * </li><br> + * <li><code>SetOfIntegerSyntax(int[][] members)</code><br> + * Flexible constructor for an instance with several single integer values + * and/or several ranges of integer values. The allowed array form is an + * array of integer arrays of length one or two. Examples are: + * <code>int[0][]</code> for empty set of integers, <code>int[][] {{1}}</code> + * , <code>int[][] {{1,5}}</code>, <code>int[][] {{1,5},{7,9}}</code>, + * <code>int[][] {{3,7},{19}}</code>. + * </li><br> + * <li><code>SetOfIntegerSyntax(String s)</code><br> + * Flexible constructor for an instance with several single integer values + * and/or several ranges of integer values. The allowed String instance have + * to be a String with comma separated ranges of integer values or single + * values. Ranges are represented by two integer with a hypen (-) or colon (:) + * between the lower and upper bound value. Whitespace characters are ignored. + * Examples are: <code>""</code> for an empty set of integers, + * <code>"1"</code>, <code>"1-5"</code>, <code>"1-5,7-9"</code>, + * <code>"3-7,19"</code> and <code>"1:2,4"</code>. + * </li> + * </ul> + * </p> + * <p> + * <b>Internal storage:</b><br> + * The set of integers are stored internally in a normalized array form. + * In the normalized array form the set of integer ranges are represented + * in as few ranges as possible and overlapping ranges are merged. The ranges + * are always represented as an integer array of length two with ranges + * stored in {lower bound, upper bound} form. The ranges are stored in + * ascending order, without any null ranges. + * </p> + * @author Michael Koch (konqueror@gmx.de) + */ +public abstract class SetOfIntegerSyntax + implements Cloneable, Serializable +{ + private static final long serialVersionUID = 3666874174847632203L; + + private int[][] members; + + private static int[][] normalize(int[][] values, int size) + { + // Sort into increasing order. First the first index is + // compared, then the second. + Arrays.sort(values, 0, size, new Comparator() + { + public int compare(Object o1, Object o2) + { + int[] v1 = (int[]) o1; + int[] v2 = (int[]) o2; + if (v1[0] == v2[0]) + return v1[1] - v2[1]; + return v1[0] - v2[0]; + } + }); + + // Now coalesce overlapping ranges. + int outIndex = 0; + for (int i = 0; i < size; ++i) + { + // Note that we compare with values[i][1]+1, since + // we can coalesce {0,1} with {2,x}. + int save = i; + while (i + 1 < size && values[i + 1][0] <= values[i][1] + 1) + { + values[i][1] = Math.max(values[i][1], values[i + 1][1]); + ++i; + } + values[outIndex++] = values[save]; + } + + int[][] result = new int[outIndex][]; + System.arraycopy(values, 0, result, 0, outIndex); + + return result; + } + + /** + * Creates a <code>SetOfIntegerSyntax</code> object. + * + * @param member the member value + * + * @exception IllegalArgumentException if member is < 0 + */ + protected SetOfIntegerSyntax(int member) + { + if (member < 0) + throw new IllegalArgumentException("member may not be less than 0"); + + this.members = new int[][]{{member, member}}; + } + + /** + * Creates a <code>SetOfIntegerSyntax</code> object. + * + * @param members the members to use in this set. If + * <code>null</code> an empty set is created. + * + * @exception IllegalArgumentException if any element is invalid + * @exception NullPointerException if any element of members is null + */ + protected SetOfIntegerSyntax(int[][] members) + { + int[][] newMembers; + int outIndex = 0; + if (members == null) + newMembers = new int[0][]; + else + { + newMembers = new int[members.length][]; + for (int index = 0; index < members.length; index++) + { + int lower; + int upper; + + if (members[index].length == 1) + { + lower = members[index][0]; + upper = members[index][0]; + } + else if (members[index].length == 2) + { + lower = members[index][0]; + upper = members[index][1]; + } + else + throw new IllegalArgumentException("invalid member element"); + + // We only want to reject non-null ranges where lower<0. + if (lower <= upper && lower < 0) + throw new IllegalArgumentException("invalid member element"); + + if (lower <= upper) + { + int[] range = new int[2]; + range[0] = lower; + range[1] = upper; + newMembers[outIndex++] = range; + } + } + } + + this.members = normalize(newMembers, outIndex); + } + + private boolean skipWhitespace(StringCharacterIterator i) + { + while (Character.isWhitespace(i.current())) + i.next(); + return i.current() == CharacterIterator.DONE; + } + + private boolean skipNumber(StringCharacterIterator i) + { + boolean readAny = false; + while (Character.isDigit(i.current())) + { + readAny = true; + i.next(); + } + return readAny; + } + + /** + * Creates a <code>SetOfIntegerSyntax</code> object. + * + * @param s the members to use in this set in string form. If + * <code>null</code> an empty set is created. + * + * @exception IllegalArgumentException if any element is invalid + */ + protected SetOfIntegerSyntax(String s) + { + if (s == null) + this.members = normalize(new int[0][], 0); + else + { + ArrayList vals = new ArrayList(); + + StringCharacterIterator it = new StringCharacterIterator(s); + + while (true) + { + // Skip whitespace. + if (skipWhitespace(it)) + break; + + // Parse integer. + int index = it.getIndex(); + if (! skipNumber(it)) + throw new IllegalArgumentException(); + int[] item = new int[2]; + item[0] = Integer.parseInt(s.substring(index, it.getIndex())); + + if (! skipWhitespace(it)) + { + char c = it.current(); + if (c == ':' || c == '-') + { + it.next(); + if (skipWhitespace(it)) + throw new IllegalArgumentException(); + index = it.getIndex(); + if (! skipNumber(it)) + throw new IllegalArgumentException(); + item[1] = Integer.parseInt(s.substring(index, it.getIndex())); + } + else + item[1] = item[0]; + } + else + item[1] = item[0]; + + if (item[0] <= item[1]) + vals.add(item); + + if (skipWhitespace(it)) + break; + if (it.current() != ',') + throw new IllegalArgumentException(); + it.next(); + } + + members = normalize((int[][]) vals.toArray(new int[0][]), vals.size()); + } + } + + /** + * Creates a <code>SetOfIntegerSyntax</code> object. + * + * @param lowerBound the lower bound value + * @param upperBound the upper bound value + * + * @exception IllegalArgumentException if lowerBound <= upperbound + * and lowerBound < 0 + */ + protected SetOfIntegerSyntax(int lowerBound, int upperBound) + { + // We only want to reject non-null ranges where lower<0. + if (lowerBound <= upperBound + && lowerBound < 0) + throw new IllegalArgumentException(); + + members = (lowerBound <= upperBound ? new int[][]{{lowerBound, upperBound}} + : new int[0][]); + } + + /** + * Checks if this set contains the given value. + * + * @param value the value to test for + * + * @return true if this set contains value, false otherwise + */ + public boolean contains(int value) + { + // This only works on a normalized member array. + for (int index = 0; index < members.length; index++) + { + if (value < members[index][0]) + return false; + else if (value <= members[index][1]) + return true; + } + + return false; + } + + /** + * Checks if this set contains the given value. + * + * @param value the value to test for + * + * @return true if this set contains value, false otherwise + */ + public boolean contains(IntegerSyntax value) + { + return contains(value.getValue()); + } + + /** + * Tests if the given object is equal to this object. + * + * @param obj the object to test + * + * @return true if both objects are equal, false otherwise. + */ + public boolean equals(Object obj) + { + if (! (obj instanceof SetOfIntegerSyntax)) + return false; + SetOfIntegerSyntax other = (SetOfIntegerSyntax) obj; + if (other.members.length != members.length) + return false; + for (int i = 0; i < members.length; ++i) + { + if (members[i][0] != other.members[i][0] + || members[i][1] != other.members[i][1]) + return false; + } + return true; + } + + /** + * Returns an array describing the members included in this set. + * + * @return The members in normalized array form. + */ + public int[][] getMembers() + { + return (int[][]) members.clone(); + } + + /** + * Returns the hashcode for this object. + * + * @return The hashcode. + */ + public int hashCode() + { + int result = 0; + for (int i = 0; i < members.length; ++i) + result += members[i][0] + members[i][1]; + return result; + } + + /** + * Returns the smallest value that is greater than x which is in this set. + * + * @param x an integer value + * + * @return The next smallest integer value, or <code>-1</code> if there + * is no greater integer in the set. + */ + public int next(int x) + { + for (int i = 0; i < members.length; ++i) + { + if (x >= members[i][1]) + continue; + if (x < members[i][0]) + return members[i][0]; + // X is in this range. + return x + 1; + } + return -1; + } + + /** + * Returns the string representation for this object. + * The value is a zero length string for an empty set, or a comma seperated + * list of ranges and single values in the form <code>"1-2,5-7,10"</code>. + * + * @return The string representation. + */ + public String toString() + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < members.length; ++i) + { + if (i > 0) + sb.append(','); + sb.append(members[i][0]); + if (members[i][0] != members[i][1]) + { + sb.append('-'); + sb.append(members[i][1]); + } + } + return sb.toString(); + } +} |