1 /*
2  * $Id: JGraphModelFacade.java,v 1.2 2010/01/29 17:04:29 david Exp $
3  * Copyright (c) 2001-2006, Gaudenz Alder
4  * Copyright (c) 2005-2006, David Benson
5  *
6  * All rights reserved.
7  *
8  * This file is licensed under the JGraph software license, a copy of which
9  * will have been provided to you in the file LICENSE at the root of your
10  * installation directory. If you are unable to locate this file please
11  * contact JGraph sales for another copy.
12  */
13 package com.jgraph.layout;
14 
15 import java.awt.geom.Rectangle2D;
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.HashSet;
19 import java.util.List;
20 import java.util.Set;
21 
22 import org.jgraph.graph.DefaultGraphModel;
23 import org.jgraph.graph.GraphModel;
24 
25 import com.jgraph.algebra.JGraphAlgebra;
26 import com.jgraph.algebra.cost.JGraphCostFunction;
27 import com.jgraph.algebra.cost.JGraphDistanceCostFunction;
28 
29 /**
30  * An abstract description of a graph that can be used by a layout algorithm.
31  * This abstracts visibility, grouping, directed edges, any root cells,
32  * translation and scaling functions. It also stores the actual graph to be
33  * acted upon by the layout and provides utility method to determine the
34  * characteristics of the contained cells. After the layout has been applied
35  * this class stores the result of that layout as a nested attribute map.
36  *
37  */
38 public class JGraphModelFacade extends JGraphFacade {
39 
40 	/**
41 	 * Constructs a JGraphGraphFacade specifying the graph passed in as the
42 	 * input graph
43 	 *
44 	 * @param model
45 	 *            the GraphModel to be laid out
46 	 */
JGraphModelFacade(GraphModel model)47 	public JGraphModelFacade(GraphModel model) {
48 		this(model, null);
49 	}
50 
51 	/**
52 	 * Constructs a JGraphGraphFacade specifying the graph passed in as the
53 	 * input graph
54 	 *
55 	 * @param model
56 	 *            the JGraph to be laid out
57 	 * @param roots
58 	 *            the root vertices to be used by tree layouts. This is not the
59 	 *            same thing as the roots of the graph model.
60 	 */
JGraphModelFacade(GraphModel model, Object[] roots)61 	public JGraphModelFacade(GraphModel model, Object[] roots) {
62 		this(model, roots, true, false, true, true);
63 	}
64 
65 	/**
66 	 * Constructs a JGraphGraphFacade
67 	 *
68 	 * @see #JGraphModelFacade(GraphModel, Object[], boolean, boolean, boolean, boolean,
69 	 *      JGraphCostFunction, JGraphAlgebra)
70 	 */
JGraphModelFacade(GraphModel model, Object[] roots, boolean ignoresHiddenCells, boolean ignoresCellsInGroups, boolean ignoresUnconnectedCells, boolean directed)71 	public JGraphModelFacade(GraphModel model, Object[] roots,
72 			boolean ignoresHiddenCells, boolean ignoresCellsInGroups,
73 			boolean ignoresUnconnectedCells, boolean directed) {
74 		this(model, roots, ignoresHiddenCells, ignoresCellsInGroups,
75 				ignoresUnconnectedCells, directed,
76 				new JGraphDistanceCostFunction(null),
77 				JGraphAlgebra.getSharedInstance());
78 	}
79 
80 	/**
81 	 * Creates a JGraphGraphFacade specifying the graph passed in as the input
82 	 * graph. Also configures properties of layout, whether or not edge
83 	 * direction is to be taken into account, whether or not invisible cells are
84 	 * to be considered and whether or not only root cells are to be considered
85 	 * or roots and all their children. A root is only used if the isVertex
86 	 * method returns true.
87 	 *
88 	 * @see #isVertex
89 	 *
90 	 * @param model
91 	 *            The graph used as input to the layout
92 	 * @param roots
93 	 *            the root vertices to be used by tree layouts
94 	 * @param ignoresHiddenCells
95 	 * @see #ignoresHiddenCells
96 	 * @param ignoresCellsInGroups
97 	 * @see #ignoresCellsInGroups
98 	 * @param ignoresUnconnectedCells
99 	 * @see #ignoresUnconnectedCells
100 	 * @param directed
101 	 * @see #directed
102 	 * @param distanceCostFunction
103 	 *            the cost function that defines the distance metrics
104 	 * @param algebra
105 	 *            the algebra used for basic algorithms and functions
106 	 */
JGraphModelFacade(GraphModel model, Object[] roots, boolean ignoresHiddenCells, boolean ignoresCellsInGroups, boolean ignoresUnconnectedCells, boolean directed, JGraphCostFunction distanceCostFunction, JGraphAlgebra algebra)107 	public JGraphModelFacade(GraphModel model, Object[] roots,
108 			boolean ignoresHiddenCells, boolean ignoresCellsInGroups,
109 			boolean ignoresUnconnectedCells, boolean directed,
110 			JGraphCostFunction distanceCostFunction, JGraphAlgebra algebra) {
111 		super(model, roots, ignoresHiddenCells, ignoresCellsInGroups,
112 				ignoresUnconnectedCells, directed, distanceCostFunction,
113 				algebra);
114 	}
115 
116 	/**
117 	 * A shortcut method that calls getNeighbours with no cells to exclude.
118 	 *
119 	 * @see #getNeighbours(Object, Set, boolean)
120 	 */
getNeighbours(Object cell, boolean ordered)121 	public List getNeighbours(Object cell, boolean ordered) {
122 		return getNeighbours(cell, null, ordered);
123 	}
124 
125 	/**
126 	 * Returns a collection of cells that are connected to the specified cell by
127 	 * edges. Any cells specified in the exclude set will be ignored.
128 	 *
129 	 * @param cell
130 	 *            The cell from which the neighbours will be determined
131 	 * @param exclude
132 	 *            The set of cells to ignore when searching
133 	 * @param ordered
134 	 *            whether or not to order the returned value in the order of the
135 	 *            current <code>order</code> comparator. <b>Be very careful</b>
136 	 *            using the default comparator on the default graph model,
137 	 *            <code>getIndexOfRoot</code> has linear performance and so
138 	 *            sorting the entire model roots will have quadratic
139 	 *            performance.
140 	 * @return Returns the set of neighbours for <code>cell</code>
141 	 */
getNeighbours(Object cell, Set exclude, boolean ordered)142 	public List getNeighbours(Object cell, Set exclude, boolean ordered) {
143 		Object[] fanout = (directed) ? DefaultGraphModel.getOutgoingEdges(
144 				model, cell) : DefaultGraphModel.getEdges(model,
145 				new Object[] { cell }).toArray();
146 		List connectedCells = new ArrayList(fanout.length);
147 		Set localExclude = new HashSet(fanout.length + 8, (float) 0.75);
148 		for (int i = 0; i < fanout.length; i++) {
149 			Object neighbour = DefaultGraphModel.getOpposite(model, fanout[i],
150 					cell);
151 			if (neighbour != null
152 					&& (exclude == null || !exclude.contains(neighbour))
153 					&& !localExclude.contains(neighbour)) {
154 				localExclude.add(neighbour);
155 				connectedCells.add(neighbour);
156 			}
157 		}
158 		if (ordered && order != null)
159 			Collections.sort(connectedCells, order);
160 		return connectedCells;
161 	}
162 
163 	/**
164 	 * Returns the outgoing edges for cell. Cell should be a port or a vertex.
165 	 *
166 	 * @param cell
167 	 *            The cell from which the outgoing edges will be determined
168 	 * @param exclude
169 	 *            The set of edges to ignore when searching
170 	 * @param visibleCells
171 	 *            whether or not only visible cells should be processed
172 	 * @param selfLoops
173 	 *            whether or not to include self loops in the returned list
174 	 * @return Returns the list of outgoing edges for <code>cell</code>
175 	 */
getOutgoingEdges(Object cell, Set exclude, boolean visibleCells, boolean selfLoops)176 	public List getOutgoingEdges(Object cell, Set exclude,
177 			boolean visibleCells, boolean selfLoops) {
178 		Object[] edges = DefaultGraphModel.getEdges(model, cell, false);
179 
180 		List edgeList = new ArrayList(edges.length);
181 		Set localExclude = new HashSet(edges.length);
182 		for (int i = 0; i < edges.length; i++) {
183 			// Check that the edge is neiter in the passed in exclude set or
184 			// the local exclude set. Also, if visibleCells is true check
185 			// the edge is visible in the cache.
186 			if ((exclude == null || !exclude.contains(edges[i]))
187 					&& !localExclude.contains(edges[i])) {
188 				// Add the edge to the list if all edges, including self loops
189 				// are allowed. If self loops are not allowed, ensure the
190 				// source and target of the edge are different
191 				if (selfLoops == true
192 						|| model.getSource(edges[i]) != model
193 								.getTarget(edges[i])) {
194 					edgeList.add(edges[i]);
195 				}
196 				localExclude.add(edges[i]);
197 			}
198 		}
199 		return edgeList;
200 	}
201 
202 	/**
203 	 * Returns the incoming edges for cell. Cell should be a port or a vertex.
204 	 *
205 	 * @param cell
206 	 *            The cell from which the incoming edges will be determined
207 	 * @param exclude
208 	 *            The set of edges to ignore when searching
209 	 * @param visibleCells
210 	 *            whether or not only visible cells should be processed
211 	 * @param selfLoops
212 	 *            whether or not to include self loops in the returned list
213 	 * @return Returns the list of incoming edges for <code>cell</code>
214 	 */
getIncomingEdges(Object cell, Set exclude, boolean visibleCells, boolean selfLoops)215 	public List getIncomingEdges(Object cell, Set exclude,
216 			boolean visibleCells, boolean selfLoops) {
217 		Object[] edges = DefaultGraphModel.getEdges(model, cell, true);
218 
219 		List edgeList = new ArrayList(edges.length);
220 		Set localExclude = new HashSet(edges.length);
221 		for (int i = 0; i < edges.length; i++) {
222 			// Check that the edge is neiter in the passed in exclude set or
223 			// the local exclude set. Also, if visibleCells is true check
224 			// the edge is visible in the cache.
225 			if ((exclude == null || !exclude.contains(edges[i]))
226 					&& !localExclude.contains(edges[i])) {
227 				// Add the edge to the list if all edges, including self loops
228 				// are allowed. If self loops are not allowed, ensure the
229 				// source and target of the edge are different
230 				if (selfLoops == true
231 						|| model.getSource(edges[i]) != model
232 								.getTarget(edges[i])) {
233 					edgeList.add(edges[i]);
234 				}
235 				localExclude.add(edges[i]);
236 			}
237 		}
238 		return edgeList;
239 	}
240 
241 	/**
242 	 * Returns the minimal rectangular bounds that enclose all the elements in
243 	 * the <code>bounds</code> map. After a layout has completed this method
244 	 * will return the collective bounds of the new laid out graph.
245 	 *
246 	 * @return <code>null</code>
247 	 */
getGraphBounds()248 	public Rectangle2D getGraphBounds() {
249 		return getCellBounds();
250 	}
251 }
252