GraphPath.java

package eu.javaexperience.algorithm.search.graph;

import java.util.Iterator;
import java.util.NoSuchElementException;

import eu.javaexperience.asserts.AssertArgument;
import eu.javaexperience.exceptions.IllegalOperationException;
import eu.javaexperience.reflect.Mirror;

public class GraphPath<V, E> implements Cloneable, Iterable<GraphSearchVertexTransition<V, E>>
{
	protected GraphSearchVertexTransition<V, E> first;
	protected GraphSearchVertexTransition<V, E> last;
	protected int transitions = 0;
	
	
	public GraphSearchVertexTransition<V, E> addInitialVertext(V init)
	{
		AssertArgument.assertNotNull(init, "Vertex");
		if(null != first || null != last)
		{
			throw new IllegalStateException("GraphPath is already initalized");
		}
		
		++transitions;
		
		 first = last = new GraphSearchVertexTransition<>(this, init, null, init);
		 return last;
	}
	
	@Override
	protected GraphPath<V, E> clone() throws CloneNotSupportedException
	{
		GraphPath<V, E> ret = (GraphPath<V, E>) super.clone();
		
		GraphSearchVertexTransition<V, E> crnt = first;
		if(null != crnt)
		{
			GraphSearchVertexTransition<V, E> build = (GraphSearchVertexTransition<V, E>) crnt.clone();
			ret.first = build;
			build.setOwner(ret);
			while(null != (crnt = crnt.next))
			{
				GraphSearchVertexTransition<V, E> n = (GraphSearchVertexTransition<V, E>) crnt.clone();
				n.setOwner(ret);
				
				build.next = n;
				n.prev = build;
				
				
				build = n;
			}
			
			ret.last = build;
		}
		
		return ret;
	}
	
	protected void registerFirst(GraphSearchVertexTransition<V, E> first)
	{
		if(first.next != this.first)
		{
			throw new IllegalOperationException("Bad chaining, new first Node is not pointed to the current first Node");
		}
		++transitions;
		this.first = first;
	}
	
	public void registerLast(GraphSearchVertexTransition<V, E> last)
	{
		if(last.prev != this.last)
		{
			throw new IllegalOperationException("Bad chaining, new last Node is not pointed to the current last Node");
		}
		++transitions;
		this.last = last;
	}

	public int getTransitionsCount()
	{
		return transitions;
	}
	
	@Override
	public String toString()
	{
		StringBuilder sb = new StringBuilder();
		
		sb.append("GraphPath: ");
		
		GraphSearchVertexTransition<V, E> crnt = first;
		if(null != crnt)
		{
			sb.append("[");
			sb.append(crnt.getFrom());
			sb.append("]");
			
			if(!crnt.isInitial())
			{
				do
				{
					sb.append(" ---");
					sb.append(crnt.getEdge());
					sb.append("---> [");
					sb.append(crnt.getTo());
					sb.append("]");
				}
				while(null != (crnt = crnt.next));
			}
		}
		
		return sb.toString();
	}
	
	protected void reOwn(GraphPath<V, E> owner)
	{
		GraphSearchVertexTransition<V, E> crnt = first;
		if(null != crnt)
		{
			crnt.setOwner(owner);
			while(null != (crnt = crnt.next))
			{
				crnt.setOwner(owner);
			}
		}
	}
	
	
	public GraphPath<V, E> merge(GraphPath<V, E> with)
	{
		if(!last.getTo().equals(with.first.getFrom()))
		{
			throw new IllegalOperationException("Can't merge path "+this+" to "+with);
		}
		
		try
		{
			GraphPath<V, E> ret = this.clone();
			GraphPath<V, E> w = with.clone();
			
			ret.last.next = w.first;
			ret.last = w.last;
			w.reOwn(ret);			
			ret.transitions = transitions+w.transitions;
			
			return ret;
		}
		catch (CloneNotSupportedException e)
		{
			Mirror.propagateAnyway(e);
			return null;
		}
	}

	public GraphSearchVertexTransition<V, E> getFirst()
	{
		return first;
	}

	public GraphSearchVertexTransition<V, E> getLast()
	{
		return last;
	}
	
	@Override
	public Iterator<GraphSearchVertexTransition<V, E>> iterator()
	{
		return new Iterator<GraphSearchVertexTransition<V,E>>()
		{
			GraphSearchVertexTransition<V, E> crnt = null;
			
			protected GraphSearchVertexTransition<V, E> getNext()
			{
				return null == crnt?first:crnt.next;
			}
			
			@Override
			public boolean hasNext()
			{
				return null != getNext();
			}

			@Override
			public GraphSearchVertexTransition<V, E> next()
			{
				crnt = getNext();
				if(null == crnt)
				{
					throw new NoSuchElementException("GraphPath has no more Transition");
				}
				return crnt;
			}
		};
	}
	
	@Override
	public boolean equals(Object obj)
	{
		if(this == obj)
		{
			return true;
		}
		
		if(!(obj instanceof GraphPath))
		{
			return false; 
		}
		
		GraphPath other = (GraphPath) obj;
		
		if(transitions != other.transitions)
		{
			return false;
		}
		
		if(!first.equals(other.first))
		{
			return false;
		}
		
		if(!last.equals(other.last))
		{
			return false;
		}
		
		GraphSearchVertexTransition<V, E> tr = first;
		GraphSearchVertexTransition<V, E> otr = other.first;
		
		while(null != tr)
		{
			if(!tr.equals(otr))
			{
				return false;
			}
			tr = tr.next;
		}
		
		return true;
	}
	
	protected int hashCode = 0;
	
	@Override
	public int hashCode()
	{
		if(0 == hashCode)
		{
			int ret = 27;
			
			GraphSearchVertexTransition<V, E> tr = first;
			
			while(null != tr)
			{
				ret = 31* ret + tr.hashCode();
				tr = tr.next;
			}
			
			hashCode = ret;
		}
		return hashCode;
	}
}