/** * Copyright (c) 2002-2010 "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.util; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.Iterator; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; import org.neo4j.helpers.collection.IterableWrapper; import org.neo4j.index.impl.btree.BTree.RelTypes; import org.neo4j.index.impl.sortedtree.SortedTree; public class SortedNodeCollection<T extends NodeWrapper> extends AbstractSet<T> { private Node rootNode; private Class<T> instanceClass; private Comparator<T> comparator; private SortedTree index; public SortedNodeCollection( Node rootNode, Comparator<T> comparator, Class<T> instanceClass ) { this.rootNode = rootNode; this.instanceClass = instanceClass; this.comparator = comparator; instantiateIndex(); } private Node ensureTheresARoot() { Node result = null; Relationship relationship = rootNode.getSingleRelationship( RelTypes.TREE_ROOT, Direction.OUTGOING ); if ( relationship != null ) { result = relationship.getOtherNode( rootNode ); } else { result = rootNode.getGraphDatabase().createNode(); rootNode.createRelationshipTo( result, RelTypes.TREE_ROOT ); } return result; } private void instantiateIndex() { Node treeRootNode = ensureTheresARoot(); this.index = new SortedTree( rootNode.getGraphDatabase(), treeRootNode, new ComparatorWrapper( this.comparator ) ); } protected Node rootNode() { return this.rootNode; } protected SortedTree index() { return this.index; } protected T instantiateItem( Node itemNode ) { return NodeWrapperImpl.newInstance( instanceClass, itemNode ); } public boolean add( T item ) { return index().addNode( item.getUnderlyingNode() ); } public void clear() { index().delete(); instantiateIndex(); } public boolean contains( Object item ) { T nodeItem = ( T ) item; return index().containsNode( nodeItem.getUnderlyingNode() ); } public boolean isEmpty() { return index().getSortedNodes().iterator().hasNext(); } public Iterator<T> iterator() { return new IterableWrapper<T, Node>( index().getSortedNodes() ) { @Override protected T underlyingObjectToObject( Node node ) { return instantiateItem( node ); } }.iterator(); } public boolean remove( Object item ) { T nodeItem = ( T ) item; return index().removeNode( nodeItem.getUnderlyingNode() ); } public boolean retainAll( Collection<?> items ) { throw new UnsupportedOperationException( "Not implemented yet" ); } public int size() { return toArray().length; } private <R> Collection<R> toCollection() { Collection<R> result = new ArrayList<R>(); for ( Node node : index().getSortedNodes() ) { result.add( ( R ) instantiateItem( node ) ); } return result; } public Object[] toArray() { return toCollection().toArray(); } public <R> R[] toArray( R[] array ) { return toCollection().toArray( array ); } /** * Since this collection creates a sub-root to the supplied collection * root node, it will have to be explicitly deleted from outside when * you don't want this collection to exist anymore. */ public void delete() { index().delete(); } private class ComparatorWrapper implements Comparator<Node> { private Comparator<T> source; ComparatorWrapper( Comparator<T> source ) { this.source = source; } public int compare( Node o1, Node o2 ) { // This is slow, I guess return source.compare( NodeWrapperImpl.newInstance( instanceClass, o1 ), NodeWrapperImpl.newInstance( instanceClass, o2 ) ); } } }