]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.g2d/src/org/simantics/g2d/canvas/impl/AbstractCanvasParticipant.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / canvas / impl / AbstractCanvasParticipant.java
index a7950d26d6cf268939661b4120abf2b37917ff0a..cc298f3aa20e1f7ad05d6d47ab213a63a0e9183d 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- *     VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.g2d.canvas.impl;\r
-\r
-import java.lang.reflect.Field;\r
-import java.util.HashSet;\r
-import java.util.Set;\r
-\r
-import org.simantics.g2d.canvas.ICanvasContext;\r
-import org.simantics.g2d.canvas.ICanvasParticipant;\r
-import org.simantics.g2d.canvas.IContentContext;\r
-import org.simantics.g2d.canvas.impl.DependencyReflection.ReferenceDefinition;\r
-import org.simantics.g2d.canvas.impl.DependencyReflection.ReferenceType;\r
-import org.simantics.g2d.canvas.impl.HintReflection.HintListenerDefinition;\r
-import org.simantics.g2d.canvas.impl.SGNodeReflection.CanvasSGNodeDefinition;\r
-import org.simantics.scenegraph.g2d.events.EventHandlerReflection;\r
-import org.simantics.scenegraph.g2d.events.IEventHandler;\r
-import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandlerDefinition;\r
-import org.simantics.utils.datastructures.context.IContext;\r
-import org.simantics.utils.datastructures.context.IContextListener;\r
-import org.simantics.utils.datastructures.hints.HintContext;\r
-import org.simantics.utils.datastructures.hints.IHintContext;\r
-import org.simantics.utils.datastructures.hints.IHintStack;\r
-import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
-import org.simantics.utils.datastructures.prioritystack.IPriorityStack;\r
-import org.simantics.utils.threads.IThreadWorkQueue;\r
-import org.simantics.utils.threads.ThreadUtils;\r
-\r
-\r
-/**\r
- * AbstractCanvasParticipant is base implementation for canvas participants.\r
- * There is an assertion that states AbstractCanvasParticipant can be added\r
- * only once to a canvas.\r
- * \r
- * <p>\r
- * There is convenience mechanism for adding painter and event handler\r
- * listeners. Subclasses create listeners with usage of Painter and\r
- * EventHandler annotations. Listeners are automatically added and\r
- * removed to/from canvas context.\r
- * </p>\r
- * \r
- * <p>\r
- * Example:\r
- * </p>\r
- * <blockquote><pre>\r
- *   @EventHandler(priority=200)\r
- *   public boolean handleEvent(Event e) {\r
- *       return false;\r
- *   }\r
- * \r
- *   @EventHandler(priority=400)\r
- *   public boolean handleMousePressEvent(MouseButtonPressedEvent e) {\r
- *       return false;\r
- *   }\r
- * \r
- *   IG2DNode node;\r
- * \r
- *   @SGInit\r
- *   public void initSG(G2DParentNode parent) {\r
- *       // Insert a node into the scene graph rendered by the canvas\r
- *       // this participant is attached to and initialize it to render\r
- *       // something. Later on, to modify what the node will render, just\r
- *       // update the node's attributes.\r
- *       node = parent.addNode("myNode", MyNode.class);\r
- *       node.setZIndex(Integer.MIN_VALUE);\r
- *   }\r
- * \r
- *   @SGCleanup\r
- *   public void cleanup() {\r
- *       // Remove our node from the scene graph where it belonged.\r
- *       // The node must not be used after this anymore.\r
- *       if (node != null) {\r
- *           node.remove();\r
- *           node = null;\r
- *       }\r
- *   }\r
- * </pre></blockquote>\r
- * \r
- * <p>\r
- * Local fields are automatically assigned with ICanvasParticipant instances if they\r
- * are annotated with either <code>@Dependency</code> or <code>@Reference</code> annotation tag.\r
- * <code>@Depedency</code> is a requirement, <code>@Reference</code> is optional.\r
- * \r
- * assertDependencies() verifies that dependencies are satisfied.\r
- * Local depsSatisfied field is true when dependencies are satisfied.\r
- * \r
- * <p>\r
- * Example:\r
- * </p>\r
- * <blockquote><pre>\r
- *   class MyParticipant implements ICanvasParticipant {\r
- *       @Reference MouseMonitor mouseMonitor;\r
- *       @Dependency TimeParticipant timeParticipant;\r
- *\r
- *       @Painter(priority=100)\r
- *       public void paint(GraphicsContext gc) {\r
- *           assertDependencies(); // timeParticipant != null\r
- * \r
- *           timeParticipant.doSomething();\r
- * \r
- *           if (mouseMonitor!=null) doSomethingElse();\r
- *       }\r
- *   }</pre></blockquote>\r
- * \r
- * <p>\r
- * Hint listener annotation.\r
- * </p>\r
- * \r
- * <p>\r
- * Example:\r
- * </p>\r
- * <blockquote><pre>\r
- *   @HintListener(Class=Hints.class, Field="KEY_CANVAS_TRANSFORM")\r
- *   public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {\r
- *       ...\r
- *   }\r
- *   @HintListener(Class=Hints.class, Field="KEY_CANVAS_TRANSFORM")\r
- *   public void hintRemoved(IHintObservable sender, Key key, Object oldValue) {\r
- *       ...\r
- *   }\r
- * </pre></blockquote>\r
- * \r
- * @author Toni Kalajainen\r
- */\r
-public abstract class AbstractCanvasParticipant implements ICanvasParticipant {\r
-\r
-    /** The interactor/canvas context */\r
-    private ICanvasContext context;\r
-\r
-    /** The thread used in the context */\r
-    private IThreadWorkQueue thread;\r
-\r
-    /** the local hint context */\r
-    protected IHintContext localHintCtx = null;\r
-    protected int localPriority = 0;\r
-\r
-    /** wrapped local hint context. reads from hint stack */\r
-    protected IHintContext hintCtx = null;\r
-\r
-    /** Cached hint stack value */\r
-    protected IHintStack hintStack;\r
-\r
-    /** Painters found with reflection */\r
-    protected CanvasSGNodeDefinition[] sghandlers;\r
-\r
-    /** Painters found with reflection */\r
-    protected EventHandlerDefinition[] eventHandlers;\r
-\r
-    protected HintListenerDefinition[] hintListeners;\r
-\r
-    /** Reference definitions */\r
-    protected ReferenceDefinition[] refDefs;\r
-    protected boolean depsSatisfied = false;\r
-    private final IContextListener<ICanvasParticipant> ctxListener =\r
-        new IContextListener<ICanvasParticipant>() {\r
-        @Override\r
-        public void itemAdded(IContext<ICanvasParticipant> sender, ICanvasParticipant item) {\r
-            _itemAdded(sender, item);\r
-            if (!depsSatisfied) depsSatisfied = checkDependencies();\r
-        }\r
-        @Override\r
-        public void itemRemoved(IContext<ICanvasParticipant> sender, ICanvasParticipant item) {\r
-            _itemRemoved(sender, item);\r
-            AbstractCanvasParticipant.this.depsSatisfied = depsSatisfied;\r
-        }\r
-    };\r
-\r
-    Set<Field> missingDependencies = new HashSet<>();\r
-    \r
-    @SuppressWarnings("unchecked")\r
-    private void _itemAdded(IContext<ICanvasParticipant> sender, ICanvasParticipant item)\r
-    {\r
-       // This code handles @Dependency annotation\r
-       // Synchronizing because the calling thread may be anything\r
-       synchronized ( ctxListener ) {\r
-               Class<ICanvasParticipant> c = (Class<ICanvasParticipant>) item.getClass();\r
-               try {\r
-                   for (ReferenceDefinition def : refDefs)\r
-                   {\r
-                       Class<?>        defClass = def.requirement;\r
-                       if (!defClass.isAssignableFrom(c)) continue;\r
-                       Field   f = def.field;\r
-                       Object  value = f.get(AbstractCanvasParticipant.this);\r
-                       assert(value==null);\r
-                       f.set(AbstractCanvasParticipant.this, item);\r
-                   }\r
-               } catch (Exception e) {\r
-                   throw new RuntimeException(e);\r
-               }\r
-       }\r
-    }\r
-    @SuppressWarnings("unchecked")\r
-    private void _itemRemoved(IContext<ICanvasParticipant> sender, ICanvasParticipant item)\r
-    {\r
-       synchronized ( ctxListener ) {          \r
-               Class<ICanvasParticipant> c = (Class<ICanvasParticipant>) item.getClass();\r
-               //boolean depsSatisfied = true;\r
-               try {\r
-                   for (ReferenceDefinition def : refDefs)\r
-                   {\r
-                       Class<?>        defClass = def.requirement;\r
-                       Field   f = def.field;\r
-                       //Object        value = f.get(AbstractCanvasParticipant.this);\r
-                       if (defClass.isAssignableFrom(c)) {\r
-                           //value = null;\r
-                           f.set(AbstractCanvasParticipant.this, null);\r
-                       }\r
-                       //depsSatisfied &= !def.dependency || value!=null;\r
-                   }\r
-               } catch (Exception e) {\r
-                   throw new RuntimeException(e);\r
-               }\r
-       }\r
-    }\r
-\r
-    /**\r
-     * AbstractInteractor adds itself to the context set in the constructor\r
-     * argument.\r
-     * \r
-     * The constructor creates hint constructor and adds it into the context\r
-     * with priority value Integer.MIN_VALUE. Generally, base service interactors\r
-     * only read hints. Those hints are usually overrideable and thus the default\r
-     * hint context priority is as small value as possible. Use this constructor for\r
-     * BASE SERVICE interactors that only read values.\r
-     * \r
-     * @param ctx\r
-     */\r
-    public AbstractCanvasParticipant()\r
-    {\r
-        sghandlers = SGNodeReflection.getSGHandlers(this);\r
-        eventHandlers = EventHandlerReflection.getEventHandlers(this);\r
-        refDefs = DependencyReflection.getDependencies(this, ReferenceType.CanvasParticipant);\r
-        hintListeners = HintReflection.getDependencies(this);\r
-        if (refDefs.length==0) depsSatisfied = true;\r
-    }\r
-\r
-    @Override\r
-    public void addedToContext(ICanvasContext ctx)\r
-    {\r
-        assert(ctx!=null);\r
-        assert(context==null);\r
-        this.context = ctx;\r
-        this.hintStack = ctx.getHintStack();\r
-        this.thread = ctx.getThreadAccess();\r
-\r
-        // Add event handlers\r
-        IPriorityStack<IEventHandler> eventHandlerStack = getContext().getEventHandlerStack();\r
-        for (EventHandlerDefinition eventHandler : eventHandlers)\r
-            eventHandlerStack.add(eventHandler.eventHandler, eventHandler.priority);\r
-\r
-        // Fill References & Monitor for changes\r
-        if (refDefs.length!=0) {\r
-            getContext().addContextListener(ctxListener);\r
-            for (ICanvasParticipant cp : getContext().toArray())\r
-                _itemAdded(getContext(), cp);\r
-            depsSatisfied = checkDependencies();\r
-        }\r
-\r
-        // Add hint reflections\r
-        IHintStack stack = getContext().getHintStack();\r
-        IThreadWorkQueue thread = getContext().getThreadAccess();\r
-        for (HintListenerDefinition def : hintListeners)\r
-            stack.addKeyHintListener(thread, def.key, def);\r
-\r
-\r
-        // Create SceneGraph nodes\r
-        for (CanvasSGNodeDefinition sg : sghandlers) {\r
-            if (sg.initDesignation != null) {\r
-                switch (sg.initDesignation) {\r
-                    case CONTROL:\r
-                        sg.init(ctx.getSceneGraph());\r
-                        break;\r
-                    case CANVAS:\r
-                        sg.init(ctx.getCanvasNode());\r
-                        break;\r
-                }\r
-            }\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void removedFromContext(ICanvasContext ctx)\r
-    {\r
-        assert(ctx!=null);\r
-        if (context==null)\r
-            throw new RuntimeException("Interactor was not in any context");\r
-\r
-        // Remove context listener\r
-        if (refDefs.length!=0) {\r
-            getContext().removeContextListener(ctxListener);\r
-        }\r
-\r
-        // Clean up SceneGraph nodes\r
-        for (CanvasSGNodeDefinition sg : sghandlers) {\r
-            sg.cleanup();\r
-        }\r
-\r
-        IPriorityStack<IEventHandler> eventHandlerStack = context.getEventHandlerStack();\r
-        for (EventHandlerDefinition eventHandler : eventHandlers)\r
-            eventHandlerStack.remove(eventHandler.eventHandler);\r
-\r
-        IHintStack stack = getContext().getHintStack();\r
-        IThreadWorkQueue thread = getContext().getThreadAccess();\r
-        for (HintListenerDefinition def : hintListeners)\r
-            stack.removeKeyHintListener(thread, def.key, def);\r
-\r
-        if (localHintCtx!=null) {\r
-            context.getHintStack().removeHintContext(localHintCtx);\r
-        }\r
-        context = null;\r
-    }\r
-\r
-    /**\r
-     * Has this interactor been removed from the context\r
-     * @return true if interactor has been removed from the context\r
-     */\r
-    public boolean isRemoved()\r
-    {\r
-        return context==null;\r
-    }\r
-\r
-    /**\r
-     * Remove Self.\r
-     * \r
-     * Removes this interactor from its designed context.\r
-     * This methods can be invoked only once.\r
-     */\r
-    public void remove()\r
-    {\r
-        if (isRemoved())\r
-            throw new RuntimeException("Interactor has already been removed from the context");\r
-        context.remove(this);\r
-    }\r
-\r
-    /**\r
-     * Returns the hint stack of the context.\r
-     * @return hint stack\r
-     */\r
-    public IHintStack getHintStack()\r
-    {\r
-        return hintStack;\r
-    }\r
-\r
-    /**\r
-     * Get local hint context. This context contains all hints set by this\r
-     * particular interactor. Modifications affect the hint stack of the\r
-     * interaction context, although some modifications may not become effective\r
-     * in case the key is overridden by another hint context in the stack.\r
-     * <p>\r
-     * Local context must be created with createLocalHintContext() method.\r
-     * constructor has defualt value Integer.MIN_VALUE.\r
-     * <p>\r
-     * Reading from this context returns only the local hint and not\r
-     * from the shared hint stack of the canvas context.\r
-     * <p>\r
-     * To read from the shared hint stack, use getHint() or getHintStack() instead.\r
-     * \r
-     * @return the local hint context\r
-     */\r
-    public synchronized IHintContext getLocalHintContext()\r
-    {\r
-        return localHintCtx;\r
-    }\r
-\r
-    /**\r
-     * Get hint context. Read operations are to hint stack, writes to local stack.\r
-     * \r
-     * @return\r
-     */\r
-    public synchronized IHintContext getWriteableHintStack()\r
-    {\r
-        if (hintCtx==null)\r
-            hintCtx = getHintStack().createStackRead(getLocalHintContext());\r
-        return hintCtx;\r
-    }\r
-\r
-    /**\r
-     * Get hint context; local if exists, otherwise stack's default context.\r
-     * \r
-     * @return\r
-     */\r
-    public IHintContext getWriteableHintContext()\r
-    {\r
-        ICanvasContext ctx = context;\r
-        assert(ctx!=null);\r
-        if (localHintCtx!=null) return localHintCtx;\r
-        return ctx.getDefaultHintContext();\r
-    }\r
-\r
-    /**\r
-     * Read hint from the hint stack.\r
-     * \r
-     * @param key\r
-     * @return\r
-     */\r
-    public <E> E getHint(Key key)\r
-    {\r
-        return hintStack.getHint(key);\r
-    }\r
-\r
-    public boolean hasHint(Key key)\r
-    {\r
-        return hintStack.getHint(key)!=null;\r
-    }\r
-\r
-    /**\r
-     * Set hint to the local hint stack.\r
-     * \r
-     * Thread safe - switches to context thread\r
-     * \r
-     * @param key the key\r
-     * @param value the value\r
-     */\r
-    public void setHint(final Key key, final Object value)\r
-    {\r
-        if (getThread().currentThreadAccess())\r
-            getWriteableHintContext().setHint(key, value);\r
-        else {\r
-//                     syncExec(new Runnable() {\r
-//                             @Override\r
-//                             public void run() {\r
-//                                     getWriteableHintContext().setHint(key, value);\r
-//                             }});\r
-            throw new IllegalStateException("illegal thread access, expected " + getThread().getThread() + ", was in "\r
-                    + Thread.currentThread());\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Remove hint from local hint context / stack's default context\r
-     * \r
-     * Thread safe - switches to context thread\r
-     * \r
-     * @param key the key\r
-     */\r
-    public void removeHint(final Key key)\r
-    {\r
-        if (getThread().currentThreadAccess())\r
-            getWriteableHintContext().removeHint(key);\r
-        else\r
-//                     syncExec(new Runnable() {\r
-//                             @Override\r
-//                             public void run() {\r
-//                                     getWriteableHintContext().removeHint(key);\r
-//                             }});\r
-            throw new IllegalStateException("illegal thread access");\r
-    }\r
-\r
-\r
-    public void syncExec(Runnable r)\r
-    {\r
-        ThreadUtils.syncExec(getThread(), r);\r
-    }\r
-\r
-    public void asyncExec(Runnable r)\r
-    {\r
-        ThreadUtils.asyncExec(getThread(), r);\r
-    }\r
-\r
-    /**\r
-     * Create local hint context and add it to the hint stack. It will be\r
-     * automatically removed when this participant is removed from the canvas.\r
-     * <p>\r
-     * Local hint context overrides default context in the following convenience methods:\r
-     *  getHint(), setHint(), setHintAsync()\r
-     * <p>\r
-     * Q: What is the purpose of local hint stack?\r
-     * A: To override hints for the lifetime of the participant.\r
-     *           For example, Special editing mode overrides viewport and toolmode.\r
-     * \r
-     * @param priority\r
-     */\r
-    public void createLocalHintContext(int priority)\r
-    {\r
-        localHintCtx = new HintContext();\r
-    }\r
-\r
-    /**\r
-     * Set hint to the local hint stack if one exists otherwise to the default context.\r
-     * Switches thread to the appropriate context thread.\r
-     * \r
-     * @param key the key\r
-     * @param value the value\r
-     */\r
-    public void setHintAsync(final Key key, final Object value)\r
-    {\r
-        assert(context!=null);\r
-        asyncExec(new Runnable() {\r
-            @Override\r
-            public void run() {\r
-                if (isRemoved())\r
-                    return;\r
-                if (localHintCtx!=null)\r
-                    localHintCtx.setHint(key, value);\r
-                else\r
-                    context.getDefaultHintContext().setHint(key, value);\r
-            }});\r
-    }\r
-\r
-    /**\r
-     * Get the interactor context.\r
-     * @return the context\r
-     */\r
-    public ICanvasContext getContext()\r
-    {\r
-        return context;\r
-    }\r
-\r
-    public IThreadWorkQueue getThread()\r
-    {\r
-        return thread;\r
-    }\r
-\r
-\r
-    /**\r
-     * Asserts that dependencies of participants are satisfied.\r
-     */\r
-    public void assertDependencies()\r
-    {\r
-        assert(depsSatisfied);\r
-    }\r
-\r
-    private boolean checkDependencies() {\r
-       synchronized ( ctxListener ) {          \r
-               try {\r
-                   for (ReferenceDefinition rd : refDefs) {\r
-                       if (!rd.dependency)\r
-                           continue;\r
-                       Field f = rd.field;\r
-                       Object o = f.get(this);\r
-                       if (o == null) {\r
-                           missingDependencies.add(f);\r
-                           return false;\r
-                       } else {\r
-                           missingDependencies.remove(f);\r
-                       }\r
-                   }\r
-               } catch (Exception e) {\r
-                   throw new RuntimeException(e);\r
-               }\r
-               return true;\r
-       }\r
-    }\r
-\r
-    public void setDirty()\r
-    {\r
-       ICanvasContext ctx = getContext();\r
-       if ( ctx==null ) return;\r
-       IContentContext cctx = ctx.getContentContext();\r
-       if ( cctx==null ) return;\r
-        cctx.setDirty();\r
-    }\r
-\r
-}\r
+/*******************************************************************************
+ * 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.canvas.impl;
+
+import java.lang.reflect.Field;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.simantics.g2d.canvas.ICanvasContext;
+import org.simantics.g2d.canvas.ICanvasParticipant;
+import org.simantics.g2d.canvas.IContentContext;
+import org.simantics.g2d.canvas.impl.DependencyReflection.ReferenceDefinition;
+import org.simantics.g2d.canvas.impl.DependencyReflection.ReferenceType;
+import org.simantics.g2d.canvas.impl.HintReflection.HintListenerDefinition;
+import org.simantics.g2d.canvas.impl.SGNodeReflection.CanvasSGNodeDefinition;
+import org.simantics.scenegraph.g2d.events.EventHandlerReflection;
+import org.simantics.scenegraph.g2d.events.IEventHandler;
+import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandlerDefinition;
+import org.simantics.utils.datastructures.context.IContext;
+import org.simantics.utils.datastructures.context.IContextListener;
+import org.simantics.utils.datastructures.hints.HintContext;
+import org.simantics.utils.datastructures.hints.IHintContext;
+import org.simantics.utils.datastructures.hints.IHintStack;
+import org.simantics.utils.datastructures.hints.IHintContext.Key;
+import org.simantics.utils.datastructures.prioritystack.IPriorityStack;
+import org.simantics.utils.threads.IThreadWorkQueue;
+import org.simantics.utils.threads.ThreadUtils;
+
+
+/**
+ * AbstractCanvasParticipant is base implementation for canvas participants.
+ * There is an assertion that states AbstractCanvasParticipant can be added
+ * only once to a canvas.
+ * 
+ * <p>
+ * There is convenience mechanism for adding painter and event handler
+ * listeners. Subclasses create listeners with usage of Painter and
+ * EventHandler annotations. Listeners are automatically added and
+ * removed to/from canvas context.
+ * </p>
+ * 
+ * <p>
+ * Example:
+ * </p>
+ * <blockquote><pre>
+ *   @EventHandler(priority=200)
+ *   public boolean handleEvent(Event e) {
+ *       return false;
+ *   }
+ * 
+ *   @EventHandler(priority=400)
+ *   public boolean handleMousePressEvent(MouseButtonPressedEvent e) {
+ *       return false;
+ *   }
+ * 
+ *   IG2DNode node;
+ * 
+ *   @SGInit
+ *   public void initSG(G2DParentNode parent) {
+ *       // Insert a node into the scene graph rendered by the canvas
+ *       // this participant is attached to and initialize it to render
+ *       // something. Later on, to modify what the node will render, just
+ *       // update the node's attributes.
+ *       node = parent.addNode("myNode", MyNode.class);
+ *       node.setZIndex(Integer.MIN_VALUE);
+ *   }
+ * 
+ *   @SGCleanup
+ *   public void cleanup() {
+ *       // Remove our node from the scene graph where it belonged.
+ *       // The node must not be used after this anymore.
+ *       if (node != null) {
+ *           node.remove();
+ *           node = null;
+ *       }
+ *   }
+ * </pre></blockquote>
+ * 
+ * <p>
+ * Local fields are automatically assigned with ICanvasParticipant instances if they
+ * are annotated with either <code>@Dependency</code> or <code>@Reference</code> annotation tag.
+ * <code>@Depedency</code> is a requirement, <code>@Reference</code> is optional.
+ * 
+ * assertDependencies() verifies that dependencies are satisfied.
+ * Local depsSatisfied field is true when dependencies are satisfied.
+ * 
+ * <p>
+ * Example:
+ * </p>
+ * <blockquote><pre>
+ *   class MyParticipant implements ICanvasParticipant {
+ *       @Reference MouseMonitor mouseMonitor;
+ *       @Dependency TimeParticipant timeParticipant;
+ *
+ *       @Painter(priority=100)
+ *       public void paint(GraphicsContext gc) {
+ *           assertDependencies(); // timeParticipant != null
+ * 
+ *           timeParticipant.doSomething();
+ * 
+ *           if (mouseMonitor!=null) doSomethingElse();
+ *       }
+ *   }</pre></blockquote>
+ * 
+ * <p>
+ * Hint listener annotation.
+ * </p>
+ * 
+ * <p>
+ * Example:
+ * </p>
+ * <blockquote><pre>
+ *   @HintListener(Class=Hints.class, Field="KEY_CANVAS_TRANSFORM")
+ *   public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {
+ *       ...
+ *   }
+ *   @HintListener(Class=Hints.class, Field="KEY_CANVAS_TRANSFORM")
+ *   public void hintRemoved(IHintObservable sender, Key key, Object oldValue) {
+ *       ...
+ *   }
+ * </pre></blockquote>
+ * 
+ * @author Toni Kalajainen
+ */
+public abstract class AbstractCanvasParticipant implements ICanvasParticipant {
+
+    /** The interactor/canvas context */
+    private ICanvasContext context;
+
+    /** The thread used in the context */
+    private IThreadWorkQueue thread;
+
+    /** the local hint context */
+    protected IHintContext localHintCtx = null;
+    protected int localPriority = 0;
+
+    /** wrapped local hint context. reads from hint stack */
+    protected IHintContext hintCtx = null;
+
+    /** Cached hint stack value */
+    protected IHintStack hintStack;
+
+    /** Painters found with reflection */
+    protected CanvasSGNodeDefinition[] sghandlers;
+
+    /** Painters found with reflection */
+    protected EventHandlerDefinition[] eventHandlers;
+
+    protected HintListenerDefinition[] hintListeners;
+
+    /** Reference definitions */
+    protected ReferenceDefinition[] refDefs;
+    protected boolean depsSatisfied = false;
+    private final IContextListener<ICanvasParticipant> ctxListener =
+        new IContextListener<ICanvasParticipant>() {
+        @Override
+        public void itemAdded(IContext<ICanvasParticipant> sender, ICanvasParticipant item) {
+            _itemAdded(sender, item);
+            if (!depsSatisfied) depsSatisfied = checkDependencies();
+        }
+        @Override
+        public void itemRemoved(IContext<ICanvasParticipant> sender, ICanvasParticipant item) {
+            _itemRemoved(sender, item);
+            AbstractCanvasParticipant.this.depsSatisfied = depsSatisfied;
+        }
+    };
+
+    Set<Field> missingDependencies = new HashSet<>();
+    
+    @SuppressWarnings("unchecked")
+    private void _itemAdded(IContext<ICanvasParticipant> sender, ICanvasParticipant item)
+    {
+       // This code handles @Dependency annotation
+       // Synchronizing because the calling thread may be anything
+       synchronized ( ctxListener ) {
+               Class<ICanvasParticipant> c = (Class<ICanvasParticipant>) item.getClass();
+               try {
+                   for (ReferenceDefinition def : refDefs)
+                   {
+                       Class<?>        defClass = def.requirement;
+                       if (!defClass.isAssignableFrom(c)) continue;
+                       Field   f = def.field;
+                       Object  value = f.get(AbstractCanvasParticipant.this);
+                       assert(value==null);
+                       f.set(AbstractCanvasParticipant.this, item);
+                   }
+               } catch (Exception e) {
+                   throw new RuntimeException(e);
+               }
+       }
+    }
+    @SuppressWarnings("unchecked")
+    private void _itemRemoved(IContext<ICanvasParticipant> sender, ICanvasParticipant item)
+    {
+       synchronized ( ctxListener ) {          
+               Class<ICanvasParticipant> c = (Class<ICanvasParticipant>) item.getClass();
+               //boolean depsSatisfied = true;
+               try {
+                   for (ReferenceDefinition def : refDefs)
+                   {
+                       Class<?>        defClass = def.requirement;
+                       Field   f = def.field;
+                       //Object        value = f.get(AbstractCanvasParticipant.this);
+                       if (defClass.isAssignableFrom(c)) {
+                           //value = null;
+                           f.set(AbstractCanvasParticipant.this, null);
+                       }
+                       //depsSatisfied &= !def.dependency || value!=null;
+                   }
+               } catch (Exception e) {
+                   throw new RuntimeException(e);
+               }
+       }
+    }
+
+    /**
+     * AbstractInteractor adds itself to the context set in the constructor
+     * argument.
+     * 
+     * The constructor creates hint constructor and adds it into the context
+     * with priority value Integer.MIN_VALUE. Generally, base service interactors
+     * only read hints. Those hints are usually overrideable and thus the default
+     * hint context priority is as small value as possible. Use this constructor for
+     * BASE SERVICE interactors that only read values.
+     * 
+     * @param ctx
+     */
+    public AbstractCanvasParticipant()
+    {
+        sghandlers = SGNodeReflection.getSGHandlers(this);
+        eventHandlers = EventHandlerReflection.getEventHandlers(this);
+        refDefs = DependencyReflection.getDependencies(this, ReferenceType.CanvasParticipant);
+        hintListeners = HintReflection.getDependencies(this);
+        if (refDefs.length==0) depsSatisfied = true;
+    }
+
+    @Override
+    public void addedToContext(ICanvasContext ctx)
+    {
+        assert(ctx!=null);
+        assert(context==null);
+        this.context = ctx;
+        this.hintStack = ctx.getHintStack();
+        this.thread = ctx.getThreadAccess();
+
+        // Add event handlers
+        IPriorityStack<IEventHandler> eventHandlerStack = getContext().getEventHandlerStack();
+        for (EventHandlerDefinition eventHandler : eventHandlers)
+            eventHandlerStack.add(eventHandler.eventHandler, eventHandler.priority);
+
+        // Fill References & Monitor for changes
+        if (refDefs.length!=0) {
+            getContext().addContextListener(ctxListener);
+            for (ICanvasParticipant cp : getContext().toArray())
+                _itemAdded(getContext(), cp);
+            depsSatisfied = checkDependencies();
+        }
+
+        // Add hint reflections
+        IHintStack stack = getContext().getHintStack();
+        IThreadWorkQueue thread = getContext().getThreadAccess();
+        for (HintListenerDefinition def : hintListeners)
+            stack.addKeyHintListener(thread, def.key, def);
+
+
+        // Create SceneGraph nodes
+        for (CanvasSGNodeDefinition sg : sghandlers) {
+            if (sg.initDesignation != null) {
+                switch (sg.initDesignation) {
+                    case CONTROL:
+                        sg.init(ctx.getSceneGraph());
+                        break;
+                    case CANVAS:
+                        sg.init(ctx.getCanvasNode());
+                        break;
+                }
+            }
+        }
+    }
+
+    @Override
+    public void removedFromContext(ICanvasContext ctx)
+    {
+        assert(ctx!=null);
+        if (context==null)
+            throw new RuntimeException("Interactor was not in any context");
+
+        // Remove context listener
+        if (refDefs.length!=0) {
+            getContext().removeContextListener(ctxListener);
+        }
+
+        // Clean up SceneGraph nodes
+        for (CanvasSGNodeDefinition sg : sghandlers) {
+            sg.cleanup();
+        }
+
+        IPriorityStack<IEventHandler> eventHandlerStack = context.getEventHandlerStack();
+        for (EventHandlerDefinition eventHandler : eventHandlers)
+            eventHandlerStack.remove(eventHandler.eventHandler);
+
+        IHintStack stack = getContext().getHintStack();
+        IThreadWorkQueue thread = getContext().getThreadAccess();
+        for (HintListenerDefinition def : hintListeners)
+            stack.removeKeyHintListener(thread, def.key, def);
+
+        if (localHintCtx!=null) {
+            context.getHintStack().removeHintContext(localHintCtx);
+        }
+        context = null;
+    }
+
+    /**
+     * Has this interactor been removed from the context
+     * @return true if interactor has been removed from the context
+     */
+    public boolean isRemoved()
+    {
+        return context==null;
+    }
+
+    /**
+     * Remove Self.
+     * 
+     * Removes this interactor from its designed context.
+     * This methods can be invoked only once.
+     */
+    public void remove()
+    {
+        if (isRemoved())
+            throw new RuntimeException("Interactor has already been removed from the context");
+        context.remove(this);
+    }
+
+    /**
+     * Returns the hint stack of the context.
+     * @return hint stack
+     */
+    public IHintStack getHintStack()
+    {
+        return hintStack;
+    }
+
+    /**
+     * Get local hint context. This context contains all hints set by this
+     * particular interactor. Modifications affect the hint stack of the
+     * interaction context, although some modifications may not become effective
+     * in case the key is overridden by another hint context in the stack.
+     * <p>
+     * Local context must be created with createLocalHintContext() method.
+     * constructor has defualt value Integer.MIN_VALUE.
+     * <p>
+     * Reading from this context returns only the local hint and not
+     * from the shared hint stack of the canvas context.
+     * <p>
+     * To read from the shared hint stack, use getHint() or getHintStack() instead.
+     * 
+     * @return the local hint context
+     */
+    public synchronized IHintContext getLocalHintContext()
+    {
+        return localHintCtx;
+    }
+
+    /**
+     * Get hint context. Read operations are to hint stack, writes to local stack.
+     * 
+     * @return
+     */
+    public synchronized IHintContext getWriteableHintStack()
+    {
+        if (hintCtx==null)
+            hintCtx = getHintStack().createStackRead(getLocalHintContext());
+        return hintCtx;
+    }
+
+    /**
+     * Get hint context; local if exists, otherwise stack's default context.
+     * 
+     * @return
+     */
+    public IHintContext getWriteableHintContext()
+    {
+        ICanvasContext ctx = context;
+        assert(ctx!=null);
+        if (localHintCtx!=null) return localHintCtx;
+        return ctx.getDefaultHintContext();
+    }
+
+    /**
+     * Read hint from the hint stack.
+     * 
+     * @param key
+     * @return
+     */
+    public <E> E getHint(Key key)
+    {
+        return hintStack.getHint(key);
+    }
+
+    public boolean hasHint(Key key)
+    {
+        return hintStack.getHint(key)!=null;
+    }
+
+    /**
+     * Set hint to the local hint stack.
+     * 
+     * Thread safe - switches to context thread
+     * 
+     * @param key the key
+     * @param value the value
+     */
+    public void setHint(final Key key, final Object value)
+    {
+        if (getThread().currentThreadAccess())
+            getWriteableHintContext().setHint(key, value);
+        else {
+//                     syncExec(new Runnable() {
+//                             @Override
+//                             public void run() {
+//                                     getWriteableHintContext().setHint(key, value);
+//                             }});
+            throw new IllegalStateException("illegal thread access, expected " + getThread().getThread() + ", was in "
+                    + Thread.currentThread());
+        }
+    }
+
+    /**
+     * Remove hint from local hint context / stack's default context
+     * 
+     * Thread safe - switches to context thread
+     * 
+     * @param key the key
+     */
+    public void removeHint(final Key key)
+    {
+        if (getThread().currentThreadAccess())
+            getWriteableHintContext().removeHint(key);
+        else
+//                     syncExec(new Runnable() {
+//                             @Override
+//                             public void run() {
+//                                     getWriteableHintContext().removeHint(key);
+//                             }});
+            throw new IllegalStateException("illegal thread access");
+    }
+
+
+    public void syncExec(Runnable r)
+    {
+        ThreadUtils.syncExec(getThread(), r);
+    }
+
+    public void asyncExec(Runnable r)
+    {
+        ThreadUtils.asyncExec(getThread(), r);
+    }
+
+    /**
+     * Create local hint context and add it to the hint stack. It will be
+     * automatically removed when this participant is removed from the canvas.
+     * <p>
+     * Local hint context overrides default context in the following convenience methods:
+     *  getHint(), setHint(), setHintAsync()
+     * <p>
+     * Q: What is the purpose of local hint stack?
+     * A: To override hints for the lifetime of the participant.
+     *           For example, Special editing mode overrides viewport and toolmode.
+     * 
+     * @param priority
+     */
+    public void createLocalHintContext(int priority)
+    {
+        localHintCtx = new HintContext();
+    }
+
+    /**
+     * Set hint to the local hint stack if one exists otherwise to the default context.
+     * Switches thread to the appropriate context thread.
+     * 
+     * @param key the key
+     * @param value the value
+     */
+    public void setHintAsync(final Key key, final Object value)
+    {
+        assert(context!=null);
+        asyncExec(new Runnable() {
+            @Override
+            public void run() {
+                if (isRemoved())
+                    return;
+                if (localHintCtx!=null)
+                    localHintCtx.setHint(key, value);
+                else
+                    context.getDefaultHintContext().setHint(key, value);
+            }});
+    }
+
+    /**
+     * Get the interactor context.
+     * @return the context
+     */
+    public ICanvasContext getContext()
+    {
+        return context;
+    }
+
+    public IThreadWorkQueue getThread()
+    {
+        return thread;
+    }
+
+
+    /**
+     * Asserts that dependencies of participants are satisfied.
+     */
+    public void assertDependencies()
+    {
+        assert(depsSatisfied);
+    }
+
+    private boolean checkDependencies() {
+       synchronized ( ctxListener ) {          
+               try {
+                   for (ReferenceDefinition rd : refDefs) {
+                       if (!rd.dependency)
+                           continue;
+                       Field f = rd.field;
+                       Object o = f.get(this);
+                       if (o == null) {
+                           missingDependencies.add(f);
+                           return false;
+                       } else {
+                           missingDependencies.remove(f);
+                       }
+                   }
+               } catch (Exception e) {
+                   throw new RuntimeException(e);
+               }
+               return true;
+       }
+    }
+
+    public void setDirty()
+    {
+       ICanvasContext ctx = getContext();
+       if ( ctx==null ) return;
+       IContentContext cctx = ctx.getContentContext();
+       if ( cctx==null ) return;
+        cctx.setDirty();
+    }
+
+}