source: src/main/java/weka/core/Range.java @ 20

Last change on this file since 20 was 4, checked in by gnappo, 14 years ago

Import di weka.

File size: 12.3 KB
Line 
1/*
2 *    This program is free software; you can redistribute it and/or modify
3 *    it under the terms of the GNU General Public License as published by
4 *    the Free Software Foundation; either version 2 of the License, or
5 *    (at your option) any later version.
6 *
7 *    This program is distributed in the hope that it will be useful,
8 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
9 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 *    GNU General Public License for more details.
11 *
12 *    You should have received a copy of the GNU General Public License
13 *    along with this program; if not, write to the Free Software
14 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 */
16
17/*
18 *    Range.java
19 *    Copyright (C) 1999 University of Waikato, Hamilton, New Zealand
20 *
21 */
22
23package weka.core;
24
25import java.io.Serializable;
26import java.util.Enumeration;
27import java.util.Vector;
28
29/**
30 * Class representing a range of cardinal numbers. The range is set by a
31 * string representation such as: <P>
32 *
33 * <code>
34 *   first-last
35 *   1,2,3,4
36 * </code> <P>
37 * or combinations thereof. The range is internally converted from
38 * 1-based to 0-based (so methods that set or get numbers not in string
39 * format should use 0-based numbers).
40 *
41 * @author Len Trigg (trigg@cs.waikato.ac.nz)
42 * @version $Revision: 5953 $
43 */
44public class Range
45  implements Serializable, RevisionHandler {
46 
47  /** for serialization */
48  static final long serialVersionUID = 3667337062176835900L;
49
50  /** Record the string representations of the columns to delete */
51  /*@non_null spec_public@*/Vector m_RangeStrings = new Vector();
52
53  /** Whether matching should be inverted */
54  /*@spec_public@*/ boolean m_Invert;
55
56  /** The array of flags for whether an column is selected */
57  /*@spec_public@*/boolean [] m_SelectFlags;
58
59  /** Store the maximum value permitted in the range. -1 indicates that
60      no upper value has been set */
61  /*@spec_public@*/ int m_Upper = -1;
62
63  /** Default constructor. */
64  //@assignable this.*;
65    public Range() {
66  }
67
68  /**
69   * Constructor to set initial range.
70   *
71   * @param rangeList the initial range
72   * @throws IllegalArgumentException if the range list is invalid
73   */
74  public Range(/*@non_null@*/ String rangeList) {
75
76    setRanges(rangeList);
77  }
78
79  /**
80   * Sets the value of "last".
81   *
82   * @param newUpper the value of "last"
83   */
84  public void setUpper(int newUpper) {
85
86    if (newUpper >= 0) {
87      m_Upper = newUpper;
88      setFlags();
89    }
90  }
91 
92  /**
93   * Gets whether the range sense is inverted, i.e. all <i>except</i>
94   * the values included by the range string are selected.
95   *
96   * @return whether the matching sense is inverted
97   */
98  //@ensures \result <==> m_Invert;
99  public /*@pure@*/boolean getInvert() {
100
101    return m_Invert;
102  }
103
104  /**
105   * Sets whether the range sense is inverted, i.e. all <i>except</i>
106   * the values included by the range string are selected.
107   *
108   * @param newSetting true if the matching sense is inverted
109   */
110  public void setInvert(boolean newSetting) {
111
112    m_Invert = newSetting;
113  }
114
115  /**
116   * Gets the string representing the selected range of values
117   *
118   * @return the range selection string
119   */
120  public /*@non_null pure@*/String getRanges() {
121
122    StringBuffer result = new StringBuffer(m_RangeStrings.size()*4);
123    boolean first = true;
124    char sep = ',';
125    for (int i = 0; i < m_RangeStrings.size(); i++) {
126      if (first) {
127        result.append((String)m_RangeStrings.elementAt(i));
128        first = false;
129      } else {
130        result.append(sep + (String)m_RangeStrings.elementAt(i));
131      }
132    }
133    return result.toString();
134  }
135
136  /**
137   * Sets the ranges from a string representation. Note that setUpper()
138   * must be called afterwards for ranges to be actually set internally.
139   *
140   * @param rangeList the comma separated list of ranges. The empty
141   * string sets the range to empty.
142   * @throws IllegalArgumentException if the rangeList was not well formed
143   */
144  //@requires rangeList != null;
145  //@assignable m_RangeStrings,m_SelectFlags;
146  public void setRanges(String rangeList) {
147
148    Vector<String> ranges = new Vector<String> (10);
149
150    // Split the rangeList up into the vector
151    while (!rangeList.equals("")) {
152      String range = rangeList.trim();
153      int commaLoc = rangeList.indexOf(',');
154      if (commaLoc != -1) {
155        range = rangeList.substring(0, commaLoc).trim();
156        rangeList = rangeList.substring(commaLoc + 1).trim();
157      } else {
158        rangeList = "";
159      }
160      if (!range.equals("")) {
161        ranges.addElement(range);
162      }
163    }
164    m_RangeStrings = ranges;
165    m_SelectFlags = null;
166  }
167
168  /**
169   * Gets whether the supplied cardinal number is included in the current
170   * range.
171   *
172   * @param index the number of interest
173   * @return true if index is in the current range
174   * @throws RuntimeException if the upper limit of the range hasn't been defined
175   */
176  //@requires m_Upper >= 0;
177  //@requires 0 <= index && index < m_SelectFlags.length;
178  public /*@pure@*/ boolean isInRange(int index) {
179
180    if (m_Upper == -1) {
181      throw new RuntimeException("No upper limit has been specified for range");
182    }
183    if (m_Invert) {
184      return !m_SelectFlags[index];
185    } else {
186      return m_SelectFlags[index];
187    }
188  }
189
190  /**
191   * Constructs a representation of the current range. Being a string
192   * representation, the numbers are based from 1.
193   *
194   * @return the string representation of the current range
195   */
196  public /*@non_null pure@*/ String toString() {
197
198    if (m_RangeStrings.size() == 0) {
199      return "Empty";
200    }
201    String result ="Strings: ";
202    Enumeration enu = m_RangeStrings.elements();
203    while (enu.hasMoreElements()) {
204      result += (String)enu.nextElement() + " ";
205    }
206    result += "\n";
207
208    result += "Invert: " + m_Invert + "\n";
209
210    try {
211      if (m_Upper == -1) {
212        throw new RuntimeException("Upper limit has not been specified");
213      }
214      String cols = null;
215      for (int i = 0; i < m_SelectFlags.length; i++) {
216        if (isInRange(i)) {
217          if (cols == null) {
218            cols = "Cols: " + (i + 1);
219          } else {
220            cols += "," + (i + 1);
221          }
222        }
223      }
224      if (cols != null) {
225        result += cols + "\n";
226      }
227    } catch (Exception ex) {
228      result += ex.getMessage();
229    }
230    return result;
231  }
232
233  /**
234   * Gets an array containing all the selected values, in the order
235   * that they were selected (or ascending order if range inversion is on)
236   *
237   * @return the array of selected values
238   * @throws RuntimeException if the upper limit of the range hasn't been defined
239   */
240  //@requires m_Upper >= 0;
241  public /*@non_null@*/ int [] getSelection() {
242
243    if (m_Upper == -1) {
244      throw new RuntimeException("No upper limit has been specified for range");
245    }
246    int [] selectIndices = new int [m_Upper + 1];
247    int numSelected = 0;
248    if (m_Invert)
249    {
250      for (int i = 0; i <= m_Upper; i++) {
251        if (!m_SelectFlags[i]) {
252          selectIndices[numSelected++] = i;
253        }
254      }
255    }
256    else
257    {
258      Enumeration enu = m_RangeStrings.elements();
259      while (enu.hasMoreElements()) {
260        String currentRange = (String)enu.nextElement();
261        int start = rangeLower(currentRange);
262        int end = rangeUpper(currentRange);
263        for (int i = start; (i <= m_Upper) && (i <= end); i++) {
264          if (m_SelectFlags[i]) {
265            selectIndices[numSelected++] = i;
266          }
267        }
268      }
269    }
270    int [] result = new int [numSelected];
271    System.arraycopy(selectIndices, 0, result, 0, numSelected);
272    return result;
273  }
274
275  /**
276   * Creates a string representation of the indices in the supplied array.
277   *
278   * @param indices an array containing indices to select.
279   * Since the array will typically come from a program, indices are assumed
280   * from 0, and thus will have 1 added in the String representation.
281   * @return the string representation of the indices
282   */
283  public static /*@non_null pure@*/String indicesToRangeList(/*@non_null@*/ int []indices) {
284
285    StringBuffer rl = new StringBuffer();
286    int last = -2;
287    boolean range = false;
288    for(int i = 0; i < indices.length; i++) {
289      if (i == 0) {
290        rl.append(indices[i] + 1);
291      } else if (indices[i] == last) {
292        range = true;
293      } else {
294        if (range) {
295          rl.append('-').append(last);
296          range = false;
297        }
298        rl.append(',').append(indices[i] + 1);
299      }
300      last = indices[i] + 1;
301    }
302    if (range) {
303      rl.append('-').append(last);
304    }
305    return rl.toString();
306  }
307
308  /** Sets the flags array. */
309  protected void setFlags() {
310
311    m_SelectFlags = new boolean [m_Upper + 1];
312    Enumeration enu = m_RangeStrings.elements();
313    while (enu.hasMoreElements()) {
314      String currentRange = (String)enu.nextElement();
315      if (!isValidRange(currentRange)) {
316        throw new IllegalArgumentException("Invalid range list at " + currentRange);
317      }
318      int start = rangeLower(currentRange);
319      int end = rangeUpper(currentRange);
320      for (int i = start; (i <= m_Upper) && (i <= end); i++) {
321        m_SelectFlags[i] = true;
322      }
323    }
324  }
325
326
327  /**
328   * Translates a single string selection into it's internal 0-based equivalent
329   *
330   * @param single the string representing the selection (eg: 1 first last)
331   * @return the number corresponding to the selected value
332   */
333  protected /*@pure@*/ int rangeSingle(/*@non_null@*/ String single) {
334
335    if (single.toLowerCase().equals("first")) {
336      return 0;
337    }
338    if (single.toLowerCase().equals("last")) {
339      return m_Upper;
340    }
341    int index = Integer.parseInt(single) - 1;
342    if (index < 0) {
343      index = 0;
344    }
345    if (index > m_Upper) {
346      index = m_Upper;
347    }
348    return index;
349  }
350
351  /**
352   * Translates a range into it's lower index.
353   *
354   * @param range the string representation of the range
355   * @return the lower index of the range
356   */ 
357  protected int rangeLower(/*@non_null@*/ String range) {
358
359    int hyphenIndex;
360    if ((hyphenIndex = range.indexOf('-')) >= 0) {
361      return Math.min(rangeLower(range.substring(0, hyphenIndex)),
362                       rangeLower(range.substring(hyphenIndex + 1)));
363    }
364    return rangeSingle(range);
365  }
366
367  /**
368   * Translates a range into it's upper index. Must only be called once
369   * setUpper has been called.
370   *
371   * @param range the string representation of the range
372   * @return the upper index of the range
373   */
374  protected int rangeUpper(/*@non_null@*/ String range) {
375
376    int hyphenIndex;
377    if ((hyphenIndex = range.indexOf('-')) >= 0) {
378      return Math.max(rangeUpper(range.substring(0, hyphenIndex)),
379                       rangeUpper(range.substring(hyphenIndex + 1)));
380    }
381    return rangeSingle(range);
382  }
383
384  /**
385   * Determines if a string represents a valid index or simple range.
386   * Examples: <code>first  last   2   first-last  first-4  4-last</code>
387   * Doesn't check that a < b for a-b
388   *
389   * @param range the string to check
390   * @return true if the range is valid
391   */
392  protected boolean isValidRange(String range) {
393
394    if (range == null) {
395      return false;
396    }
397    int hyphenIndex;
398    if ((hyphenIndex = range.indexOf('-')) >= 0) {
399      if (isValidRange(range.substring(0, hyphenIndex)) &&
400          isValidRange(range.substring(hyphenIndex + 1))) {
401        return true;
402      }
403      return false;
404    }
405    if (range.toLowerCase().equals("first")) {
406      return true;
407    }
408    if (range.toLowerCase().equals("last")) {
409      return true;
410    }
411    try {
412      int index = Integer.parseInt(range);
413      if ((index > 0) && (index <= m_Upper + 1)){
414        return true;
415      }
416      return false;
417    } catch (NumberFormatException ex) {
418      return false;
419    }
420  }
421 
422  /**
423   * Returns the revision string.
424   *
425   * @return            the revision
426   */
427  public String getRevision() {
428    return RevisionUtils.extract("$Revision: 5953 $");
429  }
430
431  /**
432   * Main method for testing this class.
433   *
434   * @param argv one parameter: a test range specification
435   */
436  public static void main(String [] argv) {
437
438    try {
439      if (argv.length == 0) {
440        throw new Exception("Usage: Range <rangespec>");
441      }
442      Range range = new Range();
443      range.setRanges(argv[0]);
444      range.setUpper(9);
445      range.setInvert(false);
446      System.out.println("Input: " + argv[0] + "\n"
447                         + range.toString());
448      int [] rangeIndices = range.getSelection();
449      for (int i = 0; i < rangeIndices.length; i++)
450        System.out.print(" " + (rangeIndices[i] + 1));
451      System.out.println("");
452    } catch (Exception ex) {
453      System.out.println(ex.getMessage());
454    }
455  }
456}
457
458
Note: See TracBrowser for help on using the repository browser.