/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library 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 * Lesser General Public License for more details. */ package org.geotools.graph.structure.opt; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.geotools.graph.structure.DirectedEdge; import org.geotools.graph.structure.DirectedNode; import org.geotools.graph.structure.Edge; import org.geotools.graph.structure.Node; /** * Optimized implementation of DirectedNode. The following optimizations * reduce space and increase performance. <BR> * <UL> * <LI>In and Out edge adjacency list stored as arrays of exact size.</LI> * <LI>Support from removing edges is removed</LI> * <LI>The related component iterators iterates over the underlying edge * arrays of the node instread of newly created collections. * </UL> * Using an optimized directed node requires that the size of the in and out * edge adjacency lists be known before its creation. * * @author Justin Deoliveira, Refractions Research Inc, jdeolive@refractions.net * @see DirectedNode * * * @source $URL$ */ public class OptDirectedNode extends OptGraphable implements DirectedNode { /** in edge adjacency list **/ transient private DirectedEdge[] m_in; /** out edge adjacency list **/ transient private DirectedEdge[] m_out; /** * Constructs a new OptDirectedNode. This constructor does not create * the edge adjacency arrays for the node. * */ public OptDirectedNode() { this(0,0); } /** * Constructs a new OptDirectedNode. * * @param indegree Number of in adjacenct edges to the node. * @param outdegree Number of out adjacent edges to the node. */ public OptDirectedNode(int indegree, int outdegree) { super(); m_in = new DirectedEdge[indegree]; m_out = new DirectedEdge[outdegree]; } /** * Not supported. * * @throws UnsupportedOperationException */ public void add(Edge e) { throw new UnsupportedOperationException( getClass().getName() + "#add(Edge)" ); } /** * @see DirectedNode#addIn(DirectedEdge) */ public void addIn(DirectedEdge e) { for (int i = 0; i < m_in.length; i++) { if (m_in[i] == null) { m_in[i] = e; return; } } } /** * @see DirectedNode#addOut(DirectedEdge) */ public void addOut(DirectedEdge e) { for (int i = 0; i < m_out.length; i++) { if (m_out[i] == null) { m_out[i] = e; return; } } } /** * Unsupported Operation. * * @throws UnsupportedOperationException */ public void remove(Edge e) { throw new UnsupportedOperationException( getClass().getName() + "#remove(Edge)" ); } /** * Unsupported Operation. * * @throws UnsupportedOperationException */ public void removeIn(DirectedEdge e) { throw new UnsupportedOperationException( getClass().getName() + "#removeIn(DirectedEdge)" ); } /** * Unsupported Operation. * * @throws UnsupportedOperationException */ public void removeOut(DirectedEdge e) { throw new UnsupportedOperationException( getClass().getName() + "#removeOut(DirectedEdge)" ); } /** * @see Node#getEdge(Node) */ public Edge getEdge(Node other) { Edge e = getInEdge((DirectedNode)other); if (e == null) e = getOutEdge((DirectedNode)other); return(e); } /** * @see DirectedNode#getInEdge(DirectedNode) */ public Edge getInEdge(DirectedNode other) { for (int i = 0; i < m_in.length; i++) { if (m_in[i].getInNode().equals(other)) return(m_in[i]); } return(null); } /** * @see DirectedNode#getOutEdge(DirectedNode) */ public Edge getOutEdge(DirectedNode other) { ArrayList edges = new ArrayList(); for (int i = 0; i < m_out.length; i++) { if (m_out[i].getOutNode().equals(other)) return(m_out[i]); } return(null); } /** * @see Node#getEdges(Node) */ public List getEdges(Node other) { List l = getInEdges((DirectedNode)other); l.addAll(getOutEdges((DirectedNode)other)); return(l); } /** * @see DirectedNode#getInEdges(DirectedNode) */ public List getInEdges(DirectedNode other) { ArrayList edges = new ArrayList(); for (int i = 0; i < m_in.length; i++) { if (m_in[i].getInNode().equals(other)) edges.add(m_in[i]); } return(edges); } /** * @see DirectedNode#getOutEdges(DirectedNode) */ public List getOutEdges(DirectedNode other) { ArrayList edges = new ArrayList(); for (int i = 0; i < m_out.length; i++) { if (m_out[i].getOutNode().equals(other)) edges.add(m_out[i]); } return(edges); } /** * @see Node#getEdges() */ public List getEdges() { ArrayList edges = new ArrayList(); for (int i = 0; i < m_in.length; i++) edges.add(m_in[i]); for (int i = 0; i < m_out.length; i++) edges.add(m_out[i]); return(edges); } /** * Returns the in adjacency edge array of the node. * * @return An array of in edges for the node. */ public DirectedEdge[] getInEdgeArray() { return(m_in); } /** * @see DirectedNode#getInEdges() */ public List getInEdges() { ArrayList edges = new ArrayList(); for (int i = 0; i < m_in.length; i++) { edges.add(m_in[i]); } return(edges); } /** * Returns the out adjacency edge array of the node. * * @return An array of out edges for the node. */ public DirectedEdge[] getOutEdgeArray() { return(m_out); } /** * @see DirectedNode#getOutEdges() */ public List getOutEdges() { ArrayList edges = new ArrayList(); for (int i = 0; i < m_out.length; i++) { edges.add(m_out[i]); } return(edges); } /** * @see Node#getDegree() */ public int getDegree() { return(m_in.length + m_out.length); } /** * Sets the in degree of the node. This method builds the in edge adjacency * list of the node. * * @param indegree The in degree / size of in edge array of the node. */ public void setInDegree(int indegree) { m_in = new DirectedEdge[indegree]; } /** * @see DirectedNode#getInDegree() */ public int getInDegree() { return(m_in.length); } /** * Sets the out degree of the node. This method builds the out edge adjacency * list of the node. * * @param outdegree The out degree / size of out edge array of the node. */ public void setOutDegree(int outdegree) { m_out = new DirectedEdge[outdegree]; } /** * @see DirectedNode#getOutDegree() */ public int getOutDegree() { return(m_out.length); } /** * This iterator iterates over the underlying edge arrays of the node. * * @see org.geotools.graph.structure.Graphable#getRelated() */ public Iterator getRelated() { return(new RelatedIterator(RelatedIterator.BOTH)); } /** * This iterator iterates over the underlying in edge array of the node. * * @see org.geotools.graph.structure.DirectedGraphable#getInRelated() */ public Iterator getInRelated() { return(new RelatedIterator(RelatedIterator.IN)); } /** * This iterator iterates over the underlying out edge array of the node. */ public Iterator getOutRelated() { return(new RelatedIterator(RelatedIterator.OUT)); } /** * Overrides the default deserialization operation. Since edge adjacency * lists of Nodes are not written out upon serialization, they must be * recreated upon deserialization. * * @param in Object input stream containing serialized objects. * * @throws IOException * @throws ClassNotFoundException */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); //read the degree of the node setInDegree(in.readInt()); setOutDegree(in.readInt()); } /** * Overrides the default serialization operation. Since edge adjacency * lists of Nodes are not written out upon serialization, all the information * needed to recreate them must be written to the object stream as well. Since * the edge list is not written out, and the node does not store its degree * explicitly, it must be written to the output stream. * * @param in Object output stream containing serialized objects. * * @throws IOException * @throws ClassNotFoundException */ private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); //write the degree of the node to the output stream out.writeInt(getInDegree()); out.writeInt(getOutDegree()); } /** * Iterator used to iterate over related nodes. * * @author Justin Deoliveira, Refractions Research Inc, jdeolive@refractions.net * */ public class RelatedIterator implements Iterator { /** in iteration mode **/ public static final int IN = 0; /** out iteration mode **/ public static final int OUT = 1; /** both iteration mode **/ public static final int BOTH = 2; /** iteration mode **/ private int m_mode; /** iteration index **/ private int m_index; /** * Constructs a new iterator. * * @param mode Iteration mode. */ public RelatedIterator(int mode) { m_mode = mode; m_index = 0; } /** * Not supported. * * @throws UnsupportedOperationException */ public void remove() { throw new UnsupportedOperationException( getClass().getName() + "#remove()" ); } /** * Determines if there are any more related nodes to return. * * @see Iterator#hasNext() */ public boolean hasNext() { switch(m_mode) { case IN: return(m_index < m_in.length); case OUT: return(m_index < m_out.length); case BOTH: return(m_index < m_in.length + m_out.length); } return(false); } /** * Returns the next related node. * * @see Iterator#next() */ public Object next() { switch(m_mode) { case IN: return(m_in[m_index++].getInNode()); case OUT: return(m_out[m_index++].getOutNode()); case BOTH: return( m_index < m_in.length ? m_in[m_index++].getInNode() : m_out[m_index++ - m_in.length].getOutNode() ); } return(null); } } }