/* * Copyright (c) 2008-2009 "Neo Technology," * Network Engine for Objects in Lund AB [http://neotechnology.com] * * This file is part of Neo4j. * * Neo4j is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program 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 * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.neo4j.remote; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; import java.util.Queue; import java.util.Set; import java.util.Stack; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.RelationshipType; import org.neo4j.graphdb.ReturnableEvaluator; import org.neo4j.graphdb.StopEvaluator; import org.neo4j.graphdb.TraversalPosition; import org.neo4j.graphdb.Traverser.Order; class LocalTraversalService { public Iterable<TraversalPosition> performExternalEvaluatorTraversal( final Node startNode, final Order order, final StopEvaluator stopEvaluator, final ReturnableEvaluator returnableEvaluator, final RelationshipType[] types, final Direction[] directions ) { switch ( order ) { case BREADTH_FIRST: return new BredthFirstTraversal( new Position( 0, 0, null, startNode ), stopEvaluator, returnableEvaluator, types, directions ); case DEPTH_FIRST: return new DepthFirstTraversal( new Position( 0, 0, null, startNode ), stopEvaluator, returnableEvaluator, types, directions ); default: throw new IllegalArgumentException( "Unsupported traversal order: " + order ); } } private static class BredthFirstTraversal extends Traversal<Queue<Expansion>> { BredthFirstTraversal( Position position, StopEvaluator stopEvaluator, ReturnableEvaluator returnableEvaluator, RelationshipType[] types, Direction[] directions ) { super( position, stopEvaluator, returnableEvaluator, types, directions ); } @Override Expansion current( Queue<Expansion> store ) { return store.peek(); } @Override void extendStore( Queue<Expansion> store, Expansion expand ) { store.add( expand ); } @Override Queue<Expansion> initStore() { return new LinkedList<Expansion>(); } @Override void removeCurrent( Queue<Expansion> store ) { store.poll(); } } private static class DepthFirstTraversal extends Traversal<Stack<Expansion>> { DepthFirstTraversal( Position position, StopEvaluator stopEvaluator, ReturnableEvaluator returnableEvaluator, RelationshipType[] types, Direction[] directions ) { super( position, stopEvaluator, returnableEvaluator, types, directions ); } @Override Expansion current( Stack<Expansion> store ) { if ( store.isEmpty() ) { return null; } return store.peek(); } @Override void extendStore( Stack<Expansion> store, Expansion expand ) { store.push( expand ); } @Override Stack<Expansion> initStore() { return new Stack<Expansion>(); } @Override void removeCurrent( Stack<Expansion> store ) { if ( !store.isEmpty() ) { store.pop(); } } } private static abstract class Traversal<S> implements Iterable<TraversalPosition> { private final Position start; private final StopEvaluator stopEvaluator; private final ReturnableEvaluator returnableEvaluator; private final RelationshipType[] types; private final Direction[] directions; Traversal( Position position, StopEvaluator stopEvaluator, ReturnableEvaluator returnableEvaluator, RelationshipType[] types, Direction[] directions ) { this.start = position; this.stopEvaluator = stopEvaluator; this.returnableEvaluator = returnableEvaluator; this.types = types; this.directions = directions; } boolean shouldReturn( Position currentPos ) { return returnableEvaluator.isReturnableNode( currentPos ); } boolean shouldExpand( Position currentPos ) { return !stopEvaluator.isStopNode( currentPos ); } public Iterator<TraversalPosition> iterator() { return new Iterator<TraversalPosition>() { final Set<Node> visited = new HashSet<Node>(); int returned = 0; Position current; Position last = start; S store = initStore(); { visited.add( start.currentNode() ); if ( shouldReturn( start ) ) { current = start; } if ( shouldExpand( last ) ) { extendStore( store, new Expansion( start, types, directions ) ); } } public boolean hasNext() { if ( current != null ) { return true; } else if ( last == null ) { return false; } else { Position next = expand( last ); if ( next != null ) { current = next; return true; } else { last = null; return false; } } } public TraversalPosition next() { if ( hasNext() ) { last = current; current = null; returned++; return last; } else { throw new NoSuchElementException(); } } public void remove() { throw new UnsupportedOperationException(); } private Position expand( Position position ) { Expansion expansion = current( store ); while ( expansion != null ) { if ( !expansion.hasNext() ) { removeCurrent( store ); expansion = current( store ); continue; } Relationship relation = expansion.next(); if ( !visited.add( expansion.otherNode( relation ) ) ) { continue; } Position candidate = expansion.position( returned, relation ); if ( shouldExpand( candidate ) ) { extendStore( store, new Expansion( candidate, types, directions ) ); } if ( shouldReturn( candidate ) ) { return candidate; } } return null; } }; } abstract S initStore(); abstract void extendStore( S store, Expansion expand ); abstract Expansion current( S store ); abstract void removeCurrent( S store ); } private static class Expansion { private final Position from; private final Iterator<Iterator<Relationship>> relations; private Iterator<Relationship> current = null; Expansion( Position from, RelationshipType[] types, Direction[] directions ) { this.from = from; @SuppressWarnings( "hiding" ) List<Iterator<Relationship>> relations = new LinkedList<Iterator<Relationship>>(); for ( int i = 0; i < types.length; i++ ) { Iterator<Relationship> iter = from.currentNode() .getRelationships( types[ i ], directions[ i ] ).iterator(); if ( iter.hasNext() ) { relations.add( iter ); } } this.relations = relations.iterator(); if ( this.relations.hasNext() ) { this.current = this.relations.next(); } } Node otherNode( Relationship relationship ) { return relationship.getOtherNode( from.currentNode() ); } Position position( int returned, Relationship rel ) { return new Position( from.depth() + 1, returned, rel, otherNode( rel ) ); } boolean hasNext() { if ( current == null ) { return false; } else if ( current.hasNext() ) { return true; } else if ( relations.hasNext() ) { current = relations.next(); return true; } else { current = null; return false; } } Relationship next() { if ( current == null ) { throw new NoSuchElementException(); } return current.next(); } } }