/*
* Copyright (C) 2006-2014 Gabriel Burca (gburca dash virtmus at ebixio dot com)
*
* This program 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
* of the License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package com.ebixio.util;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A class that iterates through user-specified positive number ranges.
*
* Usage:
* for (int i : new NumberRange("3-5,9,2-6")) print(i);
*
* @author Gabriel Burca <gburca dash virtmus at ebixio dot com>
*/
public class NumberRange implements Iterable<Integer> {
List<Pair<Integer, Integer>> ranges = new ArrayList<>();
public NumberRange(int start, int end) {
if (start <= end) {
ranges.add(new Pair<>(start, end));
}
}
/**
* Constructs a number range by parsing the input string.
*
* Range bounds are separated by "-", and ranges are separated by ",". Ex:
* "3-5,7,9-10". Negative numbers can not be used. Degenerate ranges ("5") are
* supported. Inverse ranges ("5-3") are silently ignored - they're represented
* by the empty set. The ranges do not have to be consecutive, and may overlap.
* For example: "5-9, 2-7".
*
* Usage:
* for (int i : new NumberRange("3-5,9,2-6")) print(i);
*
* @param rangeStr A description of the range to generate.
*/
public NumberRange(String rangeStr) {
String clean = rangeStr.replaceAll("[^-,0-9]", "");
clean = clean.replaceAll("^\\D+", "");
clean = clean.replaceAll("\\D+$", "");
// number, optional ("-" + another number), end is another comma or string end
Pattern nextVal = Pattern.compile("([0-9]+)(?:-([0-9]+))?(?:,|$)");
Matcher m = nextVal.matcher(clean);
while (m.find()) {
Pair<Integer, Integer> p;
Integer i1 = Integer.parseInt(m.group(1));
if (m.group(2) == null) {
// Degenerate range
p = new Pair<>(i1, i1);
} else {
p = new Pair<>(i1, Integer.parseInt(m.group(2)));
}
if (p.first <= p.second) {
ranges.add(p);
}
}
}
@Override
public Iterator iterator() {
return new RangeIterator(ranges);
}
public class RangeIterator implements Iterator {
List<Pair<Integer, Integer>> ranges;
int range;
int current;
boolean hasNext;
public RangeIterator(List<Pair<Integer, Integer>> ranges) {
if (ranges == null || ranges.isEmpty()) {
hasNext = false;
} else {
hasNext = true;
this.ranges = ranges;
range = 0;
current = ranges.get(range).first;
}
}
@Override
public boolean hasNext() {
return hasNext;
}
@Override
public Integer next() {
Integer val = current;
if (current < ranges.get(range).second) {
current++;
} else if (range < ranges.size() - 1) {
range++;
current = ranges.get(range).first;
} else {
hasNext = false;
}
return val;
}
@Override
public void remove() {
// not supported
}
}
}