package ecologylab.bigsemantics.metametadata;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ecologylab.bigsemantics.metadata.Metadata;
import ecologylab.bigsemantics.metadata.MetadataBase;
import ecologylab.bigsemantics.metadata.MetadataFieldDescriptor;
/**
* Iterates through a Collection of things, and then through an Iterator of such (nested)
* Collections of things. Provides flat access to all members.
*
* @author jmole, damaraju
*
* @param <I>
* Class that we iterate over.
* @param <O>
* Class of objects that are applied in the context of what we iterate over. This typically
* starts as this, but shifts as we iterate through the nested Collection of Iterators.
*/
public class ClassAndCollectionIterator implements Iterator<MetadataBase>
{
static Logger logger;
static
{
logger = LoggerFactory.getLogger(ClassAndCollectionIterator.class);
}
private Iterator<MetaMetadataField> iterator;
private Iterator<MetadataBase> collectionIterator;
private MetaMetadataField root;
private MetadataBase currentObject;
private MetaMetadataField currentMMField;
private Metadata metadata;
private HashSet<Metadata> visitedMetadata;
/**
*
* @param firstObject
* - The object whose elements need to be iterated over.
* @param visitedMetadata TODO
*/
public ClassAndCollectionIterator(MetaMetadataField firstObject, Metadata metadata, HashSet<Metadata> visitedMetadata)
{
root = firstObject;
this.iterator = firstObject.iterator();
this.metadata = metadata;
this.visitedMetadata = visitedMetadata;
visitedMetadata.add(metadata);
}
/**
* @return The next field in the Object.<br>
* If the next object is a non-null collection, it iterates through the objects of that
* collection
*
* Note: returned field can be null. use {@link #hasNext()} to see if there are more elements.
*/
public MetadataBase next()
{
try
{
if (collectionIterator != null)
return nextInCollection();
if (iterator.hasNext())
{
MetaMetadataField firstNext = iterator.next();
currentMMField = firstNext;
if (firstNext instanceof MetaMetadataCollectionField)
{
MetadataFieldDescriptor mfd = firstNext.getMetadataFieldDescriptor();
Collection c = mfd.getCollection(metadata);
if (c != null)
{
collectionIterator = c.iterator();
return nextInCollection();
}
}
MetadataFieldDescriptor mfd = firstNext.getMetadataFieldDescriptor();
if (mfd == null)
{
firstNext
.error("Can't find MetadataFieldDescriptor. This probably means that the MetaMetadata compiler was not run or encountered errors!");
return null;
}
Field field = mfd.getField();
MetadataBase md = null;
try
{
md = (MetadataBase) field.get((MetadataBase) metadata);
}
catch(IllegalArgumentException e)
{
logger.warn("Failed to get field " + field.getName() + " from " + metadata, e);
}
boolean isComposite = (md instanceof Metadata);
if (isComposite && visitedMetadata.contains(md))
{
return next();
}
currentObject = md;
if (isComposite)
visitedMetadata.add((Metadata) md);
return md;
}
}
catch (IllegalArgumentException e)
{
logger.warn("Exception when iterating thru fields of " + metadata
+ ", currentMMField=" + currentMMField
+ ", currentObject=" + currentObject, e);
}
catch (IllegalAccessException e)
{
logger.warn("Exception when iterating thru fields of " + metadata
+ ", currentMMField=" + currentMMField
+ ", currentObject=" + currentObject, e);
}
return null;
}
private MetadataBase nextInCollection()
{
if (!collectionIterator.hasNext())
{
collectionIterator = null;
return next();
}
MetadataBase next = collectionIterator.next();
while (visitedMetadata.contains(next) && collectionIterator.hasNext())
next = collectionIterator.next();
boolean isComposite = (next instanceof Metadata);
if (isComposite && visitedMetadata.contains(next))
{
collectionIterator = null;
return next();
}
currentObject = next;
if (isComposite)
visitedMetadata.add((Metadata) next);
return next;
}
/**
*
* @return
*/
public MetadataBase currentObject()
{
return currentObject;
}
public void remove()
{
throw new UnsupportedOperationException();
}
/**
*
* @return Returns true if the iteration has more elements.
*/
public boolean hasNext()
{
return iterator.hasNext() || (collectionIterator != null && collectionIterator.hasNext());
}
public MetaMetadataField getCurrentMMField()
{
return currentMMField;
}
}