/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.g2d.diagram.impl; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import org.eclipse.collections.api.list.ImmutableList; import org.eclipse.collections.api.list.MutableList; import org.eclipse.collections.api.set.MutableSet; import org.eclipse.collections.impl.factory.Lists; import org.eclipse.collections.impl.factory.Sets; import org.simantics.g2d.diagram.DiagramClass; import org.simantics.g2d.diagram.IDiagram; import org.simantics.g2d.diagram.handler.DiagramAdapter; import org.simantics.g2d.diagram.handler.ElementListener; import org.simantics.g2d.diagram.handler.LifeCycle; import org.simantics.g2d.element.IElement; import org.simantics.utils.datastructures.ListenerList; import org.simantics.utils.datastructures.hints.IHintContext; import org.simantics.utils.datastructures.hints.IHintListener; import org.simantics.utils.threads.IThreadWorkQueue; /** * @author Toni Kalajainen */ public class AbstractDiagram implements IDiagram { protected ListenerList compositionVetoListeners; protected ListenerList compositionListeners; protected MutableList list = Lists.mutable.empty(); protected MutableSet elements = Sets.mutable.empty(); protected volatile ImmutableList snapshot = null; private DiagramClass clazz; protected IHintContext hintCtx; public AbstractDiagram(DiagramClass clazz, IHintContext hintCtx) { if (clazz == null) throw new NullPointerException("null clazz"); this.clazz = clazz; this.hintCtx = hintCtx; } public void destroy() { List ss = getElements(); dispose(); // Fire destroy elements for (IElement e : ss) e.destroy(); // Fire destroy diagram for (org.simantics.g2d.diagram.handler.LifeCycle lc : clazz.getItemsByClass(org.simantics.g2d.diagram.handler.LifeCycle.class)) lc.onDiagramDestroyed(this); } public void dispose() { List ss = getElements(); // Fire deactiavate elements for (IElement e : ss) for (org.simantics.g2d.element.handler.LifeCycle lc : e.getElementClass().getItemsByClass(org.simantics.g2d.element.handler.LifeCycle.class)) lc.onElementDeactivated(this, e); // Fire deactivate diagram fireDeactivated(); // Dispose all elements to ensure their hints are freed up. for (IElement e : ss) { e.addedToDiagram(null); e.dispose(); } list.clear(); snapshot = null; hintCtx.clearWithoutNotification(); } public DiagramClass getDiagramClass() { return clazz; } public synchronized void addCompositionListener(CompositionListener listener) { if (compositionListeners ==null) compositionListeners = new ListenerList(CompositionListener.class); compositionListeners.add(listener); } public synchronized void removeCompositionListener(CompositionListener listener) { if (compositionListeners == null) return; compositionListeners.remove(listener); if (compositionListeners.isEmpty()) compositionListeners = null; } public synchronized void addCompositionVetoListener(CompositionVetoListener listener) { if (compositionVetoListeners ==null) compositionVetoListeners = new ListenerList(CompositionVetoListener.class); compositionVetoListeners.add(listener); } public synchronized void removeCompositionVetoListener(CompositionVetoListener listener) { if (compositionVetoListeners == null) return; compositionVetoListeners.remove(listener); if (compositionVetoListeners.isEmpty()) compositionVetoListeners = null; } /** * Adds a new element. The element will not be initialized * @param clazz element class * @return element of class */ public synchronized void addElement(IElement e) { assert(clazz!=null); // Give the possibility for listeners to veto an element addition if (!fireBeforeElementAdded(e)) { System.out.println("Element addition VETOED for " + e); return; } snapshot = null; list.add(e); elements.add(e); e.addedToDiagram(this); for (org.simantics.g2d.element.handler.LifeCycle lc : e.getElementClass().getItemsByClass(org.simantics.g2d.element.handler.LifeCycle.class)) lc.onElementActivated(this, e); for (ElementListener el : clazz.getItemsByClass(ElementListener.class)) el.onElementAdded(this, e); fireElementAdded(e); } protected void assertHasElement(IElement e) { assert(elements.contains(e)); } @Override public boolean containsElement(IElement element) { return elements.contains(element); } /** * Remove element from the diagram * @param element element to remove */ public synchronized void removeElement(IElement e) { // new Exception(e.toString()).printStackTrace(); // Give the possibility for listeners to veto an element removal. if (!fireBeforeElementRemoved(e)) { System.out.println("Element removal VETOED for " + e); return; } //System.out.println("[" + this + "] removed element " + e + ""); boolean removed = elements.remove(e); assert(removed); list.remove(e); snapshot = null; e.addedToDiagram(null); for (ElementListener el : clazz.getItemsByClass(ElementListener.class)) el.onElementRemoved(this, e); for (org.simantics.g2d.element.handler.LifeCycle lc : e.getElementClass().getItemsByClass(org.simantics.g2d.element.handler.LifeCycle.class)) lc.onElementDeactivated(this, e); fireElementRemoved(e); } /** * @param e the element to be added * @return true if the addition was allowed by all listeners or * false if the addition was vetoed */ protected boolean fireBeforeElementAdded(IElement e) { if (compositionVetoListeners==null) return true; for (CompositionVetoListener l : compositionVetoListeners.getListeners()) if (!l.beforeElementAdded(this, e)) return false; return true; } /** * @param e the element to be removed * @return true if the removal was allowed by all listeners or * false if the removal was vetoed */ protected boolean fireBeforeElementRemoved(IElement e) { if (compositionVetoListeners==null) return true; for (CompositionVetoListener l : compositionVetoListeners.getListeners()) if (!l.beforeElementRemoved(this, e)) return false; return true; } protected void fireElementAdded(IElement e) { if (compositionListeners==null) return; for (CompositionListener l : compositionListeners.getListeners()) l.onElementAdded(this, e); } protected void fireElementRemoved(IElement e) { if (compositionListeners==null) return; for (CompositionListener l : compositionListeners.getListeners()) l.onElementRemoved(this, e); } @Override public List getSnapshot() { ImmutableList snap = snapshot; if (snap != null) return snap.castToList(); synchronized (this) { snap = snapshot; if (snap == null) snapshot = snap = list.toImmutable(); } return snap.castToList(); } protected static void fireDestroyed(IDiagram e) { for (LifeCycle lc : e.getDiagramClass().getItemsByClass(LifeCycle.class)) lc.onDiagramDestroyed(e); } protected static void fireDeactivated(IDiagram e) { for (LifeCycle lc : e.getDiagramClass().getItemsByClass(LifeCycle.class)) lc.onDiagramDisposed(e); } protected static void fireCreated(IDiagram e) { for (LifeCycle lc : e.getDiagramClass().getItemsByClass(LifeCycle.class)) lc.onDiagramCreated(e); } protected static void fireLoaded(IDiagram e) { for (LifeCycle lc : e.getDiagramClass().getItemsByClass(LifeCycle.class)) lc.onDiagramLoaded(e, e.getElements()); } protected void fireCreated() { fireCreated(this); } protected void fireLoaded() { fireLoaded(this); } protected void fireDestroyed() { fireDestroyed(this); } protected void fireDeactivated() { fireDeactivated(this); } @Override public void clearWithoutNotification() { hintCtx.clearWithoutNotification(); } @Override public E removeHint(Key key) { return hintCtx.removeHint(key); } @Override public void setHint(Key key, Object value) { hintCtx.setHint(key, value); } @Override public void setHints(Map hints) { hintCtx.setHints(hints); } @Override public void addHintListener(IHintListener listener) { hintCtx.addHintListener(listener); } @Override public void addHintListener(IThreadWorkQueue threadAccess, IHintListener listener) { hintCtx.addHintListener(threadAccess, listener); } @Override public void addKeyHintListener(Key key, IHintListener listener) { hintCtx.addKeyHintListener(key, listener); } @Override public void addKeyHintListener(IThreadWorkQueue threadAccess, Key key, IHintListener listener) { hintCtx.addKeyHintListener(threadAccess, key, listener); } @Override public boolean containsHint(Key key) { return hintCtx.containsHint(key); } @Override public E getHint(Key key) { return hintCtx.getHint(key); } @Override public Map getHints() { return hintCtx.getHints(); } @Override public Map getHintsUnsafe() { return hintCtx.getHintsUnsafe(); } @Override public Map getHintsOfClass(Class clazz) { return hintCtx.getHintsOfClass(clazz); } @Override public void removeHintListener(IHintListener listener) { hintCtx.removeHintListener(listener); } @Override public void removeHintListener(IThreadWorkQueue threadAccess, IHintListener listener) { hintCtx.removeHintListener(threadAccess, listener); } @Override public void removeKeyHintListener(Key key, IHintListener listener) { hintCtx.removeKeyHintListener(key, listener); } @Override public void removeKeyHintListener(IThreadWorkQueue threadAccess, Key key, IHintListener listener) { hintCtx.removeKeyHintListener(threadAccess, key, listener); } @Override public boolean bringToTop(IElement e) { assertHasElement(e); if (list.get(list.size()-1)==e) return false; list.add( e ); list.remove( e ); snapshot = null; return true; } @Override public boolean bringUp(IElement e) { assertHasElement(e); int i = list.indexOf(e); if (i==list.size()-1) return false; int j = i+1; IElement upper = list.get(j); list.set(j, e); list.set(i, upper); snapshot = null; return true; } @Override public boolean sendDown(IElement e) { assertHasElement(e); int i = list.indexOf(e); if (i==0) return false; int j = i-1; IElement lower = list.get(j); list.set(j, e); list.set(i, lower); snapshot = null; return true; } @Override public boolean sendToBottom(IElement e) { assertHasElement(e); if (list.get(0)==e) return false; list.remove(e); list.add(0, e); snapshot = null; return true; } @Override public boolean moveTo(IElement e, int position) { assertHasElement(e); int indexOf = list.indexOf(e); if (indexOf==position) return false; list.remove(indexOf); list.add(position, e); snapshot = null; return true; } @Override public List getElements() { return getSnapshot(); } @Override public void sort(Comparator comparator) { Collections.sort(list, comparator); } @SuppressWarnings("rawtypes") public Object getAdapter(Class adapter) { for (DiagramAdapter ea : clazz.getItemsByClass(DiagramAdapter.class)) { Object result = ea.getAdapter(this, adapter); if (result != null) return result; } return null; } }