1 /*
2 * File: IntervalIntegerSetParser.java
3 * Created: 13.10.2006 8:52:33
4 *
5 * Copyright 2006 Michal Burda.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21 package net.sf.webmancer.util.integerset;
22
23 /**
24 * <p>
25 * This class parses string of a list of non-negative integers and creates a {@link IIntegerSet} from it. The recognized
26 * format is a list of non-negative integers delimited by colon (","), also intervals could be used. The delimiter of
27 * interval values is minus sign ("-"). Whitespaces are skipped.
28 * </p>
29 * <p>
30 * Example: <code>"-2,3,6-10,13,20-"</code> results in a {@link IIntegerSet} that contains all numbers from
31 * {@link #minimum} to 2 (inclusive), then also values 3, 6, 7, 8, 9, 10, 13 and all numbers from 20 (inclusive) to
32 * {@link #maximum}.
33 * </p>
34 *
35 * @author Michal Burda
36 */
37 public class IntervalIntegerSetParser implements IIntegerSetParser {
38 /**
39 * The minimum value (used as a boundary for open intervals).
40 */
41 private int minimum;
42
43 /**
44 * The maximum value (used as a boundary for open intervals).
45 */
46 private int maximum;
47
48 /**
49 * Create new parser using given boundaries. Note that <code>minimum</code> must be lower or equal to
50 * <code>maximum</code>.
51 *
52 * @param minimum
53 * The minimum value (lower boundary for open intervals). The value must be non-negative since this
54 * parser does not support negative integers.
55 * @param maximum
56 * The maximum value (upper boundary for open intervals). A value of {@link Integer#MAX_VALUE} means
57 * "infinity".
58 */
59 public IntervalIntegerSetParser(final int minimum, final int maximum) {
60 // TODO: check contract: minimum >= 0, maximum >= 0, minimum <= maximum
61 super();
62 this.minimum = minimum;
63 this.maximum = maximum;
64 }
65
66 /**
67 * Create new parser using boundaries from 0 to {@link Integer#MAX_VALUE}.
68 *
69 * @see #IntervalIntegerSetParser(int, int)
70 */
71 public IntervalIntegerSetParser() {
72 this(0, Integer.MAX_VALUE);
73 }
74
75 /**
76 * Parse the given string to integer set.
77 *
78 * @param str
79 * A string to be parsed
80 * @return An integer set corresponding to given list of integers in <code>str</code>
81 * @throws NumberFormatException
82 * if any non-integer number is found
83 * @see cz.vsb.cs.ruleminer.util.integerset.IIntegerSetParser#parse(java.lang.String)
84 */
85 public IIntegerSet parse(final String str) throws NumberFormatException {
86 assert this.minimum >= 0;
87 assert this.maximum >= 0;
88 assert this.minimum <= this.maximum;
89
90 IntervalIntegerSet intervals = new IntervalIntegerSet();
91 if (str != null) {
92 String[] intervalStrings = str.split(",");
93 for (String element : intervalStrings) {
94 if (element.trim().equals("")) {
95 continue;
96 }
97 String[] fromTo = element.split("-", 2);
98 assert (fromTo.length == 1 || fromTo.length == 2);
99 int from = getFrom(fromTo);
100 if (fromTo.length == 1) {
101 intervals.addInterval(from, from);
102 } else {
103 int to = getTo(fromTo);
104 if (to < from) {
105 throw new NumberFormatException("Wrong interval: " + from + " - " + to);
106 }
107 intervals.addInterval(from, to);
108 }
109 }
110 }
111 return intervals;
112 }
113
114 /**
115 * Get the left boundary of an pre-parsed interval
116 *
117 * @param fromTo
118 * @return
119 */
120 private int getFrom(final String[] fromTo) {
121 assert (fromTo.length == 1 || fromTo.length == 2);
122 if (fromTo[0].trim().equals("")) {
123 return this.minimum;
124 } else {
125 int from = Integer.parseInt(fromTo[0].trim());
126 checkValue(from);
127 return from;
128 }
129 }
130
131 /**
132 * @param fromTo
133 * @return
134 */
135 private int getTo(final String[] fromTo) {
136 assert (fromTo.length == 2);
137 if (fromTo[1].trim().equals("")) {
138 return this.maximum;
139 } else {
140 int to = Integer.parseInt(fromTo[1].trim());
141 checkValue(to);
142 return to;
143 }
144 }
145
146 /**
147 * @param value
148 * @throws NumberFormatException
149 */
150 private void checkValue(final int value) throws NumberFormatException {
151 if (value < this.minimum) {
152 throw new NumberFormatException("Value too low: " + value + ". Minimum allowed value is: " + this.minimum);
153 }
154 if (value > this.maximum) {
155 throw new NumberFormatException("Value too high: " + value + ". Maximum allowed value is: " + this.maximum);
156 }
157 }
158 }