1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.g2d.element;
14 import java.lang.annotation.ElementType;
15 import java.lang.annotation.Retention;
16 import java.lang.annotation.RetentionPolicy;
17 import java.lang.annotation.Target;
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.Collection;
21 import java.util.HashSet;
24 import org.simantics.g2d.diagram.impl.AbstractHandlerClass;
25 import org.simantics.g2d.element.handler.ElementHandler;
26 import org.simantics.g2d.element.handler.HandleMouseEvent;
27 import org.simantics.g2d.element.handler.InternalSize;
28 import org.simantics.g2d.element.handler.Move;
29 import org.simantics.g2d.element.handler.Rotate;
30 import org.simantics.g2d.element.handler.SceneGraph;
31 import org.simantics.g2d.element.handler.Transform;
32 import org.simantics.g2d.element.handler.Validator;
35 * ElementClass is a class of elements. It consists of element handlers.
36 * Each handler contributes functions to some aspects of the diagram.
38 * Handlers are ordered. For instance, if there are multiple Paint handlers,
39 * the paint order is determined by the order of the handlers in the class.
41 * The minimum requirement is implementation for the following interfaces:
45 * Typical element class has the following implementations:
50 * @see InternalSize or @see Scale
53 * Interactive element classes have implementation to:
54 * @see HandleMouseEvent
59 * See package org.simantics.g2d.element.handler for handler interfaces.
60 * See package org.simantics.g2d.element.handler.impl for common implementations.
61 * Note that some implementations implement several interface. Also note that
62 * some handler interfaces may be implemented several times (e.g. Paint,
63 * Validator, ToolTip, Pick). In contrast, some handlers may have at most one
64 * implementation, such as Transform, Move, Rotate, Scale, Size.
66 * Tip: Use SymbolUtil (CanvasParticipant) to manage graphical resources (images).
68 * @author Toni Kalajainen
70 public final class ElementClass extends AbstractHandlerClass<ElementHandler> {
72 private static final long serialVersionUID = -446421782709451743L;
74 /** Handler with this annotation may have at most one implementation */
75 @Retention(RetentionPolicy.RUNTIME)
76 @Target(ElementType.TYPE)
77 public static @interface Single {}
79 /** Handler with this annotation must be implemented (once or more) */
80 @Retention(RetentionPolicy.RUNTIME)
81 @Target(ElementType.TYPE)
82 public static @interface Required {}
84 private static final Class<?>[] REQUIRED_HANDLERS =
85 new Class<?>[] {Transform.class, InternalSize.class};
87 private String id = "";
90 * Compile new element class from a set of handler
91 * @param contributions
94 public static ElementClass compile(Collection<ElementHandler> contributions)
96 return new ElementClass(contributions);
99 public static ElementClass compile(Collection<ElementHandler> contributions, boolean check)
101 return new ElementClass(contributions, check);
105 * Compile new element class from a set of handler
106 * @param contributions
109 public static ElementClass compile(ElementHandler... contributions)
111 if (contributions.length == 0)
112 return new ElementClass(Arrays.asList(contributions));
113 ArrayList<ElementHandler> al = new ArrayList<ElementHandler>(contributions.length);
114 for (ElementHandler eh : contributions)
116 return new ElementClass(al);
119 ElementClass(Collection<ElementHandler> contributions) {
120 super(contributions);
121 assertClassValid(contributions);
124 ElementClass(Collection<ElementHandler> contributions, boolean check) {
125 super(contributions);
126 if(check) assertClassValid(contributions);
130 * Validates that handler is valid.
132 * @param contributions
135 public static void assertClassValid(Collection<ElementHandler> contributions)
137 // 1. Verify requirements
139 for (Class<?> requiredClass : REQUIRED_HANDLERS) {
140 for (ElementHandler eh : contributions)
141 if (requiredClass.isInstance(eh))
142 continue nextRequirement;
143 throw new Error("Element class does not implement "+requiredClass.getName());
146 // 2. Verify singletons
147 // 2.1. Collect implemented handlers
148 Set<Class<ElementHandler>> implementedHandlerInterfaces = new HashSet<Class<ElementHandler>>();
149 for (ElementHandler eh : contributions)
150 _traverseElementHandlerInterfaces(eh.getClass(), implementedHandlerInterfaces);
152 // 2.2. Verify singletons are implemented only once
153 for (Class<ElementHandler> ehc : implementedHandlerInterfaces)
155 if (!_isSingletonHandler(ehc)) continue;
156 int implementationCount = 0;
157 for (ElementHandler eh : contributions)
159 if (!ehc.isInstance(eh)) continue;
160 implementationCount++;
162 if (implementationCount>1)
163 throw new Error("Element class has "+implementationCount+" implementations to a _singleton_ element handler \""+ehc.getName()+"\": " + contributions);
167 private static boolean _isSingletonHandler(Class<ElementHandler> elementHandlerClass)
169 Single s = elementHandlerClass.getAnnotation(Single.class);
173 @SuppressWarnings("unchecked")
174 private static void _traverseElementHandlerInterfaces(Class<?> clazz, Collection<Class<ElementHandler>> result)
176 // Add implemented interfaces (that are inherited from ElementHandler)
177 for (Class<?> inf : clazz.getInterfaces())
179 if (!ElementHandler.class.isAssignableFrom(inf)) continue;
180 result.add((Class<ElementHandler>)inf);
184 Class<?> superType = clazz.getSuperclass();
186 _traverseElementHandlerInterfaces(superType, result);
190 public String toString() {
194 StringBuilder sb = new StringBuilder();
197 for (ElementHandler eh : super.getAll())
199 if (i++>0) sb.append(", ");
200 sb.append( eh.getClass().getSimpleName() );
203 return sb.toString();
206 public ElementClass newClassWith(ElementHandler... addedHandlers) {
207 if (addedHandlers.length == 0)
209 Collection<ElementHandler> newHandlers = new ArrayList<ElementHandler>(getAll());
210 for (ElementHandler h : addedHandlers)
212 return ElementClass.compile(newHandlers).setId(id);
215 public ElementClass newClassWith(boolean check, ElementHandler... addedHandlers) {
216 if (addedHandlers.length == 0)
218 Collection<ElementHandler> newHandlers = new ArrayList<ElementHandler>(getAll());
219 for (ElementHandler h : addedHandlers)
221 return ElementClass.compile(newHandlers, check).setId(id);
224 public ElementClass newClassWith(Collection<ElementHandler> addedHandlers) {
225 if (addedHandlers.isEmpty())
227 Collection<ElementHandler> newHandlers = new ArrayList<ElementHandler>(getAll());
228 newHandlers.addAll(addedHandlers);
229 return ElementClass.compile(newHandlers).setId(id);
232 public ElementClass setId(String id) {
237 public String getId() {