From eefb44b4447e7ad8335378b97e589d00e0f19666 Mon Sep 17 00:00:00 2001
From: Simon Barner <barner@fortiss.org>
Date: Tue, 26 Aug 2014 07:55:14 +0000
Subject: [PATCH] - Revise and document IAnnotationValueProvider API and add
 some TODOs for further refinement - Provide generic abstract base classes:  
 - ValueProviderBase -> for generic annotated specifications   -
 SinglePrimitiveAttributeValueProviderBase, for annotated specifications whose
 storage is implemented by a single primitive attribute (Double, Integer,
 String, etc.)

---
 .../base/ui/annotation/AnnotationEntry.java   |   2 +-
 .../ui/annotation/AnnotationValueService.java |  31 ++--
 .../IAnnotationValueProvider.java             | 110 ++++++++---
 ...lePrimitiveAttributeValueProviderBase.java | 173 ++++++++++++++++++
 .../valueprovider/ValueProviderBase.java      |  86 +++++++++
 5 files changed, 369 insertions(+), 33 deletions(-)
 create mode 100644 org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/valueprovider/SinglePrimitiveAttributeValueProviderBase.java
 create mode 100644 org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/valueprovider/ValueProviderBase.java

diff --git a/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/AnnotationEntry.java b/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/AnnotationEntry.java
index 829d303e7..8ab3644ed 100644
--- a/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/AnnotationEntry.java
+++ b/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/AnnotationEntry.java
@@ -167,7 +167,7 @@ public final class AnnotationEntry {
 			Class<? extends IAnnotatedSpecification> clazz, Object parent) {
 		for(IAnnotatedSpecification s : specificationsList) {
 			if(clazz.isInstance(s)) {
-				return providerSpecMapping.get(clazz).getEditElement(viewer, clazz, parent);
+				return providerSpecMapping.get(clazz).getEditingSupport(viewer, clazz, parent);
 			}
 		}
 
diff --git a/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/AnnotationValueService.java b/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/AnnotationValueService.java
index 2c53dbbdd..1681a2aa4 100644
--- a/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/AnnotationValueService.java
+++ b/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/AnnotationValueService.java
@@ -82,15 +82,24 @@ public class AnnotationValueService extends
 
 						List<? extends IModelElement> allChildren =
 								getAllChildren(root, element.getClass(),
-										annotationProvider.filterOut());
+										annotationProvider.excludeModelElementTypeFromAnnotatedSpecification());
 
 						for(final IModelElement e : allChildren) {
 							IAnnotatedSpecification annotatedSpecification =
-									EcoreUtils.pickFirstInstanceOf(annotationProvider.getClazz(),
+									EcoreUtils.pickFirstInstanceOf(
+											annotationProvider.getAnnotationClazz(),
 											e.getSpecifications());
 							if(annotatedSpecification == null) {
-								annotatedSpecification =
-										annotationProvider.addNewSpecToModelElement(e);
+								try {
+									annotatedSpecification =
+											annotationProvider
+													.getAnnotatedSpecificationForModelElement(e);
+								} catch(Exception ex) {
+									System.out
+											.println("Error instantating new annotation: createAnnotatedSpecificationForModelElement() failed for " +
+													annotationProvider.getAnnotationClazz() + ".");
+									return;
+								}
 							}
 
 							boolean add = true;
@@ -122,11 +131,11 @@ public class AnnotationValueService extends
 	 * filterOut list
 	 */
 	private <S, T> List<? extends IModelElement> getAllChildren(EObject element,
-			Class<? extends IModelElement> type, List<Class<? extends EObject>> filterOut) {
+			Class<? extends IModelElement> type, List<Class<? extends EObject>> modelElementTypesExcludedFromAnnotatedSpecification) {
 
 		List<? extends IModelElement> results = new ArrayList<IModelElement>();
-		if(filterOut != null)
-			getAllChildren2(element, type, filterOut, results);
+		if(modelElementTypesExcludedFromAnnotatedSpecification != null)
+			getAllChildren2(element, type, modelElementTypesExcludedFromAnnotatedSpecification, results);
 		else
 			results = EcoreUtils.getChildrenWithType(element, type);
 		return results;
@@ -136,21 +145,21 @@ public class AnnotationValueService extends
 	/** depth search for desired elements */
 	@SuppressWarnings("unchecked")
 	private <T, S> void getAllChildren2(EObject element, Class<? extends IModelElement> type,
-			List<Class<? extends EObject>> filterOut, final List<T> results) {
+			List<Class<? extends EObject>> modelElementTypesExcludedFromAnnotatedSpecification, final List<T> results) {
 
 		EList<EObject> eContents = element.eContents();
 		for(EObject o : eContents) {
 			if(type.isInstance(o)) {
 				results.add((T)o);
-				getAllChildren2(o, type, filterOut, results);
+				getAllChildren2(o, type, modelElementTypesExcludedFromAnnotatedSpecification, results);
 			} else {
 				boolean filter = false;
-				for(Class<?> c : filterOut) {
+				for(Class<?> c : modelElementTypesExcludedFromAnnotatedSpecification) {
 					if(c.isInstance(o))
 						filter = true;
 				}
 				if(!filter)
-					getAllChildren2(o, type, filterOut, results);
+					getAllChildren2(o, type, modelElementTypesExcludedFromAnnotatedSpecification, results);
 			}
 		}
 	}
diff --git a/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/valueprovider/IAnnotationValueProvider.java b/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/valueprovider/IAnnotationValueProvider.java
index fecdc43f2..c5ab4f930 100644
--- a/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/valueprovider/IAnnotationValueProvider.java
+++ b/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/valueprovider/IAnnotationValueProvider.java
@@ -24,14 +24,41 @@ import org.eclipse.jface.viewers.ColumnViewer;
 import org.eclipse.jface.viewers.EditingSupport;
 import org.fortiss.tooling.base.model.element.IAnnotatedSpecification;
 import org.fortiss.tooling.base.model.element.IModelElement;
+import org.fortiss.tooling.base.ui.annotation.view.GenericAnnotationView;
 import org.fortiss.tooling.kernel.service.base.IEObjectAware;
 
 /**
- * Interface for {@link IAnnotatedSpecification}s. It defines the methods that need to be
- * implemented by a value provider to define the handling of the stored data and to retrieve
- * information relevant to the UI, like the support for complex value types.
+ * Value provider interface for {@link IAnnotatedSpecification}s that
  * 
- * @author eder, diewald
+ * <p>
+ * <ul>
+ * <li>establishes the link between the model (data storage) and the UI (data exchange and
+ * conversion)</li>
+ * <li>provides related annotation data-type specific operations, e.g. factory methods to create
+ * <ul>
+ * <li>new annotation instances</li>
+ * <li>{@link EditingSupport} UI controls for the generic table-based {@link GenericAnnotationView}</li>
+ * </ul>
+ * </ul>
+ * 
+ * Concrete annotations need to provide specific implementations of this interface to perform the
+ * data exchange and conversion, and to provide the required factory methods.
+ * </p>
+ * 
+ * <p>
+ * The interface / some of its methods take two template parameters
+ * 
+ * <ul>
+ * <li><tt>T</tt>: Meta-model type of the specification, i.e. the particular subclass of
+ * {@link IAnnotatedSpecification} supported by the respective implementation of this
+ * {@link IAnnotationValueProvider} interface.</li>
+ * <li><tt>U</tt>: Data type used in the UI to support the respective annotation. For <tt>U</tt>, a
+ * reasonable {@code toString()} implementation must be provided that is used by the framework to
+ * render the value of the annotation in text-based editing support UI controls.</li>
+ * </ul>
+ * </p>
+ * 
+ * @author eder, diewald, barner
  * @author $Author$
  * @version $Rev$
  * @ConQAT.Rating YELLOW Hash: 8BE6A1ECB76A0AD4141B7EE11CAE21DA
@@ -43,44 +70,85 @@ public interface IAnnotationValueProvider<T extends IAnnotatedSpecification> ext
 	 * Returns a boolean whether the implemented specification supports storing multiple values.
 	 * This method is used to handle these classes in the view part in a simpler manner.
 	 */
+	// TODO: Review
 	public boolean allowsMultipleValues();
 
-	/** Returns the name of the Annotation for 'specification' */
+	/**
+	 * Returns the name of the annotation (e.g., used as column label in the table-based
+	 * {@link GenericAnnotationView}).
+	 */
 	public String getAnnotationName(T specification);
 
-	/** Sets a new name for the annotation and overrides the old one */
-	public void setAnnotationName(String name, T specification) throws IllegalArgumentException;
+	/**
+	 * Sets a new name for the annotation (e.g., used as column label in the table-based
+	 * {@link GenericAnnotationView}). The return value indicates if the rename operation was
+	 * successful.
+	 */
+	public boolean setAnnotationName(String name, T specification);
 
-	/** Returns the value of the annotation for 'specification' */
-	public <V> V getAnnotationValue(T specification);
+	/** Returns the value of the annotation in the user-specified data-type. */
+	public <U> U getAnnotationValue(T specification);
 
-	/** Sets a new value for the annotation. */
-	public <V> void setAnnotationValue(V value, T specification) throws IllegalArgumentException;
+	/**
+	 * Sets a new value for the annotation, taking an instance the user-specified data type has an
+	 * input.
+	 * 
+	 * @throws Exception
+	 *             is thrown if the conversion from the UI internal value representation ({@code U})
+	 *             to the respective specialization of {@link IAnnotatedSpecification} has failed,
+	 *             or if this operation is not available.
+	 */
+	public <U> void setAnnotationValue(U value, T specification) throws Exception;
 
-	/** Sets a new value in the annotation from a {@link String} representation. */
+	/**
+	 * Sets a new value in the annotation from a {@link String} representation.
+	 * 
+	 * @throws Exception
+	 *             is thrown if the conversion from {@link String} to the respective specialization
+	 *             of {@link IAnnotatedSpecification} has failed, or if this operation is not
+	 *             available.
+	 */
 	public void setAnnotationValue(String value, T specification) throws Exception;
 
 	/**
 	 * If this method does not return null, a combo box is provided with the specific return values
 	 */
+	// TODO: Review / Remove from interface, implement {@link EditingSupport} for Enums instead
 	public List<String> getFixedValues();
 
 	/**
-	 * Returns a custom EditingSupport element for the annotation view
+	 * Returns a custom {@link EditingSupport} support element for the table-based
+	 * {@link GenericAnnotationView}. In case {@code null} is returned, the annotation framework
+	 * tries to fall back to one of the stand {@link EditingSupport}s instead.
 	 */
-	public EditingSupport getEditElement(ColumnViewer viewer,
+	public EditingSupport getEditingSupport(ColumnViewer viewer,
 			Class<? extends IAnnotatedSpecification> class1, Object parent);
 
 	/**
-	 * Adds a new {@link IAnnotatedSpecification} of type T to the given model element and returns
-	 * it
+	 * Factory method that creates a new {@link IAnnotatedSpecification} specialization of type T to
+	 * the given model {@code element} and returns a reference to it.
+	 * 
+	 * In case the annotation already exists in the model, this method does not create a new object,
+	 * but returns the existing one.
+	 * 
+	 * @throws Exception
+	 *             is thrown if no existing element could be retrieved, and the creation of a new
+	 *             element has failed.
 	 */
-	public T addNewSpecToModelElement(IModelElement element);
+	public T getAnnotatedSpecificationForModelElement(IModelElement element) throws Exception;
 
-	/** Returns the class of T */
-	public Class<T> getClazz();
+	/**
+	 * Returns the concrete specialization {@code T} of {@link IAnnotatedSpecification} supported by
+	 * this specialization of {@link IAnnotationValueProvider}.
+	 */
+	public Class<T> getAnnotationClazz();
 
-	/** Returns a List of Objects which children will not have {@link IAnnotatedSpecification}s */
-	public List<Class<? extends EObject>> filterOut();
+	/**
+	 * Returns a {@link List} of model element types which will not have the
+	 * {@link IAnnotatedSpecification} managed by this {@link IAnnotationValueProvider}, refining
+	 * the registration of the respective {@link IAnnotatedSpecification} in the
+	 * {@code org.fortiss.tooling.base.ui.annotationViewPart} extension point.
+	 */
+	public List<Class<? extends EObject>> excludeModelElementTypeFromAnnotatedSpecification();
 
 }
diff --git a/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/valueprovider/SinglePrimitiveAttributeValueProviderBase.java b/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/valueprovider/SinglePrimitiveAttributeValueProviderBase.java
new file mode 100644
index 000000000..b8ad8fb72
--- /dev/null
+++ b/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/valueprovider/SinglePrimitiveAttributeValueProviderBase.java
@@ -0,0 +1,173 @@
+/*--------------------------------------------------------------------------+
+$Id$
+|                                                                          |
+| Copyright 2014 ForTISS GmbH                     |
+|                                                                          |
+| Licensed under the Apache License, Version 2.0 (the "License");          |
+| you may not use this file except in compliance with the License.         |
+| You may obtain a copy of the License at                                  |
+|                                                                          |
+|    http://www.apache.org/licenses/LICENSE-2.0                            |
+|                                                                          |
+| Unless required by applicable law or agreed to in writing, software      |
+| distributed under the License is distributed on an "AS IS" BASIS,        |
+| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
+| See the License for the specific language governing permissions and      |
+| limitations under the License.                                           |
++--------------------------------------------------------------------------*/
+package org.fortiss.tooling.base.ui.annotation.valueprovider;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EFactory;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.fortiss.tooling.base.model.element.IAnnotatedSpecification;
+import org.fortiss.tooling.base.model.element.IModelElement;
+import org.fortiss.tooling.base.model.element.IModelElementSpecification;
+
+/**
+ * Base class for {@link IAnnotationValueProvider}s for {@link IAnnotatedSpecification}s that
+ * contain only a single (primitive type) attribute.
+ * 
+ * @author barner
+ * @author $Author$
+ * @version $Rev$
+ * @ConQAT.Rating RED Hash:
+ */
+public abstract class SinglePrimitiveAttributeValueProviderBase<T extends IAnnotatedSpecification>
+		extends ValueProviderBase<T> {
+
+	/**
+	 * {@link EClass} of {@link IAnnotatedSpecification} managed by this
+	 * {@link IAnnotationValueProvider}.
+	 */
+	final private EClass annotatedSpecificationEClass;
+
+	/**
+	 * {@link EFactory} of ECore model that contains {@link IAnnotatedSpecification} identified by
+	 * {@code annotatedSpecificationEClass} (used to create new instances of the annotation).
+	 */
+	final private EFactory eFactory;
+
+	/**
+	 * {@link Class} of the primitive attribute used to implemented the storage of the underlying
+	 * {@link IAnnotatedSpecification}.
+	 */
+	final private Class<?> attributeClazz;
+
+	/**
+	 * String representation of default value used to initialize the primitive attribute of the
+	 * underlying {@link IAnnotatedSpecification}. If it is {@code null}, the default constructor
+	 * of the data type identified by {@code attributeClazz} is used to create the attribute
+	 * instance.
+	 */
+	final String attributeDefaultValue;
+
+	/**
+	 * Constructs a {@link IAnnotationValueProvider} for a {@link IAnnotatedSpecification} that
+	 * whose storage is implemented by a single, primitive attribute. Unless the type of the
+	 * attribute (contained in {@code attributeClazz} is {@link Double} or {@link Integer}, this
+	 * constructor assumes that the attribute is default constructible. Otherwise, the new instances
+	 * of the attribute are initialized to {@code 0}.
+	 */
+	public SinglePrimitiveAttributeValueProviderBase(EClass annotatedSpecificationEClass,
+			EFactory eFactory, Class<?> attributeClazz) {
+		this(annotatedSpecificationEClass, eFactory, attributeClazz,
+				getDefaultValueInitializer(attributeClazz));
+	}
+
+	/**
+	 * Constructs a {@link IAnnotationValueProvider} for a {@link IAnnotatedSpecification} that
+	 * whose storage is implemented by a single, primitive attribute. The constructor assumes that
+	 * the attribute type has a constructor that can initialize the new instance from a
+	 * {@link String}.
+	 */
+	public SinglePrimitiveAttributeValueProviderBase(EClass annotatedSpecificationEClass,
+			EFactory eFactory, Class<?> attributeClazz, final String attributeDefaultValue) {
+		this.annotatedSpecificationEClass = annotatedSpecificationEClass;
+		this.eFactory = eFactory;
+		this.attributeClazz = attributeClazz;
+		this.attributeDefaultValue = attributeDefaultValue;
+	}
+
+	/**
+	 * Returns the initializer {@link String} in case this {@link IAnnotationValueProvider} is
+	 * constructed without specifying a default value, i.e. using
+	 * {@link #SinglePrimitiveAttributeValueProviderBase(EClass, EFactory, Class)}, or {@code null}
+	 * that indicates that the attribute type's default constructor should be used to initialize new
+	 * instances of the {@link IAnnotatedSpecification}'s attribute.
+	 */
+	private static String getDefaultValueInitializer(Class<?> attributeClazz) {
+		if(attributeClazz.equals(Double.class) || attributeClazz.equals(Integer.class)) {
+			return "0";
+		}
+
+		return null;
+	}
+
+	/**
+	 * Returns the structural {@link EStructuralFeature} of the attribute used to implement the
+	 * storage of the underlying {@link IAnnotatedSpecification}. This implementation returns the
+	 * first attribute of the annotation identified by {@code annotatedSpecificationEClass}.
+	 */
+	private EStructuralFeature getStructuralFeature() {
+		return annotatedSpecificationEClass.getEStructuralFeatures().get(0);
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public void setAnnotationValue(String value, T specification) throws Exception {
+		specification.eSet(getStructuralFeature(),
+				attributeClazz.getDeclaredConstructor(String.class).newInstance(value));
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public <U> void setAnnotationValue(U value, T specification) throws Exception {
+		specification.eSet(getStructuralFeature(), value);
+	}
+
+	/** {@inheritDoc} */
+	@SuppressWarnings("unchecked")
+	@Override
+	public <U> U getAnnotationValue(T specification) {
+		return (U)specification.eGet(getStructuralFeature());
+	}
+
+	/** {@inheritDoc} */
+	@SuppressWarnings("unchecked")
+	@Override
+	public T getAnnotatedSpecificationForModelElement(IModelElement element) throws Exception {
+		// Return existing annotation
+		for(IModelElementSpecification specification : element.getSpecifications()) {
+			if(annotatedSpecificationEClass.equals(specification.eClass())) {
+				return (T)specification;
+			}
+		}
+
+		// Create annotation
+		T specification = (T)eFactory.create(annotatedSpecificationEClass);
+		Object attributeValue;
+
+		// Create annotation attribute
+		if(attributeDefaultValue != null) {
+			attributeValue =
+					attributeClazz.getDeclaredConstructor(String.class).newInstance(
+							attributeDefaultValue);
+		} else {
+			attributeValue = attributeClazz.newInstance();
+		}
+		specification.eSet(getStructuralFeature(), attributeValue);
+
+		// Hook specification to model element
+		element.addSpecification(specification);
+
+		return specification;
+	}
+
+	/** {@inheritDoc} */
+	@SuppressWarnings("unchecked")
+	@Override
+	public Class<T> getAnnotationClazz() {
+		return (Class<T>)annotatedSpecificationEClass.getClass();
+	}
+}
diff --git a/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/valueprovider/ValueProviderBase.java b/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/valueprovider/ValueProviderBase.java
new file mode 100644
index 000000000..abe8ef1c4
--- /dev/null
+++ b/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/valueprovider/ValueProviderBase.java
@@ -0,0 +1,86 @@
+/*--------------------------------------------------------------------------+
+$Id$
+|                                                                          |
+| Copyright 2014 ForTISS GmbH                     |
+|                                                                          |
+| Licensed under the Apache License, Version 2.0 (the "License");          |
+| you may not use this file except in compliance with the License.         |
+| You may obtain a copy of the License at                                  |
+|                                                                          |
+|    http://www.apache.org/licenses/LICENSE-2.0                            |
+|                                                                          |
+| Unless required by applicable law or agreed to in writing, software      |
+| distributed under the License is distributed on an "AS IS" BASIS,        |
+| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
+| See the License for the specific language governing permissions and      |
+| limitations under the License.                                           |
++--------------------------------------------------------------------------*/
+package org.fortiss.tooling.base.ui.annotation.valueprovider;
+
+import java.util.List;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.EditingSupport;
+import org.fortiss.tooling.base.model.element.IAnnotatedSpecification;
+
+/**
+ * Base class for {@link IAnnotationValueProvider}s.
+ * 
+ * @author diewald
+ * @author $Author$
+ * @version $Rev$
+ * @ConQAT.Rating RED Hash:
+ */
+public abstract class ValueProviderBase<T extends IAnnotatedSpecification> implements
+		IAnnotationValueProvider<T> {
+
+	/** {@inheritDoc} */
+	@Override
+	public boolean setAnnotationName(String name, T specification) {
+		// Renaming this annotation is not supported.
+		return false;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public void setAnnotationValue(String value, T specification) throws Exception {
+		throw new Exception(
+				"setAnnotationValue(String) is not supported / has not been implemented for annotations of type " +
+						getClass());
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public <U> void setAnnotationValue(U value, T specification) throws Exception {
+		throw new Exception(
+				"setAnnotationValue(U) is not supported / has not been implemented for annotations of type " +
+						getClass());
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public boolean allowsMultipleValues() {
+		return false;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public List<String> getFixedValues() {
+		return null;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public EditingSupport getEditingSupport(ColumnViewer viewer,
+			Class<? extends IAnnotatedSpecification> class1, Object parent) {
+		return null;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public List<Class<? extends EObject>> excludeModelElementTypeFromAnnotatedSpecification() {
+		return null;
+	}
+
+}
-- 
GitLab