Source code: com/phoenixst/plexus/ObservableGraphWrapper.java
1 /*
2 * $Id: ObservableGraphWrapper.java,v 1.18 2003/11/04 20:01:11 rconner Exp $
3 *
4 * Copyright (C) 1994-2003 by Phoenix Software Technologists,
5 * Inc. and others. All rights reserved.
6 *
7 * THIS PROGRAM AND DOCUMENTATION IS PROVIDED UNDER THE TERMS OF THE
8 * COMMON PUBLIC LICENSE ("AGREEMENT") WHICH ACCOMPANIES IT. ANY
9 * USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES
10 * RECIPIENT'S ACCEPTANCE OF THE AGREEMENT.
11 *
12 * The license text can also be found at
13 * http://opensource.org/licenses/cpl.php
14 */
15
16 package com.phoenixst.plexus;
17
18 import java.util.*;
19
20
21 /**
22 * A wrapper around a {@link Graph} so that it can be watched for
23 * structural changes. <strong>Note:</strong> the {@link
24 * Traverser#remove} method on <code>Traversers</code> created by
25 * this class will only work if the <code>Traversers</code> created
26 * by the wrapped <code>Graph</code> can tolerate having edges
27 * removed while the traversal is in progress.
28 *
29 * @version $Revision: 1.18 $
30 * @author Ray A. Conner
31 *
32 * @since 1.0
33 */
34 public class ObservableGraphWrapper extends GraphWrapper
35 implements ObservableGraph
36 {
37
38 private transient List listeners;
39 private transient GraphListener[] listenerArray;
40
41
42 ////////////////////////////////////////
43 // Constructor
44 ////////////////////////////////////////
45
46
47 /**
48 * Creates a new <code>ObservableGraphWrapper</code>.
49 */
50 public ObservableGraphWrapper( Graph g )
51 {
52 super( g );
53 }
54
55
56
57 ////////////////////////////////////////
58 // Protected wrap/unwrap methods
59 ////////////////////////////////////////
60
61
62 protected Iterator wrapNodeIterator( Iterator nodeIter )
63 {
64 return new ObservableNodeIteratorWrapper( nodeIter );
65 }
66
67
68 protected Iterator wrapEdgeIterator( Iterator edgeIter )
69 {
70 return new ObservableEdgeIteratorWrapper( edgeIter );
71 }
72
73
74 protected Traverser wrapTraverser( Traverser traverser )
75 {
76 return new ObservableTraverserWrapper( traverser );
77 }
78
79
80 ////////////////////////////////////////
81 // Node removal helper method
82 ////////////////////////////////////////
83
84
85 protected void removeIncidentEdges( Object node )
86 {
87 Traverser t = traverser( node );
88 while( t.hasNext() ) {
89 t.next();
90 t.removeEdge();
91 }
92 }
93
94
95 ////////////////////////////////////////
96 // Graph methods
97 ////////////////////////////////////////
98
99
100 public boolean addNode( Object node )
101 {
102 if( super.addNode( node ) ) {
103 fireNodeAdded( node );
104 return true;
105 } else {
106 return false;
107 }
108 }
109
110
111 public boolean removeNode( Object node )
112 {
113 if( !super.containsNode( node ) ) {
114 return false;
115 }
116 removeIncidentEdges( node );
117 // Even though we shouldn't have to, check the return value
118 // from removeNode() anyway, since someone else may have
119 // nuked it and we don't want to send out duplicate events.
120 if( super.removeNode( node ) ) {
121 fireNodeRemoved( node );
122 }
123 return true;
124 }
125
126
127 public Edge addEdge( Object object, Object tail, Object head )
128 {
129 Edge edge = super.addEdge( object, tail, head );
130 if( edge != null ) {
131 fireEdgeAdded( edge );
132 }
133 return edge;
134 }
135
136
137 public boolean removeEdge( Edge edge )
138 {
139 if( super.removeEdge( edge ) ) {
140 fireEdgeRemoved( edge );
141 return true;
142 } else {
143 return false;
144 }
145 }
146
147
148 public void clear()
149 {
150 Iterator nodeIter = nodeIterator();
151 while( nodeIter.hasNext() ) {
152 nodeIter.next();
153 nodeIter.remove();
154 }
155 }
156
157
158 ////////////////////////////////////////
159 // ObservableGraph methods
160 ////////////////////////////////////////
161
162
163 public synchronized void addGraphListener( GraphListener listener )
164 {
165 if( listeners == null ) {
166 listeners = new ArrayList();
167 }
168 listeners.add( listener );
169 listenerArray = null;
170 }
171
172
173 public synchronized void removeGraphListener( GraphListener listener )
174 {
175 if( listeners == null ) {
176 return;
177 }
178 listeners.remove( listener );
179 listenerArray = null;
180 }
181
182
183 ////////////////////////////////////////
184 // Event firing methods
185 ////////////////////////////////////////
186
187
188 /**
189 *
190 */
191 protected final GraphListener[] createListenerArray()
192 {
193 if( listeners == null || listeners.isEmpty() ) {
194 return null;
195 }
196 if( listenerArray == null ) {
197 listenerArray = new GraphListener[ listeners.size() ];
198 listenerArray = (GraphListener[]) listeners.toArray( listenerArray );
199 }
200 return listenerArray;
201 }
202
203
204 /**
205 *
206 */
207 protected synchronized void fireNodeAdded( Object node )
208 {
209 GraphListener[] array = createListenerArray();
210 if( array != null ) {
211 GraphEvent event = new GraphEvent( this, node );
212 for( int i = 0; i < array.length; i++ ) {
213 array[i].nodeAdded( event );
214 }
215 }
216 }
217
218
219 /**
220 *
221 */
222 protected synchronized void fireNodeRemoved( Object node )
223 {
224 GraphListener[] array = createListenerArray();
225 if( array != null ) {
226 GraphEvent event = new GraphEvent( this, node );
227 for( int i = 0; i < array.length; i++ ) {
228 array[i].nodeRemoved( event );
229 }
230 }
231 }
232
233
234 /**
235 *
236 */
237 protected synchronized void fireEdgeAdded( Edge edge )
238 {
239 GraphListener[] array = createListenerArray();
240 if( array != null ) {
241 GraphEvent event = new GraphEvent( this, edge );
242 for( int i = 0; i < array.length; i++ ) {
243 array[i].edgeAdded( event );
244 }
245 }
246 }
247
248
249 /**
250 *
251 */
252 protected synchronized void fireEdgeRemoved( Edge edge )
253 {
254 GraphListener[] array = createListenerArray();
255 if( array != null ) {
256 GraphEvent event = new GraphEvent( this, edge );
257 for( int i = 0; i < array.length; i++ ) {
258 array[i].edgeRemoved( event );
259 }
260 }
261 }
262
263
264 ////////////////////////////////////////
265 // Private iterator wrapper classes
266 ////////////////////////////////////////
267
268
269 /**
270 *
271 */
272 private class ObservableNodeIteratorWrapper
273 extends NodeIteratorWrapper
274 {
275 private Object currentNode;
276 private boolean isCurrentValid = false;
277
278 private ObservableNodeIteratorWrapper( Iterator i )
279 {
280 super( i );
281 }
282
283 public Object next()
284 {
285 currentNode = super.next();
286 isCurrentValid = true;
287 return currentNode;
288 }
289
290 public void remove()
291 {
292 if( !isCurrentValid ) {
293 throw new IllegalStateException();
294 }
295 removeIncidentEdges( currentNode );
296 super.remove();
297 fireNodeRemoved( currentNode );
298 isCurrentValid = false;
299 }
300 }
301
302
303 /**
304 *
305 */
306 private class ObservableEdgeIteratorWrapper
307 extends EdgeIteratorWrapper
308 {
309 private Edge currentEdge;
310
311 private ObservableEdgeIteratorWrapper( Iterator i )
312 {
313 super( i );
314 }
315
316 public Object next()
317 {
318 currentEdge = (Edge) super.next();
319 return currentEdge;
320 }
321
322 public void remove()
323 {
324 super.remove();
325 fireEdgeRemoved( currentEdge );
326 }
327 }
328
329
330 /**
331 *
332 */
333 private class ObservableTraverserWrapper
334 extends TraverserWrapper
335 {
336 protected Object currentNode;
337 private boolean isCurrentValid;
338
339 protected ObservableTraverserWrapper( Traverser i )
340 {
341 super( i );
342 isCurrentValid = false;
343 }
344
345 public Object next()
346 {
347 currentNode = super.next();
348 isCurrentValid = true;
349 return currentNode;
350 }
351
352 public void remove()
353 {
354 if( !isCurrentValid ) {
355 throw new IllegalStateException();
356 }
357 removeIncidentEdges( currentNode );
358 super.remove();
359 fireNodeRemoved( currentNode );
360 isCurrentValid = false;
361 }
362
363 public void removeEdge()
364 {
365 Edge edge = super.getEdge();
366 super.removeEdge();
367 fireEdgeRemoved( edge );
368 }
369 }
370
371 }