Merge branch 'master' into bugs/296598
diff --git a/plugins/org.eclipse.uml2.uml/src/org/eclipse/uml2/uml/util/UMLUtil.java b/plugins/org.eclipse.uml2.uml/src/org/eclipse/uml2/uml/util/UMLUtil.java
index 3b8fa86..0cc6916 100644
--- a/plugins/org.eclipse.uml2.uml/src/org/eclipse/uml2/uml/util/UMLUtil.java
+++ b/plugins/org.eclipse.uml2.uml/src/org/eclipse/uml2/uml/util/UMLUtil.java
@@ -12,7 +12,7 @@
  *   Kenn Hussey - 286329, 313601, 314971, 344907, 236184, 335125
  *   Kenn Hussey (CEA) - 327039, 358792, 364419, 366350, 307343, 382637, 273949, 389542, 389495, 316165, 392833, 399544, 322715, 163556, 212765, 397324, 204658, 408612, 411731
  *   Yann Tanguy (CEA) - 350402
- *   Christian W. Damus (CEA) - 392833, 251963
+ *   Christian W. Damus (CEA) - 392833, 251963, 405061
  *
  */
 package org.eclipse.uml2.uml.util;
@@ -2394,6 +2394,8 @@
 		protected static final Pattern ANNOTATION_DETAIL_PATTERN = Pattern
 			.compile("\\s+((?>\\\\.|\\S)+)\\s*+=\\s*((['\"])((?>\\\\.|.)*?)\\3)"); //$NON-NLS-1$
 
+		protected static final String ANNOTATION_DETAIL__ORIGINAL_NAME = "originalName"; //$NON-NLS-1$
+
 		protected static final String OCL_DELEGATE_URI = "http://www.eclipse.org/emf/2002/Ecore/OCL"; //$NON-NLS-1$
 
 		protected final Map<Element, EModelElement> elementToEModelElementMap = new LinkedHashMap<Element, EModelElement>();
@@ -2406,9 +2408,30 @@
 
 		protected Map<Object, Object> context = null;
 
-		protected void setName(ENamedElement eNamedElement, String name,
+		/**
+		 * Sets the {@code name} of a converted element.
+		 * <p>
+		 * As of the 4.2 API version, if the Ecore name differs for any reason
+		 * from the original UML name, it is recorded for
+		 * {@linkplain UMLUtil#getOriginalName(ENamedElement) later retrieval}.
+		 * </p>
+		 * 
+		 * @param eNamedElement
+		 *            the converted Ecore element in which to set a name
+		 * @param name
+		 *            the name to set
+		 * @param validate
+		 *            whether to ensure that the name is a valid Java
+		 *            identifier, munging it if necessary
+		 * 
+		 * @see UMLUtil#getOriginalName(ENamedElement)
+		 * @see UML2Util#getValidJavaIdentifier(String)
+		 */
+		protected void setName(ENamedElement eNamedElement, final String name,
 				boolean validate) {
 
+			String ecoreName = name;
+
 			if (!isEmpty(name)
 				&& options != null
 				&& !OPTION__IGNORE
@@ -2450,19 +2473,13 @@
 				if (OPTION__PROCESS.equals(options
 					.get(OPTION__CAMEL_CASE_NAMES))) {
 
-					eNamedElement.setName(validate
-						? getValidJavaIdentifier(camelCaseName)
-						: camelCaseName);
-				} else {
-					eNamedElement.setName(validate
-						? getValidJavaIdentifier(name)
-						: name);
+					ecoreName = camelCaseName;
 				}
 
 				if (!camelCaseName.equals(name)) {
 
 					if (DEBUG) {
-						System.out.println(name + " -> " + camelCaseNameBuffer); //$NON-NLS-1$
+						System.out.println(name + " -> " + camelCaseName); //$NON-NLS-1$
 					}
 
 					if (OPTION__PROCESS.equals(options
@@ -2498,11 +2515,23 @@
 								new Object[]{eNamedElement}));
 					}
 				}
-			} else {
-				eNamedElement.setName(validate
-					? getValidJavaIdentifier(name)
-					: name);
 			}
+
+			if (validate) {
+				ecoreName = getValidJavaIdentifier(ecoreName);
+			}
+
+			if (!safeEquals(ecoreName, name)) {
+				// record the original name, regardless whether it was converted
+				// by the camel-case names option or by munging to get a valid
+				// Java identifier, because clients such as OCL will require the
+				// traceability
+				EcoreUtil.setAnnotation(eNamedElement,
+					UML2_UML_PACKAGE_2_0_NS_URI,
+					ANNOTATION_DETAIL__ORIGINAL_NAME, name);
+			}
+
+			eNamedElement.setName(ecoreName);
 		}
 
 		protected void setName(ENamedElement eNamedElement,
@@ -2519,6 +2548,40 @@
 			}
 		}
 
+		/**
+		 * Queries the original name (as defined in the source UML model) of the
+		 * given Ecore named element, in the case that the original name was not a
+		 * valid Ecore/Java name and was transformed either via the
+		 * {@link UML2EcoreConverter#OPTION__CAMEL_CASE_NAMES} option or simply
+		 * validating the name.
+		 * 
+		 * @param eNamedElement
+		 *            an Ecore named element
+		 * 
+		 * @return its original name in the UML model in which it was defined, or
+		 *         just its Ecore name if the original name is not recorded or is
+		 *         not different
+		 * 
+		 * @since 4.2
+		 * 
+		 * @see UML2EcoreConverter#setName(ENamedElement, String, boolean)
+		 */
+		public static String getOriginalName(ENamedElement eNamedElement) {
+			String result = eNamedElement.getName();
+
+			EAnnotation annotation = eNamedElement
+				.getEAnnotation(UML2_UML_PACKAGE_2_0_NS_URI);
+			if (annotation != null) {
+				String originalName = annotation.getDetails().get(
+					ANNOTATION_DETAIL__ORIGINAL_NAME);
+				if (originalName != null) {
+					result = originalName;
+				}
+			}
+
+			return result;
+		}
+
 		protected EClassifier getEType(Type type) {
 			EClassifier eType = null;
 
@@ -10901,5 +10964,4 @@
 
 		return false;
 	}
-
 }
diff --git a/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/Bug405061.uml b/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/Bug405061.uml
new file mode 100644
index 0000000..a35f0b4
--- /dev/null
+++ b/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/Bug405061.uml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<uml:Package xmi:version="20110701" xmlns:xmi="http://www.omg.org/spec/XMI/20110701" xmlns:uml="http://www.eclipse.org/uml2/4.0.0/UML" xmi:id="_JyuasDWiEeOonfkTJ1eP9w" name="foo" URI="http://foo">
+  <elementImport xmi:id="_dTZlADWiEeOonfkTJ1eP9w">
+    <importedElement xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#Boolean"/>
+  </elementImport>
+  <elementImport xmi:id="_dTazIDWiEeOonfkTJ1eP9w">
+    <importedElement xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#Integer"/>
+  </elementImport>
+  <elementImport xmi:id="_dTazITWiEeOonfkTJ1eP9w">
+    <importedElement xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#Real"/>
+  </elementImport>
+  <elementImport xmi:id="_dTbaMDWiEeOonfkTJ1eP9w">
+    <importedElement xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#String"/>
+  </elementImport>
+  <elementImport xmi:id="_dTbaMTWiEeOonfkTJ1eP9w">
+    <importedElement xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#UnlimitedNatural"/>
+  </elementImport>
+  <packagedElement xmi:type="uml:Class" xmi:id="_OJx6gDWiEeOonfkTJ1eP9w" name="My Class">
+    <ownedAttribute xmi:id="_SilGwDWiEeOonfkTJ1eP9w" name="Name">
+      <type xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#String"/>
+    </ownedAttribute>
+    <ownedAttribute xmi:id="_iDLG0DWiEeOonfkTJ1eP9w" name="last name">
+      <type xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#String"/>
+    </ownedAttribute>
+    <ownedAttribute xmi:id="_lZOcoDWiEeOonfkTJ1eP9w" name="social_security_no">
+      <type xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#String"/>
+    </ownedAttribute>
+    <ownedAttribute xmi:id="_nQaLQDWjEeOonfkTJ1eP9w" name="not.java$safe">
+      <type xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#String"/>
+    </ownedAttribute>
+    <ownedAttribute xmi:id="_sJjHoDWnEeO_nts5NvuY2w" name="noProblemHere">
+      <type xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#String"/>
+    </ownedAttribute>
+  </packagedElement>
+</uml:Package>
diff --git a/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/Bug405061Test.java b/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/Bug405061Test.java
new file mode 100644
index 0000000..89afd7e
--- /dev/null
+++ b/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/Bug405061Test.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2013 CEA and others.
+ * All rights reserved.   This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *   Christian W. Damus (CEA) - initial API and implementation
+ *   
+ */
+package org.eclipse.uml2.uml.bug.tests;
+
+import java.net.URL;
+import java.util.Collection;
+import java.util.Map;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.ENamedElement;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.uml2.common.util.UML2Util;
+import org.eclipse.uml2.uml.Class;
+import org.eclipse.uml2.uml.Element;
+import org.eclipse.uml2.uml.Package;
+import org.eclipse.uml2.uml.Property;
+import org.eclipse.uml2.uml.Stereotype;
+import org.eclipse.uml2.uml.UMLPackage;
+import org.eclipse.uml2.uml.tests.util.StandaloneSupport;
+import org.eclipse.uml2.uml.util.UMLUtil;
+import org.eclipse.uml2.uml.util.UMLUtil.Ecore2UMLConverter;
+import org.eclipse.uml2.uml.util.UMLUtil.UML2EcoreConverter;
+
+/**
+ * Test suite for the changes introduced for Bug 405061.
+ * 
+ * @see https://bugs.eclipse.org/bugs/show_bug.cgi?id=405061
+ */
+public class Bug405061Test
+		extends TestCase {
+
+	private ResourceSet rset;
+
+	private Package model;
+
+	private EPackage converted;
+
+	private EClass myClassConverted;
+
+	/**
+	 * Initializes me with my name.
+	 * 
+	 * @param name
+	 *            my name
+	 */
+	public Bug405061Test(String name) {
+		super(name);
+	}
+
+	public static Test suite() {
+		return new TestSuite(Bug405061Test.class, "Bug 405061 tests"); //$NON-NLS-1$
+	}
+
+	public void testInitialCapName() {
+		assertOriginalName("Name",
+			myClassConverted.getEStructuralFeature("name"));
+	}
+
+	public void testWhitespaceName() {
+		assertOriginalName("My Class", myClassConverted);
+		assertOriginalName("last name",
+			myClassConverted.getEStructuralFeature("lastName"));
+	}
+
+	public void testUnderscoreName() {
+		assertOriginalName("social_security_no",
+			myClassConverted.getEStructuralFeature("socialSecurityNo"));
+	}
+
+	public void testInvalidCharName() {
+		assertOriginalName("not.java$safe",
+			myClassConverted.getEStructuralFeature("notjava$safe"));
+	}
+
+	public void testUnmodifiedName() {
+		EStructuralFeature property = myClassConverted
+			.getEStructuralFeature("noProblemHere");
+		assertOriginalName("noProblemHere", property);
+		assertNull("Original name should not have been recorded.",
+			EcoreUtil.getAnnotation(property,
+				UMLUtil.UML2_UML_PACKAGE_2_0_NS_URI,
+				PrivateUML2EcoreConverter.ANNOTATION_DETAIL__ORIGINAL_NAME));
+	}
+
+	public void testValidateButNoCamelCaseOption() {
+		Map<String, String> options = new java.util.HashMap<String, String>();
+		options.put(UML2EcoreConverter.OPTION__CAMEL_CASE_NAMES,
+			UMLUtil.OPTION__IGNORE);
+		EPackage alternative = UMLUtil.convertToEcore(model, options)
+			.iterator().next();
+
+		EClass eClass = (EClass) alternative.getEClassifier("MyClass");
+		assertOriginalName("My Class", eClass);
+		assertOriginalName("Name", eClass.getEStructuralFeature("Name"));
+		assertOriginalName("last name",
+			eClass.getEStructuralFeature("lastname"));
+		assertOriginalName("social_security_no",
+			eClass.getEStructuralFeature("social_security_no"));
+		assertOriginalName("not.java$safe",
+			eClass.getEStructuralFeature("notjava$safe"));
+	}
+
+	public void testOriginalNameAnnotationNotReversedToUML() {
+		Map<String, String> options = new java.util.HashMap<String, String>();
+		options.put(Ecore2UMLConverter.OPTION__ANNOTATION_DETAILS,
+			UMLUtil.OPTION__PROCESS);
+		options.put(Ecore2UMLConverter.OPTION__ECORE_TAGGED_VALUES,
+			UMLUtil.OPTION__PROCESS);
+		Package alternative = UMLUtil.convertFromEcore(converted, options)
+			.iterator().next();
+		Class umlClass = (Class) alternative.getOwnedType("MyClass");
+		assertNotAnnotated(umlClass);
+		for (Property next : umlClass.getOwnedAttributes()) {
+			assertNotAnnotated(next);
+		}
+	}
+
+	//
+	// Test framework
+	//
+
+	@Override
+	protected void setUp()
+			throws Exception {
+
+		rset = new ResourceSetImpl();
+
+		if (StandaloneSupport.isStandalone()) {
+			StandaloneSupport.init(rset);
+		}
+
+		model = getTestModel();
+
+		Map<String, String> options = new java.util.HashMap<String, String>();
+		options.put(UML2EcoreConverter.OPTION__CAMEL_CASE_NAMES,
+			UMLUtil.OPTION__PROCESS);
+		converted = UMLUtil.convertToEcore(model, options).iterator().next();
+		myClassConverted = (EClass) converted.getEClassifier("MyClass");
+		assertNotNull(myClassConverted);
+	}
+
+	@Override
+	protected void tearDown()
+			throws Exception {
+
+		model = null;
+
+		for (Resource next : rset.getResources()) {
+			next.unload();
+		}
+
+		rset.getResources().clear();
+		rset.eAdapters().clear();
+		rset = null;
+	}
+
+	void assertOriginalName(String expectedName, ENamedElement element) {
+		assertEquals(expectedName,
+			UMLUtil.UML2EcoreConverter.getOriginalName(element));
+	}
+
+	void assertNotAnnotated(Element umlElement) {
+		for (Stereotype next : umlElement.getAppliedStereotypes()) {
+			if (UMLUtil.PROFILE__ECORE.equals(next.getProfile().getName())) {
+				assertTrue("element has annotations",
+					((Collection<?>) umlElement.getValue(next,
+						UMLUtil.TAG_DEFINITION__ANNOTATIONS)).isEmpty());
+			}
+		}
+
+		assertTrue("element has annotations", umlElement.getEAnnotations()
+			.isEmpty());
+	}
+
+	Package getTestModel() {
+		URL url = getClass().getResource("Bug405061.uml"); //$NON-NLS-1$
+		return (Package) UML2Util.load(rset,
+			URI.createURI(url.toExternalForm()), UMLPackage.Literals.PACKAGE);
+	}
+
+	protected static class PrivateUML2EcoreConverter
+			extends UMLUtil.UML2EcoreConverter {
+
+		static final String ANNOTATION_DETAIL__ORIGINAL_NAME = UMLUtil.UML2EcoreConverter.ANNOTATION_DETAIL__ORIGINAL_NAME;
+	}
+}
diff --git a/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/UMLBugTests.java b/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/UMLBugTests.java
index a43d733..3af1659 100644
--- a/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/UMLBugTests.java
+++ b/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/UMLBugTests.java
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *   Christian W. Damus (CEA) - initial API and implementation
+ *   Christian W. Damus (CEA) - 403365, 300957, 405061
  */
 package org.eclipse.uml2.uml.bug.tests;
 
@@ -37,6 +38,7 @@
 		result.addTest(Bug392833Test.suite());
 		result.addTest(Bug403365Test.suite());
 		result.addTest(Bug300957Test.suite());
+		result.addTest(Bug405061Test.suite());
 
 		return result;
 	}