blob: eccb67341ac62ade056e37bc225b155e3061cc7b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.ace.range;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.StringTokenizer;
import aQute.bnd.annotation.ProviderType;
/**
* Collection that stores a sorted set of ranges and is able to represent them
* as a string.
*/
@ProviderType
public class SortedRangeSet {
/**
* A static set which contains all possible values.
*/
public final static SortedRangeSet FULL_SET = new SortedRangeSet(0 + "-" + Long.MAX_VALUE) {
public boolean contains(long number) {
return true;
}
};
private List<Range> m_ranges = new ArrayList<>();
/**
* Creates a new instance from a string representation.
*
* @param representation The string representation of a <code>SortedRangeSet</code>.
* @throws NumberFormatException If the string representation does not contain a valid <code>SortedRangeSet</code>.
*/
public SortedRangeSet(String representation) {
StringTokenizer st = new StringTokenizer(representation, ",");
while (st.hasMoreTokens()) {
m_ranges.add(new Range(st.nextToken()));
}
}
/**
* Creates a new instance from an array of longs. The array can contain the longs in random order,
* and duplicates will be filtered out.
*
* @param items Array of longs
*/
public SortedRangeSet(long[] items) {
Arrays.sort(items);
Range r = null;
for (int i = 0; i < items.length; i++) {
if (r == null) {
r = new Range(items[i]);
}
else {
if (items[i] == r.getHigh()) {
// ignore this duplicate
}
else if (items[i] == r.getHigh() + 1) {
r.setHigh(items[i]);
}
else {
m_ranges.add(r);
r = new Range(items[i]);
}
}
}
if (r != null) {
m_ranges.add(r);
}
}
private SortedRangeSet() {
}
/**
* Retrieve a string representation of the <code>SortedRangeSet</code>.
*
* @return A string representation of the <code>SortedRangeSet</code>.
*/
public String toRepresentation() {
StringBuffer result = new StringBuffer();
Iterator<Range> i = m_ranges.iterator();
while (i.hasNext()) {
Range r = i.next();
if (result.length() > 0) {
result.append(',');
}
result.append(r.toRepresentation());
}
return result.toString();
}
/**
* Creates the difference between this set and <code>dest</code>, by (in set notation)<br>
* <code>result = dest \ this</code>,<br>
* that is, if <code>dest = {1, 2}</code> and <code>this = {2, 3}</code>, then
* <code>result = {1, 2} \ {2, 3} = {1}</code>
*
* @param dest The set from which this set should be 'set-minussed'.
* @return The resulting set after the diff.
*/
public SortedRangeSet diffDest(SortedRangeSet dest) {
SortedRangeSet result = new SortedRangeSet();
RangeIterator i = dest.iterator();
while (i.hasNext()) {
long number = i.next();
if (!contains(number)) {
result.add(number);
}
}
return result;
}
/**
* Checks if a number falls within any range in this set.
*
* @param number the number to check
* @return <code>true</code> if the number was inside any range in this set
*/
public boolean contains(long number) {
Iterator<Range> i = m_ranges.iterator();
while (i.hasNext()) {
Range r = i.next();
if (r.contains(number)) {
return true;
}
}
return false;
}
/**
* Adds a number to the set of ranges. Tries to be as smart as possible about it.
*
* @param number the number to add
*/
private void add(long number) {
ListIterator<Range> i = m_ranges.listIterator();
while (i.hasNext()) {
int index = i.nextIndex();
Range r = i.next();
if (r.contains(number)) {
return;
}
long low = r.getLow();
long high = r.getHigh();
if (number < low) {
if (number == (low - 1)) {
r.setLow(number);
return;
}
else {
Range nr = new Range(number);
m_ranges.add(index, nr);
return;
}
}
if (number == (high + 1)) {
r.setHigh(number);
if (i.hasNext()) {
Range nr = (Range) i.next();
if (number == nr.getLow() - 1) {
r.setHigh(nr.getHigh());
i.remove();
}
}
return;
}
}
Range nr = new Range(number);
m_ranges.add(nr);
}
/**
* Returns an iterator that iterates over all the ranges in this set.
*
* @return a range iterator
*/
public RangeIterator iterator() {
return new RangeIterator(m_ranges.listIterator(), false);
}
/**
* Returns an iterator that iterates over all the ranges in this set in reverse order.
*
* @return a range iterator
*/
public RangeIterator reverseIterator() {
return new RangeIterator(m_ranges.listIterator(m_ranges.size()), true);
}
/**
* Returns an iterator that iterates over all the <code>Range</code> instances in this set.
*
* @return an iterator of <code>Range</code> objects
*/
public Iterator rangeIterator() {
return m_ranges.iterator();
}
/**
* Returns the highest value present in any of the ranges in this <code>SortredRangeSet</code>.
*
* @return the highest value present in any of the ranges in this <code>SortredRangeSet</code>
* or <code>0</code> if the <code>SortedRangeSet</code> is empty.
*/
public long getHigh() {
int size = m_ranges.size();
if (size > 0) {
Range range = m_ranges.get(size - 1);
return range.getHigh();
}
else {
return 0;
}
}
/**
* Returns the union of this set and the provided set.
*
* @param dest a set to union with ourselves
* @return the resulting set
*/
public SortedRangeSet union(SortedRangeSet dest) {
SortedRangeSet result = new SortedRangeSet();
RangeIterator i = dest.iterator();
while (i.hasNext()) {
long number = i.next();
result.add(number);
}
i = iterator();
while (i.hasNext()) {
long number = i.next();
result.add(number);
}
return result;
}
@Override
public String toString() {
return "SortedRangeSet[" + toRepresentation() + "]";
}
}