package org.gbif.checklistbank.neo.traverse;
import org.gbif.checklistbank.neo.Labels;
import org.gbif.checklistbank.neo.RelType;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
import com.google.common.collect.Lists;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Ordering;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.PathExpander;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.traversal.BranchState;
/**
* depth first, rank then scientific name order based branching.
*/
public class TaxonomicOrderExpander implements PathExpander {
/**
* Expander following the parent_of relations in taxonomic order
*/
public final static TaxonomicOrderExpander TREE_EXPANDER = new TaxonomicOrderExpander();
/**
* Expander following the parent_of and synonym_of relations in taxonomic order
*/
public final static TaxonomicOrderExpander TREE_WITH_SYNONYMS_EXPANDER = new TaxonomicOrderExpander(RelType.SYNONYM_OF);
/**
* Expander following the parent_of, synonym_of and proparte_synonym_of relations in taxonomic order
*/
public final static TaxonomicOrderExpander TREE_WITH_PPSYNONYMS_EXPANDER = new TaxonomicOrderExpander(RelType.SYNONYM_OF, RelType.PROPARTE_SYNONYM_OF);
private final Set<RelType> synRels;
private static final Ordering<Relationship> CHILDREN_ORDER = Ordering.from(new TaxonomicOrder()).onResultOf(
new Function<Relationship, Node>() {
@Nullable
@Override
public Node apply(Relationship rel) {
return rel.getEndNode();
}
}
);
private static final Ordering<Relationship> SYNONYM_ORDER = Ordering.natural().reverse().onResultOf(
new Function<Relationship, Boolean>() {
@Nullable
@Override
public Boolean apply(Relationship rel) {
return rel.getStartNode().hasLabel(Labels.BASIONYM);
}
}
).compound(
Ordering.from(new TaxonomicOrder()).onResultOf(
new Function<Relationship, Node>() {
@Nullable
@Override
public Node apply(Relationship rel) {
return rel.getStartNode();
}
}
)
);
private TaxonomicOrderExpander(RelType ... synonymRelations) {
if (synonymRelations == null) {
this.synRels = ImmutableSet.of();
} else {
this.synRels = ImmutableSet.copyOf(synonymRelations);
}
}
@Override
public Iterable<Relationship> expand(Path path, BranchState state) {
List<Relationship> children = CHILDREN_ORDER.sortedCopy(path.endNode().getRelationships(RelType.PARENT_OF, Direction.OUTGOING));
if (synRels.isEmpty()) {
return children;
} else {
List<Iterable<Relationship>> synResults = Lists.newArrayList();
for (RelType rt : synRels) {
synResults.add(path.endNode().getRelationships(rt, Direction.INCOMING));
}
return Iterables.concat(
SYNONYM_ORDER.sortedCopy(Iterables.concat(synResults)),
children
);
}
}
@Override
public PathExpander reverse() {
throw new UnsupportedOperationException();
}
}