Merge from mainline
[official-gcc.git] / libjava / classpath / javax / print / attribute / SetOfIntegerSyntax.java
blob4c0dd2f19a9bb3599f2c2ff7adc3e1ac8204258b
1 /* SetOfIntegerSyntax.java --
2 Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
38 package javax.print.attribute;
40 import java.io.Serializable;
41 import java.text.CharacterIterator;
42 import java.text.StringCharacterIterator;
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.Comparator;
47 /**
48 * <code>SetOfIntegerSyntax</code> is the abstract base class of all attribute
49 * classes which provide a set of non-negative integers as value (e.g. the
50 * page ranges to print) represented as single values or ranges of values.
51 * <p>
52 * A <code>SetOfIntegerSyntax</code> instance consists of an integer array of
53 * ranges. Ranges may have the same lower and upper bound representing a single
54 * integer value. Ranges with a lower bound greater than the upper bound are
55 * null ranges and discarded. Ranges may overlap in their values. In no case
56 * negative integers are allowed.
57 * </p>
58 * <p>
59 * There are several constructors available:
60 * <ul>
61 * <li><code>SetOfIntegerSyntax(int member)</code><br>
62 * Constructor for an instance with only one integer value.
63 * </li><br>
64 * <li><code>SetOfIntegerSyntax(int lowerBound, int upperBound)</code><br>
65 * Constructor for an instance with one range of integer values.
66 * </li><br>
67 * <li><code>SetOfIntegerSyntax(int[][] members)</code><br>
68 * Flexible constructor for an instance with several single integer values
69 * and/or several ranges of integer values. The allowed array form is an
70 * array of integer arrays of length one or two. Examples are:
71 * <code>int[0][]</code> for empty set of integers, <code>int[][] {{1}}</code>
72 * , <code>int[][] {{1,5}}</code>, <code>int[][] {{1,5},{7,9}}</code>,
73 * <code>int[][] {{3,7},{19}}</code>.
74 * </li><br>
75 * <li><code>SetOfIntegerSyntax(String s)</code><br>
76 * Flexible constructor for an instance with several single integer values
77 * and/or several ranges of integer values. The allowed String instance have
78 * to be a String with comma separated ranges of integer values or single
79 * values. Ranges are represented by two integer with a hypen (-) or colon (:)
80 * between the lower and upper bound value. Whitespace characters are ignored.
81 * Examples are: <code>""</code> for an empty set of integers,
82 * <code>"1"</code>, <code>"1-5"</code>, <code>"1-5,7-9"</code>,
83 * <code>"3-7,19"</code> and <code>"1:2,4"</code>.
84 * </li>
85 * </ul>
86 * </p>
87 * <p>
88 * <b>Internal storage:</b><br>
89 * The set of integers are stored internally in a normalized array form.
90 * In the normalized array form the set of integer ranges are represented
91 * in as few ranges as possible and overlapping ranges are merged. The ranges
92 * are always represented as an integer array of length two with ranges
93 * stored in {lower bound, upper bound} form. The ranges are stored in
94 * ascending order, without any null ranges.
95 * </p>
96 * @author Michael Koch (konqueror@gmx.de)
98 public abstract class SetOfIntegerSyntax
99 implements Cloneable, Serializable
101 private static final long serialVersionUID = 3666874174847632203L;
103 private int[][] members;
105 private static int[][] normalize(int[][] values, int size)
107 // Sort into increasing order. First the first index is
108 // compared, then the second.
109 Arrays.sort(values, 0, size, new Comparator()
111 public int compare(Object o1, Object o2)
113 int[] v1 = (int[]) o1;
114 int[] v2 = (int[]) o2;
115 if (v1[0] == v2[0])
116 return v1[1] - v2[1];
117 return v1[0] - v2[0];
121 // Now coalesce overlapping ranges.
122 int outIndex = 0;
123 for (int i = 0; i < size; ++i)
125 // Note that we compare with values[i][1]+1, since
126 // we can coalesce {0,1} with {2,x}.
127 int save = i;
128 while (i + 1 < size && values[i + 1][0] <= values[i][1] + 1)
130 values[i][1] = Math.max(values[i][1], values[i + 1][1]);
131 ++i;
133 values[outIndex++] = values[save];
136 int[][] result = new int[outIndex][];
137 System.arraycopy(values, 0, result, 0, outIndex);
139 return result;
143 * Creates a <code>SetOfIntegerSyntax</code> object.
145 * @param member the member value
147 * @exception IllegalArgumentException if member is &lt; 0
149 protected SetOfIntegerSyntax(int member)
151 if (member < 0)
152 throw new IllegalArgumentException("member may not be less than 0");
154 this.members = new int[][]{{member, member}};
158 * Creates a <code>SetOfIntegerSyntax</code> object.
160 * @param members the members to use in this set. If
161 * <code>null</code> an empty set is created.
163 * @exception IllegalArgumentException if any element is invalid
164 * @exception NullPointerException if any element of members is null
166 protected SetOfIntegerSyntax(int[][] members)
168 int[][] newMembers;
169 int outIndex = 0;
170 if (members == null)
171 newMembers = new int[0][];
172 else
174 newMembers = new int[members.length][];
175 for (int index = 0; index < members.length; index++)
177 int lower;
178 int upper;
180 if (members[index].length == 1)
182 lower = members[index][0];
183 upper = members[index][0];
185 else if (members[index].length == 2)
187 lower = members[index][0];
188 upper = members[index][1];
190 else
191 throw new IllegalArgumentException("invalid member element");
193 // We only want to reject non-null ranges where lower<0.
194 if (lower <= upper && lower < 0)
195 throw new IllegalArgumentException("invalid member element");
197 if (lower <= upper)
199 int[] range = new int[2];
200 range[0] = lower;
201 range[1] = upper;
202 newMembers[outIndex++] = range;
207 this.members = normalize(newMembers, outIndex);
210 private boolean skipWhitespace(StringCharacterIterator i)
212 while (Character.isWhitespace(i.current()))
213 i.next();
214 return i.current() == CharacterIterator.DONE;
217 private boolean skipNumber(StringCharacterIterator i)
219 boolean readAny = false;
220 while (Character.isDigit(i.current()))
222 readAny = true;
223 i.next();
225 return readAny;
229 * Creates a <code>SetOfIntegerSyntax</code> object.
231 * @param s the members to use in this set in string form. If
232 * <code>null</code> an empty set is created.
234 * @exception IllegalArgumentException if any element is invalid
236 protected SetOfIntegerSyntax(String s)
238 if (s == null)
239 this.members = normalize(new int[0][], 0);
240 else
242 ArrayList vals = new ArrayList();
244 StringCharacterIterator it = new StringCharacterIterator(s);
246 while (true)
248 // Skip whitespace.
249 if (skipWhitespace(it))
250 break;
252 // Parse integer.
253 int index = it.getIndex();
254 if (! skipNumber(it))
255 throw new IllegalArgumentException();
256 int[] item = new int[2];
257 item[0] = Integer.parseInt(s.substring(index, it.getIndex()));
259 if (! skipWhitespace(it))
261 char c = it.current();
262 if (c == ':' || c == '-')
264 it.next();
265 if (skipWhitespace(it))
266 throw new IllegalArgumentException();
267 index = it.getIndex();
268 if (! skipNumber(it))
269 throw new IllegalArgumentException();
270 item[1] = Integer.parseInt(s.substring(index, it.getIndex()));
272 else
273 item[1] = item[0];
275 else
276 item[1] = item[0];
278 if (item[0] <= item[1])
279 vals.add(item);
281 if (skipWhitespace(it))
282 break;
283 if (it.current() != ',')
284 throw new IllegalArgumentException();
285 it.next();
288 members = normalize((int[][]) vals.toArray(new int[0][]), vals.size());
293 * Creates a <code>SetOfIntegerSyntax</code> object.
295 * @param lowerBound the lower bound value
296 * @param upperBound the upper bound value
298 * @exception IllegalArgumentException if lowerBound &lt;= upperbound
299 * and lowerBound &lt; 0
301 protected SetOfIntegerSyntax(int lowerBound, int upperBound)
303 // We only want to reject non-null ranges where lower<0.
304 if (lowerBound <= upperBound
305 && lowerBound < 0)
306 throw new IllegalArgumentException();
308 members = (lowerBound <= upperBound ? new int[][]{{lowerBound, upperBound}}
309 : new int[0][]);
313 * Checks if this set contains the given value.
315 * @param value the value to test for
317 * @return true if this set contains value, false otherwise
319 public boolean contains(int value)
321 // This only works on a normalized member array.
322 for (int index = 0; index < members.length; index++)
324 if (value < members[index][0])
325 return false;
326 else if (value <= members[index][1])
327 return true;
330 return false;
334 * Checks if this set contains the given value.
336 * @param value the value to test for
338 * @return true if this set contains value, false otherwise
340 public boolean contains(IntegerSyntax value)
342 return contains(value.getValue());
346 * Tests if the given object is equal to this object.
348 * @param obj the object to test
350 * @return true if both objects are equal, false otherwise.
352 public boolean equals(Object obj)
354 if (! (obj instanceof SetOfIntegerSyntax))
355 return false;
356 SetOfIntegerSyntax other = (SetOfIntegerSyntax) obj;
357 if (other.members.length != members.length)
358 return false;
359 for (int i = 0; i < members.length; ++i)
361 if (members[i][0] != other.members[i][0]
362 || members[i][1] != other.members[i][1])
363 return false;
365 return true;
369 * Returns an array describing the members included in this set.
371 * @return The members in normalized array form.
373 public int[][] getMembers()
375 return (int[][]) members.clone();
379 * Returns the hashcode for this object.
381 * @return The hashcode.
383 public int hashCode()
385 int result = 0;
386 for (int i = 0; i < members.length; ++i)
387 result += members[i][0] + members[i][1];
388 return result;
392 * Returns the smallest value that is greater than x which is in this set.
394 * @param x an integer value
396 * @return The next smallest integer value, or <code>-1</code> if there
397 * is no greater integer in the set.
399 public int next(int x)
401 for (int i = 0; i < members.length; ++i)
403 if (x >= members[i][1])
404 continue;
405 if (x < members[i][0])
406 return members[i][0];
407 // X is in this range.
408 return x + 1;
410 return -1;
414 * Returns the string representation for this object.
415 * The value is a zero length string for an empty set, or a comma seperated
416 * list of ranges and single values in the form <code>"1-2,5-7,10"</code>.
418 * @return The string representation.
420 public String toString()
422 StringBuilder sb = new StringBuilder();
423 for (int i = 0; i < members.length; ++i)
425 if (i > 0)
426 sb.append(',');
427 sb.append(members[i][0]);
428 if (members[i][0] != members[i][1])
430 sb.append('-');
431 sb.append(members[i][1]);
434 return sb.toString();