/** * Copyright (C) 2012 Red Hat, Inc. (jdcasey@commonjava.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.commonjava.cartographer.graph.spi.neo4j.model; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import org.apache.commons.codec.digest.DigestUtils; import org.commonjava.cartographer.graph.model.GraphPath; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Path; import org.neo4j.graphdb.Relationship; public class Neo4jGraphPath implements GraphPath<Long> { private final long[] relationships; private final long startNode; private final long endNode; public Neo4jGraphPath( final Neo4jGraphPath parent, final Relationship... relationships ) { if ( parent == null ) { throw new NullPointerException( "Parent path cannot be null" ); } this.startNode = parent.startNode; if ( relationships.length > 0 ) { this.endNode = relationships[relationships.length - 1].getEndNode() .getId(); } else { this.endNode = parent.endNode; } final int parentLen = parent.relationships.length; this.relationships = new long[parentLen + relationships.length]; if ( parentLen > 0 ) { System.arraycopy( parent.relationships, 0, this.relationships, 0, parent.relationships.length ); } if ( this.relationships.length > 0 ) { for ( int i = parentLen; i < this.relationships.length; i++ ) { this.relationships[i] = relationships[i - parentLen].getId(); } } } public Neo4jGraphPath( final Path path ) { this.startNode = path.startNode() .getId(); this.endNode = path.endNode() .getId(); final List<Long> ids = new ArrayList<Long>(); for ( final Relationship r : path.relationships() ) { ids.add( r.getId() ); } this.relationships = new long[ids.size()]; for ( int i = 0; i < ids.size(); i++ ) { this.relationships[i] = ids.get( i ); } } public Neo4jGraphPath( final Node start, final Node end, final long[] rids ) { this.startNode = start.getId(); this.endNode = end.getId(); this.relationships = rids; } public Neo4jGraphPath( final Relationship[] relationships ) { if ( relationships.length > 0 ) { this.startNode = relationships[0].getStartNode() .getId(); this.endNode = relationships[relationships.length - 1].getEndNode() .getId(); } else { throw new IllegalArgumentException( "Cannot initialize path with zero relationships and no explicit start node!" ); } this.relationships = new long[relationships.length]; final int i = 0; for ( final Relationship relationship : relationships ) { this.relationships[i] = relationship.getId(); } } private Neo4jGraphPath( final Neo4jGraphPath parent, final long endNode, final long[] newRelationships ) { this.startNode = parent.startNode; this.endNode = endNode; final int parentLen = parent.relationships.length; this.relationships = new long[parentLen + newRelationships.length]; System.arraycopy( parent.relationships, 0, this.relationships, 0, parentLen ); System.arraycopy( newRelationships, 0, this.relationships, parentLen, newRelationships.length ); } public Neo4jGraphPath append( final Neo4jGraphPath childPath ) { if ( length() > 0 && getLastRelationshipId() != childPath.getFirstRelationshipId() ) { throw new IllegalArgumentException( "Cannot splice " + childPath + " onto " + this + ". They don't overlap on last/first relationshipId!" ); } if ( childPath.length() < 2 ) { return this; } final long[] ids = new long[childPath.length() - 1]; System.arraycopy( childPath.getRelationshipIds(), 1, ids, 0, ids.length ); return new Neo4jGraphPath( this, childPath.endNode, ids ); } public long getStartNodeId() { return startNode; } public long getEndNodeId() { return endNode; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + Long.valueOf( startNode ).hashCode(); result = prime * result + Long.valueOf( endNode ).hashCode(); result = prime * result + Arrays.hashCode( relationships ); return result; } @Override public boolean equals( final Object obj ) { if ( this == obj ) { return true; } if ( obj == null ) { return false; } if ( getClass() != obj.getClass() ) { return false; } final Neo4jGraphPath other = (Neo4jGraphPath) obj; if ( startNode != other.startNode ) { return false; } if ( endNode != other.endNode ) { return false; } return Arrays.equals( relationships, other.relationships ); } @Override public Iterator<Long> iterator() { return new Iterator<Long>() { private int next = 0; @Override public boolean hasNext() { return relationships.length > next; } @Override public Long next() { return relationships[next++]; } @Override public void remove() { throw new UnsupportedOperationException( "Immutable array of relationship ID's. Remove not supported." ); } }; } @Override public String toString() { return String.format( "%s [relationships=%s, from=%s, to=%s]", getClass().getSimpleName(), Arrays.toString( relationships ), startNode, endNode ); } @Override public String getKey() { final StringBuilder sb = new StringBuilder(); for ( final long id : relationships ) { if ( sb.length() > 0 ) { sb.append( ',' ); } sb.append( id ); } return DigestUtils.shaHex( sb.toString() ); } public long getLastRelationshipId() { if ( relationships.length < 1 ) { return -1; } return relationships[relationships.length - 1]; } public long getFirstRelationshipId() { if ( relationships.length < 1 ) { return -1; } return relationships[0]; } public long[] getRelationshipIds() { return relationships; } public int length() { return relationships.length; } public boolean contains( final long id ) { for ( final long rid : relationships ) { if ( rid == id ) { return true; } } return false; } }