/** * Copyright (c) 2002-2013 "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 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.neo4j.kernel.impl.core; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.NotFoundException; import org.neo4j.graphdb.RelationshipType; import org.neo4j.kernel.impl.nioneo.store.PropertyData; import org.neo4j.kernel.impl.transaction.LockException; import org.neo4j.kernel.impl.transaction.LockType; import org.neo4j.kernel.impl.util.ArrayMap; abstract class RelationshipImpl extends Primitive { RelationshipImpl( long startNodeId, long endNodeId, boolean newRel ) { super( newRel ); } protected RelationshipType assertTypeNotNull( RelationshipType type ) { if ( type == null ) { throw new IllegalArgumentException( "Null type" ); } return type; } @Override public boolean equals( Object obj ) { return this == obj || ( obj instanceof RelationshipImpl && ( (RelationshipImpl) obj ).getId() == getId() ); } @Override protected PropertyData changeProperty( NodeManager nodeManager, PropertyData property, Object value ) { return nodeManager.relChangeProperty( this, property, value ); } @Override protected PropertyData addProperty( NodeManager nodeManager, PropertyIndex index, Object value ) { return nodeManager.relAddProperty( this, index, value ); } @Override protected void removeProperty( NodeManager nodeManager, PropertyData property ) { nodeManager.relRemoveProperty( this, property ); } @Override protected ArrayMap<Integer, PropertyData> loadProperties( NodeManager nodeManager, boolean light ) { return nodeManager.loadProperties( this, light ); } public Node[] getNodes( NodeManager nodeManager ) { return new Node[] { new NodeProxy( getStartNodeId(), nodeManager ), new NodeProxy( getEndNodeId(), nodeManager ) }; } public Node getOtherNode( NodeManager nodeManager, Node node ) { if ( getStartNodeId() == node.getId() ) { return new NodeProxy( getEndNodeId(), nodeManager ); } if ( getEndNodeId() == node.getId() ) { return new NodeProxy( getStartNodeId(), nodeManager ); } throw new NotFoundException( "Node[" + node.getId() + "] not connected to this relationship[" + getId() + "]" ); } public Node getStartNode( NodeManager nodeManager ) { return new NodeProxy( getStartNodeId(), nodeManager ); } abstract long getStartNodeId(); public Node getEndNode( NodeManager nodeManager ) { return new NodeProxy( getEndNodeId(), nodeManager ); } abstract long getEndNodeId(); public abstract RelationshipType getType( NodeManager nodeManager ); public boolean isType( NodeManager nodeManager, RelationshipType otherType ) { return otherType != null && otherType.name().equals( this.getType( nodeManager ).name() ); } public void delete( NodeManager nodeManager ) { NodeImpl startNode = null; NodeImpl endNode = null; boolean startNodeLocked = false; boolean endNodeLocked = false; boolean thisLocked = false; boolean success = false; try { startNode = nodeManager.getLightNode( getStartNodeId() ); if ( startNode != null ) { nodeManager.acquireLock( startNode, LockType.WRITE ); startNodeLocked = true; } endNode = nodeManager.getLightNode( getEndNodeId() ); if ( endNode != null ) { nodeManager.acquireLock( endNode, LockType.WRITE ); endNodeLocked = true; } nodeManager.acquireLock( this, LockType.WRITE ); thisLocked = true; // no need to load full relationship, all properties will be // deleted when relationship is deleted ArrayMap<Integer,PropertyData> skipMap = nodeManager.getCowPropertyRemoveMap( this, true ); ArrayMap<Integer,PropertyData> removedProps = nodeManager.deleteRelationship( this ); if ( removedProps.size() > 0 ) { for ( int index : removedProps.keySet() ) { skipMap.put( index, removedProps.get( index ) ); } } success = true; RelationshipType type = getType( nodeManager ); long id = getId(); if ( startNode != null ) { startNode.removeRelationship( nodeManager, type, id ); } if ( endNode != null ) { endNode.removeRelationship( nodeManager, type, id ); } success = true; } finally { boolean releaseFailed = false; try { if ( thisLocked ) { nodeManager.releaseLock( this, LockType.WRITE ); } } catch ( Exception e ) { releaseFailed = true; e.printStackTrace(); } try { if ( startNodeLocked ) { nodeManager.releaseLock( startNode, LockType.WRITE ); } } catch ( Exception e ) { releaseFailed = true; e.printStackTrace(); } try { if ( endNodeLocked ) { nodeManager.releaseLock( endNode, LockType.WRITE ); } } catch ( Exception e ) { releaseFailed = true; e.printStackTrace(); } if ( !success ) { nodeManager.setRollbackOnly(); } if ( releaseFailed ) { throw new LockException( "Unable to release locks [" + startNode + "," + endNode + "] in relationship delete->" + this ); } } } @Override public String toString() { return "RelationshipImpl #" + this.getId() + " of type " + getType( null ) + " between Node[" + getStartNodeId() + "] and Node[" + getEndNodeId() + "]"; } }