Bug 578486: Resolve metamodel proxies during back conversion after the migration

* Extend BackwardConverter to resolve proxy URLs that point to the
migration metamodel because it will no longer exist once the migration
is finished.
* Load Resources with UUIDs in MetamodelUtils to allow referencing
elements in a stable way: UUIDs don't change when an element is renamed.
* Add DynamicEcoreFactory but don't register it automatically. This
allows clients to register it if needed but does not change existing
behavior.

Change-Id: I40a5da6b1905c8505ab85e13ec6e204d7e713153
Signed-off-by: Lucas Koehler <lkoehler@eclipsesource.com>
diff --git a/plugins/org.eclipse.emf.edapt.common/src/org/eclipse/emf/edapt/internal/common/MetamodelUtils.java b/plugins/org.eclipse.emf.edapt.common/src/org/eclipse/emf/edapt/internal/common/MetamodelUtils.java
index ec05505..8fb7b9d 100644
--- a/plugins/org.eclipse.emf.edapt.common/src/org/eclipse/emf/edapt/internal/common/MetamodelUtils.java
+++ b/plugins/org.eclipse.emf.edapt.common/src/org/eclipse/emf/edapt/internal/common/MetamodelUtils.java
@@ -16,6 +16,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 
 import org.eclipse.emf.common.util.URI;
 import org.eclipse.emf.ecore.EClass;
@@ -27,9 +28,12 @@
 import org.eclipse.emf.ecore.EcorePackage;
 import org.eclipse.emf.ecore.resource.Resource;
 import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.ecore.resource.impl.ResourceFactoryImpl;
 import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
 import org.eclipse.emf.ecore.util.EcoreUtil;
 import org.eclipse.emf.ecore.util.EcoreUtil.Copier;
+import org.eclipse.emf.ecore.xmi.XMLResource;
+import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl;
 
 /**
  * Helper methods for metamodel access.
@@ -188,6 +192,10 @@
 	public static ResourceSet createIndependentMetamodelCopy(
 		Collection<EPackage> rootPackages, URI metamodelURI) {
 		final ResourceSet resourceSet = new ResourceSetImpl();
+		// Use custom resource factory to have UUIDs
+		final Map<String, Object> map = resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap();
+		map.put(ResourceUtils.ECORE_FILE_EXTENSION, new MetamodelEcoreResourceFactoryImpl());
+
 		final Resource resource = resourceSet.createResource(metamodelURI);
 
 		@SuppressWarnings("serial")
@@ -221,4 +229,24 @@
 		return resourceSet;
 	}
 
+	/**
+	 * Resource factory that creates resources with UUIDs.
+	 * UUIDs allow stable references to meta model elements even if the elements are renamed during the migration.
+	 */
+	static class MetamodelEcoreResourceFactoryImpl extends ResourceFactoryImpl {
+
+		@Override
+		public Resource createResource(URI uri) {
+			final XMLResource result = new XMIResourceImpl(uri) {
+				@Override
+				protected boolean useUUIDs() {
+					return true;
+				}
+			};
+			result.setEncoding("UTF-8"); //$NON-NLS-1$
+
+			return result;
+		}
+	}
+
 }
diff --git a/plugins/org.eclipse.emf.edapt.migration/src/org/eclipse/emf/edapt/internal/migration/internal/BackwardConverter.java b/plugins/org.eclipse.emf.edapt.migration/src/org/eclipse/emf/edapt/internal/migration/internal/BackwardConverter.java
index ee64f02..821b3a9 100644
--- a/plugins/org.eclipse.emf.edapt.migration/src/org/eclipse/emf/edapt/internal/migration/internal/BackwardConverter.java
+++ b/plugins/org.eclipse.emf.edapt.migration/src/org/eclipse/emf/edapt/internal/migration/internal/BackwardConverter.java
@@ -17,7 +17,9 @@
 
 import org.eclipse.emf.common.util.EList;
 import org.eclipse.emf.common.util.Enumerator;
+import org.eclipse.emf.common.util.URI;
 import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EClassifier;
 import org.eclipse.emf.ecore.EEnum;
 import org.eclipse.emf.ecore.EEnumLiteral;
 import org.eclipse.emf.ecore.EObject;
@@ -35,6 +37,7 @@
 import org.eclipse.emf.edapt.internal.common.TwoWayIdentityHashMap;
 import org.eclipse.emf.edapt.spi.migration.AttributeSlot;
 import org.eclipse.emf.edapt.spi.migration.Instance;
+import org.eclipse.emf.edapt.spi.migration.MetamodelResource;
 import org.eclipse.emf.edapt.spi.migration.Model;
 import org.eclipse.emf.edapt.spi.migration.ModelResource;
 import org.eclipse.emf.edapt.spi.migration.ReferenceSlot;
@@ -53,9 +56,11 @@
 
 	/** Mapping from graph nodes to EMF model elements. */
 	protected ReversableMap<Instance, EObject> mapping;
+	protected Model model;
 
 	/** Convert model graph to EMF elements. */
 	public ResourceSet convert(Model model) {
+		this.model = model;
 		model.getMetamodel().refreshCaches();
 
 		mapping = new TwoWayIdentityHashMap<Instance, EObject>();
@@ -82,12 +87,67 @@
 		for (final Instance element : type.getInstances()) {
 			final EObject eObject = EcoreUtil.create(targetClass);
 			if (element.isProxy()) {
-				((InternalEObject) eObject).eSetProxyURI(element.getUri());
+				if (isMetamodelProxy(element.getUri())) {
+					final URI resolvedProxyUri = resolveMetamodelProxy(element.getUri());
+					((InternalEObject) eObject).eSetProxyURI(resolvedProxyUri);
+				} else {
+					((InternalEObject) eObject).eSetProxyURI(element.getUri());
+				}
 			}
 			mapping.put(element, eObject);
 		}
 	}
 
+	/**
+	 * @return true if the proxyUri points to a meta element in the migrated metamodel (e.g. an EClass of the migrated
+	 *         model).
+	 */
+	protected boolean isMetamodelProxy(URI proxyUri) {
+		final MetamodelResource metaRes = getMetamodelResource();
+		// Remove fragment and query. The uri proxies to an element in the metamodel if the base URI is the same.
+		final URI baseUri = proxyUri.trimFragment().trimQuery();
+		return metaRes.getUri().equals(baseUri);
+	}
+
+	/**
+	 * Proxy urls to meta elements (e.g. EClasses, EStructuralFeatures) that are part of the migrated metamodel point to
+	 * the virtual URI of the migration metamodel resource. This URI is no longer valid after the migration finished.
+	 * Without additional resolvement this leads to broken proxy URIs in the migrated data.
+	 * Thus, it is resolved to the real URI of the meta element in its migrated version.
+	 */
+	protected URI resolveMetamodelProxy(URI proxyUri) {
+		final MetamodelResource metaRes = getMetamodelResource();
+		final Resource packagesRes = getSourceResource(metaRes);
+		final EObject resolvedMetaObject = packagesRes.getEObject(proxyUri.fragment());
+
+		if (resolvedMetaObject instanceof EClassifier) {
+			final EClassifier classifier = (EClassifier) resolvedMetaObject;
+			return URI.createURI(String.format("%s#//%s", classifier.getEPackage().getNsURI(), classifier.getName())); //$NON-NLS-1$
+		} else if (resolvedMetaObject instanceof EStructuralFeature) {
+			final EStructuralFeature feature = (EStructuralFeature) resolvedMetaObject;
+			final EClass containingClass = feature.getEContainingClass();
+			return URI.createURI(String.format("%s#//%s/%s", containingClass.getEPackage().getNsURI(), //$NON-NLS-1$
+				containingClass.getName(), feature.getName()));
+		}
+
+		// fallback: original proxy uri. will be broken but migration continues
+		return proxyUri;
+	}
+
+	/** @return The {@link MetamodelResource} of the migrated metamodel. */
+	protected MetamodelResource getMetamodelResource() {
+		// The metamodel is always contained in the first resource.
+		return model.getMetamodel().getResources().get(0);
+	}
+
+	/**
+	 * {@link MetamodelResource}s do not actually contain any packages but only reference them. This returns the actual
+	 * {@link Resource} containing this MetamodelResource's packages.
+	 */
+	protected Resource getSourceResource(MetamodelResource metamodelResource) {
+		return metamodelResource.getRootPackages().get(0).eResource();
+	}
+
 	/** Resolve the class to which an instance should be converted. */
 	protected EClass resolveEClass(EClass eClass) {
 		return eClass;
diff --git a/plugins/org.eclipse.emf.edapt.migration/src/org/eclipse/emf/edapt/migration/DynamicEcoreFactory.java b/plugins/org.eclipse.emf.edapt.migration/src/org/eclipse/emf/edapt/migration/DynamicEcoreFactory.java
new file mode 100644
index 0000000..8ba8dfd
--- /dev/null
+++ b/plugins/org.eclipse.emf.edapt.migration/src/org/eclipse/emf/edapt/migration/DynamicEcoreFactory.java
@@ -0,0 +1,570 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2022 EclipseSource Muenchen GmbH and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * jfaltermeier - initial API and implementation
+ * lkoehler - Override basicCreate to create non-dynamic EObjects
+ ******************************************************************************/
+package org.eclipse.emf.edapt.migration;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.text.ParseException;
+import java.util.Date;
+
+import org.eclipse.emf.common.util.WrappedException;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EDataType;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EcoreFactory;
+import org.eclipse.emf.ecore.impl.BasicEObjectImpl;
+import org.eclipse.emf.ecore.impl.EFactoryImpl;
+
+/**
+ * {@link org.eclipse.emf.ecore.EFactory EFactory} implementation for creating objects for the custom data types of the
+ * {@link org.eclipse.emf.ecore.EcoreFactory EcoreFactory}.
+ *
+ * Since edapt uses dynamic EMF the {@link org.eclipse.emf.ecore.EcoreFactory EcoreFactory} is not used when loading a
+ * model, we need a custom implementation. Moreover the switch should be based on the data type names, since the ids
+ * might differ as well.
+ *
+ * @author jfaltermeier
+ * @since 1.5
+ *
+ */
+public class DynamicEcoreFactory extends EFactoryImpl {
+
+	private static final String E_BIG_DECIMAL = "EBigDecimal";//$NON-NLS-1$
+	private static final String E_BIG_INTEGER = "EBigInteger";//$NON-NLS-1$
+	private static final String E_BOOLEAN = "EBoolean";//$NON-NLS-1$
+	private static final String E_BOOLEAN_OBJECT = "EBooleanObject";//$NON-NLS-1$
+	private static final String E_BYTE = "EByte";//$NON-NLS-1$
+	private static final String E_BYTE_ARRAY = "EByteArray";//$NON-NLS-1$
+	private static final String E_BYTE_OBJECT = "EByteObject";//$NON-NLS-1$
+	private static final String E_CHAR = "EChar";//$NON-NLS-1$
+	private static final String E_CHARACTER_OBJECT = "ECharacterObject";//$NON-NLS-1$
+	private static final String E_DATE = "EDate";//$NON-NLS-1$
+	private static final String E_DOUBLE = "EDouble";//$NON-NLS-1$
+	private static final String E_DOUBLE_OBJECT = "EDoubleObject";//$NON-NLS-1$
+	private static final String E_FLOAT = "EFloat";//$NON-NLS-1$
+	private static final String E_FLOAT_OBJECT = "EFloatObject";//$NON-NLS-1$
+	private static final String E_INT = "EInt";//$NON-NLS-1$
+	private static final String E_INTEGER_OBJECT = "EIntegerObject";//$NON-NLS-1$
+	private static final String E_JAVA_CLASS = "EJavaClass";//$NON-NLS-1$
+	private static final String E_JAVA_OBJECT = "EJavaObject";//$NON-NLS-1$
+	private static final String E_LONG = "ELong";//$NON-NLS-1$
+	private static final String E_LONG_OBJECT = "ELongObject";//$NON-NLS-1$
+	private static final String E_SHORT = "EShort";//$NON-NLS-1$
+	private static final String E_SHORT_OBJECT = "EShortObject";//$NON-NLS-1$
+	private static final String E_STRING = "EString";//$NON-NLS-1$
+
+	/**
+	 *
+	 * {@inheritDoc}
+	 *
+	 * @see org.eclipse.emf.ecore.impl.EFactoryImpl#createFromString(org.eclipse.emf.ecore.EDataType, java.lang.String)
+	 */
+	// BEGIN COMPLEX CODE
+	@Override
+	public Object createFromString(EDataType eDataType, String initialValue) {
+		final String id = eDataType.getName();
+
+		if (E_BIG_DECIMAL.equals(id)) {
+			return createEBigDecimalFromString(eDataType, initialValue);
+		} else if (E_BIG_INTEGER.equals(id)) {
+			return createEBigIntegerFromString(eDataType, initialValue);
+		} else if (E_BOOLEAN.equals(id)) {
+			return createEBooleanFromString(eDataType, initialValue);
+		} else if (E_BOOLEAN_OBJECT.equals(id)) {
+			return createEBooleanObjectFromString(eDataType, initialValue);
+		} else if (E_BYTE.equals(id)) {
+			return createEByteFromString(eDataType, initialValue);
+		} else if (E_BYTE_ARRAY.equals(id)) {
+			return createEByteArrayFromString(eDataType, initialValue);
+		} else if (E_BYTE_OBJECT.equals(id)) {
+			return createEByteObjectFromString(eDataType, initialValue);
+		} else if (E_CHAR.equals(id)) {
+			return createECharFromString(eDataType, initialValue);
+		} else if (E_CHARACTER_OBJECT.equals(id)) {
+			return createECharacterObjectFromString(eDataType, initialValue);
+		} else if (E_DATE.equals(id)) {
+			return createEDateFromString(eDataType, initialValue);
+		} else if (E_DOUBLE.equals(id)) {
+			return createEDoubleFromString(eDataType, initialValue);
+		} else if (E_DOUBLE_OBJECT.equals(id)) {
+			return createEDoubleObjectFromString(eDataType, initialValue);
+		} else if (E_FLOAT.equals(id)) {
+			return createEFloatFromString(eDataType, initialValue);
+		} else if (E_FLOAT_OBJECT.equals(id)) {
+			return createEFloatObjectFromString(eDataType, initialValue);
+		} else if (E_INT.equals(id)) {
+			return createEIntFromString(eDataType, initialValue);
+		} else if (E_INTEGER_OBJECT.equals(id)) {
+			return createEIntegerObjectFromString(eDataType, initialValue);
+		} else if (E_JAVA_CLASS.equals(id)) {
+			return createEJavaClassFromString(eDataType, initialValue);
+		} else if (E_JAVA_OBJECT.equals(id)) {
+			return createEJavaObjectFromString(eDataType, initialValue);
+		} else if (E_LONG.equals(id)) {
+			return createELongFromString(eDataType, initialValue);
+		} else if (E_LONG_OBJECT.equals(id)) {
+			return createELongObjectFromString(eDataType, initialValue);
+		} else if (E_SHORT.equals(id)) {
+			return createEShortFromString(eDataType, initialValue);
+		} else if (E_SHORT_OBJECT.equals(id)) {
+			return createEShortObjectFromString(eDataType, initialValue);
+		} else if (E_STRING.equals(id)) {
+			return createEStringFromString(eDataType, initialValue);
+		}
+		return super.createFromString(eDataType, initialValue);
+	}
+
+	// END COMPLEX CODE
+
+	@Override
+	protected EObject basicCreate(EClass eClass) {
+		if (eClass.getInstanceClassName() == "java.util.Map$Entry") { //$NON-NLS-1$
+			return super.basicCreate(eClass);
+		}
+		// Create real objects instead of dynamic EObjects (like EFactory) because references to meta objects (e.g.
+		// EStructuralFeatures) can break the migration when they are dynamic EObjects.
+		final EObject eObject = EcoreFactory.eINSTANCE.create(eClass);
+		// Explicitly set the EClass because it is not set by default. When it is not set, .eClass() calls on the
+		// EObject fall back to the EClass in the registered Ecore EPackage. However, the migration might not use the
+		// registered EPackage. Thus, the EClass is set here.
+		BasicEObjectImpl.class.cast(eObject).eSetClass(eClass);
+		return eObject;
+	}
+
+	private Boolean booleanValueOf(String initialValue) {
+		if ("true".equalsIgnoreCase(initialValue)) { //$NON-NLS-1$
+			return Boolean.TRUE;
+		} else if ("false".equalsIgnoreCase(initialValue)) { //$NON-NLS-1$
+			return Boolean.FALSE;
+		} else {
+			throw new IllegalArgumentException("Expecting true or false"); //$NON-NLS-1$
+		}
+	}
+
+	private Boolean createEBooleanObjectFromString(EDataType metaObject,
+		String initialValue) {
+		return initialValue == null ? null : booleanValueOf(initialValue);
+	}
+
+	private Character createECharacterObjectFromString(EDataType metaObject,
+		String initialValue) {
+		if (initialValue == null) {
+			return null;
+		}
+
+		char charValue = 0;
+		try {
+			charValue = (char) Integer.parseInt(initialValue);
+		} catch (final NumberFormatException e) {
+			final char[] carray = initialValue.toCharArray();
+			charValue = carray[0];
+		}
+		return charValue;
+	}
+
+	private Date createEDateFromString(EDataType eDataType, String initialValue) {
+		if (initialValue == null) {
+			return null;
+		}
+
+		Exception exception = null;
+		for (int i = 0; i < EDATE_FORMATS.length; ++i) {
+			try {
+				return EDATE_FORMATS[i].parse(initialValue);
+			} catch (final ParseException parseException) {
+				exception = parseException;
+			}
+		}
+		throw new WrappedException(exception);
+	}
+
+	private Double createEDoubleObjectFromString(EDataType metaObject,
+		String initialValue) {
+		return initialValue == null ? null : Double.valueOf(initialValue);
+	}
+
+	private Float createEFloatObjectFromString(EDataType metaObject,
+		String initialValue) {
+		return initialValue == null ? null : Float.valueOf(initialValue);
+	}
+
+	private Integer createEIntegerObjectFromString(EDataType metaObject,
+		String initialValue) {
+		return initialValue == null ? null : Integer.valueOf(initialValue);
+	}
+
+	private BigDecimal createEBigDecimalFromString(EDataType eDataType,
+		String initialValue) {
+		return initialValue == null ? null : new BigDecimal(initialValue);
+	}
+
+	private BigInteger createEBigIntegerFromString(EDataType eDataType,
+		String initialValue) {
+		return initialValue == null ? null : new BigInteger(initialValue);
+	}
+
+	private String createEStringFromString(EDataType metaObject,
+		String initialValue) {
+		return initialValue;
+	}
+
+	private Integer createEIntFromString(EDataType metaObject,
+		String initialValue) {
+		return initialValue == null ? null : Integer.valueOf(initialValue);
+	}
+
+	private Boolean createEBooleanFromString(EDataType metaObject,
+		String initialValue) {
+		return initialValue == null ? null : booleanValueOf(initialValue);
+	}
+
+	private Byte createEByteObjectFromString(EDataType metaObject,
+		String initialValue) {
+		return initialValue == null ? null : Byte.valueOf(initialValue);
+	}
+
+	private Float createEFloatFromString(EDataType metaObject,
+		String initialValue) {
+		return initialValue == null ? null : Float.valueOf(initialValue);
+	}
+
+	private Character createECharFromString(EDataType metaObject,
+		String initialValue) {
+		if (initialValue == null) {
+			return null;
+		}
+		char charValue = 0;
+		try {
+			charValue = (char) Integer.parseInt(initialValue);
+		} catch (final NumberFormatException e) {
+			final char[] carray = initialValue.toCharArray();
+			charValue = carray[0];
+		}
+		return charValue;
+	}
+
+	private Long createELongFromString(EDataType metaObject, String initialValue) {
+		return initialValue == null ? null : Long.valueOf(initialValue);
+	}
+
+	private Double createEDoubleFromString(EDataType metaObject,
+		String initialValue) {
+		return initialValue == null ? null : Double.valueOf(initialValue);
+	}
+
+	private Byte createEByteFromString(EDataType metaObject, String initialValue) {
+		return initialValue == null ? null : Byte.valueOf(initialValue);
+	}
+
+	private Short createEShortFromString(EDataType metaObject,
+		String initialValue) {
+		return initialValue == null ? null : Short.valueOf(initialValue);
+	}
+
+	private Class<?> createEJavaClassFromString(EDataType metaObject,
+		String initialValue) {
+		try {
+			if (initialValue == null) {
+				return null;
+			} else if ("boolean".equals(initialValue)) { //$NON-NLS-1$
+				return boolean.class;
+			} else if ("byte".equals(initialValue)) { //$NON-NLS-1$
+				return byte.class;
+			} else if ("char".equals(initialValue)) { //$NON-NLS-1$
+				return char.class;
+			} else if ("double".equals(initialValue)) { //$NON-NLS-1$
+				return double.class;
+			} else if ("float".equals(initialValue)) { //$NON-NLS-1$
+				return float.class;
+			} else if ("int".equals(initialValue)) { //$NON-NLS-1$
+				return int.class;
+			} else if ("long".equals(initialValue)) { //$NON-NLS-1$
+				return long.class;
+			} else if ("short".equals(initialValue)) { //$NON-NLS-1$
+				return short.class;
+			} else {
+				return Class.forName(initialValue);
+			}
+		} catch (final ClassNotFoundException e) {
+			throw new WrappedException(e);
+		}
+	}
+
+	private Object createEJavaObjectFromString(EDataType eDataType,
+		String initialValue) {
+		return createFromString(initialValue);
+	}
+
+	private Long createELongObjectFromString(EDataType metaObject,
+		String initialValue) {
+		return initialValue == null ? null : Long.valueOf(initialValue);
+	}
+
+	private Short createEShortObjectFromString(EDataType metaObject,
+		String initialValue) {
+		return initialValue == null ? null : Short.valueOf(initialValue);
+	}
+
+	private byte[] createEByteArrayFromString(EDataType eDataType,
+		String initialValue) {
+		return hexStringToByteArray(initialValue);
+	}
+
+	private byte[] hexStringToByteArray(String initialValue) {
+		if (initialValue == null) {
+			return null;
+		}
+
+		final int size = initialValue.length();
+		int limit = (size + 1) / 2;
+		final byte[] result = new byte[limit];
+		if (size % 2 != 0) {
+			result[--limit] = hexCharToByte(initialValue.charAt(size - 1));
+		}
+
+		for (int i = 0, j = 0; i < limit; ++i) {
+			final byte high = hexCharToByte(initialValue.charAt(j++));
+			final byte low = hexCharToByte(initialValue.charAt(j++));
+			result[i] = (byte) (high << 4 | low);
+		}
+		return result;
+	}
+
+	// BEGIN COMPLEX CODE
+	private byte hexCharToByte(char character) {
+		switch (character) {
+		case '0':
+		case '1':
+		case '2':
+		case '3':
+		case '4':
+		case '5':
+		case '6':
+		case '7':
+		case '8':
+		case '9': {
+			return (byte) (character - '0');
+		}
+		case 'a':
+		case 'b':
+		case 'c':
+		case 'd':
+		case 'e':
+		case 'f': {
+			return (byte) (character - 'a' + 10);
+		}
+		case 'A':
+		case 'B':
+		case 'C':
+		case 'D':
+		case 'E':
+		case 'F': {
+			return (byte) (character - 'A' + 10);
+		}
+		default: {
+			throw new NumberFormatException("Invalid hexadecimal"); //$NON-NLS-1$
+		}
+		}
+	}
+
+	// END COMPLEX CODE
+
+	// BEGIN COMPLEX CODE
+	@Override
+	public String convertToString(EDataType eDataType, Object instanceValue) {
+
+		final String id = eDataType.getName();
+
+		if (E_BIG_DECIMAL.equals(id)) {
+			return convertEBigDecimalToString(eDataType, instanceValue);
+		} else if (E_BIG_INTEGER.equals(id)) {
+			return convertEBigIntegerToString(eDataType, instanceValue);
+		} else if (E_BOOLEAN.equals(id)) {
+			return convertEBooleanToString(eDataType, instanceValue);
+		} else if (E_BOOLEAN_OBJECT.equals(id)) {
+			return convertEBooleanObjectToString(eDataType, instanceValue);
+		} else if (E_BYTE.equals(id)) {
+			return convertEByteToString(eDataType, instanceValue);
+		} else if (E_BYTE_ARRAY.equals(id)) {
+			return convertEByteArrayToString(eDataType, instanceValue);
+		} else if (E_BYTE_OBJECT.equals(id)) {
+			return convertEByteObjectToString(eDataType, instanceValue);
+		} else if (E_CHAR.equals(id)) {
+			return convertECharToString(eDataType, instanceValue);
+		} else if (E_CHARACTER_OBJECT.equals(id)) {
+			return convertECharacterObjectToString(eDataType, instanceValue);
+		} else if (E_DATE.equals(id)) {
+			return convertEDateToString(eDataType, instanceValue);
+		} else if (E_DOUBLE.equals(id)) {
+			return convertEDoubleToString(eDataType, instanceValue);
+		} else if (E_DOUBLE_OBJECT.equals(id)) {
+			return convertEDoubleObjectToString(eDataType, instanceValue);
+		} else if (E_FLOAT.equals(id)) {
+			return convertEFloatToString(eDataType, instanceValue);
+		} else if (E_FLOAT_OBJECT.equals(id)) {
+			return convertEFloatObjectToString(eDataType, instanceValue);
+		} else if (E_INT.equals(id)) {
+			return convertEIntToString(eDataType, instanceValue);
+		} else if (E_INTEGER_OBJECT.equals(id)) {
+			return convertEIntegerObjectToString(eDataType, instanceValue);
+		} else if (E_JAVA_CLASS.equals(id)) {
+			return convertEJavaClassToString(eDataType, instanceValue);
+		} else if (E_JAVA_OBJECT.equals(id)) {
+			return convertEJavaObjectToString(eDataType, instanceValue);
+		} else if (E_LONG.equals(id)) {
+			return convertELongToString(eDataType, instanceValue);
+		} else if (E_LONG_OBJECT.equals(id)) {
+			return convertELongObjectToString(eDataType, instanceValue);
+		} else if (E_SHORT.equals(id)) {
+			return convertEShortToString(eDataType, instanceValue);
+		} else if (E_SHORT_OBJECT.equals(id)) {
+			return convertEShortObjectToString(eDataType, instanceValue);
+		} else if (E_STRING.equals(id)) {
+			return convertEStringToString(eDataType, instanceValue);
+		}
+		return super.convertToString(eDataType, instanceValue);
+	}
+
+	// END COMPLEX CODE
+
+	private String convertEBooleanObjectToString(EDataType metaObject, Object instanceValue) {
+		return instanceValue == null ? null : instanceValue.toString();
+	}
+
+	private String convertECharacterObjectToString(EDataType metaObject, Object instanceValue) {
+		if (instanceValue instanceof Character) {
+			return Integer.toString((Character) instanceValue);
+		}
+
+		return instanceValue == null ? null : instanceValue.toString();
+	}
+
+	private String convertEDateToString(EDataType eDataType, Object instanceValue) {
+		if (instanceValue == null) {
+			return null;
+		}
+
+		return EDATE_FORMATS[0].format((Date) instanceValue);
+
+	}
+
+	private String convertEDoubleObjectToString(EDataType metaObject, Object instanceValue) {
+		return instanceValue == null ? null : instanceValue.toString();
+	}
+
+	private String convertEFloatObjectToString(EDataType metaObject, Object instanceValue) {
+		return instanceValue == null ? null : instanceValue.toString();
+	}
+
+	private String convertEIntegerObjectToString(EDataType metaObject, Object instanceValue) {
+		return instanceValue == null ? null : instanceValue.toString();
+	}
+
+	private String convertEBigDecimalToString(EDataType eDataType, Object instanceValue) {
+		return instanceValue == null ? null : instanceValue.toString();
+	}
+
+	private String convertEBigIntegerToString(EDataType eDataType, Object instanceValue) {
+		return instanceValue == null ? null : instanceValue.toString();
+	}
+
+	private String convertEStringToString(EDataType metaObject, Object instanceValue) {
+		return (String) instanceValue;
+	}
+
+	private String convertEIntToString(EDataType metaObject, Object instanceValue) {
+		return instanceValue == null ? null : instanceValue.toString();
+	}
+
+	private String convertEBooleanToString(EDataType metaObject, Object instanceValue) {
+		return instanceValue == null ? null : instanceValue.toString();
+	}
+
+	private String convertEByteObjectToString(EDataType metaObject, Object instanceValue) {
+		return instanceValue == null ? null : instanceValue.toString();
+	}
+
+	private String convertEFloatToString(EDataType metaObject, Object instanceValue) {
+		return instanceValue == null ? null : instanceValue.toString();
+	}
+
+	private String convertECharToString(EDataType metaObject, Object instanceValue) {
+		if (instanceValue instanceof Character) {
+			return Integer.toString((Character) instanceValue);
+		}
+
+		return instanceValue == null ? null : instanceValue.toString();
+	}
+
+	private String convertELongToString(EDataType metaObject, Object instanceValue) {
+		return instanceValue == null ? null : instanceValue.toString();
+	}
+
+	private String convertEDoubleToString(EDataType metaObject, Object instanceValue) {
+		return instanceValue == null ? null : instanceValue.toString();
+	}
+
+	private String convertEByteToString(EDataType metaObject, Object instanceValue) {
+		return instanceValue == null ? null : instanceValue.toString();
+	}
+
+	private String convertEByteArrayToString(EDataType eDataType, Object instanceValue) {
+		if (instanceValue == null) {
+			return null;
+		}
+		final byte[] bytes = (byte[]) instanceValue;
+		return convertBytesToHexString(bytes, bytes.length);
+
+	}
+
+	private String convertEShortToString(EDataType metaObject, Object instanceValue) {
+		return instanceValue == null ? null : instanceValue.toString();
+	}
+
+	private String convertEJavaClassToString(EDataType metaObject, Object instanceValue) {
+		return instanceValue == null ? "" : ((Class<?>) instanceValue).getName(); //$NON-NLS-1$
+	}
+
+	private String convertEJavaObjectToString(EDataType eDataType, Object instanceValue) {
+		return convertToString(instanceValue);
+	}
+
+	private String convertELongObjectToString(EDataType metaObject, Object instanceValue) {
+		return instanceValue == null ? null : instanceValue.toString();
+	}
+
+	private String convertEShortObjectToString(EDataType metaObject, Object instanceValue) {
+		return instanceValue == null ? null : instanceValue.toString();
+	}
+
+	private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
+		'E', 'F' };
+
+	private String convertBytesToHexString(byte[] bytes, int count) {
+		if (bytes == null) {
+			return null;
+		}
+		final char[] result = new char[2 * count];
+		for (int i = 0, j = 0; i < count; ++i) {
+			final int high = bytes[i] >> 4 & 0xF;
+			final int low = bytes[i] & 0xF;
+			result[j++] = HEX_DIGITS[high];
+			result[j++] = HEX_DIGITS[low];
+		}
+		return new String(result);
+
+	}
+
+}