/*
* 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.instance.model.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import eu.esdihumboldt.hale.common.instance.model.DataSet;
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;
/**
* An instance collection which consists of multiple instance collections. For
* instance references it uses the underlying instance collections mechanism
* which may be inefficient. The iterator supports
* {@link ResourceIterator#remove()} if the underlying InstanceCollection's
* ResourceIterator does so.
*
* @author Kai Schwierczek
*/
public class MultiInstanceCollection implements InstanceCollection {
private final List<InstanceCollection> collections;
/**
* Constructor using a list of instance collections..
*
* @param collections the list of instance collections
*/
public MultiInstanceCollection(List<InstanceCollection> collections) {
this.collections = new ArrayList<InstanceCollection>(collections);
}
/**
* @see eu.esdihumboldt.hale.common.instance.model.InstanceResolver#getReference(eu.esdihumboldt.hale.common.instance.model.Instance)
*/
@Override
public InstanceReference getReference(Instance instance) {
MultiInstanceCollectionInstance inst = (MultiInstanceCollectionInstance) instance;
return new MultiInstanceCollectionReference(collections.get(inst.listIndex).getReference(
inst.getOriginalInstance()), inst.listIndex);
}
/**
* @see eu.esdihumboldt.hale.common.instance.model.InstanceResolver#getInstance(eu.esdihumboldt.hale.common.instance.model.InstanceReference)
*/
@Override
public Instance getInstance(InstanceReference reference) {
MultiInstanceCollectionReference ref = (MultiInstanceCollectionReference) reference;
return new MultiInstanceCollectionInstance(collections.get(ref.listIndex).getInstance(
ref.reference), ref.listIndex);
}
/**
* @see eu.esdihumboldt.hale.common.instance.model.InstanceCollection#iterator()
*/
@Override
public ResourceIterator<Instance> iterator() {
return new MultiInstanceCollectionResourceIterator();
}
/**
* @see eu.esdihumboldt.hale.common.instance.model.InstanceCollection#hasSize()
*/
@Override
public boolean hasSize() {
for (InstanceCollection collection : collections)
if (!collection.hasSize())
return false;
return true;
}
/**
* @see eu.esdihumboldt.hale.common.instance.model.InstanceCollection#size()
*/
@Override
public int size() {
int result = 0;
for (InstanceCollection collection : collections) {
int size = collection.size();
if (size == UNKNOWN_SIZE)
return UNKNOWN_SIZE;
else
result += size;
}
return result;
}
/**
* @see eu.esdihumboldt.hale.common.instance.model.InstanceCollection#isEmpty()
*/
@Override
public boolean isEmpty() {
for (InstanceCollection collection : collections)
if (!collection.isEmpty())
return false;
return true;
}
/**
* @see eu.esdihumboldt.hale.common.instance.model.InstanceCollection#select(eu.esdihumboldt.hale.common.instance.model.Filter)
*/
@Override
public InstanceCollection select(Filter filter) {
/*
* Delegate filter to each collection - there may some optimization take
* place, e.g. with type filters
*/
List<InstanceCollection> result = new ArrayList<>();
for (InstanceCollection collection : collections) {
result.add(FilteredInstanceCollection.applyFilter(collection, filter));
}
return createNew(result);
}
/**
* Create a new multi instance collection with filtered child collections
*
* @param filtered the filtered child collections
* @return the multi instance collection
*/
protected MultiInstanceCollection createNew(List<InstanceCollection> filtered) {
return new MultiInstanceCollection(filtered);
}
/**
* Internal resource iterator iterating over all given instance collections
* in order. Supports {@link #remove()} if the underlying iterator supports
* remove.
*
* @author Kai Schwierczek
*/
private class MultiInstanceCollectionResourceIterator implements ResourceIterator<Instance> {
private int currentCollection = -1;
private ResourceIterator<Instance> currentIterator = null;
private int hasNextIndex = -1; // index of the next collection that has
// elements
private boolean closed = false;
/**
* @see java.util.Iterator#hasNext()
*/
@Override
public boolean hasNext() {
if (closed)
return false;
// do not advance here, because after hasNext(), before next(),
// a call to remove may still work
if (currentIterator != null && currentIterator.hasNext()) {
hasNextIndex = currentCollection;
return true;
}
for (int i = currentCollection + 1; i < collections.size(); i++)
if (!collections.get(i).isEmpty()) {
hasNextIndex = i;
return true;
}
hasNextIndex = -1;
return false;
}
/**
* @see java.util.Iterator#next()
*/
@Override
public Instance next() {
// check for hasNext first, because after a unsuccessful call to
// next(),
// remove() may still work
if (hasNext()) {
// advance iterator if necessary
if (currentCollection != hasNextIndex) {
if (currentIterator != null)
currentIterator.close();
currentCollection = hasNextIndex;
currentIterator = collections.get(currentCollection).iterator();
}
// return next of current iterator
return new MultiInstanceCollectionInstance(currentIterator.next(),
currentCollection);
}
else
throw new NoSuchElementException();
}
/**
* @see java.util.Iterator#remove()
*/
@Override
public void remove() {
if (currentIterator != null)
currentIterator.remove();
else
throw new IllegalStateException();
}
/**
* @see eu.esdihumboldt.hale.common.instance.model.ResourceIterator#close()
*/
@Override
public void close() {
closed = true;
if (currentIterator != null) {
currentIterator.close();
currentIterator = null;
}
}
}
/**
* Internal class for decorating the instances with the index of the
* instance collection list.
*
* @author Kai Schwierczek
*/
private static class MultiInstanceCollectionInstance extends InstanceDecorator {
private final int listIndex;
/**
* Default constructor.
*
* @param instance the instance to decorate
* @param listIndex the index of the list this instance originated from
*/
public MultiInstanceCollectionInstance(Instance instance, int listIndex) {
super(instance);
this.listIndex = listIndex;
}
}
/**
* Internal class for decorating the references with the index of the
* instance collection list.
*
* @author Kai Schwierczek
*/
private static class MultiInstanceCollectionReference implements InstanceReference {
private final InstanceReference reference;
private final int listIndex;
/**
* Default constructor.
*
* @param reference the reference to decorate
* @param listIndex the index of the list this reference originated from
*/
public MultiInstanceCollectionReference(InstanceReference reference, int listIndex) {
this.reference = reference;
this.listIndex = listIndex;
}
/**
* @see eu.esdihumboldt.hale.common.instance.model.InstanceReference#getDataSet()
*/
@Override
public DataSet getDataSet() {
return reference.getDataSet();
}
/**
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + listIndex;
result = prime * result + reference.hashCode();
return result;
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (obj instanceof MultiInstanceCollectionReference) {
MultiInstanceCollectionReference other = (MultiInstanceCollectionReference) obj;
return listIndex == other.listIndex && reference.equals(other.reference);
}
else
return false;
}
}
}