package com.openMap1.mapper.converters;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.Vector;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.ENamedElement;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcoreFactory;
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.ResourceSetImpl;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import com.openMap1.mapper.core.MapperException;
import com.openMap1.mapper.util.ModelUtil;
/**
* Class to do post-processing on an EMF Ecore model got
* from the LRA class model through mappings.
*
* The conversion consists of changing any EReference, whose target class
* is in the uml kernel package, to an EAttribute of the appropriate type.
*
* @author Robert
*
*/
public class LRAConverter {
private boolean tracing = false;
//------------------------------------------------------------------------------------------------------------
// Convert from a dynamic EObject model to a true ECore model
//------------------------------------------------------------------------------------------------------------
/**
*
* @param model an Ecore model made generically of DynamicEObjectImpls
* @param targetLocation where it is going to be saved
* Convert the model to an Ecore model made with the EMF ECore package, and save
* it with the same name to the target location - so it overwrites the first model.
*/
public void saveAsEcore(EObject model,URI modelInstanceURI) throws MapperException
{
trace("Overwrite of " + modelInstanceURI.toString());
// copy to a true Ecore model
EPackage newModel = copyModel(model);
// convert the EReferences to kernel datatype classes, to EAttributes - only if the top package is 'lra'
String packageName = newModel.getName();
boolean doPostProcess = ((packageName != null) && (packageName.equals("lra")));
if (doPostProcess) postProcess(newModel);
trace("Model converted");
// register the Ecore package
EcorePackage.eINSTANCE.getEFactoryInstance();
ResourceSet resourceSet = new ResourceSetImpl();
// register the factory
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().
put("ecore", new XMIResourceFactoryImpl());
// create and fill the resource
Resource resource = resourceSet.createResource(modelInstanceURI);
resource.getContents().add(newModel);
// save the resource
try {
resource.save(null);
trace("written");
}
catch (IOException ex) {throw new MapperException(ex.getMessage());}
}
/**
*
* @param model an Ecore model made generically of DynamidEObjectImpls
* @return the model converted to the classes of Ecore - which 'knows' it is an instance of the Ecore model
*/
private EPackage copyModel(EObject model)
{
// lookup table from old to new objects
Hashtable<EObject,EObject> newObjects = new Hashtable<EObject,EObject>();
// recursive copy, doing only attributes and containment relations, and filling the lookup table
EPackage newModel = (EPackage)copyTree(model,newObjects);
// copy across non-containment EReferences, using the lookup table
copyNonContainments(model, newObjects);
return newModel;
}
/**
* @param modelObj a subtree of an Ecore model made generically of DynamidEObjectImpls
* @param newObjects lookup table from old to new objects, to be filled
* @return the subtree copied to proper ECore classes, doing only EAttributes
* and non-containment EReferences
*/
private EObject copyTree(EObject modelObj,Hashtable<EObject,EObject> newObjects)
{
ENamedElement result = EcoreFactory.eINSTANCE.createEPackage();
String className = modelObj.eClass().getName(); // name of the metamodel class
int classId = getClassId(className);
trace("Copying " + className);
// create the appropriate ECore object, and store it in the lookup table
if (classId == EPACKAGE)
{
result = EcoreFactory.eINSTANCE.createEPackage();
result.setName((String)getValue(modelObj,"name"));
EList<?> subPackages = (EList<?>)getValue(modelObj,"eSubpackages");
for (Iterator<?> ip = subPackages.iterator();ip.hasNext();)
(((EPackage)result).getESubpackages()).add((EPackage)copyTree((EObject)ip.next(),newObjects));
EList<?> classes = (EList<?>)getValue(modelObj,"eClassifiers");
for (Iterator<?> ip = classes.iterator();ip.hasNext();)
{
EObject next = (EObject)ip.next();
String cName = (String)getValue(next,"name"); // name of the class in the model
if (!badClass(cName)) (((EPackage)result).getEClassifiers()).add((EClass)copyTree(next,newObjects));
}
}
if (classId == ECLASS)
{
result = EcoreFactory.eINSTANCE.createEClass();
result.setName((String)getValue(modelObj,"name"));
EList<?> references = (EList<?>)getValue(modelObj,"eStructuralFeatures");
for (Iterator<?> ip = references.iterator();ip.hasNext();)
(((EClass)result).getEStructuralFeatures()).add((EReference)copyTree((EObject)ip.next(),newObjects));
}
if (classId == EATTRIBUTE)
{
result = EcoreFactory.eINSTANCE.createEAttribute();
result.setName((String)getValue(modelObj,"name"));
((EAttribute)result).setLowerBound((Integer)getValue(modelObj,"lowerBound"));
}
if (classId == EREFERENCE)
{
result = EcoreFactory.eINSTANCE.createEReference();
result.setName((String)getValue(modelObj,"name"));
((EReference)result).setLowerBound((Integer)getValue(modelObj,"lowerBound"));
((EReference)result).setUpperBound((Integer)getValue(modelObj,"upperBound"));
}
newObjects.put(modelObj, result);
return result;
}
// spurious classes that can be removed at the first pass (there are no EReferences to them)
private boolean badClass(String cName)
{
boolean badClass = ((cName == null)||
(cName.equals("Text"))||
(cName.startsWith("$diagram"))||
(cName.startsWith("extract")));
return badClass;
}
/**
*
* @param obj an EObject in the Ecore model
* @param featureName the name of some feature
* @return the value of the feature (an EList if it is multi-valued)
*/
private Object getValue(EObject obj, String featureName)
{
Object value = null;
for (Iterator<EStructuralFeature> it = obj.eClass().getEAllStructuralFeatures().iterator();it.hasNext();)
{
EStructuralFeature feature = it.next();
if (feature.getName().equals(featureName)) value = obj.eGet(feature);
}
return value;
}
/**
* @param model a subtree of an Ecore model made generically of DynamidEObjectImpls
* @param newModel the subtree copied to proper Ecore classes, but not yet with non-containment ralations
* @param newObjects already-filled lookup table from old to new objects
* copy the non-containment relations across from the old to the new EObject, using the lookup table;
* and recursively descend via containment relations
*/
private void copyNonContainments(EObject modelObj, Hashtable<EObject,EObject> newObjects)
{
EObject newModelObj = newObjects.get(modelObj);
if (newModelObj == null) return;
int id = getClassId(modelObj);
if (id == ECLASS)
{
EList<?> supers = (EList<?>)getValue(modelObj,"eSuperTypes");
for (Iterator<?> ip = supers.iterator();ip.hasNext();)
{
EObject oldSuper = (EObject)ip.next();
EObject newSuper = newObjects.get(oldSuper);
if (newSuper != null) (((EClass)newModelObj).getESuperTypes()).add((EClass)newSuper);
}
}
else if (id == EREFERENCE)
{
EReference ref = (EReference)newModelObj;
Object oldTarget = getValue(modelObj,"eType");
if (oldTarget != null)
{
EClass target = (EClass)newObjects.get((EObject)oldTarget);
ref.setEType(target);
// if the target class is in a sub-package of the datatypes package, make the EReference a containment
EPackage pack = target.getEPackage();
if (pack != null)
{
EPackage superP = pack.getESuperPackage();
if ((superP != null) && (superP.getName().equals("datatypes")))
ref.setContainment(true);
}
}
}
// recursively descend the containment tree
for (Iterator<EObject> it = modelObj.eContents().iterator(); it.hasNext();)
copyNonContainments(it.next(),newObjects);
}
private static int EPACKAGE = 0;
private static int ECLASS = 1;
private static int EREFERENCE = 2;
private static int EATTRIBUTE = 3;
/**
* @param className the name of an ECore class
* @return and integer code for the class
*/
private int getClassId(String className)
{
int id = -1;
if (className.equals("EPackage")) id = EPACKAGE;
else if (className.equals("EClass")) id = ECLASS;
else if (className.equals("EReference")) id = EREFERENCE;
else if (className.equals("EAttribute")) id = EATTRIBUTE;
else System.out.println("Unrecognised ECore class " + className);
return id;
}
private int getClassId(EObject modelObj)
{
return getClassId(modelObj.eClass().getName());
}
//------------------------------------------------------------------------------------------------------------
// After conversion to a true Ecore model, post-processing
//------------------------------------------------------------------------------------------------------------
private void postProcess(EPackage classModel)
{
Vector<EClass> allClasses = ModelUtil.getAllClasses(classModel);
for (Iterator<EClass> it = allClasses.iterator(); it.hasNext();)
{
EClass theClass = it.next();
postProcessLRAClass(theClass);
}
}
private void postProcessLRAClass(EClass theClass)
{
// remove any '.' from the class name
theClass.setName(removeStops(theClass.getName()));
// look at EReferences
for (Iterator<EReference> ir = theClass.getEReferences().iterator();ir.hasNext();)
{
EReference ref = ir.next();
EClass target = (EClass)ref.getEType();
// EReferences with maximum multiplicity 1 and no target class get converted to EAttributes of data type EString
if (target == null)
{
// only add an EAttribute if the max multiplicity is 1
if (ref.getUpperBound() == 1)
{
// make the EAttribute
theClass.getEStructuralFeatures().add(makeAttribute(ref));
trace("Attribute " + ref.getName() + "; EString");
}
// remove the EReference (even if the max multiplicity is -1)
theClass.getEStructuralFeatures().remove(ref);
}
// EReference points to a datatypes kernel or derived class; convert it to an EAttribute of the right data type
else if (target != null)
{
String targetPackageName = target.getEPackage().getName();
if ((targetPackageName.equals("kernel"))|(targetPackageName.equals("derived")))
{
// make the EAttribute
theClass.getEStructuralFeatures().add(makeAttribute(ref));
trace("Attribute " + ref.getName() + "; type " + target.getName());
// remove the EReference
theClass.getEStructuralFeatures().remove(ref);
}
}
}
}
/*
* replace '.' by underscore in a name
*/
private String removeStops(String name)
{
String newName = "";
StringTokenizer st = new StringTokenizer(name,".");
while (st.hasMoreTokens())
{
newName = newName + st.nextToken();
if (st.hasMoreTokens()) newName = newName + "_";
}
return newName;
}
private EAttribute makeAttribute(EReference ref)
{
EAttribute att = EcoreFactory.eINSTANCE.createEAttribute();
EClass target = (EClass)ref.getEType();
att.setName(ref.getName());
att.setEType(getDataType(target));
att.setLowerBound(ref.getLowerBound());
return att;
}
private EDataType getDataType(EClass target)
{
EDataType theType = EcorePackage.eINSTANCE.getEString(); // default if no better
if (target != null)
{
String typeName = target.getName();
String superTypeName = getSuperName(target);
if (superTypeName.equals("String")) {}
// do not let Integer convert up to Real
else if (typeName.equals("Integer")) {theType = EcorePackage.eINSTANCE.getEInt();}
else if (superTypeName.equals("Real")) {theType = EcorePackage.eINSTANCE.getEDouble();}
else if (superTypeName.equals("Boolean")) {theType = EcorePackage.eINSTANCE.getEBoolean();}
}
return theType;
}
// name of a class or its highest superclass - which will often turn out to be 'String'
private String getSuperName(EClass target)
{
if (target.getESuperTypes().size() > 0)
return getSuperName(target.getESuperTypes().get(0));
return target.getName();
}
//------------------------------------------------------------------------------------------------------------------
// Checks and trivia
//------------------------------------------------------------------------------------------------------------------
public void modelCheck(EObject root)
{
System.out.println("\nCheck of superclasses");
for (Iterator<EObject> it = root.eAllContents();it.hasNext();)
{
EObject node = it.next();
if (node.eClass().getName().equals("EClass"))
{
String answer = "Class " + getName(node) + "; superclasses ";
boolean noSupers = true;
for (Iterator<EReference> ir = node.eClass().getEAllReferences().iterator(); ir.hasNext();)
{
EReference ref = ir.next();
if (ref.getName().equals("eSuperTypes"))
{
EList<?> supers = (EList<?>)node.eGet(ref);
for (Iterator<?> ic = supers.iterator(); ic.hasNext();)
{
EObject next = (EObject)ic.next();
answer = answer + getName(next) + " ";
noSupers = false;
}
}
}
if (noSupers) System.out.println(answer);
}
}
}
private String getName(EObject node)
{
String className = null;
for (Iterator<EAttribute> ia = node.eClass().getEAllAttributes().iterator();ia.hasNext();)
{
EAttribute att = ia.next();
if (att.getName().equals("name")) className = (String)node.eGet(att);
}
return className;
}
private void trace(String s) {if (tracing) System.out.println(s);}
}