2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
41
package org.netbeans.api.visual.graph;
43
import org.netbeans.api.visual.model.ObjectScene;
44
import org.netbeans.api.visual.widget.Widget;
45
import org.netbeans.modules.visual.util.GeomUtil;
50
* This class holds and manages graph-oriented model.
52
* In comparison with the GraphScene class, in this class the graph consists of nodes, edges and pins. Each pin is assigned to a node (the assigned cannot be change at any time.
53
* Each edge could be attach to a single source and target pin.
55
* The class is abstract and manages only data model and mapping with widgets. The graphics (widgets) has to be supplied
56
* by a developer by overriding the attachNodeWidget, attachPinWidget, attachEdgeWidget, attachEdgeSourceAnchor and attachEdgeTargetAnchor abstract methods.
58
* This class is using generics and allows you to specify type representation for nodes and edges in the graph model. Example:
60
* class MyGraph extends GraphScene<MyNode, MyEdge, MyPin> { ... }
62
* Since the type of nodes, edges and pins could be the same, all node, edge and pin instances have to be unique within the whole scene.
64
* @author David Kaspar
66
// TODO - is it asserted that removing a node removes all its pins
67
// TODO - is it asserted that removing a pin disconnects all the attached edges
68
public abstract class GraphPinScene<N, E, P> extends ObjectScene {
70
private HashSet<N> nodes = new HashSet<N> ();
71
private Set<N> nodesUm = Collections.unmodifiableSet (nodes);
72
private HashSet<E> edges = new HashSet<E> ();
73
private Set<E> edgesUm = Collections.unmodifiableSet (edges);
74
private HashSet<P> pins = new HashSet<P> ();
75
private Set<P> pinsUm = Collections.unmodifiableSet (pins);
77
private HashMap<N, HashSet<P>> nodePins = new HashMap<N, HashSet<P>> ();
78
private HashMap<P, N> pinNodes = new HashMap<P, N> ();
79
private HashMap<E, P> edgeSourcePins = new HashMap<E, P> ();
80
private HashMap<E, P> edgeTargetPins = new HashMap<E, P> ();
81
private HashMap<P, List<E>> pinInputEdges = new HashMap<P, List<E>> ();
82
private HashMap<P, List<E>> pinOutputEdges = new HashMap<P, List<E>> ();
85
* Creates a graph scene.
87
public GraphPinScene () {
92
* @param node the node to be added; the node must not be null, must not be already in the model, must be unique in the model
93
* (means: there is no other node, edge or pin in the model has is equal to this node)
94
* and must not be a Widget
95
* @return the widget that is created by attachNodeWidget; null if the node is non-visual
97
public final Widget addNode (N node) {
98
assert node != null : "Null parameter";
99
assert ! nodes.contains (node) : "Node (" + node + ") already added";
100
Widget widget = attachNodeWidget (node);
102
nodePins.put (node, new HashSet<P> ());
103
addObject (node, widget);
104
notifyNodeAdded (node, widget);
109
* Removes a node with all pins that are assigned to the node.
110
* @param node the node to be removed; the node must not be null and must be already in the model
112
public final void removeNode (N node) {
113
assert node != null && nodes.contains (node);
114
for (P pin : new HashSet<P> (nodePins.get (node)))
117
nodePins.remove (node);
118
Widget widget = findWidget (node);
119
detachNodeWidget (node, widget);
124
* Removes a node with all pins that are assign to the node and with all edges that are connected to the pins.
125
* @param node the node to be removed; the node must not be null and must be already in the model
127
public final void removeNodeWithEdges (N node) {
128
assert node != null && nodes.contains (node);
129
for (P pin : nodePins.get (node))
130
for (E edge : findPinEdges (pin, true, true))
137
* Returns a collection of all nodes registered in the graph model.
138
* @return the collection of all nodes registered in the graph model
140
public final Collection<N> getNodes () {
146
* @param edge the edge to be added; the edge must not be null, must not be already in the model, must be unique in the model
147
* (means: there is no other node, edge or pin in the model has is equal to this edge)
148
* and must not be a Widget
149
* @return the widget that is created by attachEdgeWidget; null if the edge is non-visual
151
public final Widget addEdge (E edge) {
152
assert edge != null && ! edges.contains (edge);
153
Widget widget = attachEdgeWidget (edge);
155
addObject (edge, widget);
156
notifyEdgeAdded (edge, widget);
161
* Removes an edge and detaches it from its source and target pins.
162
* @param edge the edge to be removed; the edge must not be null and must be already in the model
164
public final void removeEdge (E edge) {
165
assert edge != null && edges.contains (edge);
166
setEdgeSource (edge, null);
167
setEdgeTarget (edge, null);
169
edgeSourcePins.remove (edge);
170
edgeTargetPins.remove (edge);
171
Widget widget = findWidget (edge);
172
detachEdgeWidget (edge, widget);
177
* Returns a collection of all edges registered in the graph model.
178
* @return the collection of all edges registered in the graph model
180
public final Collection<E> getEdges () {
185
* Adds a pin and assigns it to a specified node.
186
* @param node the node where the pin is assigned to
187
* @param pin the pin to be added; the pin must not be null, must not be already in the model, must be unique in the model
188
* (means: there is no other node, edge or pin in the model has is equal to this pin)
189
* and must not be a Widget
190
* @return the widget that is created by attachPinWidget; null if the pin is non-visual
192
public final Widget addPin (N node, P pin) {
193
assert node != null && pin != null && ! pins.contains (pin);
194
Widget widget = attachPinWidget (node, pin);
196
nodePins.get (node).add (pin);
197
pinNodes.put (pin, node);
198
pinInputEdges.put (pin, new ArrayList<E> ());
199
pinOutputEdges.put (pin, new ArrayList<E> ());
200
addObject (pin, widget);
201
notifyPinAdded (node, pin, widget);
206
* Removes an pin and detaches all edges that are connected to it.
207
* @param pin the pin to be removed; the pin must not be null and must be already in the model
209
public final void removePin (P pin) {
210
assert pin != null && pins.contains (pin);
211
for (E edge : findPinEdges (pin, true, false))
212
setEdgeSource (edge, null);
213
for (E edge : findPinEdges (pin, false, true))
214
setEdgeTarget (edge, null);
216
N node = pinNodes.remove (pin);
217
nodePins.get (node).remove (pin);
218
pinInputEdges.remove (pin);
219
pinOutputEdges.remove (pin);
220
Widget widget = findWidget (pin);
221
detachPinWidget (pin, widget);
226
* Removes a pin with all edges that are connected to the pin.
227
* @param pin the pin to be removed; the pin must not be null and must be already in the model
229
public final void removePinWithEdges (P pin) {
230
assert pin != null && pins.contains (pin);
231
for (E edge : findPinEdges (pin, true, true))
238
* Returns a node where the pin assigned to.
240
* @return the node where the pin assigned to.
242
public final N getPinNode (P pin) {
243
return pinNodes.get (pin);
247
* Returns all pins registered in the graph model.
248
* @return the collection of all pins registered in the graph model
250
public final Collection<P> getPins () {
255
* Returns a collection of pins that are assigned to a specified node
256
* @param node the node
257
* @return the collection of pins; null if node does not exist in the scene
259
public final Collection<P> getNodePins (N node) {
262
HashSet<P> ps = nodePins.get (node);
265
return Collections.unmodifiableCollection (ps);
269
* Sets an edge source.
270
* @param edge the edge which source is going to be changed
271
* @param sourcePin the source pin; if null, then the edge source will be detached
273
public final void setEdgeSource (E edge, P sourcePin) {
274
assert edge != null && edges.contains (edge);
275
if (sourcePin != null)
276
assert pins.contains (sourcePin);
277
P oldPin = edgeSourcePins.put (edge, sourcePin);
278
if (GeomUtil.equals (oldPin, sourcePin))
281
pinOutputEdges.get (oldPin).remove (edge);
282
if (sourcePin != null)
283
pinOutputEdges.get (sourcePin).add (edge);
284
attachEdgeSourceAnchor (edge, oldPin, sourcePin);
288
* Sets an edge target.
289
* @param edge the edge which target is going to be changed
290
* @param targetPin the target pin; if null, then the edge target will be detached
292
public final void setEdgeTarget (E edge, P targetPin) {
293
assert edge != null && edges.contains (edge);
294
if (targetPin != null)
295
assert pins.contains (targetPin);
296
P oldPin = edgeTargetPins.put (edge, targetPin);
297
if (GeomUtil.equals (oldPin, targetPin))
300
pinInputEdges.get (oldPin).remove (edge);
301
if (targetPin != null)
302
pinInputEdges.get (targetPin).add (edge);
303
attachEdgeTargetAnchor (edge, oldPin, targetPin);
307
* Returns an edge source.
308
* @param edge the edge
309
* @return the edge source; null, if edge does not have source attached
311
public final P getEdgeSource (E edge) {
312
return edgeSourcePins.get (edge);
316
* Returns an edge target.
317
* @param edge the edge
318
* @return the edge target; null, if edge does not have target attached
320
public final P getEdgeTarget (E edge) {
321
return edgeTargetPins.get (edge);
325
* Returns a collection of edges that are attached to a specified pin.
326
* @param pin the pin which edges connections are searched for
327
* @param allowOutputEdges if true, the output edges are included in the collection; if false, the output edges are not included
328
* @param allowInputEdges if true, the input edges are included in the collection; if false, the input edges are not included
329
* @return the collection of edges
331
public final Collection<E> findPinEdges (P pin, boolean allowOutputEdges, boolean allowInputEdges) {
332
assert isPin (pin) : "Pin " + pin + " is not in the scene";
333
ArrayList<E> list = new ArrayList<E> ();
335
list.addAll (pinInputEdges.get (pin));
336
if (allowOutputEdges)
337
list.addAll (pinOutputEdges.get (pin));
342
* Returns a collection of edges that are between a source and a target pin.
343
* @param sourcePin the source pin
344
* @param targetPin the target pin
345
* @return the collection of edges
347
public final Collection<E> findEdgesBetween (P sourcePin, P targetPin) {
348
assert isPin (sourcePin) : "Source pin " + sourcePin + " is not in the scene";
349
assert isPin (targetPin) : "Target pin " + targetPin + " is not in the scene";
350
HashSet<E> list = new HashSet<E> ();
351
List<E> inputEdges = pinInputEdges.get (targetPin);
352
List<E> outputEdges = pinOutputEdges.get (sourcePin);
353
for (E edge : inputEdges)
354
if (outputEdges.contains (edge))
360
* Checks whether an object is registered as a node in the graph model.
361
* @param object the object; must not be a Widget
362
* @return true, if the object is registered as a node
364
public boolean isNode (Object object) {
365
assert ! (object instanceof Widget) : "Use findObject method for getting an object assigned to a specific Widget"; // NOI18N
366
return nodes.contains (object);
370
* Checks whether an object is registered as an edge in the graph model.
371
* @param object the object; must not be a Widget
372
* @return true, if the object is registered as a edge
374
public boolean isEdge (Object object) {
375
assert ! (object instanceof Widget) : "Use findObject method for getting an object assigned to a specific Widget"; // NOI18N
376
return edges.contains (object);
380
* Checks whether an object is registered as a pin in the graph model.
381
* @param object the object; must not be a Widget
382
* @return true, if the object is registered as a pin
384
public boolean isPin (Object object) {
385
assert ! (object instanceof Widget) : "Use findObject method for getting an object assigned to a specific Widget"; // NOI18N
386
return pins.contains (object);
390
* Called by the addNode method to notify that a node is added into the graph model.
391
* @param node the added node
392
* @param widget the widget created by the attachNodeWidget method as a visual representation of the node
394
protected void notifyNodeAdded (N node, Widget widget) {
398
* Called by the addEdge method to notify that an edge is added into the graph model.
399
* @param edge the added node
400
* @param widget the widget created by the attachEdgeWidget method as a visual representation of the edge
402
protected void notifyEdgeAdded (E edge, Widget widget) {
406
* Called by the addPin method to notify that a pin is added into the graph model.
407
* @param node the node where the pin is assigned to
408
* @param pin the added pin
409
* @param widget the widget created by the attachPinWidget method as a visual representation of the pin
411
protected void notifyPinAdded (N node, P pin, Widget widget) {
415
* Called by the removeNode method to notify that a node is removed from the graph model.
416
* The default implementation removes the node widget from its parent widget.
417
* @param node the removed node
418
* @param widget the removed node widget; null if the node is non-visual
420
protected void detachNodeWidget (N node, Widget widget) {
422
widget.removeFromParent ();
426
* Called by the removeEdge method to notify that an edge is removed from the graph model.
427
* The default implementation removes the edge widget from its parent widget.
428
* @param edge the removed edge
429
* @param widget the removed edge widget; null if the edge is non-visual
431
protected void detachEdgeWidget (E edge, Widget widget) {
433
widget.removeFromParent ();
437
* Called by the removePin method to notify that a pin is removed from the graph model.
438
* The default implementation removes the pin widget from its parent widget.
439
* @param pin the removed pin
440
* @param widget the removed pin widget; null if the pin is non-visual
442
protected void detachPinWidget (P pin, Widget widget) {
444
widget.removeFromParent ();
448
* Called by the addNode method before the node is registered to acquire a widget that is going to represent the node in the scene.
449
* The method is responsible for creating the widget, adding it into the scene and returning it from the method.
450
* @param node the node that is going to be added
451
* @return the widget representing the node; null, if the node is non-visual
453
protected abstract Widget attachNodeWidget (N node);
456
* Called by the addEdge method before the edge is registered to acquire a widget that is going to represent the edge in the scene.
457
* The method is responsible for creating the widget, adding it into the scene and returning it from the method.
458
* @param edge the edge that is going to be added
459
* @return the widget representing the edge; null, if the edge is non-visual
461
protected abstract Widget attachEdgeWidget (E edge);
464
* Called by the addPin method before the pin is registered to acquire a widget that is going to represent the pin in the scene.
465
* The method is responsible for creating the widget, adding it into the scene and returning it from the method.
466
* @param node the node where the pin is assigned to
467
* @param pin the pin that is going to be added
468
* @return the widget representing the pin; null, if the pin is non-visual
470
protected abstract Widget attachPinWidget (N node, P pin);
473
* Called by the setEdgeSource method to notify about the changing the edge source in the graph model.
474
* The method is responsible for attaching a new source pin to the edge in the visual representation.
476
* Usually it is implemented as:
478
* Widget sourcePinWidget = findWidget (sourcePin);
479
* Anchor sourceAnchor = AnchorFactory.createRectangularAnchor (sourcePinWidget)
480
* ConnectionWidget edgeWidget = (ConnectionWidget) findWidget (edge);
481
* edgeWidget.setSourceAnchor (sourceAnchor);
483
* @param edge the edge which source is changed in graph model
484
* @param oldSourcePin the old source pin
485
* @param sourcePin the new source pin
487
protected abstract void attachEdgeSourceAnchor (E edge, P oldSourcePin, P sourcePin);
490
* Called by the setEdgeTarget method to notify about the changing the edge target in the graph model.
491
* The method is responsible for attaching a new target pin to the edge in the visual representation.
493
* Usually it is implemented as:
495
* Widget targetPinWidget = findWidget (targetPin);
496
* Anchor targetAnchor = AnchorFactory.createRectangularAnchor (targetPinWidget)
497
* ConnectionWidget edgeWidget = (ConnectionWidget) findWidget (edge);
498
* edgeWidget.setTargetAnchor (targetAnchor);
500
* @param edge the edge which target is changed in graph model
501
* @param oldTargetPin the old target pin
502
* @param targetPin the new target pin
504
protected abstract void attachEdgeTargetAnchor (E edge, P oldTargetPin, P targetPin);
507
* This class is a particular GraphPinScene where nodes, edges and pins are represented with String class.
509
public static abstract class StringGraph extends GraphPinScene<String, String, String> {