/*
* Copyright 2014-2016 CyberVision, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.kaaproject.kaa.server.transport;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Converts a range expression ("1-3") into a sequence of numbers from that range ([1, 2, 3]).
* If ranges overlap it returns only unique numbers. E.g: "1-3, 2-5" => (1, 2, 3, 4, 5).
*
* @author Oleg Klishch
*/
public final class RangeExpressionParser {
private static final String RANGE_DELIMITER = ",";
private static final String CORNERS_DELIMITER = "-";
private static final int RANGE_START = 0;
private static final int RANGE_END = 1;
/**
* Parse string, that represents range of values (like "1-3") and get all integers value
* corresponding this range.
*
* @param expression is expression for parsing
* @return list of integers values in range
*/
public List<Integer> getNumbersFromRanges(String expression) {
if (expression == null) {
throw new IllegalArgumentException("Expression can not be null");
}
List<Integer> numbersFromRanges = new ArrayList<>();
List<Range> ranges = parseRanges(expression);
for (Range range : mergeRanges(ranges)) {
for (int i = range.from; i <= range.to; i++) {
numbersFromRanges.add(i);
}
}
return numbersFromRanges;
}
private List<Range> parseRanges(String expression) {
String[] rangeStrings = expression.split(RANGE_DELIMITER);
List<Range> ranges = new ArrayList<>();
for (String rangeString : rangeStrings) {
ranges.add(parseRange(rangeString.trim()));
}
return ranges;
}
private Range parseRange(String rangeString) {
if (!rangeString.contains(CORNERS_DELIMITER)) {
int from = Integer.parseInt(rangeString);
return new Range(from, from);
}
String[] cornerStrings = rangeString.split(CORNERS_DELIMITER);
int from = Integer.parseInt(cornerStrings[RANGE_START]);
int to = Integer.parseInt(cornerStrings[RANGE_END]);
return new Range(from, to);
}
private List<Range> mergeRanges(List<Range> ranges) {
if (ranges.isEmpty()) {
return ranges;
}
List<Range> mergedRanges = new ArrayList<>();
Collections.sort(ranges);
Range currentRange = ranges.get(0);
for (int i = 1; i < ranges.size(); i++) {
if (currentRange.to >= ranges.get(i).from) {
currentRange.to = Math.max(currentRange.to, ranges.get(i).to);
} else {
mergedRanges.add(currentRange);
currentRange = ranges.get(i);
}
}
mergedRanges.add(currentRange);
return mergedRanges;
}
private static class Range implements Comparable<Range> {
public int from;
public int to;
private Range(int from, int to) {
this.from = from;
this.to = to;
}
@Override
public int compareTo(Range other) {
int cmp = Integer.compare(from, other.from);
if (cmp != 0) {
return cmp;
}
return Integer.compare(to, other.to);
}
}
}