org.daisy.common.transform.InputValue Maven / Gradle / Ivy
The newest version!
package org.daisy.common.transform;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.function.Supplier;
import javax.xml.namespace.QName;
/**
* A supplier of a XDM value (item or sequence of items). The items may be nodes (see {@link
* XMLInputValue}), atomic values, or maps, and by extension arbitrary objects ("external" items).
*/
public class InputValue {
private InputValue backingValue;
private V object = null;
private boolean objectSupplied = false;
/**
* @param object A Java object that represents a single atomic value, sequence of atomic values,
* map item, or other object (but not a node sequence).
*
* The object may be a plain old Java object, for example:
*
* - {@link String}: a single
xs:string
atomic value
* - {@link Integer}: a single
xs:integer
atomic value
* - {@link QName}: a single
xs:QName
atomic value
* - {@link Iterator}{@code <}{@link Object}{@code >}: a sequence of atomic values
* - {@link Map}{@code <}{@link Object}{@code ,}{@link InputValue}{@code >}: a XDM map
* (where the keys are single atomic values and the values are arbitrary values)
*
* It may also be an object representing a non-XDM item.
*/
public InputValue(V value) {
object = value;
}
protected InputValue() {
}
protected InputValue(InputValue value) {
backingValue = value;
}
public V asObject() throws UnsupportedOperationException, NoSuchElementException {
if (backingValue != null)
return backingValue.asObject();
else if (object == null && !objectSupplied)
throw new UnsupportedOperationException();
else if (valueSupplied())
throw new NoSuchElementException();
else {
objectSupplied = true;
V ret = object;
object = null;
return ret;
}
}
@SuppressWarnings("unchecked") // safe cast
public final T asObject(Class type) throws UnsupportedOperationException, NoSuchElementException {
V o = asObject();
if (!type.isInstance(o))
throw new UnsupportedOperationException();
return (T)o;
}
@Override
public String toString() {
if (backingValue != null)
return backingValue.toString();
else if (object != null)
return object.toString();
else
return super.toString();
}
/**
* Make multiples of this input.
*
* @param limit The maximum number of multiples that will be made. -1
means unlimited.
*/
public Mult extends InputValue> mult(int limit) {
return new Mult>() {
Iterable cache = cache(
iteratorOf(InputValue.this::asObject),
limit);
int supplied = 0;
public InputValue get() throws NoSuchElementException {
if (supplied >= limit) {
cache = null;
throw new NoSuchElementException();
}
supplied++;
return new InputValue() {
Iterable val = cache;
@Override
public V asObject() throws UnsupportedOperationException, NoSuchElementException {
return val.iterator().next();
}
};
}
};
}
protected boolean valueSupplied() {
return objectSupplied;
}
/**
* Cache a {@link Iterator}. Items are evicted after they have been supplied limit
* times. Exceptions thrown by the input iterator are replayed.
*/
// FIXME: limit argument is not implemented yet
protected static Iterable cache(Iterator iterator, int limit) {
if (iterator == null)
throw new IllegalArgumentException();
return new Iterable() {
private final List cache = new ArrayList();
private RuntimeException exception = null;
public final Iterator iterator() {
return new Iterator() {
private int index = 0;
public boolean hasNext() {
synchronized (cache) {
if (index < cache.size())
return true;
if (exception != null)
throw exception;
try {
return iterator.hasNext();
} catch (RuntimeException e) {
exception = e;
throw e;
}
}
}
public T next() throws NoSuchElementException {
synchronized (cache) {
if (index < cache.size())
return cache.get(index++);
if (exception != null)
throw exception;
try {
T next = iterator.next();
cache.add(next);
index++;
return next;
} catch (RuntimeException e) {
exception = e;
throw e;
}
}
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
};
}
protected static Iterator iteratorOf(Supplier supplier) {
return new Iterator() {
T next = null;
boolean nextComputed = false;
public boolean hasNext() {
if (nextComputed)
return true;
try {
next = supplier.get();
nextComputed = true;
return true;
} catch (NoSuchElementException e) {
return false;
}
}
public T next() {
if (nextComputed) {
nextComputed = false;
return next;
}
return supplier.get();
}
};
}
}