blob: 2ede2cc9a4836d683271d2e06b6b31693725029a [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.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 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.
*
* @param items Array of longs
*/
public SortedRangeSet(long[] items) {
// TODO: deal with items not being in ascending order
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() + 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 i = m_ranges.iterator();
while (i.hasNext()) {
Range r = (Range) 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 i = m_ranges.iterator();
while (i.hasNext()) {
Range r = (Range) 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 i = m_ranges.listIterator();
while (i.hasNext()) {
int index = i.nextIndex();
Range r = (Range) 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 == low - 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.iterator());
}
/**
* 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 = (Range) m_ranges.get(size - 1);
return range.getHigh();
}
else {
return 0;
}
}
}