/*
* Copyright (c) 2012 Data Harmonisation Panel
*
* All rights reserved. This program and the accompanying materials are made
* available 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* HUMBOLDT EU Integrated Project #030962
* Data Harmonisation Panel <http://www.dhpanel.eu>
*/
package eu.esdihumboldt.hale.common.headless.transform;
import java.util.NoSuchElementException;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import eu.esdihumboldt.hale.common.instance.model.Filter;
import eu.esdihumboldt.hale.common.instance.model.Instance;
import eu.esdihumboldt.hale.common.instance.model.InstanceCollection;
import eu.esdihumboldt.hale.common.instance.model.InstanceReference;
import eu.esdihumboldt.hale.common.instance.model.ResourceIterator;
import eu.esdihumboldt.hale.common.instance.model.impl.DefaultInstance;
import eu.esdihumboldt.hale.common.instance.model.impl.PseudoInstanceReference;
import eu.esdihumboldt.hale.common.schema.model.TypeIndex;
/**
* Sink that holds instances in a limbo, to be collected through the offered
* instance collection.
*
* @author Kai Schwierczek
*/
public class LimboInstanceSink extends AbstractTransformationSink {
private static final Instance END = new DefaultInstance(null, null);
private final TargetInstanceCollection collection = new TargetInstanceCollection();
private TargetResourceIterator iterator = null;
private boolean cancelled;
// XXX use a maximum number of entries?
private final BlockingDeque<Instance> queue = new LinkedBlockingDeque<Instance>(50);
@Override
protected synchronized void internalAddInstance(Instance instance) {
// ignore incoming instances if we are cancelled
if (cancelled)
return;
try {
queue.put(instance);
} catch (InterruptedException e) {
// ignore
}
}
@Override
protected void internalDone(boolean cancel) {
try {
if (cancel) {
cancelled = true;
// ensure that the iterator doesn't block anymore
if (queue.remainingCapacity() > 10)
queue.putFirst(END);
// ensure that addInstance doesn't block anymore
queue.clear();
}
// in either case add another end indicator
queue.put(END);
} catch (InterruptedException e) {
// ignore
}
}
@Override
public InstanceCollection getInstanceCollection() {
return collection;
}
@Override
public void setTypes(TypeIndex types) {
// ignore - not needed
}
@Override
public void dispose() {
queue.clear();
super.dispose();
}
private class TargetResourceIterator implements ResourceIterator<Instance> {
private Instance next = null;
private boolean endRead = false;
/**
* @see java.util.Iterator#hasNext()
*/
@Override
public boolean hasNext() {
synchronized (this) {
// first check whether a previous hasNext call got something
if (next != null)
return true;
// then check whether the execution was cancelled
// or we are finished because we read the END instance
if (cancelled || endRead)
return false;
// hasNext has to block until it knows whether the
// transformation is done, or more instances are coming
Instance result;
try {
result = queue.take();
} catch (InterruptedException e) {
// shouldn't happen
return false;
}
if (result == END) {
endRead = true;
return false;
}
else {
next = result;
return true;
}
}
}
/**
* @see java.util.Iterator#next()
*/
@Override
public Instance next() {
synchronized (this) {
if (hasNext()) {
// take the element from hasNext and set it back to null
Instance tmp = next;
next = null;
return tmp;
}
else
throw new NoSuchElementException();
}
}
/**
* @see java.util.Iterator#remove()
*/
@Override
public void remove() {
throw new UnsupportedOperationException();
}
/**
* @see eu.esdihumboldt.hale.common.instance.model.ResourceIterator#close()
*/
@Override
public void close() {
done(true);
}
}
/**
* Simple internal instance collection, does not support remove and filter
* and uses {@link PseudoInstanceReference}s and
* {@link TargetResourceIterator}. Only one iterator may be created.
*
* @author Kai Schwierczek
*/
private class TargetInstanceCollection implements InstanceCollection {
/**
* @see eu.esdihumboldt.hale.common.instance.model.InstanceResolver#getReference(eu.esdihumboldt.hale.common.instance.model.Instance)
*/
@Override
public InstanceReference getReference(Instance instance) {
return new PseudoInstanceReference(instance);
}
/**
* @see eu.esdihumboldt.hale.common.instance.model.InstanceResolver#getInstance(eu.esdihumboldt.hale.common.instance.model.InstanceReference)
*/
@Override
public Instance getInstance(InstanceReference reference) {
return ((PseudoInstanceReference) reference).getInstance();
}
/**
* @see eu.esdihumboldt.hale.common.instance.model.InstanceCollection#iterator()
*/
@Override
public ResourceIterator<Instance> iterator() {
if (iterator == null) {
iterator = new TargetResourceIterator();
return iterator;
}
else
throw new IllegalStateException();
}
/**
* @see eu.esdihumboldt.hale.common.instance.model.InstanceCollection#hasSize()
*/
@Override
public boolean hasSize() {
return false;
}
/**
* @see eu.esdihumboldt.hale.common.instance.model.InstanceCollection#size()
*/
@Override
public int size() {
return UNKNOWN_SIZE;
}
/**
* @see eu.esdihumboldt.hale.common.instance.model.InstanceCollection#isEmpty()
*/
@Override
public boolean isEmpty() {
// XXX have to return false, even if it actually may be empty
return false;
}
/**
* @see eu.esdihumboldt.hale.common.instance.model.InstanceCollection#select(eu.esdihumboldt.hale.common.instance.model.Filter)
*/
@Override
public InstanceCollection select(Filter filter) {
throw new UnsupportedOperationException();
}
}
}