/**
* 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.neo4j.graphdb.Path;
import org.neo4j.graphdb.Relationship;
public class CyclePath
implements Iterable<Long>
{
public static final class CycleIterator
implements Iterator<Long>
{
private final long[] ids;
private int next;
private final int start;
public CycleIterator( final long[] ids, final int entryPoint )
{
this.ids = ids;
start = entryPoint;
next = entryPoint;
}
@Override
public boolean hasNext()
{
if ( next >= ids.length )
{
if ( start > 0 )
{
next = 0;
return true;
}
return false;
}
else if ( next == -1 )
{
return false;
}
return true;
}
@Override
public Long next()
{
if ( hasNext() )
{
final long id = ids[next++];
if ( next == start )
{
next = -1;
}
return id;
}
else if ( next == start )
{
throw new IndexOutOfBoundsException( next + " is the starting point for this iteration!" );
}
throw new IndexOutOfBoundsException( next + " is the next index, but the array has only " + ids.length
+ " items!" );
}
@Override
public void remove()
{
throw new UnsupportedOperationException( "Cannot remove id; CyclePath is immutable." );
}
}
private int entryPoint = 0;
private final long[] ids;
public CyclePath( final long[] ids )
{
this.ids = ids;
}
public CyclePath( final List<Long> ids )
{
this.ids = new long[ids.size()];
for ( int i = 0; i < ids.size(); i++ )
{
this.ids[i] = ids.get( i );
}
}
public CyclePath( final Path path )
{
final List<Long> ids = new ArrayList<Long>();
for ( final Relationship r : path.relationships() )
{
ids.add( r.getId() );
}
this.ids = new long[ids.size()];
for ( int i = 0; i < ids.size(); i++ )
{
this.ids[i] = ids.get( i );
}
}
public void setEntryPoint( final long entryPoint )
{
for ( int i = 0; i < ids.length; i++ )
{
if ( ids[i] == entryPoint )
{
this.entryPoint = i;
return;
}
}
}
public void clearEntryPoint()
{
entryPoint = 0;
}
@Override
public Iterator<Long> iterator()
{
return new CycleIterator( ids, entryPoint );
}
public long getLastRelationshipId()
{
final int last = entryPoint > 0 ? entryPoint - 1 : ids.length - 1;
return ids[last];
}
public long getFirstRelationshipId()
{
return ids[entryPoint];
}
public long[] getRelationshipIds()
{
// if ( entryPoint == 0 )
// {
// return getRawRelationshipIds();
// }
final long[] ids = new long[this.ids.length];
final Iterator<Long> iterator = iterator();
int i = 0;
while ( iterator.hasNext() )
{
ids[i++] = iterator.next();
}
return ids;
}
@Override
public int hashCode()
{
final int prime = 37;
if ( ids.length == 0 )
{
return prime;
}
final CycleIterator it = identityIterator();
int result = prime;
int i = 0;
while ( it.hasNext() )
{
if ( i % 2 == 1 )
{
result += it.next()
.hashCode();
}
else
{
result -= it.next()
.hashCode();
}
i++;
}
return result;
}
@Override
public boolean equals( final Object obj )
{
if ( this == obj )
{
return true;
}
if ( !super.equals( obj ) )
{
return false;
}
if ( getClass() != obj.getClass() )
{
return false;
}
final CyclePath other = (CyclePath) obj;
if ( ids.length != other.ids.length )
{
return false;
}
else if ( ids.length == 0 )
{
return true;
}
final CycleIterator it = identityIterator();
final CycleIterator oit = other.identityIterator();
while ( it.hasNext() )
{
if ( it.next() != oit.next() )
{
return false;
}
}
return true;
}
public CycleIterator identityIterator()
{
final long[] sorted = new long[ids.length];
System.arraycopy( ids, 0, sorted, 0, ids.length );
Arrays.sort( sorted );
int entry = 0;
for ( int i = 0; i < ids.length; i++ )
{
if ( ids[i] == sorted[0] )
{
entry = i;
break;
}
}
return new CycleIterator( ids, entry );
}
private long[] getRawRelationshipIds()
{
return ids;
}
public CyclePath reorientToEntryPoint()
{
if ( entryPoint == 0 )
{
return this;
}
final long[] ids = getRawRelationshipIds();
final long[] reoriented = new long[ids.length];
final CycleIterator it = new CycleIterator( ids, entryPoint );
int i = 0;
while ( it.hasNext() )
{
reoriented[i++] = it.next();
}
return new CyclePath( reoriented );
}
public String getKey()
{
final StringBuilder sb = new StringBuilder();
final CycleIterator it = identityIterator();
while ( it.hasNext() )
{
if ( sb.length() > 0 )
{
sb.append( ',' );
}
sb.append( it.next() );
}
return sb.toString();
}
@Override
public String toString()
{
return "CyclePath [" + getKey() + "]";
}
public int length()
{
return ids.length;
}
}