/** * 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.kernel.impl.core; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.NotFoundException; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.RelationshipType; import org.neo4j.graphdb.ReturnableEvaluator; import org.neo4j.graphdb.StopEvaluator; import org.neo4j.graphdb.Traverser; import org.neo4j.graphdb.Traverser.Order; import org.neo4j.helpers.Pair; import org.neo4j.kernel.impl.nioneo.store.PropertyData; import org.neo4j.kernel.impl.nioneo.store.Record; import org.neo4j.kernel.impl.nioneo.store.RelationshipChainPosition; import org.neo4j.kernel.impl.transaction.LockType; import org.neo4j.kernel.impl.traversal.OldTraverserWrapper; import org.neo4j.kernel.impl.util.ArrayMap; import org.neo4j.kernel.impl.util.IntArray; class NodeImpl extends Primitive { private volatile ArrayMap<String,IntArray> relationshipMap = null; // private RelationshipGrabber relationshipGrabber = null; private RelationshipChainPosition relChainPosition = null; NodeImpl( int id ) { super( id ); } // newNode will only be true for NodeManager.createNode NodeImpl( int id, boolean newNode ) { super( id, newNode ); if ( newNode ) { relationshipMap = new ArrayMap<String,IntArray>(); relChainPosition = new RelationshipChainPosition( Record.NO_NEXT_RELATIONSHIP.intValue() ); } } @Override public int hashCode() { return id; } @Override public boolean equals( Object obj ) { return this == obj || ( obj instanceof NodeImpl && ( (NodeImpl) obj ).id == id ); } @Override protected void changeProperty( NodeManager nodeManager, int propertyId, Object value ) { nodeManager.nodeChangeProperty( this, propertyId, value ); } @Override protected int addProperty( NodeManager nodeManager, PropertyIndex index, Object value ) { return nodeManager.nodeAddProperty( this, index, value ); } @Override protected void removeProperty( NodeManager nodeManager, int propertyId ) { nodeManager.nodeRemoveProperty( this, propertyId ); } @Override protected ArrayMap<Integer, PropertyData> loadProperties( NodeManager nodeManager, boolean light ) { return nodeManager.loadProperties( this, light ); } List<RelTypeElementIterator> getAllRelationships( NodeManager nodeManager ) { ensureRelationshipMapNotNull( nodeManager ); List<RelTypeElementIterator> relTypeList = new LinkedList<RelTypeElementIterator>(); ArrayMap<String,IntArray> addMap = nodeManager.getCowRelationshipAddMap( this ); for ( String type : relationshipMap.keySet() ) { IntArray src = relationshipMap.get( type ); IntArray remove = nodeManager.getCowRelationshipRemoveMap( this, type ); IntArray add = null; if ( addMap != null ) { add = addMap.get( type ); } if ( src != null || add != null ) { relTypeList.add( RelTypeElement.create( type, this, src, add, remove ) ); } } if ( addMap != null ) { for ( String type : addMap.keySet() ) { if ( relationshipMap.get( type ) == null ) { IntArray remove = nodeManager.getCowRelationshipRemoveMap( this, type ); IntArray add = addMap.get( type ); relTypeList.add( RelTypeElement.create( type, this, null, add, remove ) ); } } } return relTypeList; } List<RelTypeElementIterator> getAllRelationshipsOfType( NodeManager nodeManager, RelationshipType... types) { ensureRelationshipMapNotNull( nodeManager ); List<RelTypeElementIterator> relTypeList = new LinkedList<RelTypeElementIterator>(); for ( RelationshipType type : types ) { IntArray src = relationshipMap.get( type.name() ); IntArray remove = nodeManager.getCowRelationshipRemoveMap( this, type.name() ); IntArray add = nodeManager.getCowRelationshipAddMap( this, type.name() ); if ( src != null || add != null ) { relTypeList.add( RelTypeElement.create( type.name(), this, src, add, remove ) ); } } return relTypeList; } public Iterable<Relationship> getRelationships( NodeManager nodeManager ) { return new IntArrayIterator( getAllRelationships( nodeManager ), this, Direction.BOTH, nodeManager, new RelationshipType[0] ); } public Iterable<Relationship> getRelationships( NodeManager nodeManager, Direction dir ) { return new IntArrayIterator( getAllRelationships( nodeManager ), this, dir, nodeManager, new RelationshipType[0] ); } public Iterable<Relationship> getRelationships( NodeManager nodeManager, RelationshipType type ) { RelationshipType types[] = new RelationshipType[] { type }; return new IntArrayIterator( getAllRelationshipsOfType( nodeManager, types ), this, Direction.BOTH, nodeManager, types ); } public Iterable<Relationship> getRelationships( NodeManager nodeManager, RelationshipType... types ) { return new IntArrayIterator( getAllRelationshipsOfType( nodeManager, types ), this, Direction.BOTH, nodeManager, types ); } public Relationship getSingleRelationship( NodeManager nodeManager, RelationshipType type, Direction dir ) { RelationshipType types[] = new RelationshipType[] { type }; Iterator<Relationship> rels = new IntArrayIterator( getAllRelationshipsOfType( nodeManager, types ), this, dir, nodeManager, types ); if ( !rels.hasNext() ) { return null; } Relationship rel = rels.next(); if ( rels.hasNext() ) { throw new NotFoundException( "More than one relationship[" + type + ", " + dir + "] found for " + this ); } return rel; } public Iterable<Relationship> getRelationships( NodeManager nodeManager, RelationshipType type, Direction dir ) { RelationshipType types[] = new RelationshipType[] { type }; return new IntArrayIterator( getAllRelationshipsOfType( nodeManager, types ), this, dir, nodeManager, types ); } public void delete( NodeManager nodeManager ) { nodeManager.acquireLock( this, LockType.WRITE ); boolean success = false; try { ArrayMap<Integer,PropertyData> skipMap = nodeManager.getCowPropertyRemoveMap( this, true ); ArrayMap<Integer,PropertyData> removedProps = nodeManager.deleteNode( this ); if ( removedProps.size() > 0 ) { for ( int index : removedProps.keySet() ) { skipMap.put( index, removedProps.get( index ) ); } } success = true; } finally { nodeManager.releaseLock( this, LockType.WRITE ); if ( !success ) { nodeManager.setRollbackOnly(); } } } /** * Returns this node's string representation. * * @return the string representation of this node */ @Override public String toString() { return "NodeImpl#" + this.getId(); } // caller is responsible for acquiring lock // this method is only called when a relationship is created or // a relationship delete is undone or when the full node is loaded void addRelationship( NodeManager nodeManager, RelationshipType type, int relId ) { IntArray relationshipSet = nodeManager.getCowRelationshipAddMap( this, type.name(), true ); relationshipSet.add( relId ); } // caller is responsible for acquiring lock // this method is only called when a undo create relationship or // a relationship delete is invoked. void removeRelationship( NodeManager nodeManager, RelationshipType type, int relId ) { IntArray relationshipSet = nodeManager.getCowRelationshipRemoveMap( this, type.name(), true ); relationshipSet.add( relId ); } private void ensureRelationshipMapNotNull( NodeManager nodeManager ) { if ( relationshipMap == null ) { loadInitialRelationships( nodeManager ); } } private void loadInitialRelationships( NodeManager nodeManager ) { Map<Integer,RelationshipImpl> map = null; synchronized ( this ) { if ( relationshipMap == null ) { this.relChainPosition = nodeManager.getRelationshipChainPosition( this ); ArrayMap<String,IntArray> tmpRelMap = new ArrayMap<String,IntArray>(); map = getMoreRelationships( nodeManager, tmpRelMap ); this.relationshipMap = tmpRelMap; } } if ( map != null ) { nodeManager.putAllInRelCache( map ); } } private Map<Integer,RelationshipImpl> getMoreRelationships( NodeManager nodeManager, ArrayMap<String,IntArray> tmpRelMap ) { if ( !relChainPosition.hasMore() ) { return null; } Pair<ArrayMap<String,IntArray>,Map<Integer,RelationshipImpl>> pair = nodeManager.getMoreRelationships( this ); ArrayMap<String,IntArray> addMap = pair.first(); if ( addMap.size() == 0 ) { return null; } for ( String type : addMap.keySet() ) { IntArray addRels = addMap.get( type ); IntArray srcRels = tmpRelMap.get( type ); if ( srcRels == null ) { tmpRelMap.put( type, addRels ); } else { srcRels.addAll( addRels ); } } return pair.other(); // nodeManager.putAllInRelCache( pair.other() ); } boolean getMoreRelationships( NodeManager nodeManager ) { // ArrayMap<String, IntArray> tmpRelMap = relationshipMap; Pair<ArrayMap<String,IntArray>,Map<Integer,RelationshipImpl>> pair; synchronized ( this ) { if ( !relChainPosition.hasMore() ) { return false; } pair = nodeManager.getMoreRelationships( this ); ArrayMap<String,IntArray> addMap = pair.first(); if ( addMap.size() == 0 ) { return false; } for ( String type : addMap.keySet() ) { IntArray addRels = addMap.get( type ); // IntArray srcRels = tmpRelMap.get( type ); IntArray srcRels = relationshipMap.get( type ); if ( srcRels == null ) { relationshipMap.put( type, addRels ); } else { srcRels.addAll( addRels ); } } } nodeManager.putAllInRelCache( pair.other() ); return true; } public Relationship createRelationshipTo( NodeManager nodeManager, Node otherNode, RelationshipType type ) { return nodeManager.createRelationship( this, otherNode, type ); } /* Tentative expansion API public Expansion<Relationship> expandAll() { return Traversal.expanderForAllTypes().expand( this ); } public Expansion<Relationship> expand( RelationshipType type ) { return expand( type, Direction.BOTH ); } public Expansion<Relationship> expand( RelationshipType type, Direction direction ) { return Traversal.expanderForTypes( type, direction ).expand( this ); } public Expansion<Relationship> expand( Direction direction ) { return Traversal.expanderForAllTypes( direction ).expand( this ); } public Expansion<Relationship> expand( RelationshipExpander expander ) { return Traversal.expander( expander ).expand( this ); } */ public Traverser traverse( NodeManager nodeManager, Order traversalOrder, StopEvaluator stopEvaluator, ReturnableEvaluator returnableEvaluator, RelationshipType relationshipType, Direction direction ) { return OldTraverserWrapper.traverse( new NodeProxy( id, nodeManager ), traversalOrder, stopEvaluator, returnableEvaluator, relationshipType, direction ); } public Traverser traverse( NodeManager nodeManager, Order traversalOrder, StopEvaluator stopEvaluator, ReturnableEvaluator returnableEvaluator, RelationshipType firstRelationshipType, Direction firstDirection, RelationshipType secondRelationshipType, Direction secondDirection ) { return OldTraverserWrapper.traverse( new NodeProxy( id, nodeManager ), traversalOrder, stopEvaluator, returnableEvaluator, firstRelationshipType, firstDirection, secondRelationshipType, secondDirection ); } public Traverser traverse( NodeManager nodeManager, Order traversalOrder, StopEvaluator stopEvaluator, ReturnableEvaluator returnableEvaluator, Object... relationshipTypesAndDirections ) { return OldTraverserWrapper.traverse( new NodeProxy( id, nodeManager ), traversalOrder, stopEvaluator, returnableEvaluator, relationshipTypesAndDirections ); } public boolean hasRelationship( NodeManager nodeManager ) { return getRelationships( nodeManager ).iterator().hasNext(); } public boolean hasRelationship( NodeManager nodeManager, RelationshipType... types ) { return getRelationships( nodeManager, types ).iterator().hasNext(); } public boolean hasRelationship( NodeManager nodeManager, Direction dir ) { return getRelationships( nodeManager, dir ).iterator().hasNext(); } public boolean hasRelationship( NodeManager nodeManager, RelationshipType type, Direction dir ) { return getRelationships( nodeManager, type, dir ).iterator().hasNext(); } protected void commitRelationshipMaps( ArrayMap<String,IntArray> cowRelationshipAddMap, ArrayMap<String,IntArray> cowRelationshipRemoveMap ) { if ( relationshipMap == null ) { // we will load full in some other tx return; } if ( cowRelationshipAddMap != null ) { for ( String type : cowRelationshipAddMap.keySet() ) { IntArray add = cowRelationshipAddMap.get( type ); IntArray remove = null; if ( cowRelationshipRemoveMap != null ) { remove = cowRelationshipRemoveMap.get( type ); } IntArray src = relationshipMap.get( type ); relationshipMap.put( type, IntArray.composeNew( src, add, remove ) ); } } if ( cowRelationshipRemoveMap != null ) { for ( String type : cowRelationshipRemoveMap.keySet() ) { if ( cowRelationshipAddMap != null && cowRelationshipAddMap.get( type ) != null ) { continue; } IntArray src = relationshipMap.get( type ); IntArray remove = cowRelationshipRemoveMap.get( type ); relationshipMap.put( type, IntArray.composeNew( src, null, remove ) ); } } } RelationshipChainPosition getRelChainPosition() { return relChainPosition; } }