/*
Copyright (C) 2007-2009 by Claas Wilke (claaswilke@gmx.net)
This file is part of the Ecore Meta Model of Dresden OCL2 for Eclipse.
Dresden OCL2 for Eclipse is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your option)
any later version.
Dresden OCL2 for Eclipse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
for more details.
You should have received a copy of the GNU Lesser General Public License along
with Dresden OCL2 for Eclipse. If not, see <http://www.gnu.org/licenses/>.
*/
package org.dresdenocl.modelinstancetype.ecore.internal.modelinstance;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.log4j.Logger;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.UniqueEList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.osgi.util.NLS;
import org.dresdenocl.modelinstance.base.AbstractModelInstance;
import org.dresdenocl.modelinstancetype.ecore.EcoreModelInstanceTypePlugin;
import org.dresdenocl.modelinstancetype.ecore.internal.msg.EcoreModelInstanceTypeMessages;
import org.dresdenocl.modelinstancetype.ecore.internal.provider.EcoreModelInstanceProvider;
import org.dresdenocl.modelinstancetype.ecore.internal.util.EcoreModelInstanceTypeUtility;
import org.dresdenocl.modelinstancetype.exception.AsTypeCastException;
import org.dresdenocl.modelinstancetype.exception.CopyForAtPreException;
import org.dresdenocl.modelinstancetype.exception.OperationAccessException;
import org.dresdenocl.modelinstancetype.exception.OperationNotFoundException;
import org.dresdenocl.modelinstancetype.exception.PropertyAccessException;
import org.dresdenocl.modelinstancetype.exception.PropertyNotFoundException;
import org.dresdenocl.modelinstancetype.exception.TypeNotFoundInModelException;
import org.dresdenocl.modelinstancetype.types.IModelInstanceBoolean;
import org.dresdenocl.modelinstancetype.types.IModelInstanceCollection;
import org.dresdenocl.modelinstancetype.types.IModelInstanceElement;
import org.dresdenocl.modelinstancetype.types.IModelInstanceEnumerationLiteral;
import org.dresdenocl.modelinstancetype.types.IModelInstanceInteger;
import org.dresdenocl.modelinstancetype.types.IModelInstanceObject;
import org.dresdenocl.modelinstancetype.types.IModelInstancePrimitiveType;
import org.dresdenocl.modelinstancetype.types.IModelInstanceReal;
import org.dresdenocl.modelinstancetype.types.IModelInstanceString;
import org.dresdenocl.modelinstancetype.types.base.AbstractModelInstanceObject;
import org.dresdenocl.pivotmodel.Operation;
import org.dresdenocl.pivotmodel.Parameter;
import org.dresdenocl.pivotmodel.Property;
import org.dresdenocl.pivotmodel.Type;
/**
* <p>
* Implements the interface {@link IModelInstanceObject} for EMF Ecore
* {@link EObject}s.
* </p>
*
* @author Claas Wilke
*/
public class EcoreModelInstanceObject extends AbstractModelInstanceObject
implements IModelInstanceObject {
/** The {@link Logger} for this class. */
private static final Logger LOGGER = EcoreModelInstanceTypePlugin
.getLogger(EcoreModelInstanceProvider.class);
/** The {@link EObject} adapted by this {@link EcoreModelInstanceObject}. */
private EObject myEObject;
/**
* The Java {@link Class} this {@link AbstractModelInstanceObject} is casted
* to. This is required to access the right {@link Property}s and
* {@link Operation}s.
*/
private Class<?> myAdaptedType;
/**
* The {@link EcoreModelInstanceFactory} of this
* {@link EcoreModelInstanceObject}. Required to adapt results of
* {@link Property} and {@link Operation} invocations.
*/
private EcoreModelInstanceFactory myFactory;
/**
* <p>
* Creates a new {@link EcoreModelInstanceObject} for a given
* {@link EObject} and a given {@link Set} of {@link Type}s.
* </p>
*
* @param object
* The {@link EObject} that shall be adapted by this
* {@link EcoreModelInstanceObject}.
* @param type
* The {@link Type} the adapted {@link EObject} implements.
* @param originalType
* The original {@link Type} the adapted {@link EObject}
* implements (as after the object has been casted to another
* {@link Type}.)
* @param factory
* The {@link EcoreModelInstanceFactory} of this
* {@link EcoreModelInstanceObject}. Required to adapt results of
* {@link Property} and {@link Operation} invocations.
*/
protected EcoreModelInstanceObject(EObject eObject, Type type,
Type originalType, EcoreModelInstanceFactory factory) {
super(type, originalType);
/* Probably debug the entry of this method. */
if (LOGGER.isDebugEnabled()) {
String msg;
msg = "EcoreModelInstanceObject("; //$NON-NLS-1$
msg += "eObject = " + eObject; //$NON-NLS-1$
msg += ", type = " + type; //$NON-NLS-1$
msg += ", originalType = " + originalType; //$NON-NLS-1$
msg += ", factory = " + factory; //$NON-NLS-1$
msg += ")"; //$NON-NLS-1$
LOGGER.debug(msg);
}
// no else.
this.myEObject = eObject;
if (this.myEObject != null) {
this.myAdaptedType = eObject.getClass();
}
// no else.
this.myType = type;
this.myFactory = factory;
/* Probably debug the exit of this method. */
if (LOGGER.isDebugEnabled()) {
String msg;
msg = "EcoreModelInstanceObject(EObject, Type, IModelInstanceFactory) - exit"; //$NON-NLS-1$
LOGGER.debug(msg);
}
// no else.
}
/**
* <p>
* Creates a new {@link EcoreModelInstanceObject} for a given
* {@link EObject} and a given {@link Set} of {@link Type}s.
* </p>
*
* @param object
* The {@link EObject} that shall be adapted by this
* {@link EcoreModelInstanceObject}.
* @param clazz
* The {@link Class} the adapted {@link EObject} shall be casted
* to.
* @param type
* The {@link Type} the adapted {@link EObject} implements.
* @param originalType
* The original {@link Type} the adapted {@link EObject}
* implements (as after the object has been casted to another
* {@link Type}.)
* @param factory
* The {@link EcoreModelInstanceFactory} of this
* {@link EcoreModelInstanceObject}. Required to adapt results of
* {@link Property} and {@link Operation} invocations.
*/
protected EcoreModelInstanceObject(EObject eObject, Class<?> clazz,
Type type, Type originalType, EcoreModelInstanceFactory factory) {
super(type, originalType);
/* Probably debug the entry of this method. */
if (LOGGER.isDebugEnabled()) {
String msg;
msg = "EcoreModelInstanceObject("; //$NON-NLS-1$
msg += "eObject = " + eObject; //$NON-NLS-1$
msg += ", clazz = " + clazz; //$NON-NLS-1$
msg += ", type = " + type; //$NON-NLS-1$
msg += ", originalType = " + originalType; //$NON-NLS-1$
msg += ", factory = " + factory; //$NON-NLS-1$
msg += ")"; //$NON-NLS-1$
LOGGER.debug(msg);
}
// no else.
this.myEObject = eObject;
this.myAdaptedType = clazz;
this.myType = type;
this.myFactory = factory;
/* Probably debug the exit of this method. */
if (LOGGER.isDebugEnabled()) {
String msg;
msg = "EcoreModelInstanceObject(EObject, Class, Type, IModelInstanceFactory) - exit"; //$NON-NLS-1$
LOGGER.debug(msg);
}
// no else.
}
/*
* (non-Javadoc)
*
* @see
* org.dresdenocl.modelbus.modelinstance.types.IModelInstanceElement
* #asType(org.dresdenocl.pivotmodel.Type)
*/
public IModelInstanceElement asType(Type type) throws AsTypeCastException {
if (type == null) {
throw new IllegalArgumentException(
"Parameter 'type' must not be null.");
}
// no else.
IModelInstanceElement result;
/* Check if cast is possible. */
if (this.getOriginalType().conformsTo(type)) {
/* If the object is null, perform the cast. */
if (this.myEObject == null) {
result = new EcoreModelInstanceObject(null, type, this
.getOriginalType(), this.myFactory);
}
/*
* Else try to find a class representing the given type (required
* for probable method invocations later on).
*/
else {
/* Try to find a class that is represented by the given type. */
Class<?> typeClass;
typeClass = EcoreModelInstanceTypeUtility.findClassOfType(
this.myEObject.getClass(), type);
/* If no class has been found, throw an exception. */
if (typeClass == null) {
String msg;
msg = EcoreModelInstanceTypeMessages.EcoreModelInstance_CannotCastTypeClassNotFound;
msg = NLS.bind(msg, this.getName(), type);
throw new AsTypeCastException(msg);
}
// no else.
/* Cast this object to the found type. */
result = new EcoreModelInstanceObject(this.myEObject,
typeClass, type, this.getOriginalType(), this.myFactory);
}
// end else.
}
/* Else cannot cast. */
else {
String msg;
msg = EcoreModelInstanceTypeMessages.EcoreModelInstance_CannotCastTypeClassNotFound;
msg = NLS.bind(msg, this.getName(), type);
throw new AsTypeCastException(msg);
}
// end else.
return result;
}
/**
* <p>
* Performs a copy of the adapted element of this
* {@link IModelInstanceElement} that can be used to store it as a @pre
* value of a postcondition's expression. The method should copy the adapted
* object and all its references that are expected to change during the
* methods execution the postcondition is defined on.
* </p>
*
* <p>
* For {@link EcoreModelInstanceObject}s this method tries to clone the
* adapted {@link Object} if the {@link Object} implements {@link Clonable}
* . Else the {@link Object} will be copied using an probably existing empty
* {@link Constructor} and a flat copy will be created (means all attributes
* and associations will lead to the same values and identities. <strong>If
* neither the <code>clone()</code> method nor the empty {@link Constructor}
* are provided, this operation will fail with an
* {@link CopyForAtPreException}.</strong>
* </p>
*
* @return A copy of the adapted element.
* @throws CopyForAtPreException
* Thrown, if an error during the copy process occurs.
*/
public IModelInstanceElement copyForAtPre() throws CopyForAtPreException {
IModelInstanceElement result;
result = null;
/* Check if the adapted object is clone-able. */
if (this.myEObject instanceof Cloneable) {
try {
result = copyForAtPreWithClone();
}
catch (CopyForAtPreException e) {
/* Catch the exception and try to copy again with reflections. */
}
}
// no else.
/*
* If not object has been created yet. Try to copy the object with
* reflections.
*/
if (result == null) {
copyForAtPreWithReflections();
}
// no else.
return result;
}
/*
* (non-Javadoc)
*
* @see
* org.dresdenocl.modelbus.modelinstance.types.IModelInstanceObject
* #getObject()
*/
public Object getObject() {
return this.myEObject;
}
/*
* (non-Javadoc)
*
* @see
* org.dresdenocl.modelbus.modelinstance.types.IModelInstanceObject
* #getProperty(org.dresdenocl.pivotmodel.Property)
*/
public IModelInstanceElement getProperty(Property property)
throws PropertyAccessException, PropertyNotFoundException {
if (property == null) {
throw new IllegalArgumentException(
"Parameter 'property' must not be null.");
}
// no else.
IModelInstanceElement result;
/* Check if this object is undefined. */
if (this.myEObject == null) {
/* The result will be undefined as well. */
result = this.myFactory.createModelInstanceElement(null, property
.getType());
}
/* Else find the StructuralFeature of the property that can be accessed. */
else {
EStructuralFeature sf = this.myEObject.eClass()
.getEStructuralFeature(property.getName());
if (sf == null) {
String msg = EcoreModelInstanceTypeMessages.EcoreModelInstanceObject_PropertyNotFoundInModelInstanceElement;
msg = NLS.bind(msg, property, this.myEObject);
throw new PropertyAccessException(msg);
}
Object adapteeResult = this.myEObject.eGet(sf);
result = AbstractModelInstance.adaptInvocationResult(adapteeResult,
property.getType(), this.myFactory);
}
// end else.
return result;
}
/*
* (non-Javadoc)
*
* @see
* org.dresdenocl.modelbus.modelinstance.types.IModelInstanceObject
* #invokeOperation(org.dresdenocl.pivotmodel.Operation,
* java.util.List)
*/
public IModelInstanceElement invokeOperation(Operation operation,
List<IModelInstanceElement> args)
throws OperationNotFoundException, OperationAccessException {
if (operation == null) {
throw new IllegalArgumentException(
"Parameter 'operation' must not be null.");
}
// no else.
else if (args == null) {
throw new IllegalArgumentException(
"Parameter 'args' must not be null.");
}
// no else.
IModelInstanceElement result;
/* Check if this object is undefined. */
if (this.myEObject == null) {
/* The result will be undefined as well. */
result = this.myFactory.createModelInstanceElement(null, operation
.getType());
}
/* Else find and invoke the operation. */
else {
Method operationMethod;
int argSize;
Class<?>[] argumentTypes;
Object[] argumentValues;
/* Try to find the method to invoke. */
operationMethod = this.findMethodOfAdaptedObject(operation);
argumentTypes = operationMethod.getParameterTypes();
argumentValues = new Object[args.size()];
/* Avoid errors through to much arguments given by the invocation. */
argSize = Math.min(args.size(), operation.getSignatureParameter()
.size());
/* Adapt the argument values. */
for (int index = 0; index < argSize; index++) {
argumentValues[index] = this.createAdaptedElement(args
.get(index), argumentTypes[index]);
}
/* Try to invoke the found method. */
try {
Object adapteeResult;
operationMethod.setAccessible(true);
adapteeResult = operationMethod.invoke(this.myEObject,
argumentValues);
/* Adapt the result to the expected result type. */
if (adapteeResult instanceof EObject) {
try {
Type adapteeType = this.myFactory.getTypeUtility()
.findTypeOfEObjectInModel(
(EObject) adapteeResult);
result = AbstractModelInstance.adaptInvocationResult(
adapteeResult, adapteeType, this.myFactory);
}
catch (TypeNotFoundInModelException e) {
throw new OperationAccessException(
"Result of invocation of Operation '"
+ operation.getName()
+ "' could not be adapted to any model type.",
e);
}
}
// no else.
else {
result = AbstractModelInstance.adaptInvocationResult(
adapteeResult, operation.getType(), this.myFactory);
}
}
catch (IllegalArgumentException e) {
String msg;
msg = EcoreModelInstanceTypeMessages.EcoreModelInstanceObject_OperationAccessFailed;
msg = NLS.bind(msg, operation, e.getMessage());
throw new OperationAccessException(msg, e);
}
catch (IllegalAccessException e) {
String msg;
msg = EcoreModelInstanceTypeMessages.EcoreModelInstanceObject_OperationAccessFailed;
msg = NLS.bind(msg, operation, e.getMessage());
throw new OperationAccessException(msg, e);
}
catch (InvocationTargetException e) {
String msg;
msg = EcoreModelInstanceTypeMessages.EcoreModelInstanceObject_OperationAccessFailed;
msg = NLS.bind(msg, operation, e.getMessage());
throw new OperationAccessException(msg, e);
}
}
// end else.
return result;
}
/*
* (non-Javadoc)
*
* @seeorg.dresdenocl.modelinstancetype.types.base.
* AbstractModelInstanceElement
* #isKindOf(org.dresdenocl.pivotmodel.Type)
*/
@Override
public boolean isKindOf(Type type) {
return this.getOriginalType().conformsTo(type);
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
public String toString() {
StringBuffer result;
result = new StringBuffer();
result.append(this.getClass().getSimpleName());
result.append("[type=");
result.append(this.getType().getName());
result.append(",originalType=");
result.append(this.getOriginalType().getName());
result.append(",");
result.append(this.myEObject.toString());
result.append("]");
return result.toString();
}
/**
* <p>
* A helper method that tries to copy the adapted object using the method
* {@link Object#clone()}.
* </p>
*
* @return A copy of the adapted {@link EObject} of this
* {@link EcoreModelInstanceObject}.
* @throws CopyForAtPreException
* Thrown, if the adapted {@link EObject} cannot be copied via
* clone method.
*/
private IModelInstanceElement copyForAtPreWithClone()
throws CopyForAtPreException {
IModelInstanceElement result;
Method cloneMethod;
/* Try to find and invoke the clone method. */
try {
EObject adaptedResult;
cloneMethod = this.myEObject.getClass().getMethod("clone");
cloneMethod.setAccessible(true);
adaptedResult = (EObject) cloneMethod.invoke(this.myEObject);
result = new EcoreModelInstanceObject(adaptedResult,
this.myAdaptedType, this.myType, this.getOriginalType(),
this.myFactory);
}
catch (SecurityException e) {
String msg;
msg = EcoreModelInstanceTypeMessages.EcoreModelInstanceObject_CannotCopyForAtPre;
msg = NLS.bind(msg, this.getName(), e.getMessage());
throw new CopyForAtPreException(msg, e);
}
catch (NoSuchMethodException e) {
String msg;
msg = EcoreModelInstanceTypeMessages.EcoreModelInstanceObject_CannotCopyForAtPre;
msg = NLS.bind(msg, this.getName(), e.getMessage());
throw new CopyForAtPreException(msg, e);
}
catch (IllegalArgumentException e) {
String msg;
msg = EcoreModelInstanceTypeMessages.EcoreModelInstanceObject_CannotCopyForAtPre;
msg = NLS.bind(msg, this.getName(), e.getMessage());
throw new CopyForAtPreException(msg, e);
}
catch (IllegalAccessException e) {
String msg;
msg = EcoreModelInstanceTypeMessages.EcoreModelInstanceObject_CannotCopyForAtPre;
msg = NLS.bind(msg, this.getName(), e.getMessage());
throw new CopyForAtPreException(msg, e);
}
catch (InvocationTargetException e) {
String msg;
msg = EcoreModelInstanceTypeMessages.EcoreModelInstanceObject_CannotCopyForAtPre;
msg = NLS.bind(msg, this.getName(), e.getMessage());
throw new CopyForAtPreException(msg, e);
}
return result;
}
/**
* <p>
* A helper method that tries to copy the adapted {@link Object} of this
* {@link EcoreModelInstanceObject} with an empty {@link Constructor} based
* on reflections. The copied {@link Object} will be a flat copy of this
* {@link Object}. Thus, the fields will all have the same value and id.
* </p>
*
* @return A copy of the adapted {@link Object} of this
* {@link EcoreModelInstanceObject}.
* @throws CopyForAtPreException
* Thrown, if the adapted {@link EObject} cannot be copied using
* an empty {@link Constructor}.
*/
private IModelInstanceObject copyForAtPreWithReflections()
throws CopyForAtPreException {
IModelInstanceObject result;
Class<?> adapteeClass;
adapteeClass = this.myEObject.getClass();
try {
EObject copiedAdaptedObject;
Constructor<?> emptyConstructor;
/* Try to find an empty constructor. */
emptyConstructor = adapteeClass.getConstructor(new Class[0]);
/* Copy the adapted object. */
copiedAdaptedObject = (EObject) emptyConstructor
.newInstance(new Object[0]);
/* Iterate through the adapteeClass and all its super classes. */
while (adapteeClass != null) {
/* Initialize all fields with the same value. */
for (Field field : adapteeClass.getDeclaredFields()) {
field.setAccessible(true);
/* Do not set static nor final fields. */
if (!(Modifier.isFinal(field.getModifiers()) || Modifier
.isStatic(field.getModifiers()))) {
field.set(copiedAdaptedObject, field
.get(this.myEObject));
}
// no else.
}
// end for.
adapteeClass = adapteeClass.getSuperclass();
}
// end while.
/* Create the adapter. */
result = new EcoreModelInstanceObject(copiedAdaptedObject,
this.myAdaptedType, this.myType, this.getOriginalType(),
this.myFactory);
}
catch (SecurityException e) {
String msg;
msg = EcoreModelInstanceTypeMessages.EcoreModelInstanceObject_CannotCopyForAtPre;
msg = NLS.bind(msg, this.getName(), e.getMessage());
throw new CopyForAtPreException(msg, e);
}
catch (NoSuchMethodException e) {
String msg;
msg = EcoreModelInstanceTypeMessages.EcoreModelInstanceObject_CannotCopyForAtPre;
msg = NLS.bind(msg, this.getName(), e.getMessage());
throw new CopyForAtPreException(msg, e);
}
catch (IllegalArgumentException e) {
String msg;
msg = EcoreModelInstanceTypeMessages.EcoreModelInstanceObject_CannotCopyForAtPre;
msg = NLS.bind(msg, this.getName(), e.getMessage());
throw new CopyForAtPreException(msg, e);
}
catch (InstantiationException e) {
String msg;
msg = EcoreModelInstanceTypeMessages.EcoreModelInstanceObject_CannotCopyForAtPre;
msg = NLS.bind(msg, this.getName(), e.getMessage());
throw new CopyForAtPreException(msg, e);
}
catch (IllegalAccessException e) {
String msg;
msg = EcoreModelInstanceTypeMessages.EcoreModelInstanceObject_CannotCopyForAtPre;
msg = NLS.bind(msg, this.getName(), e.getMessage());
throw new CopyForAtPreException(msg, e);
}
catch (InvocationTargetException e) {
String msg;
msg = EcoreModelInstanceTypeMessages.EcoreModelInstanceObject_CannotCopyForAtPre;
msg = NLS.bind(msg, this.getName(), e.getMessage());
throw new CopyForAtPreException(msg, e);
}
// end try.
return result;
}
/**
* <p>
* Returns or creates the element that is adapted by a given
* {@link IModelInstanceElement}. E.g., if the given
* {@link IModelInstanceElement} is an {@link IModelInstanceObject}, the
* adapted {@link Object} is simply returned. For
* {@link IModelInstancePrimitiveType}, a newly created primitive is
* returned.
* </p>
*
* @param modelInstanceElement
* The {@link IModelInstanceElement} those adapted {@link Object}
* shall be returned or created.
* @param typeClass
* The {@link Class} the recreated element should be an instance
* of. This could be required for
* {@link IModelInstancePrimitiveType}s or for
* {@link IModelInstanceCollection}s.
* @return The created or adapted value ({@link Object}).
*/
@SuppressWarnings("unchecked")
private Object createAdaptedElement(
IModelInstanceElement modelInstanceElement, Class<?> typeClass) {
Object result;
/* Check for null values. */
if (modelInstanceElement == null) {
result = null;
}
/* Else check if the given element is a primitive type. */
else if (modelInstanceElement instanceof IModelInstancePrimitiveType) {
/* Probably recreate a boolean value. */
if (modelInstanceElement instanceof IModelInstanceBoolean) {
result = ((IModelInstanceBoolean) modelInstanceElement)
.getBoolean();
}
/* Else probably recreate an integer value. */
else if (modelInstanceElement instanceof IModelInstanceInteger) {
result = createAdaptedIntegerValue(
(IModelInstanceInteger) modelInstanceElement, typeClass);
}
/* Else probably recreate a real value. */
else if (modelInstanceElement instanceof IModelInstanceReal) {
result = createAdaptedRealValue(
(IModelInstanceReal) modelInstanceElement, typeClass);
}
/* Else probably recreate an String value. */
else if (modelInstanceElement instanceof IModelInstanceString) {
result = createAdaptedStringValue(
(IModelInstanceString) modelInstanceElement, typeClass);
}
else {
/* Other primitive types are not supported. Return null. */
result = null;
}
}
/* Else check if the given element is an enumeration literal. */
else if (modelInstanceElement instanceof IModelInstanceEnumerationLiteral) {
result = createAdaptedEnumerationLiteral(
(IModelInstanceEnumerationLiteral) modelInstanceElement,
typeClass);
}
/* Else check if the given element is a collection. */
else if (modelInstanceElement instanceof IModelInstanceCollection) {
/* Eventually adapt to an array. */
if (typeClass.isArray()) {
result = createAdaptedArray(modelInstanceElement, typeClass);
}
/* Else use the collection. */
else if (Collection.class.isAssignableFrom(typeClass)) {
result = createAdaptedCollection(
(IModelInstanceCollection<IModelInstanceElement>) modelInstanceElement,
typeClass);
}
/* Else throw an exception. */
else {
String msg;
msg = EcoreModelInstanceTypeMessages.EcoreModelInstance_CannotRecreateCollection;
throw new IllegalArgumentException(msg);
}
}
/* Else check if the given element is an adapted object. */
else if (modelInstanceElement instanceof IModelInstanceObject) {
result = ((IModelInstanceObject) modelInstanceElement).getObject();
}
/* Other types are not known. */
else {
result = null;
}
return result;
}
/**
* <p>
* A helper method the converts a given {@link IModelInstanceElement} into
* an Array value of a given {@link Class}.
* </p>
*
* @param modelInstanceElement
* The {@link IModelInstanceElement} that shall be converted.
* @param type
* The {@link Class} to that the given
* {@link IModelInstanceElement} shall be converted.
* @return The converted {@link Object}.
*/
@SuppressWarnings("unchecked")
private Object createAdaptedArray(
IModelInstanceElement modelInstanceElement, Class<?> type) {
Object result;
if (modelInstanceElement instanceof IModelInstanceCollection) {
IModelInstanceCollection<IModelInstanceElement> modelInstanceCollection;
Collection<IModelInstanceElement> adaptedCollection;
Class<?> componentType;
int index;
componentType = type.getComponentType();
modelInstanceCollection = (IModelInstanceCollection<IModelInstanceElement>) modelInstanceElement;
adaptedCollection = modelInstanceCollection.getCollection();
if (componentType.isPrimitive()) {
/* Probably create an array of boolean. */
if (boolean.class.isAssignableFrom(componentType)) {
boolean[] array;
array = new boolean[adaptedCollection.size()];
index = 0;
for (IModelInstanceElement anElement : adaptedCollection) {
array[index] = ((IModelInstanceBoolean) anElement)
.getBoolean().booleanValue();
}
result = array;
}
/* Probably create an array of byte. */
else if (byte.class.isAssignableFrom(componentType)) {
byte[] array;
array = new byte[adaptedCollection.size()];
index = 0;
for (IModelInstanceElement anElement : adaptedCollection) {
array[index] = ((IModelInstanceInteger) anElement)
.getLong().byteValue();
}
result = array;
}
/* Probably create an array of char. */
else if (char.class.isAssignableFrom(componentType)) {
char[] array;
array = new char[adaptedCollection.size()];
index = 0;
for (IModelInstanceElement anElement : adaptedCollection) {
array[index] = ((IModelInstanceString) anElement)
.getString().charAt(0);
}
result = array;
}
/* Probably create an array of double. */
else if (double.class.isAssignableFrom(componentType)) {
double[] array;
array = new double[adaptedCollection.size()];
index = 0;
for (IModelInstanceElement anElement : adaptedCollection) {
array[index] = ((IModelInstanceReal) anElement)
.getDouble().doubleValue();
}
result = array;
}
/* Probably create an array of float. */
else if (float.class.isAssignableFrom(componentType)) {
float[] array;
array = new float[adaptedCollection.size()];
index = 0;
for (IModelInstanceElement anElement : adaptedCollection) {
array[index] = ((IModelInstanceReal) anElement)
.getDouble().floatValue();
}
result = array;
}
/* Probably create an array of int. */
else if (int.class.isAssignableFrom(componentType)) {
int[] array;
array = new int[adaptedCollection.size()];
index = 0;
for (IModelInstanceElement anElement : adaptedCollection) {
array[index] = ((IModelInstanceInteger) anElement)
.getLong().intValue();
}
result = array;
}
/* Probably create an array of long. */
else if (long.class.isAssignableFrom(componentType)) {
long[] array;
array = new long[adaptedCollection.size()];
index = 0;
for (IModelInstanceElement anElement : adaptedCollection) {
array[index] = ((IModelInstanceInteger) anElement)
.getLong().longValue();
}
result = array;
}
/* Probably create an array of short. */
else if (short.class.isAssignableFrom(componentType)) {
short[] array;
array = new short[adaptedCollection.size()];
index = 0;
for (IModelInstanceElement anElement : adaptedCollection) {
array[index] = ((IModelInstanceInteger) anElement)
.getLong().shortValue();
}
result = array;
}
/* Else throw an exception. */
else {
throw new IllegalArgumentException(
EcoreModelInstanceTypeMessages.EcoreModelInstance_CannotRecreateArray);
}
}
else {
Object[] array;
/* Create a new array of the given type. */
array = (Object[]) Array.newInstance(componentType,
adaptedCollection.size());
index = 0;
/* Fill the array with elements. */
for (IModelInstanceElement anElement : adaptedCollection) {
array[index] = createAdaptedElement(anElement,
componentType);
}
// end for.
result = array;
}
// end else.
}
else {
throw new IllegalArgumentException(
EcoreModelInstanceTypeMessages.EcoreModelInstance_CannotRecreateArray);
}
return result;
}
/**
* <p>
* A helper method that converts a given {@link IModelInstanceElement} into
* an {@link Collection} of a given {@link Class} type.
* </p>
*
* @param modelInstanceCollection
* The {@link IModelInstanceCollection} that shall be converted.
* @param type
* The {@link Collection} {@link Class} to that the given
* {@link IModelInstanceElement} shall be converted.
* @return The converted {@link Collection}.
*/
@SuppressWarnings("unchecked")
private Collection<?> createAdaptedCollection(
IModelInstanceCollection<IModelInstanceElement> modelInstanceCollection,
Class<?> clazzType) {
Collection<Object> result;
if (Collection.class.isAssignableFrom(clazzType)) {
/*
* Try to initialize the collection using the an empty constructor
* found via reflections.
*/
try {
Constructor<?> collectionConstructor;
collectionConstructor = clazzType.getConstructor(new Class[0]);
result = (Collection<Object>) collectionConstructor
.newInstance(new Object[0]);
}
/* Catch all possible exceptions and probably initialize with null. */
catch (SecurityException e) {
result = null;
}
catch (NoSuchMethodException e) {
result = null;
}
catch (IllegalArgumentException e) {
result = null;
}
catch (InstantiationException e) {
result = null;
}
catch (IllegalAccessException e) {
result = null;
}
catch (InvocationTargetException e) {
result = null;
}
/*
* This could be implemented for other existing implementations of
* EList. For generated EMF Ecore without hacks and extendes ELists
* this should work.
*/
if (result == null) {
if (UniqueEList.class.isAssignableFrom(clazzType)) {
result = new UniqueEList<Object>();
}
else if (List.class.isAssignableFrom(clazzType)) {
result = new BasicEList<Object>();
}
else if (Set.class.isAssignableFrom(clazzType)) {
result = new HashSet<Object>();
}
// no else.
}
/* Probably create the contained elements. */
if (result != null) {
Class<?> elementClassType;
/*
* TODO: The question how to retrieve the generic type of a List
* (if any exists) should be investigated very soon.
*/
/* Try to get the elements class. */
if (clazzType.getTypeParameters().length == 1
&& clazzType.getTypeParameters()[0].getBounds().length == 1
&& clazzType.getTypeParameters()[0].getBounds()[0] instanceof Class) {
elementClassType = (Class<?>) clazzType.getTypeParameters()[0]
.getBounds()[0];
}
else {
elementClassType = Object.class;
}
/* Create the value for all elements. */
for (IModelInstanceElement anElement : modelInstanceCollection
.getCollection()) {
result
.add(createAdaptedElement(anElement,
elementClassType));
}
// end for.
}
/* Else throw an exception. */
else {
String msg;
msg = EcoreModelInstanceTypeMessages.EcoreModelInstance_CannotRecreateCollection;
throw new IllegalArgumentException(msg);
}
}
/* Else throw an exception. */
else {
String msg;
msg = EcoreModelInstanceTypeMessages.EcoreModelInstance_CannotRecreateCollection;
throw new IllegalArgumentException(msg);
}
return result;
}
/**
* <p>
* A helper method the converts a given
* {@link IModelInstanceEnumerationLiteral} into an {@link Object} value of
* a given {@link Class}. If the given {@link Class} does not represents an
* {@link Enum}, a {@link IllegalArgumentException} is thrown.
* </p>
*
* @param modelInstanceEnumerationLiteral
* The {@link IModelInstanceEnumerationLiteral} that shall be
* converted.
* @param type
* The {@link Class} to that the given
* {@link IModelInstanceEnumerationLiteral} shall be converted.
* @return The converted {@link Object}.
*/
private Object createAdaptedEnumerationLiteral(
IModelInstanceEnumerationLiteral modelInstanceEnumerationLiteral,
Class<?> typeClass) {
Object result;
/* Check if the given class represents an enumeration. */
if (typeClass.isEnum()) {
result = null;
/*
* Try to find an enum constant having the same name as the
* enumeration literal.
*/
for (Object anEnumConstant : typeClass.getEnumConstants()) {
if (anEnumConstant.toString().equals(
modelInstanceEnumerationLiteral.getLiteral().getName())) {
result = anEnumConstant;
break;
}
// no else.
}
// end for.
if (result == null) {
String msg;
msg = EcoreModelInstanceTypeMessages.EcoreModelInstance_EnumerationLiteralNotFound;
msg = NLS
.bind(
modelInstanceEnumerationLiteral.getLiteral()
.getQualifiedName(),
"The enumeration literal could not be adapted to any constant of the given Enum class.");
throw new IllegalArgumentException(msg);
}
// no else.
}
/*
* Else check if the given class represents a super class of an Enum
* represented by this literal.
*/
else {
List<String> enumerationQualifiedName;
String enumClassName;
enumerationQualifiedName = modelInstanceEnumerationLiteral
.getLiteral().getQualifiedNameList();
/* Remove the name of the literal. */
enumerationQualifiedName
.remove(enumerationQualifiedName.size() - 1);
enumClassName = EcoreModelInstanceTypeUtility
.toCanonicalName(enumerationQualifiedName);
try {
Class<?> enumClass;
enumClass = this.loadJavaClass(enumClassName);
/* Check if the found class represents an enumeration. */
if (enumClass.isEnum()) {
result = null;
/*
* Try to find an enum constant having the same name as the
* enumeration literal.
*/
for (Object anEnumConstant : enumClass.getEnumConstants()) {
if (anEnumConstant.toString().equals(
modelInstanceEnumerationLiteral.getLiteral()
.getName())) {
result = anEnumConstant;
break;
}
}
// end for.
if (result == null) {
String msg;
msg = EcoreModelInstanceTypeMessages.EcoreModelInstance_EnumerationLiteralNotFound;
msg = NLS
.bind(
modelInstanceEnumerationLiteral
.getLiteral()
.getQualifiedName(),
"The enumeration literal could not be adapted to any constant of the given Enum class.");
throw new IllegalArgumentException(msg);
}
// no else.
}
else {
String msg;
msg = EcoreModelInstanceTypeMessages.EcoreModelInstance_EnumerationLiteralNotFound;
msg = NLS.bind(modelInstanceEnumerationLiteral.getLiteral()
.getQualifiedName(), "The found class " + enumClass
+ " is not an Enum.");
throw new IllegalArgumentException(msg);
}
}
catch (ClassNotFoundException e) {
String msg;
msg = EcoreModelInstanceTypeMessages.EcoreModelInstance_EnumerationLiteralNotFound;
msg = NLS.bind(modelInstanceEnumerationLiteral.getLiteral()
.getQualifiedName(), e.getMessage());
throw new IllegalArgumentException(msg, e);
}
}
return result;
}
/**
* <p>
* A helper method the converts a given {@link IModelInstanceInteger} into
* an Integer value of a given {@link Class}. If the given {@link Class}
* represents an unknown integer {@link Class}, a {@link Long} is returned.
* </p>
*
* @param modelInstanceInteger
* The {@link IModelInstanceElement} that shall be converted.
* @param type
* The {@link Class} to that the given
* {@link IModelInstanceElement} shall be converted.
* @return The converted {@link Object}.
*/
private Object createAdaptedIntegerValue(
IModelInstanceInteger modelInstanceInteger, Class<?> type) {
Object result;
/* Probably recreate a BigDecimal value. */
if (type.equals(BigDecimal.class)) {
result = new BigDecimal(modelInstanceInteger.getLong());
}
/* Else probably recreate a BigInteger value. */
else if (type.equals(BigInteger.class)) {
result = BigInteger.valueOf(modelInstanceInteger.getLong());
}
/* Else probably recreate a Byte value. */
else if (type.equals(byte.class) || type.equals(Byte.class)) {
result = modelInstanceInteger.getLong().byteValue();
}
/* Else probably recreate an Integer value. */
else if (type.equals(int.class) || type.equals(Integer.class)) {
result = modelInstanceInteger.getLong().intValue();
}
/* Else probably recreate a Long value. */
else if (type.equals(long.class) || type.equals(Long.class)) {
result = modelInstanceInteger.getLong();
}
/* Else probably recreate a Short value. */
else if (type.equals(short.class) || type.equals(Short.class)) {
result = modelInstanceInteger.getLong().shortValue();
}
else {
/* Other integer types are not supported. Return the Long value. */
result = modelInstanceInteger.getLong();
}
return result;
}
/**
* <p>
* A helper method the converts a given {@link IModelInstanceReal} into a
* Real value of a given {@link Class}. If the given {@link Class}
* represents an unknown real {@link Class}, a {@link Number} is returned.
* </p>
*
* @param modelInstanceReal
* The {@link IModelInstanceReal} that shall be converted.
* @param type
* The {@link Class} to that the given {@link IModelInstanceReal}
* shall be converted.
* @return The converted {@link Object}.
*/
private Object createAdaptedRealValue(IModelInstanceReal modelInstanceReal,
Class<?> type) {
Object result;
/* Probably recreate a Double value. */
if (type.equals(double.class) || type.equals(BigInteger.class)) {
result = modelInstanceReal.getDouble();
}
/* Else probably recreate a Float value. */
else if (type.equals(float.class) || type.equals(Float.class)) {
result = modelInstanceReal.getDouble().floatValue();
}
else {
/* Other integer types are not supported. Return the Double value. */
result = modelInstanceReal.getDouble();
}
return result;
}
/**
* <p>
* A helper method the converts a given {@link IModelInstanceString} into a
* String value of a given {@link Class}. If the given {@link Class}
* represents an unknown String {@link Class}, a {@link String} is returned.
* </p>
*
* @param modelInstanceString
* The {@link IModelInstanceString} that shall be converted.
* @param type
* The {@link Class} to that the given
* {@link IModelInstanceString} shall be converted.
* @return The converted {@link Object}.
*/
private Object createAdaptedStringValue(
IModelInstanceString modelInstanceString, Class<?> type) {
Object result;
String stringValue;
stringValue = modelInstanceString.getString();
/* Probably recreate a char value. */
if (type.equals(char.class) || type.equals(BigInteger.class)) {
if (stringValue.length() > 0) {
result = stringValue.toCharArray()[0];
}
else {
result = null;
}
}
/* Else probably recreate a Character value. */
else if (type.equals(float.class) || type.equals(Float.class)) {
if (stringValue.length() > 0) {
result = new Character(stringValue.toCharArray()[0]);
}
else {
result = null;
}
}
else {
/*
* Other integer types are not supported (except of String). Return
* the String value.
*/
result = stringValue;
}
return result;
}
/**
* <p>
* A helper {@link Method} used to find a {@link Method} of the adapted
* {@link Object} of this {@link EcoreModelInstanceObject} that conforms to
* a given {@link Operation}.
* </p>
*
* @param operation
* The {@link Operation} for that a {@link Method} shall be
* found.
* @return The found {@link Method}.
* @throws OperationNotFoundException
* If no matching {@link Method} for the given {@link Operation}
* can be found.
*/
private Method findMethodOfAdaptedObject(Operation operation)
throws OperationNotFoundException {
Method result;
Class<?> methodSourceClass;
result = null;
methodSourceClass = this.myAdaptedType;
/*
* Try to find an according method in the adapted objects class, or one
* of its super classes.
*/
while (methodSourceClass != null && result == null) {
for (Method aMethod : methodSourceClass.getDeclaredMethods()) {
boolean nameIsEqual;
boolean resultTypeIsConform;
boolean argumentSizeIsEqual;
/* Check if the name matches to the given operation's name. */
nameIsEqual = aMethod.getName().equals(operation.getName());
/*
* Check if the return type matches to the given operation's
* type.
*/
resultTypeIsConform = EcoreModelInstanceTypeUtility
.conformsTypeToType(aMethod.getGenericReturnType(),
operation.getType());
/*
* Check if the method has the same size of arguments as the
* given operation.
*/
argumentSizeIsEqual = aMethod.getParameterTypes().length == operation
.getSignatureParameter().size();
if (nameIsEqual && resultTypeIsConform && argumentSizeIsEqual) {
java.lang.reflect.Type[] javaTypes;
List<Parameter> pivotModelParamters;
boolean matches;
javaTypes = aMethod.getGenericParameterTypes();
pivotModelParamters = operation.getSignatureParameter();
matches = true;
/* Compare the types of all arguments. */
for (int index = 0; index < operation
.getSignatureParameter().size(); index++) {
if (!EcoreModelInstanceTypeUtility.conformsTypeToType(
javaTypes[index], pivotModelParamters
.get(index).getType())) {
matches = false;
break;
}
// no else.
}
if (matches) {
result = aMethod;
break;
}
// no else.
}
// no else.
}
// end for.
methodSourceClass = methodSourceClass.getSuperclass();
}
// end while.
/* Probably throw an exception. */
if (result == null) {
String msg;
msg = EcoreModelInstanceTypeMessages.EcoreModelInstanceObject_OperationNotFound;
msg = NLS.bind(msg, operation, this.myEObject.getClass());
throw new OperationNotFoundException(msg);
}
// no else.
return result;
}
/**
* <p>
* A helper method that tries to load a {@link Class} for a given canonical
* name using all {@link ClassLoader}s of this {@link JavaModelInstance}.
* </p>
*
* @param canonicalName
* The canonical name of the {@link Class} that shall be loaded.
* @return
* @throws ClassNotFoundException
* Thrown, if the {@link Class} cannot be found by any
* {@link ClassLoader} of this {@link JavaModelInstance}.
*/
private Class<?> loadJavaClass(String canonicalName)
throws ClassNotFoundException {
Class<?> result;
result = null;
result = this.myAdaptedType.getClassLoader().loadClass(canonicalName);
return result;
}
}