X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;ds=sidebyside;f=bundles%2Forg.simantics.g2d%2Fsrc%2Forg%2Fsimantics%2Fg2d%2Felement%2FElementClass.java;h=5d689760143772252b0407340e48620c6bb55e4f;hb=bb2be932187c70082a01e12e1f451dd06650a48c;hp=124a26cb217476e6f6b10a0acdf4abbb81ebd7ef;hpb=969bd23cab98a79ca9101af33334000879fb60c5;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/element/ElementClass.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/element/ElementClass.java index 124a26cb2..5d6897601 100644 --- a/bundles/org.simantics.g2d/src/org/simantics/g2d/element/ElementClass.java +++ b/bundles/org.simantics.g2d/src/org/simantics/g2d/element/ElementClass.java @@ -1,243 +1,241 @@ -/******************************************************************************* - * 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.element; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -import org.simantics.g2d.diagram.impl.AbstractHandlerClass; -import org.simantics.g2d.element.handler.ElementHandler; -import org.simantics.g2d.element.handler.HandleMouseEvent; -import org.simantics.g2d.element.handler.InternalSize; -import org.simantics.g2d.element.handler.Move; -import org.simantics.g2d.element.handler.Rotate; -import org.simantics.g2d.element.handler.SceneGraph; -import org.simantics.g2d.element.handler.Transform; -import org.simantics.g2d.element.handler.Validator; - -/** - * ElementClass is a class of elements. It consists of element handlers. - * Each handler contributes functions to some aspects of the diagram. - *

- * Handlers are ordered. For instance, if there are multiple Paint handlers, - * the paint order is determined by the order of the handlers in the class. - *

- * The minimum requirement is implementation for the following interfaces: - * @see Transform - * @see InternalSize - * - * Typical element class has the following implementations: - * @see Transform - * @see SceneGraph - * @see Move - * @see Rotate - * @see InternalSize or @see Scale - * @see Selectable - * - * Interactive element classes have implementation to: - * @see HandleMouseEvent - * - * See also: - * @see Validator - * - * See package org.simantics.g2d.element.handler for handler interfaces. - * See package org.simantics.g2d.element.handler.impl for common implementations. - * Note that some implementations implement several interface. Also note that - * some handler interfaces may be implemented several times (e.g. Paint, - * Validator, ToolTip, Pick). In contrast, some handlers may have at most one - * implementation, such as Transform, Move, Rotate, Scale, Size. - *

- * Tip: Use SymbolUtil (CanvasParticipant) to manage graphical resources (images). - * - * @author Toni Kalajainen - */ -public final class ElementClass extends AbstractHandlerClass { - - private static final long serialVersionUID = -446421782709451743L; - - /** Handler with this annotation may have at most one implementation */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - public static @interface Single {} - - /** Handler with this annotation must be implemented (once or more) */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - public static @interface Required {} - - private static final Class[] REQUIRED_HANDLERS = - new Class[] {Transform.class, InternalSize.class}; - - private String id = ""; - - /** - * Compile new element class from a set of handler - * @param contributions - * @return - */ - public static ElementClass compile(Collection contributions) - { - assertClassValid(contributions); - return new ElementClass(contributions); - } - - public static ElementClass compile(Collection contributions, boolean check) - { - if(check) assertClassValid(contributions); - return new ElementClass(contributions, check); - } - - /** - * Compile new element class from a set of handler - * @param contributions - * @return - */ - public static ElementClass compile(ElementHandler... contributions) - { - if (contributions.length == 0) - return new ElementClass(Arrays.asList(contributions)); - ArrayList al = new ArrayList(contributions.length); - for (ElementHandler eh : contributions) - al.add(eh); - return new ElementClass(al); - } - - ElementClass(Collection contributions) { - super(contributions); - assertClassValid(contributions); - } - - ElementClass(Collection contributions, boolean check) { - super(contributions); - if(check) assertClassValid(contributions); - } - - /** - * Validates that handler is valid. - * - * @param contributions - * @return - */ - public static void assertClassValid(Collection contributions) - { - // 1. Verify requirements - nextRequirement: - for (Class requiredClass : REQUIRED_HANDLERS) { - for (ElementHandler eh : contributions) - if (requiredClass.isInstance(eh)) - continue nextRequirement; - throw new Error("Element class does not implement "+requiredClass.getName()); - } - - // 2. Verify singletons - // 2.1. Collect implemented handlers - Set> implementedHandlerInterfaces = new HashSet>(); - for (ElementHandler eh : contributions) - _traverseElementHandlerInterfaces(eh.getClass(), implementedHandlerInterfaces); - - // 2.2. Verify singletons are implemented only once - for (Class ehc : implementedHandlerInterfaces) - { - if (!_isSingletonHandler(ehc)) continue; - int implementationCount = 0; - for (ElementHandler eh : contributions) - { - if (!ehc.isInstance(eh)) continue; - implementationCount++; - } - if (implementationCount>1) - throw new Error("Element class has "+implementationCount+" implementations to a _singleton_ element handler \""+ehc.getName()+"\": " + contributions); - } - } - - private static boolean _isSingletonHandler(Class elementHandlerClass) - { - Single s = elementHandlerClass.getAnnotation(Single.class); - return s != null; - } - - @SuppressWarnings("unchecked") - private static void _traverseElementHandlerInterfaces(Class clazz, Collection> result) - { - // Add implemented interfaces (that are inherited from ElementHandler) - for (Class inf : clazz.getInterfaces()) - { - if (!ElementHandler.class.isAssignableFrom(inf)) continue; - result.add((Class)inf); - } - - // Traverse parent - Class superType = clazz.getSuperclass(); - if (superType!=null) - _traverseElementHandlerInterfaces(superType, result); - } - - @Override - public String toString() { - if (!id.isEmpty()) - return id; - - StringBuilder sb = new StringBuilder(); - sb.append("["); - int i=0; - for (ElementHandler eh : super.getAll()) - { - if (i++>0) sb.append(", "); - sb.append( eh.getClass().getSimpleName() ); - } - sb.append("]"); - return sb.toString(); - } - - public ElementClass newClassWith(ElementHandler... addedHandlers) { - if (addedHandlers.length == 0) - return this; - Collection newHandlers = new ArrayList(getAll()); - for (ElementHandler h : addedHandlers) - newHandlers.add(h); - return ElementClass.compile(newHandlers).setId(id); - } - - public ElementClass newClassWith(boolean check, ElementHandler... addedHandlers) { - if (addedHandlers.length == 0) - return this; - Collection newHandlers = new ArrayList(getAll()); - for (ElementHandler h : addedHandlers) - newHandlers.add(h); - return ElementClass.compile(newHandlers, check).setId(id); - } - - public ElementClass newClassWith(Collection addedHandlers) { - if (addedHandlers.isEmpty()) - return this; - Collection newHandlers = new ArrayList(getAll()); - newHandlers.addAll(addedHandlers); - return ElementClass.compile(newHandlers).setId(id); - } - - public ElementClass setId(String id) { - this.id = id; - return this; - } - - public String getId() { - return id; - } - -} +/******************************************************************************* + * 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.element; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import org.simantics.g2d.diagram.impl.AbstractHandlerClass; +import org.simantics.g2d.element.handler.ElementHandler; +import org.simantics.g2d.element.handler.HandleMouseEvent; +import org.simantics.g2d.element.handler.InternalSize; +import org.simantics.g2d.element.handler.Move; +import org.simantics.g2d.element.handler.Rotate; +import org.simantics.g2d.element.handler.SceneGraph; +import org.simantics.g2d.element.handler.Transform; +import org.simantics.g2d.element.handler.Validator; + +/** + * ElementClass is a class of elements. It consists of element handlers. + * Each handler contributes functions to some aspects of the diagram. + *

+ * Handlers are ordered. For instance, if there are multiple Paint handlers, + * the paint order is determined by the order of the handlers in the class. + *

+ * The minimum requirement is implementation for the following interfaces: + * @see Transform + * @see InternalSize + * + * Typical element class has the following implementations: + * @see Transform + * @see SceneGraph + * @see Move + * @see Rotate + * @see InternalSize or @see Scale + * @see Selectable + * + * Interactive element classes have implementation to: + * @see HandleMouseEvent + * + * See also: + * @see Validator + * + * See package org.simantics.g2d.element.handler for handler interfaces. + * See package org.simantics.g2d.element.handler.impl for common implementations. + * Note that some implementations implement several interface. Also note that + * some handler interfaces may be implemented several times (e.g. Paint, + * Validator, ToolTip, Pick). In contrast, some handlers may have at most one + * implementation, such as Transform, Move, Rotate, Scale, Size. + *

+ * Tip: Use SymbolUtil (CanvasParticipant) to manage graphical resources (images). + * + * @author Toni Kalajainen + */ +public final class ElementClass extends AbstractHandlerClass { + + private static final long serialVersionUID = -446421782709451743L; + + /** Handler with this annotation may have at most one implementation */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + public static @interface Single {} + + /** Handler with this annotation must be implemented (once or more) */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + public static @interface Required {} + + private static final Class[] REQUIRED_HANDLERS = + new Class[] {Transform.class, InternalSize.class}; + + private String id = ""; + + /** + * Compile new element class from a set of handler + * @param contributions + * @return + */ + public static ElementClass compile(Collection contributions) + { + return new ElementClass(contributions); + } + + public static ElementClass compile(Collection contributions, boolean check) + { + return new ElementClass(contributions, check); + } + + /** + * Compile new element class from a set of handler + * @param contributions + * @return + */ + public static ElementClass compile(ElementHandler... contributions) + { + if (contributions.length == 0) + return new ElementClass(Arrays.asList(contributions)); + ArrayList al = new ArrayList(contributions.length); + for (ElementHandler eh : contributions) + al.add(eh); + return new ElementClass(al); + } + + ElementClass(Collection contributions) { + super(contributions); + assertClassValid(contributions); + } + + ElementClass(Collection contributions, boolean check) { + super(contributions); + if(check) assertClassValid(contributions); + } + + /** + * Validates that handler is valid. + * + * @param contributions + * @return + */ + public static void assertClassValid(Collection contributions) + { + // 1. Verify requirements + nextRequirement: + for (Class requiredClass : REQUIRED_HANDLERS) { + for (ElementHandler eh : contributions) + if (requiredClass.isInstance(eh)) + continue nextRequirement; + throw new Error("Element class does not implement "+requiredClass.getName()); + } + + // 2. Verify singletons + // 2.1. Collect implemented handlers + Set> implementedHandlerInterfaces = new HashSet>(); + for (ElementHandler eh : contributions) + _traverseElementHandlerInterfaces(eh.getClass(), implementedHandlerInterfaces); + + // 2.2. Verify singletons are implemented only once + for (Class ehc : implementedHandlerInterfaces) + { + if (!_isSingletonHandler(ehc)) continue; + int implementationCount = 0; + for (ElementHandler eh : contributions) + { + if (!ehc.isInstance(eh)) continue; + implementationCount++; + } + if (implementationCount>1) + throw new Error("Element class has "+implementationCount+" implementations to a _singleton_ element handler \""+ehc.getName()+"\": " + contributions); + } + } + + private static boolean _isSingletonHandler(Class elementHandlerClass) + { + Single s = elementHandlerClass.getAnnotation(Single.class); + return s != null; + } + + @SuppressWarnings("unchecked") + private static void _traverseElementHandlerInterfaces(Class clazz, Collection> result) + { + // Add implemented interfaces (that are inherited from ElementHandler) + for (Class inf : clazz.getInterfaces()) + { + if (!ElementHandler.class.isAssignableFrom(inf)) continue; + result.add((Class)inf); + } + + // Traverse parent + Class superType = clazz.getSuperclass(); + if (superType!=null) + _traverseElementHandlerInterfaces(superType, result); + } + + @Override + public String toString() { + if (!id.isEmpty()) + return id; + + StringBuilder sb = new StringBuilder(); + sb.append("["); + int i=0; + for (ElementHandler eh : super.getAll()) + { + if (i++>0) sb.append(", "); + sb.append( eh.getClass().getSimpleName() ); + } + sb.append("]"); + return sb.toString(); + } + + public ElementClass newClassWith(ElementHandler... addedHandlers) { + if (addedHandlers.length == 0) + return this; + Collection newHandlers = new ArrayList(getAll()); + for (ElementHandler h : addedHandlers) + newHandlers.add(h); + return ElementClass.compile(newHandlers).setId(id); + } + + public ElementClass newClassWith(boolean check, ElementHandler... addedHandlers) { + if (addedHandlers.length == 0) + return this; + Collection newHandlers = new ArrayList(getAll()); + for (ElementHandler h : addedHandlers) + newHandlers.add(h); + return ElementClass.compile(newHandlers, check).setId(id); + } + + public ElementClass newClassWith(Collection addedHandlers) { + if (addedHandlers.isEmpty()) + return this; + Collection newHandlers = new ArrayList(getAll()); + newHandlers.addAll(addedHandlers); + return ElementClass.compile(newHandlers).setId(id); + } + + public ElementClass setId(String id) { + this.id = id; + return this; + } + + public String getId() { + return id; + } + +}