| /* 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(); |
| } |
| } |