View Javadoc

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 }