/*
* Copyright (C) 2011 Andrea Schweer
*
* This file is part of the Digital Parrot.
*
* The Digital Parrot is free software; you can redistribute it and/or modify
* it under the terms of the Eclipse Public License as published by the Eclipse
* Foundation or its Agreement Steward, either version 1.0 of the License, or
* (at your option) any later version.
*
* The Digital Parrot 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 Eclipse Public License for
* more details.
*
* You should have received a copy of the Eclipse Public License along with the
* Digital Parrot. If not, see http://www.eclipse.org/legal/epl-v10.html.
*
*/
package net.schweerelos.parrot.model;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.SwingUtilities;
import javax.swing.event.EventListenerList;
import net.schweerelos.parrot.model.filters.ChainLink;
import net.schweerelos.parrot.util.QuadTree;
import net.schweerelos.timeline.model.IntervalChain;
import org.apache.log4j.Logger;
import org.mindswap.pellet.jena.PelletInfGraph;
import com.hp.hpl.jena.graph.Graph;
import com.hp.hpl.jena.ontology.DatatypeProperty;
import com.hp.hpl.jena.ontology.Individual;
import com.hp.hpl.jena.ontology.ObjectProperty;
import com.hp.hpl.jena.ontology.OntClass;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.ontology.OntProperty;
import com.hp.hpl.jena.ontology.OntResource;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.ResIterator;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;
class ParrotModelHelper extends Object implements ParrotModel {
private static final String PRIMARY_TYPE_LITERAL = "primary";
private static final String URI_SHOW_THIS_TYPE = "http://parrot.resnet.scms.waikato.ac.nz/Parrot/Terms/DigitalParrot/2009/02/DigitalParrot.owl#showThisType";
private static final String SECONDARY_TYPE_LITERAL = "secondary";
private OntModel model;
private Map<OntResource, NodeWrapper> nodes;
private Map<OntResource, Set<NodeWrapper>> edges;
private Map<OntResource, NodeWrapper> types;
private static Set<Property> knownNonEdgeProperties = new HashSet<Property>();
private static Set<Property> knownEdgeProperties = new HashSet<Property>();
private static Map<OntResource, Boolean> secondaryTypes = new HashMap<OntResource, Boolean>();
private static Map<OntResource, Boolean> primaryTypes = new HashMap<OntResource, Boolean>();
private EventListenerList modelListeners;
private Filter highlightFilter;
private Set<Filter> restrictFilters;
private TextSearchEngine textSearchSupport;
private boolean busy = false;
private List<NodeWrapper> currentlyRestricted;
private ParrotModel realModel;
private IntervalChain<NodeWrapper> timedThings;
private net.schweerelos.parrot.util.QuadTree<CenteredThing<NodeWrapper>> locatedThings;
private String datafile;
public ParrotModelHelper(OntModel model, ParrotModel realModel) {
this.model = model;
this.realModel = realModel;
nodes = new HashMap<OntResource, NodeWrapper>();
edges = new HashMap<OntResource, Set<NodeWrapper>>();
types = new HashMap<OntResource, NodeWrapper>();
modelListeners = new EventListenerList();
restrictFilters = new HashSet<Filter>();
textSearchSupport = new TextSearchEngine();
currentlyRestricted = new ArrayList<NodeWrapper>();
}
@Override
public Set<NodeWrapper> getAllSubjects() {
Set<NodeWrapper> subjects = new HashSet<NodeWrapper>();
for (ResIterator iterator = model.listSubjects(); iterator.hasNext();) {
Resource subRes = (Resource) iterator.next();
if (!subRes.canAs(OntResource.class)) {
continue;
}
OntResource sub = model.getOntResource(subRes);
if (nodes.containsKey(sub)) {
subjects.add(nodes.get(sub));
}
}
return subjects;
}
@Override
public Set<NodeWrapper> getAllPredicates() {
Set<NodeWrapper> predicates = new HashSet<NodeWrapper>();
for (ExtendedIterator<ObjectProperty> objProps = model.listObjectProperties(); objProps
.hasNext();) {
ObjectProperty nextProp = objProps.next();
if (!(nextProp instanceof OntProperty)) {
continue;
}
OntProperty prop = (OntProperty) nextProp;
if (showTypeAsPrimary(model, prop)) {
predicates.add(new NodeWrapper(prop));
}
}
for (ExtendedIterator<DatatypeProperty> dataProps = model.listDatatypeProperties(); dataProps
.hasNext();) {
DatatypeProperty nextProp = dataProps.next();
if (nextProp instanceof OntProperty) {
continue;
}
OntProperty prop = (OntProperty) nextProp;
if (showTypeAsPrimary(model, prop)) {
predicates.add(new NodeWrapper(prop));
}
}
return predicates;
}
@Override
public NodeWrapper getNodeWrapper(Individual instance) {
if (nodes.containsKey(instance)) {
return nodes.get(instance);
} else {
Logger logger = Logger.getLogger(ParrotModelHelper.class);
logger.warn("don't have a wrapper for " + instance);
// TODO does this cause problems?
return null;
}
}
@Override
public Set<NodeWrapper> getNodeWrappers(OntClass ontClass) {
if (edges.containsKey(ontClass)) {
return edges.get(ontClass);
} else {
Logger logger = Logger.getLogger(ParrotModelHelper.class);
logger.warn("don't have wrappers for " + ontClass);
// TODO does this cause problems?
return null;
}
}
@Override
public OntModel getOntModel() {
return model;
}
@Override
public Set<NodeWrapper> getPredicatesForSubject(NodeWrapper subject) {
Set<NodeWrapper> predicates = new HashSet<NodeWrapper>();
OntResource subjectNode = subject.getOntResource();
if (!subjectNode.isClass()) {
// something is broken if we ever get here
return predicates;
}
OntClass subjectClass = subjectNode.asClass();
for (ExtendedIterator<OntProperty> iterator = subjectClass
.listDeclaredProperties(true); iterator.hasNext();) {
OntProperty prop = iterator.next();
NodeWrapper nodeWrapper = new NodeWrapper(prop);
predicates.add(nodeWrapper);
}
return predicates;
}
@Override
public Set<NodeWrapper> getSubjectTypes() {
Set<NodeWrapper> result = new HashSet<NodeWrapper>();
for (ExtendedIterator<OntClass> namedClasses = model.listNamedClasses(); namedClasses
.hasNext();) {
OntClass ontClass = namedClasses.next();
if (showTypeAsPrimary(model, ontClass)) {
NodeWrapper classWrapper;
if (types.containsKey(ontClass)) {
classWrapper = types.get(ontClass);
} else {
classWrapper = new NodeWrapper(ontClass);
types.put(ontClass, classWrapper);
}
result.add(classWrapper);
}
}
return result;
}
@Override
public IntervalChain<NodeWrapper> getTimedThings() {
if (timedThings == null) {
timedThings = TimedThingsHelper.extractTimedThings(realModel);
}
return timedThings;
}
@Override
public QuadTree<CenteredThing<NodeWrapper>> getLocatedThings() {
if (locatedThings == null) {
locatedThings = LocatedThingsHelper.extractLocatedThings(realModel);
}
return locatedThings;
}
@Override
public void loadData(String datafile) {
this.datafile = datafile;
model.read(datafile);
model.rebind();
Graph graph = model.getGraph();
if (graph instanceof PelletInfGraph) {
PelletInfGraph pellet = (PelletInfGraph) graph;
pellet.classify();
pellet.realize();
}
}
@Override
public String getDataIdentifier() {
return datafile;
}
@Override
public void saveData() {
// TODO #13 Auto-generated method stub
}
@Override
public synchronized void addFilter(Filter filter) {
if (!busy) {
busy = true;
fireBusyChanged(true);
}
if (filter.getMode() == Filter.Mode.HIGHLIGHT) {
// there is only one highlight filter
Object oldHighlight = highlightFilter;
if (oldHighlight != filter) {
highlightFilter = filter;
updateHighlights();
fireHighlightsChanged();
}
} else if (filter.getMode() == Filter.Mode.RESTRICT) {
boolean added = restrictFilters.add(filter);
if (added) {
currentlyRestricted.addAll(filter.getMatching(realModel));
List<NodeWrapper> newRestricted = new ArrayList<NodeWrapper>(currentlyRestricted);
fireRestrictionsChanged(newRestricted);
}
}
if (busy) {
busy = false;
fireBusyChanged(false);
}
}
@Override
public void replaceFilter(Filter oldFilter, Filter newFilter) {
if (oldFilter == null && newFilter == null) {
// do nothing
return;
}
if (oldFilter == null) {
addFilter(newFilter);
return;
} else if (newFilter == null) {
removeFilter(oldFilter);
return;
}
if (oldFilter.getMode() != newFilter.getMode()) {
Logger logger = Logger.getLogger(ParrotModelHelper.class);
logger.warn("trying to replace a filter with one with a different mode, aborting");
// do nothing
return;
}
// ok, now we know we are genuinely replacing filters
if (!busy) {
busy = true;
fireBusyChanged(true);
}
// TODO #21 other filter types
if (newFilter.getMode() == Filter.Mode.HIGHLIGHT) {
// there is only one highlight filter
if (highlightFilter != oldFilter) {
Logger logger = Logger.getLogger(ParrotModelHelper.class);
logger.warn("trying to replace filters, but old filter doesn't exist. Updating the filter anyway.");
}
highlightFilter = newFilter;
updateHighlights();
fireHighlightsChanged();
} else if (newFilter.getMode() == Filter.Mode.RESTRICT) {
boolean removed = restrictFilters.remove(oldFilter);
boolean added = restrictFilters.add(newFilter);
if (removed || added) {
currentlyRestricted.clear();
for (Filter theFilter : restrictFilters) {
currentlyRestricted.addAll(theFilter.getMatching(realModel));
}
List<NodeWrapper> newRestricted = new ArrayList<NodeWrapper>(currentlyRestricted);
fireRestrictionsChanged(newRestricted);
}
}
if (busy) {
busy = false;
fireBusyChanged(false);
}
}
@Override
public synchronized void removeFilter(Filter filter) {
if (!busy) {
busy = true;
fireBusyChanged(true);
}
// TODO #21 other filter types
if (filter.getMode() == Filter.Mode.HIGHLIGHT) {
// there is only one highlight filter
highlightFilter = null;
// update highlight information in nodewrappers
updateHighlights();
fireHighlightsChanged();
} else if (filter.getMode() == Filter.Mode.RESTRICT) {
boolean removed = restrictFilters.remove(filter);
if (removed) {
currentlyRestricted.clear();
for (Filter theFilter : restrictFilters) {
currentlyRestricted.addAll(theFilter.getMatching(realModel));
}
List<NodeWrapper> newRestricted = new ArrayList<NodeWrapper>(currentlyRestricted);
fireRestrictionsChanged(newRestricted);
}
}
if (busy) {
busy = false;
fireBusyChanged(false);
}
}
private void updateHighlights() {
// first reset all highlights
List<NodeWrapper> allNodeWrappers = new ArrayList<NodeWrapper>();
allNodeWrappers.addAll(nodes.values());
for (OntResource key : edges.keySet()) {
allNodeWrappers.addAll(edges.get(key));
}
for (NodeWrapper nodeWrapper : allNodeWrappers) {
nodeWrapper.setHighlighted(false);
}
// now highlight those that should be, if we have a highlight filter
if (highlightFilter == null) {
return;
}
Set<NodeWrapper> matching = highlightFilter.getMatching(realModel);
for (NodeWrapper nodeWrapper : matching) {
nodeWrapper.setHighlighted(true);
}
}
public static boolean isPotentialNode(OntModel model, Individual individual) {
if (individual.canAs(OntClass.class)) {
// don't include classes
return false;
}
ExtendedIterator<OntClass> classes = individual.listOntClasses(false);
while (classes.hasNext()) {
OntClass ontClass = classes.next();
if (ontClass.isResource() && showTypeAsPrimary(model, ontClass)) {
// there is at least one vote in favour of showing this node
return true;
}
}
// if we get here, then there was no vote in favour of showing this node;
return false;
}
public static boolean isPotentialEdge(OntModel model, Property predicate) {
if (knownNonEdgeProperties.contains(predicate)) {
return false;
}
if (knownEdgeProperties.contains(predicate)) {
return true;
}
if (!predicate.isURIResource()) {
knownNonEdgeProperties.add(predicate);
return false; // TODO is that a good idea?
}
OntProperty prop = model.getOntProperty(predicate.getURI());
if (prop == null) {
knownNonEdgeProperties.add(predicate);
return false;
}
if (showTypeAsPrimary(model, prop)) {
knownEdgeProperties.add(predicate);
return true;
}
// if the property itself isn't tagged as to be shown, look at its super-properties
ExtendedIterator<? extends OntProperty> superProps = prop.listSuperProperties(false);
while (superProps.hasNext()) {
OntProperty superProp = superProps.next();
if (knownEdgeProperties.contains(superProp)) {
return true;
}
if (showTypeAsPrimary(model, superProp)) {
// there is at least one vote in favour of showing this node
knownEdgeProperties.add(superProp);
return true;
}
}
// if we get here, then there was no vote in favour of showing this node
knownNonEdgeProperties.add(predicate);
return false;
}
public static boolean isPotentialNode(ParrotModel pModel, Individual individual) {
return isPotentialNode(pModel.getOntModel(), individual);
}
public static boolean isPotentialEdge(ParrotModel pModel, Property predicate) {
return isPotentialEdge(pModel.getOntModel(), predicate);
}
public static boolean showTypeAsPrimary(OntModel model, OntResource ontRes) {
if (primaryTypes.containsKey(ontRes)) {
return primaryTypes.get(ontRes);
}
boolean show = showTypeAs(model, ontRes, PRIMARY_TYPE_LITERAL);
primaryTypes.put(ontRes, show);
return show;
}
public static boolean showTypeAsSecondary(OntModel model, OntResource ontRes) {
if (secondaryTypes.containsKey(ontRes)) {
return secondaryTypes.get(ontRes);
}
boolean show = showTypeAs(model, ontRes, SECONDARY_TYPE_LITERAL);
secondaryTypes.put(ontRes, show);
return show;
}
private static boolean showTypeAs(OntModel model, OntResource ontRes,
String showTypeValue) {
Property prop = model.getProperty(URI_SHOW_THIS_TYPE);
if (prop == null) {
return false;
}
RDFNode value = ontRes.getPropertyValue(prop);
if (value != null && value.isLiteral()) {
String realValue = value.as(Literal.class).getLexicalForm();
return realValue.equals(showTypeValue);
}
return false;
}
public NodeWrapper addSubject(Individual individual) {
NodeWrapper subject;
if (nodes.containsKey(individual)) {
subject = nodes.get(individual);
} else {
subject = new NodeWrapper(individual);
nodes.put(individual, subject);
textSearchSupport.add(subject);
}
return subject;
}
public NodeWrapper addPredicate(OntResource predicateResource) {
if (!edges.containsKey(predicateResource)) {
edges.put(predicateResource, new HashSet<NodeWrapper>());
}
Set<NodeWrapper> predicateList = edges.get(predicateResource);
NodeWrapper predicate = new NodeWrapper(predicateResource);
predicateList.add(predicate);
textSearchSupport.add(predicate);
return predicate;
}
public NodeWrapper addObject(RDFNode objectNode) {
NodeWrapper object = null;
if (objectNode.isLiteral()) {
// TODO make this list like for predicates?
object = new NodeWrapper(objectNode.as(Literal.class));
textSearchSupport.add(object);
} else if (objectNode.isURIResource()) {
OntResource objectResource = model.getOntResource(objectNode.as(Resource.class));
if (nodes.containsKey(objectResource)) {
object = nodes.get(objectResource);
} else {
object = new NodeWrapper(objectResource);
nodes.put(objectResource, object);
textSearchSupport.add(object);
}
}
// TODO other cases
return object;
}
@Override
public void deleteEdge(NodeWrapper edge) {
// TODO #13 implement (editing/delete edge)
}
@Override
public void deleteNode(NodeWrapper vertex) {
// TODO #13 implement (editing/delete node)
}
@Override
public synchronized void addParrotModelListener(ParrotModelListener pml) {
modelListeners.add(ParrotModelListener.class, pml);
}
@Override
public synchronized void removeParrotModelListener(ParrotModelListener pml) {
modelListeners.remove(ParrotModelListener.class, pml);
}
private void fireBusyChanged(final boolean nowBusy) {
// from EventListenerList javadocs (but minus the bugs)
final ParrotModelListener[] listeners = modelListeners
.getListeners(ParrotModelListener.class);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length - 1; i >= 0; i--) {
if (nowBusy) {
listeners[i].modelBusy();
} else {
listeners[i].modelIdle();
}
}
}
});
}
private void fireHighlightsChanged() {
// from EventListenerList javadocs (but minus the bugs)
final ParrotModelListener[] listeners = modelListeners
.getListeners(ParrotModelListener.class);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length - 1; i >= 0; i--) {
listeners[i].highlightsChanged();
}
}
});
}
private void fireRestrictionsChanged(final Collection<NodeWrapper> newRestricted) {
// from EventListenerList javadocs (but minus the bugs)
final ParrotModelListener[] listeners = modelListeners
.getListeners(ParrotModelListener.class);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length - 1; i >= 0; i--) {
listeners[i].restrictionsChanged(newRestricted);
}
}
});
}
@Override
public Set<NodeWrapper> getTypesForIndividual(NodeWrapper node) {
Set<NodeWrapper> result = new HashSet<NodeWrapper>();
if (node.isOntResource() && node.getOntResource().isIndividual()) {
Individual ind = node.getOntResource().asIndividual();
ExtendedIterator<OntClass> classes = ind.listOntClasses(false);
while (classes.hasNext()) {
OntClass ontClass = classes.next();
if (showTypeAsPrimary(model, ontClass) || showTypeAsSecondary(model, ontClass)) {
NodeWrapper classWrapper;
if (types.containsKey(ontClass)) {
classWrapper = types.get(ontClass);
} else {
classWrapper = new NodeWrapper(ontClass);
types.put(ontClass, classWrapper);
}
result.add(classWrapper);
}
}
}
return result;
}
@Override
public Set<NodeWrapper> getIndividualsForType(NodeWrapper type) {
Set<NodeWrapper> result = new HashSet<NodeWrapper>();
if (type.isType() && type.getOntResource().isClass()) {
OntClass ontClass = type.getOntResource().asClass();
ExtendedIterator<? extends OntResource> individuals = ontClass.listInstances(false);
while (individuals.hasNext()) {
OntResource individual = individuals.next();
if (individual.isIndividual()) {
result.add(getNodeWrapper(individual.asIndividual()));
}
}
}
return result;
}
@Override
public Set<NodeWrapper> getSuperPredicates(NodeWrapper node) {
Set<NodeWrapper> result = new HashSet<NodeWrapper>();
if (node.isOntResource() && node.getOntResource().isProperty()) {
OntProperty prop = node.getOntResource().asProperty();
ExtendedIterator<? extends OntProperty> superProps = prop.listSuperProperties(false);
while (superProps.hasNext()) {
OntProperty superProp = superProps.next();
if (showTypeAsPrimary(model, superProp) || showTypeAsSecondary(model, superProp)) {
result.add(new NodeWrapper(superProp));
}
}
}
return result;
}
@Override
public Set<NodeWrapper> getAllNodes() {
Set<NodeWrapper> result = new HashSet<NodeWrapper>(nodes.values().size());
result.addAll(nodes.values());
return result;
}
@Override
public Set<NodeWrapper> getAllNodeWrappers() {
Set<NodeWrapper> result = new HashSet<NodeWrapper>();
result.addAll(nodes.values());
for (OntResource edgeResource : edges.keySet()) {
result.addAll(edges.get(edgeResource));
}
return result;
}
@Override
public Set<NodeWrapper> searchNodeWrappers(String query) throws SearchFailedException {
return textSearchSupport.search(query);
}
@Override
public boolean isBusy() {
return busy;
}
/**
* Should be overridden.
*/
@Override
public boolean hasSuccessor(NodeWrapper first, NodeWrapper second) {
return false;
}
/**
* Should be overridden.
*/
@Override
public Collection<NodeWrapper> getSuccessorNodes(NodeWrapper node) {
return Collections.emptyList();
}
/**
* Should be overridden.
*/
@Override
public Collection<NodeWrapper> getEdges(NodeWrapper from, NodeWrapper to) {
return Collections.emptyList();
}
@Override
public NodeWrapper getNodeWrapperForString(String url) throws NoSuchNodeWrapperException {
Individual individual = model.getIndividual(url);
if (individual == null) {
throw new NoSuchNodeWrapperException("no nodewrapper found for string " + url);
}
return getNodeWrapper(individual);
}
@Override
public Set<NodeWrapper> getNodeWrappersOnChain(List<ChainLink> chain) {
Set<NodeWrapper> result = new HashSet<NodeWrapper>();
if (chain.isEmpty()) {
// chain is empty, done
return result;
}
if (chain.size() == 1) {
ChainLink firstInChain = chain.get(0);
if (!firstInChain.hasType() && !firstInChain.hasInstance()) {
result.addAll(nodes.values());
for (Collection<NodeWrapper> edgeSet : edges.values()) {
result.addAll(edgeSet);
}
return result;
}
}
List<List<NodeWrapper>> nodeChains = getChains(chain);
// now that potentialNodeChains only has NodeWrappers remaining that
// actually match the chain, we can go and add them all to the result
// set
// but we need to make sure we add the edges along the chains too
for (List<NodeWrapper> nodeChain : nodeChains) {
Set<NodeWrapper> chainWithEdges = makeNodeChainWithEdges(nodeChain);
result.addAll(chainWithEdges);
}
return result;
}
@Override
public List<List<NodeWrapper>> getChains(List<ChainLink> chain) {
// create data structure to hold all potentially matching NodeWrappers
// it's a list of lists of matching NodeWrappers (each "inner" list
// mirroring the chain up to the current iteration step)
List<List<NodeWrapper>> potentialNodeChains = new ArrayList<List<NodeWrapper>>();
// first step: initialise the inner lists (one for each starting point)
ChainLink firstLink = chain.get(0);
if (firstLink.hasInstance()) {
// there is only one starting point
List<NodeWrapper> potentialNodeChain = new ArrayList<NodeWrapper>(
chain.size());
potentialNodeChain.add(firstLink.getInstance());
potentialNodeChains.add(potentialNodeChain);
} else if (firstLink.hasType()) {
// every instance of firstLink's type is a starting point
Set<NodeWrapper> instances = getIndividualsForType(firstLink
.getType());
for (NodeWrapper instance : instances) {
List<NodeWrapper> potentialNodeChain = new ArrayList<NodeWrapper>(
chain.size());
potentialNodeChain.add(instance);
potentialNodeChains.add(potentialNodeChain);
}
} else {
// firstLink is any/any type
// this means *all* subjects match
List<NodeWrapper> allSubjects = new ArrayList<NodeWrapper>(nodes.values());
potentialNodeChains.add(allSubjects);
return potentialNodeChains;
}
// now that we're done initialising potentialNodeChains, go and iterate
// along the chain (starting at the *second* item since we've already
// looked at the first one)
for (int i = 1; i < chain.size(); i++) {
ChainLink link = chain.get(i);
List<List<NodeWrapper>> keepChains = new ArrayList<List<NodeWrapper>>();
List<List<NodeWrapper>> newChains = new ArrayList<List<NodeWrapper>>();
if (link.hasInstance()) {
NodeWrapper instance = link.getInstance();
// keep all potentialNodeChains whose end is one step before instance
for (List<NodeWrapper> potentialNodeChain : potentialNodeChains) {
NodeWrapper endOfChain = potentialNodeChain.get(potentialNodeChain.size() - 1);
// check whether we can get from endOfChain to instance
if (realModel.hasSuccessor(endOfChain, instance)) {
// yup -> keep this potentialNodeChain
potentialNodeChain.add(instance);
keepChains.add(potentialNodeChain);
}
}
} else if (link.hasType()) {
Set<NodeWrapper> instances = getIndividualsForType(link.getType());
// keep all potentialNodeChains whose end is one step before *one of* the instances
for (List<NodeWrapper> potentialNodeChain : potentialNodeChains) {
NodeWrapper endOfChain = potentialNodeChain.get(potentialNodeChain.size() - 1);
// endOfChain could have *several* of the instances as successors
// in that case we need to branch out
boolean alreadyFoundOne = false;
for (NodeWrapper instance : instances) {
if (realModel.hasSuccessor(endOfChain, instance)) {
if (!alreadyFoundOne) {
// the easy case: this is the first time we're finding a match
potentialNodeChain.add(instance);
keepChains.add(potentialNodeChain);
alreadyFoundOne = true;
} else {
// the tricky case: we have already found a different match before
// we need to copy the whole potentialNodeChain
List<NodeWrapper> newPotentialNodeChain = new ArrayList<NodeWrapper>(chain.size());
// need to add instance separately because it's a different one
newPotentialNodeChain.addAll(potentialNodeChain.subList(0, potentialNodeChain.size() - 1));
newPotentialNodeChain.add(instance);
// and add the copy to newChains
newChains.add(newPotentialNodeChain);
}
}
}
}
} else {
// link is any/any type
// -> keep all potentialNodeChains whose end has at least one outgoing edge
// while doing the same branchy stuff as in the 'else if' case above
for (List<NodeWrapper> potentialNodeChain : potentialNodeChains) {
NodeWrapper endOfChain = potentialNodeChain.get(potentialNodeChain.size() - 1);
Collection<NodeWrapper> successors = realModel.getSuccessorNodes(endOfChain);
for (NodeWrapper successor : successors) {
List<NodeWrapper> newPotentialNodeChain = new ArrayList<NodeWrapper>();
newPotentialNodeChain.addAll(potentialNodeChain);
newPotentialNodeChain.add(successor);
newChains.add(newPotentialNodeChain);
}
}
}
// only keep those potentialNodeChains that 'survived' this iteration
potentialNodeChains.retainAll(keepChains);
// and add the new ones
potentialNodeChains.addAll(newChains);
}
return potentialNodeChains;
}
private Set<NodeWrapper> makeNodeChainWithEdges(List<NodeWrapper> chain) {
Set<NodeWrapper> result = new HashSet<NodeWrapper>();
if (chain.isEmpty()) {
return result;
}
NodeWrapper lastNode = null;
for (NodeWrapper node : chain) {
if (lastNode == null) {
// looking at first in chain
lastNode = chain.get(0);
} else {
result.addAll(realModel.getEdges(lastNode, node));
lastNode = node;
}
result.add(lastNode);
}
return result;
}
@Override
public GraphParrotModel asGraphModel() {
throw new UnsupportedOperationException("can't make a graph model out of this");
}
@Override
public TableParrotModel asListModel() {
throw new UnsupportedOperationException("can't make a list model out of this");
}
}