Range.java
/*
** Module : Range.java
** Abstract : Range of Long numbers.
**
** Copyright (c) 2004-2017, Golden Code Development Corporation.
**
** -#- -I- --Date-- -T- --JPRM-- ----------------Description-----------------
** 001 SVL 20080901 ADD @39781 Created initial version. Basic functions
* for working with ranges were implemented.
*/
/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
**
** Additional terms under GNU Affero GPL version 3 section 7:
**
** Under Section 7 of the GNU Affero GPL version 3, the following additional
** terms apply to the works covered under the License. These additional terms
** are non-permissive additional terms allowed under Section 7 of the GNU
** Affero GPL version 3 and may not be removed by you.
**
** 0. Attribution Requirement.
**
** You must preserve all legal notices or author attributions in the covered
** work or Appropriate Legal Notices displayed by works containing the covered
** work. You may not remove from the covered work any author or developer
** credit already included within the covered work.
**
** 1. No License To Use Trademarks.
**
** This license does not grant any license or rights to use the trademarks
** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
** of Golden Code Development Corporation. You are not authorized to use the
** name Golden Code, FWD, or the names of any author or contributor, for
** publicity purposes without written authorization.
**
** 2. No Misrepresentation of Affiliation.
**
** You may not represent yourself as Golden Code Development Corporation or FWD.
**
** You may not represent yourself for publicity purposes as associated with
** Golden Code Development Corporation, FWD, or any author or contributor to
** the covered work, without written authorization.
**
** 3. No Misrepresentation of Source or Origin.
**
** You may not represent the covered work as solely your work. All modified
** versions of the covered work must be marked in a reasonable way to make it
** clear that the modified work is not originating from Golden Code Development
** Corporation or FWD. All modified versions must contain the notices of
** attribution required in this license.
*/
package com.goldencode.p2j.persist.id;
import java.util.*;
/**
* Represents a range of <code>Long</code> numbers between a lower bound
* and an upper bound (inclusive).
*/
public class Range
implements Comparable
{
/** Lower bound of range. null if the range is empty. */
private Long lowerBound;
/** Upper bound of range. May be the same as lower bound. */
private Long upperBound;
/**
* Creates an empty range.
*/
public Range()
{
this.lowerBound = null;
this.upperBound = null;
}
/**
* Creates a new range object.
*
* @param lowerBound
* Lower bound of range.
* @param upperBound
* Upper bound of range.
*/
public Range(long lowerBound, long upperBound)
{
setBounds(lowerBound, upperBound);
}
/**
* Creates a new range representing a single number.
*
* @param number
* Number to represent.
*/
public Range(long number)
{
setBounds(number, number);
}
/**
* Get the lower bound.
*
* @return Lower bound.
*/
public Long getLowerBound()
{
return lowerBound;
}
/**
* Get the upper bound.
*
* @return Upper bound.
*/
public Long getUpperBound()
{
return upperBound;
}
/**
* Return the quantity of numbers in range. More formally,
* upper bound - lower bound + 1.
*
* @return The quantity of numbers in range.
*/
public Long getRangeSize()
{
if (lowerBound == null)
{
return 0L;
}
else
{
return upperBound - lowerBound + 1;
}
}
/**
* Sets lower and upper bound of range.
*
* @param lowerBound
* Lower bound of range.
* @param upperBound
* Upper bound of range.
*/
public void setBounds(long lowerBound, long upperBound)
{
if (upperBound < lowerBound)
{
throw new IllegalArgumentException(
"Upper bound should be more or equal lower bound");
}
this.lowerBound = lowerBound;
this.upperBound = upperBound;
}
/**
* Determines whether this range is empty (contains no numbers).
*
* @return <code>true</code> if this range contain an least one number.
* <code>false</code> otherwise.
*/
public boolean isEmpty()
{
return lowerBound == null;
}
/**
* Makes this range empty.
*/
public void makeEmpty()
{
lowerBound = null;
upperBound = null;
}
/**
* Returns <code>true</code> if the specified number is contained in the
* range.
*
* @param number
* Number to be checked for containment in this range.
* @return <code>true</code> if the specified number is contained in the
* range.
*/
public boolean contains(long number)
{
return !isEmpty() && lowerBound <= number && number <= upperBound;
}
/**
* Returns the fisrt number in range AND removes it from range.
*
* @return The first number in range, or <code>null</code> if the range
* contatins no numbers.
*/
public Long extractFirstNumber()
{
if (lowerBound == null)
{
return null;
}
long num = lowerBound;
if (lowerBound + 1 > upperBound)
{
lowerBound = null;
}
else
{
lowerBound++;
}
return num;
}
/**
* Splits the range into several ranges using the given numbers as break
* points. The numbers itself are excluded from resulting ranges. E.g
* [1, 10] using {2,3,6,10} would be splitted into [1], [4,5], [7,9]
*
* @param numbers
* Break points for splitting.
* @return List of subranges.
*/
public List<Range> splitRange(SortedSet<Long> numbers)
{
List<Long> list = new ArrayList<Long>(numbers);
return splitRange(list);
}
/**
* Splits the range into several ranges using the given numbers as break
* points. The numbers itself are excluded from resulting ranges. E.g
* [1, 10] using {2,3,6,10} would be splitted into [1], [4,5], [7,9]
*
* @param numbers
* Break points for splitting. Should be sorted in ascending order.
* @return List of subranges.
*/
public List<Range> splitRange(List<Long> numbers)
{
if (isEmpty())
{
return null;
}
List<Range> res = new ArrayList<Range>();
if (numbers == null || numbers.isEmpty())
{
res.add(new Range(lowerBound, upperBound));
return res;
}
Long firstId = numbers.get(0);
if (lowerBound < firstId)
{
res.add(new Range(lowerBound, firstId - 1));
}
Long id1 = firstId;
Long id2;
for (int i = 1; i < numbers.size(); i++)
{
id2 = id1;
id1 = numbers.get(i);
if (id1 - id2 > 1)
{
res.add(new Range(id2 + 1, id1 - 1));
}
}
Long lastId = numbers.get(numbers.size() - 1);
if (lastId < upperBound)
{
res.add(new Range(lastId + 1, upperBound));
}
return res;
}
/**
* Return string representation of the range.
*
* @return String representation of the range.
*/
public String toString()
{
if (lowerBound == null)
{
return "<empty>";
}
else if (lowerBound.equals(upperBound))
{
return Long.toString(lowerBound);
}
else
{
StringBuilder buf = new StringBuilder();
buf.append("[");
buf.append(lowerBound);
buf.append(", ");
buf.append(upperBound);
buf.append("]");
return buf.toString();
}
}
/**
* Compares this object with the specified object for order. Returns a
* negative integer, zero, or a positive integer as this object is less
* than, equal to, or greater than the specified object.
*
* @param o
* The object to be compared.
* @return A negative integer, zero, or a positive integer as this object
* is less than, equal to, or greater than the specified object.
*/
public int compareTo(Object o)
{
if (o instanceof Range)
{
Range r = (Range) o;
if (r.isEmpty())
{
return isEmpty() ? 0 : 1;
}
else if (isEmpty())
{
return -1;
}
else
{
if (getLowerBound().equals(r.getLowerBound()))
{
return getRangeSize().compareTo(r.getRangeSize());
}
else
{
return getLowerBound().compareTo(r.getLowerBound());
}
}
}
else
{
return 1;
}
}
/**
* Main function for testing purposes.
*
* @param args
* Not used.
*/
public static void main(String[] args)
{
Range r1;
Range r2;
r1 = new Range(1,2);
r2 = new Range(3,4);
System.out.println(r1.compareTo(r2));
System.out.println(r2.compareTo(r1));
r1 = new Range(1,2);
r2 = new Range(2,4);
System.out.println(r1.compareTo(r2));
System.out.println(r2.compareTo(r1));
r1 = new Range(1,2);
r2 = new Range(2);
System.out.println(r1.compareTo(r2));
System.out.println(r2.compareTo(r1));
r1 = new Range(1,2);
r2 = new Range(1);
System.out.println(r1.compareTo(r2));
System.out.println(r2.compareTo(r1));
r1 = new Range(1,2);
r2 = new Range(1,2);
System.out.println(r1.compareTo(r2));
System.out.println(r2.compareTo(r1));
r1 = new Range(1);
r1.extractFirstNumber();
r2 = new Range(1);
System.out.println(r1.compareTo(r2));
System.out.println(r2.compareTo(r1));
r1 = new Range(1);
r1.extractFirstNumber();
r2 = new Range(1);
r2.extractFirstNumber();
System.out.println(r1.compareTo(r2));
System.out.println(r2.compareTo(r1));
Long l = 10L;
r1 = new Range(10, 12);
System.out.println(r1.compareTo(l));
r1 = new Range(8, 9);
System.out.println(r1.compareTo(l));
r1 = new Range(11, 12);
System.out.println(r1.compareTo(l));
List<Range> deque = new ArrayList<Range>();
deque.add(new Range(3,4));
deque.add(new Range(1,2));
deque.add(new Range(5));
deque.add(new Range(10,12));
deque.add(new Range(8));
deque.add(new Range(13,67));
System.out.println("before");
for(Range r: deque)
{
System.out.println(r);
}
Collections.sort(deque);
System.out.println("after");
for(Range r: deque)
{
System.out.println(r);
}
System.out.println("");
SortedSet<Long> set = new TreeSet<Long>();
set.add(2L);
set.add(3L);
set.add(6L);
set.add(10L);
List<Range> ranges = new Range(1,10).splitRange(set);
System.out.println("splitting");
for(Range r: ranges)
{
System.out.println(r);
}
}
}