]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/impl/AbstractDiagram.java
7b2ae5f986a772bda7e1edbc4b0c8b73fab53034
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / diagram / impl / AbstractDiagram.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.g2d.diagram.impl;
13
14 import java.util.Collections;
15 import java.util.Comparator;
16 import java.util.List;
17 import java.util.Map;
18
19 import org.eclipse.collections.api.list.ImmutableList;
20 import org.eclipse.collections.api.list.MutableList;
21 import org.eclipse.collections.api.set.MutableSet;
22 import org.eclipse.collections.impl.factory.Lists;
23 import org.eclipse.collections.impl.factory.Sets;
24 import org.simantics.g2d.diagram.DiagramClass;
25 import org.simantics.g2d.diagram.IDiagram;
26 import org.simantics.g2d.diagram.handler.DiagramAdapter;
27 import org.simantics.g2d.diagram.handler.ElementListener;
28 import org.simantics.g2d.diagram.handler.LifeCycle;
29 import org.simantics.g2d.element.IElement;
30 import org.simantics.utils.datastructures.ListenerList;
31 import org.simantics.utils.datastructures.hints.IHintContext;
32 import org.simantics.utils.datastructures.hints.IHintListener;
33 import org.simantics.utils.threads.IThreadWorkQueue;
34
35 /**
36  * @author Toni Kalajainen
37  */
38 public class AbstractDiagram implements IDiagram {
39
40     protected ListenerList<CompositionVetoListener> compositionVetoListeners;
41     protected ListenerList<CompositionListener>     compositionListeners;
42
43     protected MutableList<IElement>                 list             = Lists.mutable.empty();
44     protected MutableSet<IElement>                  elements         = Sets.mutable.empty();
45     protected volatile ImmutableList<IElement>      snapshot         = null;
46     private DiagramClass                            clazz;
47     protected IHintContext                          hintCtx;
48
49     public AbstractDiagram(DiagramClass clazz, IHintContext hintCtx)
50     {
51         if (clazz == null)
52             throw new NullPointerException("null clazz");
53         this.clazz = clazz;
54         this.hintCtx = hintCtx;
55     }
56
57     public void destroy()
58     {
59         List<IElement> ss = getElements();
60
61         dispose();
62
63         // Fire destroy elements
64         for (IElement e : ss)
65             e.destroy();
66
67         // Fire destroy diagram
68         for (org.simantics.g2d.diagram.handler.LifeCycle lc : clazz.getItemsByClass(org.simantics.g2d.diagram.handler.LifeCycle.class))
69             lc.onDiagramDestroyed(this);
70     }
71
72     public void dispose()
73     {
74         List<IElement> ss = getElements();
75         // Fire deactiavate elements
76         for (IElement e : ss)
77             for (org.simantics.g2d.element.handler.LifeCycle lc : e.getElementClass().getItemsByClass(org.simantics.g2d.element.handler.LifeCycle.class))
78                 lc.onElementDeactivated(this, e);
79
80         // Fire deactivate diagram
81         fireDeactivated();
82
83         // Dispose all elements to ensure their hints are freed up.
84         for (IElement e : ss) {
85             e.addedToDiagram(null);
86             e.dispose();
87         }
88
89         list.clear();
90         snapshot = null;
91
92         hintCtx.clearWithoutNotification();
93     }
94
95     public DiagramClass getDiagramClass()
96     {
97         return clazz;
98     }
99
100     public synchronized void addCompositionListener(CompositionListener listener) {
101         if (compositionListeners ==null)
102             compositionListeners = new ListenerList<CompositionListener>(CompositionListener.class);
103         compositionListeners.add(listener);
104     }
105     public synchronized void removeCompositionListener(CompositionListener listener) {
106         if (compositionListeners == null)
107             return;
108         compositionListeners.remove(listener);
109         if (compositionListeners.isEmpty())
110             compositionListeners = null;
111     }
112
113     public synchronized void addCompositionVetoListener(CompositionVetoListener listener) {
114         if (compositionVetoListeners ==null)
115             compositionVetoListeners = new ListenerList<CompositionVetoListener>(CompositionVetoListener.class);
116         compositionVetoListeners.add(listener);
117     }
118     public synchronized void removeCompositionVetoListener(CompositionVetoListener listener) {
119         if (compositionVetoListeners == null)
120             return;
121         compositionVetoListeners.remove(listener);
122         if (compositionVetoListeners.isEmpty())
123             compositionVetoListeners = null;
124     }
125
126     /**
127      * Adds a new element. The element will not be initialized
128      * @param clazz element class
129      * @return element of class
130      */
131     public synchronized void addElement(IElement e) {
132         assert(clazz!=null);
133
134         // Give the possibility for listeners to veto an element addition
135         if (!fireBeforeElementAdded(e)) {
136             System.out.println("Element addition VETOED for " + e);
137             return;
138         }
139
140         snapshot = null;
141         list.add(e);
142         elements.add(e);
143
144         e.addedToDiagram(this);
145
146         for (org.simantics.g2d.element.handler.LifeCycle lc : e.getElementClass().getItemsByClass(org.simantics.g2d.element.handler.LifeCycle.class))
147             lc.onElementActivated(this, e);
148
149         for (ElementListener el : clazz.getItemsByClass(ElementListener.class))
150             el.onElementAdded(this, e);
151
152         fireElementAdded(e);
153     }
154
155     protected void assertHasElement(IElement e)
156     {
157         assert(elements.contains(e));
158     }
159
160     @Override
161     public boolean containsElement(IElement element) {
162         return elements.contains(element);
163     }
164
165     /**
166      * Remove element from the diagram
167      * @param element element to remove
168      */
169     public synchronized void removeElement(IElement e) {
170
171 //        new Exception(e.toString()).printStackTrace();
172
173         // Give the possibility for listeners to veto an element removal.
174         if (!fireBeforeElementRemoved(e)) {
175             System.out.println("Element removal VETOED for " + e);
176             return;
177         }
178
179         //System.out.println("[" + this + "] removed element " + e + "");
180
181         boolean removed = elements.remove(e);
182         assert(removed);
183         list.remove(e);
184         snapshot = null;
185
186         e.addedToDiagram(null);
187
188         for (ElementListener el : clazz.getItemsByClass(ElementListener.class))
189             el.onElementRemoved(this, e);
190
191         for (org.simantics.g2d.element.handler.LifeCycle lc : e.getElementClass().getItemsByClass(org.simantics.g2d.element.handler.LifeCycle.class))
192             lc.onElementDeactivated(this, e);
193
194         fireElementRemoved(e);
195     }
196
197     /**
198      * @param e the element to be added
199      * @return <code>true</code> if the addition was allowed by all listeners or
200      *         <code>false</code> if the addition was vetoed
201      */
202     protected boolean fireBeforeElementAdded(IElement e) {
203         if (compositionVetoListeners==null) return true;
204         for (CompositionVetoListener l : compositionVetoListeners.getListeners())
205             if (!l.beforeElementAdded(this, e))
206                 return false;
207         return true;
208     }
209
210     /**
211      * @param e the element to be removed
212      * @return <code>true</code> if the removal was allowed by all listeners or
213      *         <code>false</code> if the removal was vetoed
214      */
215     protected boolean fireBeforeElementRemoved(IElement e) {
216         if (compositionVetoListeners==null) return true;
217         for (CompositionVetoListener l : compositionVetoListeners.getListeners())
218             if (!l.beforeElementRemoved(this, e))
219                 return false;
220         return true;
221     }
222     protected void fireElementAdded(IElement e) {
223         if (compositionListeners==null) return;
224         for (CompositionListener l : compositionListeners.getListeners())
225             l.onElementAdded(this, e);
226     }
227     protected void fireElementRemoved(IElement e) {
228         if (compositionListeners==null) return;
229         for (CompositionListener l : compositionListeners.getListeners())
230             l.onElementRemoved(this, e);
231     }
232
233     @Override
234     public List<IElement> getSnapshot() {
235         ImmutableList<IElement> snap = snapshot;
236         if (snap != null)
237             return snap.castToList();
238         synchronized (this) {
239             snap = snapshot;
240             if (snap == null)
241                 snapshot = snap = list.toImmutable();
242         }
243         return snap.castToList();
244     }
245
246     protected static void fireDestroyed(IDiagram e)
247     {
248         for (LifeCycle lc : e.getDiagramClass().getItemsByClass(LifeCycle.class))
249             lc.onDiagramDestroyed(e);
250     }
251
252     protected static void fireDeactivated(IDiagram e)
253     {
254         for (LifeCycle lc : e.getDiagramClass().getItemsByClass(LifeCycle.class))
255             lc.onDiagramDisposed(e);
256     }
257
258     protected static void fireCreated(IDiagram e)
259     {
260         for (LifeCycle lc : e.getDiagramClass().getItemsByClass(LifeCycle.class))
261             lc.onDiagramCreated(e);
262     }
263
264     protected static void fireLoaded(IDiagram e)
265     {
266         for (LifeCycle lc : e.getDiagramClass().getItemsByClass(LifeCycle.class))
267             lc.onDiagramLoaded(e, e.getElements());
268     }
269
270     protected void fireCreated()
271     {
272         fireCreated(this);
273     }
274
275     protected void fireLoaded()
276     {
277         fireLoaded(this);
278     }
279
280     protected void fireDestroyed()
281     {
282         fireDestroyed(this);
283     }
284
285     protected void fireDeactivated()
286     {
287         fireDeactivated(this);
288     }
289
290     @Override
291     public void clearWithoutNotification() {
292         hintCtx.clearWithoutNotification();
293     }
294
295     @Override
296     public <E> E removeHint(Key key) {
297         return hintCtx.removeHint(key);
298     }
299
300     @Override
301     public void setHint(Key key, Object value) {
302         hintCtx.setHint(key, value);
303     }
304
305     @Override
306     public void setHints(Map<Key, Object> hints) {
307         hintCtx.setHints(hints);
308     }
309
310     @Override
311     public void addHintListener(IHintListener listener) {
312         hintCtx.addHintListener(listener);
313     }
314
315     @Override
316     public void addHintListener(IThreadWorkQueue threadAccess,
317             IHintListener listener) {
318         hintCtx.addHintListener(threadAccess, listener);
319     }
320
321     @Override
322     public void addKeyHintListener(Key key, IHintListener listener) {
323         hintCtx.addKeyHintListener(key, listener);
324     }
325
326     @Override
327     public void addKeyHintListener(IThreadWorkQueue threadAccess, Key key,
328             IHintListener listener) {
329         hintCtx.addKeyHintListener(threadAccess, key, listener);
330     }
331
332     @Override
333     public boolean containsHint(Key key) {
334         return hintCtx.containsHint(key);
335     }
336
337     @Override
338     public <E> E getHint(Key key) {
339         return hintCtx.getHint(key);
340     }
341
342     @Override
343     public Map<Key, Object> getHints() {
344         return hintCtx.getHints();
345     }
346
347     @Override
348     public Map<Key, Object> getHintsUnsafe() {
349         return hintCtx.getHintsUnsafe();
350     }
351
352     @Override
353     public <E extends Key> Map<E, Object> getHintsOfClass(Class<E> clazz) {
354         return hintCtx.getHintsOfClass(clazz);
355     }
356
357     @Override
358     public void removeHintListener(IHintListener listener) {
359         hintCtx.removeHintListener(listener);
360     }
361
362     @Override
363     public void removeHintListener(IThreadWorkQueue threadAccess,
364             IHintListener listener) {
365         hintCtx.removeHintListener(threadAccess, listener);
366     }
367
368     @Override
369     public void removeKeyHintListener(Key key, IHintListener listener) {
370         hintCtx.removeKeyHintListener(key, listener);
371     }
372
373     @Override
374     public void removeKeyHintListener(IThreadWorkQueue threadAccess, Key key,
375             IHintListener listener) {
376         hintCtx.removeKeyHintListener(threadAccess, key, listener);
377     }
378
379     @Override
380     public boolean bringToTop(IElement e) {
381         assertHasElement(e);
382         if (list.get(list.size()-1)==e) return false;
383         list.add( e );
384         list.remove( e );
385         snapshot = null;
386         return true;
387     }
388
389     @Override
390     public boolean bringUp(IElement e) {
391         assertHasElement(e);
392         int i = list.indexOf(e);
393         if (i==list.size()-1) return false;
394         int j = i+1;
395         IElement upper = list.get(j);
396         list.set(j, e);
397         list.set(i, upper);
398         snapshot = null;
399         return true;
400     }
401
402     @Override
403     public boolean sendDown(IElement e) {
404         assertHasElement(e);
405         int i = list.indexOf(e);
406         if (i==0) return false;
407         int j = i-1;
408         IElement lower = list.get(j);
409         list.set(j, e);
410         list.set(i, lower);
411         snapshot = null;
412         return true;
413     }
414
415     @Override
416     public boolean sendToBottom(IElement e) {
417         assertHasElement(e);
418         if (list.get(0)==e) return false;
419         list.remove(e);
420         list.add(0, e);
421         snapshot = null;
422         return true;
423     }
424
425     @Override
426     public boolean moveTo(IElement e, int position) {
427         assertHasElement(e);
428         int indexOf = list.indexOf(e);
429         if (indexOf==position) return false;
430         list.remove(indexOf);
431         list.add(position, e);
432         snapshot = null;
433         return true;
434     }
435
436     @Override
437     public List<IElement> getElements() {
438         return getSnapshot();
439     }
440
441     @Override
442     public void sort(Comparator<IElement> comparator) {
443         Collections.sort(list, comparator);
444     }
445
446     @SuppressWarnings("rawtypes")
447     public Object getAdapter(Class adapter) {
448         for (DiagramAdapter ea : clazz.getItemsByClass(DiagramAdapter.class)) {
449             Object result = ea.getAdapter(this, adapter);
450             if (result != null)
451                 return result;
452         }
453         return null;
454     }
455
456 }