/* XXL: The eXtensible and fleXible Library for data processing
Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger
Head of the Database Research Group
Department of Mathematics and Computer Science
University of Marburg
Germany
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; If not, see <http://www.gnu.org/licenses/>.
http://code.google.com/p/xxl/
*/
package xxl.core.util;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Iterator;
import java.util.TreeSet;
import xxl.core.functions.Function;
import xxl.core.io.Convertable;
/**
* Handles identifyers which can be used for a lot of reasons.
* This class always stores the minimum and maximum values which
* were previously returned as identifyers.<br>
* There are two ways to
* obtain an identifyer. The first and most efficient one
* returns a new identifyer by looking at the statistics and
* returns a new smaller or greater identifyer than the current
* smallest/greatest value. If this method returns Long.MIN_VALUE,
* then no identifyer could be returned with this method. Then,
* the user has to call the second getIdentifyer method which
* needs a Function that returns an Iterator. This iterator
* has to contain all valid and used identifyers. If this method
* also is not able to return an identifyer, it returns
* Long.MIN_VALUE.<br>
* When identifyers are no longer used, then it can be useful to
* call the removeIdentifyer method.
*/
public class LongIdGenerator implements Convertable {
/** minimal id given so far */
private long currentMin;
/** maximal id given so far */
private long currentMax;
/** minimal id possible */
private long absoluteMin;
/** maximal id possible */
private long absoluteMax;
// Possibility for optimization: histograms for small intervals of identifyer
/**
* Allocates a LongIdGenerator with the maximal interval possible.
* Both values have to be inside the interval [Long.MIN_VALUE+1, Long.MAX_VALUE-1]
* with absoluteMax>=absoluteMin.
* @param absoluteMin minimal id value possible.
* @param absoluteMax maximal id value possible.
*/
public LongIdGenerator (long absoluteMin, long absoluteMax) {
if (absoluteMax<absoluteMin)
throw new RuntimeException("maximum is smaller minimum!");
if (absoluteMin<Long.MIN_VALUE+1 || absoluteMin>Long.MAX_VALUE-1)
throw new RuntimeException("absoluteMin outside possible values!");
if (absoluteMax<Long.MIN_VALUE+1 || absoluteMax>Long.MAX_VALUE-1)
throw new RuntimeException("absoluteMaxoutside possible values!");
this.absoluteMin = absoluteMin;
this.absoluteMax = absoluteMax;
reset();
}
/**
* Allocates a LongIdGenerator with the maximal interval possible.
*/
public LongIdGenerator () {
this(Long.MIN_VALUE+1, Long.MAX_VALUE-1);
}
/**
* Resets the current IDs, so that new IDs can be generated.
*/
public void reset() {
currentMin = Long.MAX_VALUE;
currentMax = Long.MIN_VALUE;
}
/**
* If a method from the outside has knowledge of the id generator, then
* this external method can make a reservation on its own. Then, this
* method has to inform the generator by calling makeExternalReservation.
* So, the generator can stay in a consistent state.
* @param id identifyer which is reserved from the outside. The identifyer is
* not checked to be unique.
*/
public void makeExternalReservation(long id) {
if (currentMin==Long.MAX_VALUE) {
currentMin = id;
currentMax = id;
}
else if (id>currentMax)
currentMax = id;
else if (id<currentMin)
currentMin = id;
}
/**
* Returns an identifyer only if the new identifyer can easily be
* generated.
* @return the identifyer or Long.MIN_VALUE if no identifyer can easily be
* generated. Then, call getIdentifyer.
*/
public long getDirectIdentifyer() {
if (currentMin==Long.MAX_VALUE) {
currentMin = absoluteMin;
currentMax = currentMin;
return absoluteMin;
}
else if (currentMax<absoluteMax) {
currentMax++;
return currentMax;
}
else if (currentMin>absoluteMin) {
currentMin--;
return currentMin;
}
else
return Long.MIN_VALUE;
}
/**
* Determines a free numerical identifyer. This class works with
* iterators of type java.lang.Number (Byte, Short, Integer, Long, ...).
*
* @param getIdIterator Function which returns an Iterator which
* then contains all Number values of valid and used identifyers.
* @return a free id (or Long.MIN_VALUE if no id is availlable).
*/
public long getIdentifyer(Function getIdIterator) {
long value = getDirectIdentifyer();
if (value!=Long.MIN_VALUE)
return value;
Iterator it = (Iterator) getIdIterator.invoke();
TreeSet ts = new TreeSet();
currentMin = Long.MAX_VALUE;
currentMax = Long.MIN_VALUE;
while (it.hasNext()) {
value = ((Number) it.next()).longValue();
if (value<currentMin)
currentMin = value;
if (value>currentMax)
currentMax = value;
ts.add(new Long(value));
}
value = getDirectIdentifyer();
if (value==Long.MIN_VALUE) {
long counter=absoluteMin;
while (counter<=absoluteMax) {
if (!ts.contains(new Long(counter)))
return counter;
counter++;
}
}
// Long.MIN_VALUE if no other value has been found meanwhile
return value;
}
/**
* Returns an identifyer to the generator.
* This can sometimes save some time to generate new ones
* (if minimum and maximum values are returned). This method does not
* have to be called. So, it is not possible to say how many ids
* are really out there.
* @param id identifyer which is no longer needed.
*/
public void removeIdentifyer(long id) {
if (currentMin==id)
currentMin++;
if (currentMax==id)
currentMax--;
if (currentMin>currentMax) {
currentMin = Long.MAX_VALUE;
currentMax = Long.MIN_VALUE;
}
}
/**
* Reads the state from the DataInput.
* @param dataInput DataInput
* @throws IOException in case of I/O errors
*/
public void read(DataInput dataInput) throws IOException {
absoluteMin = dataInput.readLong();
absoluteMax = dataInput.readLong();
currentMin = dataInput.readLong();
currentMax = dataInput.readLong();
}
/**
* Writes the state to a DataOutput.
* @param dataOutput DataOutput
* @throws IOException in case of I/O errors
*/
public void write(DataOutput dataOutput) throws IOException {
dataOutput.writeLong(absoluteMin);
dataOutput.writeLong(absoluteMax);
dataOutput.writeLong(currentMin);
dataOutput.writeLong(currentMax);
}
}