001/*
002 * Copyright 2006 - 2013
003 *     Stefan Balev     <stefan.balev@graphstream-project.org>
004 *     Julien Baudry    <julien.baudry@graphstream-project.org>
005 *     Antoine Dutot    <antoine.dutot@graphstream-project.org>
006 *     Yoann Pigné      <yoann.pigne@graphstream-project.org>
007 *     Guilhelm Savin   <guilhelm.savin@graphstream-project.org>
008 * 
009 * This file is part of GraphStream <http://graphstream-project.org>.
010 * 
011 * GraphStream is a library whose purpose is to handle static or dynamic
012 * graph, create them from scratch, file or any source and display them.
013 * 
014 * This program is free software distributed under the terms of two licenses, the
015 * CeCILL-C license that fits European law, and the GNU Lesser General Public
016 * License. You can  use, modify and/ or redistribute the software under the terms
017 * of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following
018 * URL <http://www.cecill.info> or under the terms of the GNU LGPL as published by
019 * the Free Software Foundation, either version 3 of the License, or (at your
020 * option) any later version.
021 * 
022 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
023 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
024 * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
025 * 
026 * You should have received a copy of the GNU Lesser General Public License
027 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
028 * 
029 * The fact that you are presently reading this means that you have had
030 * knowledge of the CeCILL-C and LGPL licenses and that you accept their terms.
031 */
032package org.graphstream.graph.implementations;
033
034import java.util.Arrays;
035import java.util.HashMap;
036import java.util.Iterator;
037import java.util.NoSuchElementException;
038
039import org.graphstream.graph.Edge;
040import org.graphstream.graph.EdgeFactory;
041import org.graphstream.graph.Graph;
042import org.graphstream.graph.Node;
043import org.graphstream.graph.NodeFactory;
044
045/**
046 * <p>
047 * A lightweight graph class intended to allow the construction of big graphs
048 * (millions of elements).
049 * </p>
050 * 
051 * <p>
052 * The main purpose here is to minimize memory consumption even if the
053 * management of such a graph implies more CPU consuming. See the
054 * <code>complexity</code> tags on each method so as to figure out the impact on
055 * the CPU.
056 * </p>
057 */
058public class AdjacencyListGraph extends AbstractGraph {
059
060        public static final double GROW_FACTOR = 1.1;
061        public static final int DEFAULT_NODE_CAPACITY = 128;
062        public static final int DEFAULT_EDGE_CAPACITY = 1024;
063
064        protected HashMap<String, AbstractNode> nodeMap;
065        protected HashMap<String, AbstractEdge> edgeMap;
066
067        protected AbstractNode[] nodeArray;
068        protected AbstractEdge[] edgeArray;
069
070        protected int nodeCount;
071        protected int edgeCount;
072
073        // *** Constructors ***
074
075        /**
076         * Creates an empty graph.
077         * 
078         * @param id
079         *            Unique identifier of the graph.
080         * @param strictChecking
081         *            If true any non-fatal error throws an exception.
082         * @param autoCreate
083         *            If true (and strict checking is false), nodes are
084         *            automatically created when referenced when creating a edge,
085         *            even if not yet inserted in the graph.
086         * @param initialNodeCapacity
087         *            Initial capacity of the node storage data structures. Use this
088         *            if you know the approximate maximum number of nodes of the
089         *            graph. The graph can grow beyond this limit, but storage
090         *            reallocation is expensive operation.
091         * @param initialEdgeCapacity
092         *            Initial capacity of the edge storage data structures. Use this
093         *            if you know the approximate maximum number of edges of the
094         *            graph. The graph can grow beyond this limit, but storage
095         *            reallocation is expensive operation.
096         */
097        public AdjacencyListGraph(String id, boolean strictChecking, boolean autoCreate,
098                        int initialNodeCapacity, int initialEdgeCapacity) {
099                super(id, strictChecking, autoCreate);
100
101                setNodeFactory(new NodeFactory<AdjacencyListNode>() {
102                        public AdjacencyListNode newInstance(String id, Graph graph) {
103                                return new AdjacencyListNode((AbstractGraph) graph, id);
104                        }
105                });
106
107                setEdgeFactory(new EdgeFactory<AbstractEdge>() {
108                        public AbstractEdge newInstance(String id, Node src, Node dst,
109                                        boolean directed) {
110                                return new AbstractEdge(id, (AbstractNode) src,
111                                                (AbstractNode) dst, directed);
112                        }
113                });
114
115                if (initialNodeCapacity < DEFAULT_NODE_CAPACITY)
116                        initialNodeCapacity = DEFAULT_NODE_CAPACITY;
117                if (initialEdgeCapacity < DEFAULT_EDGE_CAPACITY)
118                        initialEdgeCapacity = DEFAULT_EDGE_CAPACITY;
119
120                nodeMap = new HashMap<String, AbstractNode>(
121                                4 * initialNodeCapacity / 3 + 1);
122                edgeMap = new HashMap<String, AbstractEdge>(
123                                4 * initialEdgeCapacity / 3 + 1);
124                nodeArray = new AbstractNode[initialNodeCapacity];
125                edgeArray = new AbstractEdge[initialEdgeCapacity];
126                nodeCount = edgeCount = 0;
127        }
128
129        /**
130         * Creates an empty graph with default edge and node capacity.
131         * 
132         * @param id
133         *            Unique identifier of the graph.
134         * @param strictChecking
135         *            If true any non-fatal error throws an exception.
136         * @param autoCreate
137         *            If true (and strict checking is false), nodes are
138         *            automatically created when referenced when creating a edge,
139         *            even if not yet inserted in the graph.
140         */
141        public AdjacencyListGraph(String id, boolean strictChecking, boolean autoCreate) {
142                this(id, strictChecking, autoCreate, DEFAULT_NODE_CAPACITY,
143                                DEFAULT_EDGE_CAPACITY);
144        }
145
146        /**
147         * Creates an empty graph with strict checking and without auto-creation.
148         * 
149         * @param id
150         *            Unique identifier of the graph.
151         */
152        public AdjacencyListGraph(String id) {
153                this(id, true, false);
154        }
155
156        // *** Callbacks ***
157
158        @Override
159        protected void addEdgeCallback(AbstractEdge edge) {
160                edgeMap.put(edge.getId(), edge);
161                if (edgeCount == edgeArray.length) {
162                        AbstractEdge[] tmp = new AbstractEdge[(int) (edgeArray.length * GROW_FACTOR) + 1];
163                        System.arraycopy(edgeArray, 0, tmp, 0, edgeArray.length);
164                        Arrays.fill(edgeArray, null);
165                        edgeArray = tmp;
166                }
167                edgeArray[edgeCount] = edge;
168                edge.setIndex(edgeCount++);
169        }
170
171        @Override
172        protected void addNodeCallback(AbstractNode node) {
173                nodeMap.put(node.getId(), node);
174                if (nodeCount == nodeArray.length) {
175                        AbstractNode[] tmp = new AbstractNode[(int) (nodeArray.length * GROW_FACTOR) + 1];
176                        System.arraycopy(nodeArray, 0, tmp, 0, nodeArray.length);
177                        Arrays.fill(nodeArray, null);
178                        nodeArray = tmp;
179                }
180                nodeArray[nodeCount] = node;
181                node.setIndex(nodeCount++);
182        }
183
184        @Override
185        protected void removeEdgeCallback(AbstractEdge edge) {
186                edgeMap.remove(edge.getId());
187                int i = edge.getIndex();
188                edgeArray[i] = edgeArray[--edgeCount];
189                edgeArray[i].setIndex(i);
190                edgeArray[edgeCount] = null;
191        }
192
193        @Override
194        protected void removeNodeCallback(AbstractNode node) {
195                nodeMap.remove(node.getId());
196                int i = node.getIndex();
197                nodeArray[i] = nodeArray[--nodeCount];
198                nodeArray[i].setIndex(i);
199                nodeArray[nodeCount] = null;
200        }
201
202        @Override
203        protected void clearCallback() {
204                nodeMap.clear();
205                edgeMap.clear();
206                Arrays.fill(nodeArray, 0, nodeCount, null);
207                Arrays.fill(edgeArray, 0, edgeCount, null);
208                nodeCount = edgeCount = 0;
209        }
210
211        @SuppressWarnings("unchecked")
212        @Override
213        public <T extends Edge> T getEdge(String id) {
214                return (T) edgeMap.get(id);
215        }
216
217        @SuppressWarnings("unchecked")
218        @Override
219        public <T extends Edge> T getEdge(int index) {
220                if (index < 0 || index >= edgeCount)
221                        throw new IndexOutOfBoundsException("Edge " + index
222                                        + " does not exist");
223                return (T) edgeArray[index];
224        }
225
226        @Override
227        public int getEdgeCount() {
228                return edgeCount;
229        }
230
231        @SuppressWarnings("unchecked")
232        @Override
233        public <T extends Node> T getNode(String id) {
234                return (T) nodeMap.get(id);
235        }
236
237        @SuppressWarnings("unchecked")
238        @Override
239        public <T extends Node> T getNode(int index) {
240                if (index < 0 || index > nodeCount)
241                        throw new IndexOutOfBoundsException("Node " + index
242                                        + " does not exist");
243                return (T) nodeArray[index];
244        }
245
246        @Override
247        public int getNodeCount() {
248                return nodeCount;
249        }
250
251        // *** Iterators ***
252
253        protected class EdgeIterator<T extends Edge> implements Iterator<T> {
254                int iNext = 0;
255                int iPrev = -1;
256
257                public boolean hasNext() {
258                        return iNext < edgeCount;
259                }
260
261                @SuppressWarnings("unchecked")
262                public T next() {
263                        if (iNext >= edgeCount)
264                                throw new NoSuchElementException();
265                        iPrev = iNext++;
266                        return (T) edgeArray[iPrev];
267                }
268
269                public void remove() {
270                        if (iPrev == -1)
271                                throw new IllegalStateException();
272                        removeEdge(edgeArray[iPrev], true, true, true);
273                        iNext = iPrev;
274                        iPrev = -1;
275                }
276        }
277
278        protected class NodeIterator<T extends Node> implements Iterator<T> {
279                int iNext = 0;
280                int iPrev = -1;
281
282                public boolean hasNext() {
283                        return iNext < nodeCount;
284                }
285
286                @SuppressWarnings("unchecked")
287                public T next() {
288                        if (iNext >= nodeCount)
289                                throw new NoSuchElementException();
290                        iPrev = iNext++;
291                        return (T) nodeArray[iPrev];
292                }
293
294                public void remove() {
295                        if (iPrev == -1)
296                                throw new IllegalStateException();
297                        removeNode(nodeArray[iPrev], true);
298                        iNext = iPrev;
299                        iPrev = -1;
300                }
301        }
302
303        @Override
304        public <T extends Edge> Iterator<T> getEdgeIterator() {
305                return new EdgeIterator<T>();
306        }
307
308        @Override
309        public <T extends Node> Iterator<T> getNodeIterator() {
310                return new NodeIterator<T>();
311        }
312
313        /*
314         * For performance tuning
315         * 
316         * @return the number of allocated but unused array elements public int
317         * getUnusedArrayElements() { int count = 0; count += edgeArray.length -
318         * edgeCount; count += nodeArray.length - nodeCount; for (ALNode n :
319         * this.<ALNode> getEachNode()) count += n.edges.length - n.degree; return
320         * count; }
321         */
322}