package com.openMap1.mapper.util;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.Hashtable;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EModelElement;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcoreFactory;
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.XMLResource;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.BasicEMap;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.EMap;
import org.eclipse.emf.common.util.URI;
import com.openMap1.mapper.core.MapperException;
import com.openMap1.mapper.core.NamespaceSet;
import com.openMap1.mapper.core.NamespaceException;
import com.openMap1.mapper.core.Xpth;
import com.openMap1.mapper.core.XpthException;
import com.openMap1.mapper.core.ClassSet;
import com.openMap1.mapper.reader.objectRep;
import com.openMap1.mapper.reader.objectToken;
import com.openMap1.mapper.structures.MappableAssociation;
import com.openMap1.mapper.AssocEndMapping;
import com.openMap1.mapper.AssocMapping;
import com.openMap1.mapper.AttributeDef;
import com.openMap1.mapper.ClassDetails;
import com.openMap1.mapper.ConversionArgument;
import com.openMap1.mapper.ElementDef;
import com.openMap1.mapper.FixedPropertyValue;
import com.openMap1.mapper.GlobalMappingParameters;
import com.openMap1.mapper.MappedStructure;
import com.openMap1.mapper.MapperFactory;
import com.openMap1.mapper.Mapping;
import com.openMap1.mapper.MaxMult;
import com.openMap1.mapper.MinMult;
import com.openMap1.mapper.NodeDef;
import com.openMap1.mapper.NodeMappingSet;
import com.openMap1.mapper.ObjMapping;
import com.openMap1.mapper.ParameterClass;
import com.openMap1.mapper.PropMapping;
import com.openMap1.mapper.PropertyConversion;
import com.openMap1.mapper.ValueCondition;
/**
* A collection of utilities to act on the EMF mapper model,
* all static.
* @author robert
*
*/
public class ModelUtil {
//--------------------------------------------------------------------------------
// General utilities for EObjects, Mapped Structures and Ecore models
//--------------------------------------------------------------------------------
/**
* @param node any node in the model tree
* @return the root node of the tree.
* For a mapper model, this should be a MappedStructure.
*/
public static EObject getModelRoot(EObject node)
{
return node.eResource().getContents().get(0);
}
/**
* @param node any node in the model tree
* @return the top MappedStructure node.
*/
public static MappedStructure getMappedStructure(EObject node)
{
return (MappedStructure)node.eResource().getContents().get(0);
}
public static EPackage getClassModelRoot(EObject node)
{
EPackage classModelRoot = null;
EObject root = getModelRoot(node);
if (root instanceof MappedStructure) try
{
MappedStructure ms = (MappedStructure)root;
classModelRoot = ms.getClassModelRoot();
}
catch (MapperException ex) {}
return classModelRoot;
}
/**
*
* @param parent
* @param name
* @return
*/
public static ElementDef getChildElementDef(ElementDef parent, String name)
{
ElementDef child = null;
for (Iterator<ElementDef> it = parent.getChildElements().iterator(); it.hasNext();)
{
ElementDef next = it.next();
if ((next.getName() != null) && (next.getName().equals(name))) child = next;
}
return child;
}
/**
*
* @param parent
* @param path
* @return
*/
public static ElementDef getDescendantElementDef(ElementDef parent, String path)
{
StringTokenizer st = new StringTokenizer(path, ".");
ElementDef next = parent;
while ((st.hasMoreTokens()) && (next != null))
{
String step = st.nextToken();
next = getChildElementDef(next,step);
}
return next;
}
/**
* @param node any node in the model tree
* @return the global mapping parameters
*/
public static GlobalMappingParameters getGlobalMappingParameters (EObject node)
{
MappedStructure ms = (MappedStructure) getModelRoot(node);
return ms.getMappingParameters();
}
/**
* @param node any node in the model tree
* @return the global mapping parameters
*/
public static NamespaceSet getGlobalNamespaceSet (EObject node) throws NamespaceException
{
MappedStructure ms = (MappedStructure) getModelRoot(node);
if (ms.getMappingParameters() != null)
return new NamespaceSet(ms.getMappingParameters());
else return new NamespaceSet();
}
/**
* @param root root of some subtree of a EMF model EObject tree
* @param theClass an EMF model EClass
* @return a List of all objects of that class or a subclass under the root
*/
public static List<EObject> getEObjectsUnder(EObject root, EClass theClass)
{
Vector<EObject> instances = new Vector<EObject>();
addInstancesUnder(instances, root, theClass);
return instances;
}
private static void addInstancesUnder(Vector<EObject> instances, EObject root, EClass theClass)
{
if (theClass.isSuperTypeOf(root.eClass())) {instances.add(root);}
for (Iterator<EObject> it = root.eContents().iterator(); it.hasNext();)
{addInstancesUnder(instances,it.next(),theClass);}
}
/**
*
* @param eo an EObject
* @return the path of containment association names from the root of the ECore
* instance to this object
*/
public static String pathFromRoot(EObject eo)
{
String path = "";
if (eo == null) return path;
EObject container = eo.eContainer();
if (container == null) return path;
else
{
EClass thisClass = eo.eClass();
EClass containerClass = container.eClass();
for (Iterator<EReference> ir = containerClass.getEAllReferences().iterator();ir.hasNext();)
{
EReference ref = ir.next();
EClassifier reffed = ref.getEType();
if ((reffed instanceof EClass) && (((EClass)reffed)).isSuperTypeOf(thisClass))
path = pathFromRoot(container) + "/" + ref.getName();
}
}
return path;
}
/**
*
* @param subClass
* @param superClass
* @return true if subclass is a subclass of superclass, or the same class
*/
public static boolean isSubClass(EClass subClass, EClass superClass)
{
if (subClass.equals(superClass)) return true;
for (Iterator<EClass> it = subClass.getEAllSuperTypes().iterator();it.hasNext();)
{
EClass superC = it.next();
if (superC.equals(superClass)) return true;
}
return false;
}
//-------------------------------------------------------------------------------------
// Mappings
//-------------------------------------------------------------------------------------
/**
*
* @param m a property mapping or association end mapping or object mapping
* @return the object mapping of the class and subset involved
* (there should be exactly one; throws a MappingException if there is more than one,
* or less than one, or if the mapping is an association mapping)
*/
public static ObjMapping getObjectMapping(Mapping m) throws MapperException
{
if (m instanceof AssocMapping)
{throw new MapperException("Association mappings do not define a class and subset");}
ObjMapping om = getObjectMapping(getModelRoot(m),m.getClassSet());
if (om == null) throw new MapperException("Found no object mapping with class '"
+ m.getMappedClass() + "' and subset '" + m.getSubset() + "'");
return om;
}
/**
*
* @param eo any object in a mapping model tree - typically the root,
* but *not* the target object mapping itself
* @param cs the required class and subset
* @return an object mapping in the subtree below the chosen node,
* with the required class and subset, or null if there are none;
* throw a MapperException if there is more than one
*/
public static ObjMapping getObjectMapping(EObject eo, ClassSet cs)
throws MapperException
{
ObjMapping fom = null;
int found = 0;
for (Iterator<EObject> it = eo.eAllContents(); it.hasNext();)
{
EObject nex = it.next();
if (nex instanceof ObjMapping)
{
ObjMapping om = (ObjMapping)nex;
if (cs.equals(om.getClassSet()))
{
fom = om;
found++;
}
}
}
if (found > 1) throw new MapperException("There are " + found + " object mappings with class "
+ cs.stringForm());
return fom;
}
/**
*
* @param n any Node in the mapped structure
* @return absolute path from the root to that node
* @throws XpthException
*/
public static String getRootPath(NodeDef n) throws XpthException
{
return buildPathFrom(n,"");
}
/**
*
* @param n any Node in the mapped structure
* @return absolute path from the root to that node
* @throws XpthException
*/
public static Xpth getRootXpth(NodeDef n) throws XpthException
{
String XPathString = buildPathFrom(n,"");
return new Xpth(getGlobalNamespaceSet(n),XPathString);
}
private static String buildPathFrom(NodeDef currentNode, String pathBelowNode)
throws XpthException
{
String thisStep = "";
if (currentNode instanceof AttributeDef) thisStep = "/@" + currentNode.getName();
else if (currentNode instanceof ElementDef) thisStep = "/" + currentNode.getName();
String pathIncludingNode = thisStep + pathBelowNode;
EObject parent = currentNode.eContainer();
if (parent instanceof MappedStructure) return pathIncludingNode;
else if (parent instanceof NodeDef)
{return buildPathFrom((NodeDef)parent,pathIncludingNode);}
else throw new XpthException("Container of node '" + currentNode.getName() + "' is a "
+ parent.getClass().getName());
}
/**
* @param root the root of the mapped Structure *
* @param rootPath an absolute definite path from the root of the mapped structure
* @return the Node at the end of the path, or null if there is none
* @throws MapperException if any step along the path leads to more than one child node
* or if the path is not an absolute definite descending path
*/
public static NodeDef getNodeDef(MappedStructure root, Xpth rootPath) throws MapperException
{
NodeDef nd = null;
if (rootPath.size() > 0)
{
nd = root.getRootElement();
if ((nd != null) && (nd.getName().equals(rootPath.nodeName(0))))
{
if (rootPath.size() == 1) return nd;
else for (int s = 1; s < rootPath.size(); s++)
if ((nd != null) && (nd instanceof ElementDef))
{
String stepName = rootPath.nodeName(s);
boolean isSAttribute = rootPath.axis(s).equals("attribute");
nd = namedChildNode((ElementDef)nd,stepName,isSAttribute);
}
}
}
return nd;
}
/**
*
* @param parent a parent Element
* @param name the name of a child element or attribute (beginning with '@')
* @return the named child node, or null if there is none
* @throws MapperException if there is more than one child of that name7u6
*
*/
public static NodeDef namedChildNode(ElementDef parent, String name, boolean isAttribute)
throws MapperException
{
NodeDef nd = null;
int found = 0;
if (isAttribute)
for (Iterator<AttributeDef> it = parent.getAttributeDefs().iterator(); it.hasNext();)
{
AttributeDef child = it.next();
if (child.getName().equals(name)) {nd = child; found++;}
}
else
for (Iterator<ElementDef> it = parent.getChildElements().iterator(); it.hasNext();)
{
ElementDef child = it.next();
if (child.getName().equals(name)) {nd = child; found++;}
}
if (found > 1) throw new MapperException("Found " + found + " child nodes with name '"
+ name + "' beneath Element '" + parent.getName() + "'");
return nd;
}
/**
*
* @param className qualfied name of a class, prefixed by the package name
* @param node any node in the mapping set
* @return class details, if they have been specified
*/
public static ClassDetails getClassDetailsX(String className, EObject node)
{
ClassDetails cd = null;
for (Iterator<ClassDetails> it = getGlobalMappingParameters(node).getClassDetails().iterator(); it.hasNext();)
{
ClassDetails next = it.next();
if (next.getQualifiedClassName().equals(className)) cd = next;
}
return cd;
}
/**
* @param cs a class and subset
*
* @param propName a property name
* @param node any node in the mapping set
* @return all property mappings for the class, subset and property.
* There can be more than one if the mappings are multiway - choice or redundant
*/
public static Vector<PropMapping> getPropertyMappings(ClassSet cs, String propName, EObject node)
{
Vector<PropMapping> pmv = new Vector<PropMapping>();
for (Iterator<EObject> it = getModelRoot(node).eAllContents();it.hasNext();)
{
EObject next = it.next();
if (next instanceof PropMapping)
{
PropMapping pm = (PropMapping)next;
if ((pm.getClassSet().equals(cs)) && (pm.getMappedProperty().equals(propName)))
{pmv.add(pm);}
}
}
return pmv;
}
//-------------------------------------------------------------------------------------
// Convenience methods for creating mappings
//-------------------------------------------------------------------------------------
/**
* make a clone tree from the tree of ElementDefs and AttributeDefs toClone,
* and add it to the children of ElementDef parent. (parent may be null)
* Do not clone mappings or annotations.
*/
public static ElementDef cloneTree(ElementDef parent, ElementDef toClone)
{
ElementDef copyEl = MapperFactory.eINSTANCE.createElementDef();
copyEl.setName(toClone.getName());
copyEl.setDefaultValue(toClone.getDefaultValue());
copyEl.setDescription(toClone.getDescription());
copyEl.setMinMultiplicity(toClone.getMinMultiplicity());
copyEl.setMaxMultiplicity(toClone.getMaxMultiplicity());
copyEl.setFixedValue(toClone.getFixedValue());
copyEl.setType(toClone.getType());
// copy across all AttributeDefs
for (Iterator<AttributeDef> it = toClone.getAttributeDefs().iterator(); it.hasNext();)
cloneAttributeDef(copyEl,it.next());
// recursively copy across ElementDef subtrees
for (Iterator<ElementDef> it = toClone.getChildElements().iterator();it.hasNext();)
cloneTree(copyEl,it.next());
if (parent != null) parent.getChildElements().add(copyEl);
return copyEl;
}
/**
* clone an attribute def (without mappings) and copy to a new parent ElementDef
* @param parent
* @param toClone
* @return
*/
public static AttributeDef cloneAttributeDef(ElementDef parent, AttributeDef toClone)
{
AttributeDef copyAtt = MapperFactory.eINSTANCE.createAttributeDef();
copyAtt.setName(toClone.getName());
copyAtt.setDefaultValue(toClone.getDefaultValue());
copyAtt.setDescription(toClone.getDescription());
copyAtt.setFixedValue(toClone.getFixedValue());
copyAtt.setMinMultiplicity(toClone.getMinMultiplicity());
copyAtt.setType(toClone.getType());
parent.getAttributeDefs().add(copyAtt);
return copyAtt;
}
/**
* return the Node Mapping set on a node; make
* a new one if necessary
*/
public static NodeMappingSet getNodeMappingSet(NodeDef nd)
{
NodeMappingSet nms = nd.getNodeMappingSet();
if (nms == null)
{
nms = MapperFactory.eINSTANCE.createNodeMappingSet();
nd.setNodeMappingSet(nms);
}
return nms;
}
/**
* add an ObjMapping to a set of mappings; set its clasc, package, and subset
*/
public static ObjMapping addObjMapping(NodeMappingSet nms, String className, String packageName, String subset)
{
ObjMapping om = MapperFactory.eINSTANCE.createObjMapping();
om.setMappedClass(className);
om.setMappedPackage(packageName);
om.setSubset(subset);
nms.getObjectMappings().add(om);
return om;
}
/**
* add a property mapping, for a property of an object defined by an object mapping
* @param nms
* @param om
* @param propName
* @return
*/
public static PropMapping addPropMapping(NodeMappingSet nms,ObjMapping om, String propName)
{
PropMapping pm = MapperFactory.eINSTANCE.createPropMapping();
pm.setMappedClass(om.getMappedClass());
pm.setMappedPackage(om.getMappedPackage());
pm.setSubset(om.getSubset());
pm.setMappedProperty(propName);
nms.getPropertyMappings().add(pm);
return pm;
}
/**
* add a ValueCondition to a Mapping; set the left path and right value
* (test is '=' and there is no function applied)
* @param om
* @param path
* @param value
* @return the ValueCondition
*/
public static ValueCondition addValueCondition(Mapping m, String path, String value)
{
ValueCondition vc = MapperFactory.eINSTANCE.createValueCondition();
vc.setLeftPath(path);
vc.setRightValue(value);
m.getMappingConditions().add(vc);
return vc;
}
/**
* add a fixed property value to an ObjMapping;
* set the property name and the value
* @param om
* @param property
* @param value
* @return the FixedPropertyValue
*/
public static FixedPropertyValue addFixedPropertyValue(ObjMapping om, String property, String value)
{
FixedPropertyValue fp = MapperFactory.eINSTANCE.createFixedPropertyValue();
fp.setMappedProperty(property);
fp.setFixedValue(value);
om.getFixedPropertyValues().add(fp);
return fp;
}
/**
* add a simple association mapping between two mapped classes, with no further mapping
* conditions, and making the inner class dependent on the mapping.
* The associaiton role name is on the inner end.
* @param nms
* @param om1
* @param om2
* @param roleName
* @return the association mapping
*/
public static AssocMapping addSimpleAssocMapping(NodeMappingSet nms, ObjMapping om1, ObjMapping om2, String roleName)
{
AssocMapping am = MapperFactory.eINSTANCE.createAssocMapping();
// end 1
AssocEndMapping am1 = MapperFactory.eINSTANCE.createAssocEndMapping();
am1.setMappedClass(om1.getMappedClass());
am1.setMappedPackage(om1.getMappedPackage());
am1.setSubset(om1.getSubset());
am1.setMappedRole("");
am.setMappedEnd1(am1);
// end 2
AssocEndMapping am2 = MapperFactory.eINSTANCE.createAssocEndMapping();
am2.setMappedClass(om2.getMappedClass());
am2.setMappedPackage(om2.getMappedPackage());
am2.setSubset(om2.getSubset());
am2.setMappedRole(roleName);
am2.setRequiredForObject(true);
am.setMappedEnd2(am2);
nms.getAssociationMappings().add(am);
return am;
}
//-------------------------------------------------------------------------------------
// Convenience methods on an EMF ECore model
//-------------------------------------------------------------------------------------
/**
* @param packageName name of a package in the ecore model
* @param node any node in the mapped structure
* @return the package object, or null if none found
*/
public static EPackage getEPackage(String packageName,EObject node)
{
EPackage thePackage = null;
try
{
EPackage cmRoot = ((MappedStructure)getModelRoot(node)).getClassModelRoot();
if (cmRoot != null) thePackage = getPackageInPackage(cmRoot,packageName);
}
catch (MapperException ex) {GenUtil.surprise(ex, "ModelUtil.getEPackage");}
return thePackage;
}
/**
* recursive search through a package and its sub-packages for
* a package of a given name
* @param thePackage top package being searched
* @param packageName name of the package sought
* @return
*/
public static EPackage getPackageInPackage(EPackage thePackage,String packageName)
{
EPackage result = null;
// null Package name can count as Package name "", because Ecore will only store null, not ""
if ((thePackage.getName() == null) && (packageName == null))
result = thePackage;
else if ((thePackage.getName() == null) && ("".equals(packageName)))
result = thePackage;
else if ((thePackage.getName() != null) && (thePackage.getName().equals(packageName)))
result = thePackage;
else for (Iterator<EPackage> it = thePackage.getESubpackages().iterator();it.hasNext();)
{
EPackage subPackage = it.next();
if (getPackageInPackage(subPackage,packageName) != null)
result = getPackageInPackage(subPackage,packageName);
}
return result;
}
/**
*
* @param className a name
* @param packageName the package the class is in
* @param node any node in the mapping set tree
* @return the EClass if it exists; or null if it does not
*/
public static EClass getEClass(String className,String packageName,EObject node)
{
EClass theClass = null;
EPackage thePackage = getEPackage(packageName,node);
if (thePackage != null)
for (Iterator<EClassifier> it = thePackage.getEClassifiers().iterator();it.hasNext();)
{
EClassifier next = it.next();
if ((next instanceof EClass) && (next.getName().equals(className)))
theClass = (EClass)next;
}
return theClass;
}
/**
*
* @param start an EClass
* @param refName the name of an EReference
* @return the EClass at the end of the EReference, if it exists; or null otherwise
*/
public static EClass getReferencedClass(EClass start, String refName)
{
EClass refClass = null;
for (Iterator<EReference> ir = start.getEAllReferences().iterator();ir.hasNext();)
{
EReference ref = ir.next();
if ((ref.getName().equals(refName)) && (ref.getEType() instanceof EClass))
refClass = (EClass)ref.getEType();
}
return refClass;
}
/**
*
* @param className a name
* @param node any node in the mapping set tree
* @return true if the name is the name of a class in the class model;
* false if it is not, or if the class model does not exist
*/
public static boolean isInClassModel(String className,String packageName,EObject node)
{
return (getEClass(className, packageName, node) != null);
}
/**
*
* @param fromClass name of a class
* @param rolename of an association
* @param toClass name of a class
* @param node any object in the mapped structure
* @return true of there as an association from.role(to), inherited or not.
*/
public static boolean associationExists(String fromClass, String fromPackage,
String role, String toClass, String toPackage,EObject node)
{
boolean exists = false;
EClass from = getEClass(fromClass,fromPackage,node);
EClass to = getEClass(toClass,toPackage,node);
if ((from != null) && (to != null))
{
for (Iterator<EReference> it = from.getEAllReferences().iterator();it.hasNext();)
{
EReference ref = it.next();
if ((ref.getName().equals(role))
&& (ref.getEReferenceType().isSuperTypeOf(to))) exists = true;
}
}
return exists;
}
/**
* write out the values of all EAttributes and EReferences of an EObject
* @param eo
*/
public static void writeAllFeatures(EObject eo)
{
EClass ec = eo.eClass();
System.out.println("Features of object of class " + ec.getName());
for (Iterator<EAttribute> it = ec.getEAllAttributes().iterator();it.hasNext();)
{
EAttribute ea = it.next();
Object val = eo.eGet(ea);
String att = ("Attribute " + ea.getName());
if (val != null)
{att = att + " (" + val.getClass().getName() + "): " + val.toString();}
else {att = att + " (null)";}
System.out.println(att);
}
for (Iterator<EReference> it = ec.getEAllReferences().iterator();it.hasNext();)
{
EReference er = it.next();
Object val = eo.eGet(er);
String ref = ("Reference " + er.getName());
if (val == null) ref = ref + "(null)";
else
{
if (val instanceof EList<?>) ref = ref + "(" + ((EList<?>)val).size() + ")";
else {ref = ref + " (" + val.getClass().getName() + "): " + val.toString();}
}
System.out.println(ref);
}
System.out.println("");
}
/**
* clone an EAttribute
* @param ea
* @return
*/
public static EAttribute cloneEAttribute(EAttribute ea)
{
EAttribute theAtt = EcoreFactory.eINSTANCE.createEAttribute();
theAtt.setName(ea.getName());
theAtt.setEType(ea.getEType());
theAtt.setLowerBound(ea.getLowerBound());
copyMifAnnotations(ea,theAtt);
return theAtt;
}
/**
* clone an EReference, giving it the same target class as the original
* @param er
* @return
*/
public static EReference cloneEReference(EReference er)
{
EReference theRef = EcoreFactory.eINSTANCE.createEReference();
theRef.setName(er.getName());
theRef.setEType(er.getEType());
theRef.setLowerBound(er.getLowerBound());
theRef.setUpperBound(er.getUpperBound());
theRef.setContainment(er.isContainment());
copyMifAnnotations(er,theRef);
return theRef;
}
/**
* clone an EClass, putting it in the same EPackage as the original, with a different name
* @param ec
* @return the cloned EClass
*/
public static EClass cloneEClass(EClass ec, String newName)
{
EClass theClass = EcoreFactory.eINSTANCE.createEClass();
theClass.setName(newName);
ec.getEPackage().getEClassifiers().add(theClass);
for (Iterator<EStructuralFeature> it = ec.getEStructuralFeatures().iterator(); it.hasNext();)
{
EStructuralFeature next = it.next();
if (next instanceof EAttribute)
theClass.getEStructuralFeatures().add(cloneEAttribute((EAttribute)next));
if (next instanceof EReference)
theClass.getEStructuralFeatures().add(cloneEReference((EReference)next));
}
copyMifAnnotations(ec,theClass);
return theClass;
}
/**
* clone an EClass, putting it in the same EPackage as the original, with a different name
* and not cloning any EAttributes or EReferences with names in a list
* and no
* @param ec
* @return the cloned EClass
*/
public static EClass cloneEClassWithoutFeatures(EClass ec, String newName, Vector<String> featureNames)
{
EClass theClass = EcoreFactory.eINSTANCE.createEClass();
theClass.setName(newName);
ec.getEPackage().getEClassifiers().add(theClass);
for (Iterator<EStructuralFeature> it = ec.getEStructuralFeatures().iterator(); it.hasNext();)
{
EStructuralFeature next = it.next();
if (!GenUtil.inVector(next.getName(), featureNames))
{
if (next instanceof EAttribute)
theClass.getEStructuralFeatures().add(cloneEAttribute((EAttribute)next));
if (next instanceof EReference)
theClass.getEStructuralFeatures().add(cloneEReference((EReference)next));
}
}
copyMifAnnotations(ec,theClass);
return theClass;
}
//-------------------------------------------------------------------------------------
// Mapped properties and pseudo-properties
//-------------------------------------------------------------------------------------
/**
* get all properties of a class, direct or inherited
* @param className
* @param node
* @return
*/
public static Vector<String> getProperties(String className, String packageName, EObject node)
{
Vector<String> propNames = new Vector<String>();
EClass cl = getEClass(className, packageName, node);
if (cl != null) for (Iterator<EAttribute> iw = cl.getEAllAttributes().iterator();iw.hasNext();)
propNames.add(iw.next().getName());
return propNames;
}
/**
* get all properties and pseudo-properties of a class and subset
* @param className
* @param subset
* @param node any node in the mapping set
* @return
*/
public static Vector<String> getPropertiesAndPseudoProperties(String className, String packageName,String subset, EObject node)
{
Vector<String> props = getProperties(className,packageName,node);
Vector<String> pProps = getPseudoProperties(className,subset,node);
for (Iterator<String> it = pProps.iterator();it.hasNext();) props.add(it.next());
return props;
}
/**
*
* @param className
* @param packageName
* @param subset
* @param propName
* @param node any ode n the mapping set
* @return true if the property is a property of the class,
* or a pseudo-property of the class and subset
*/
public static boolean hasPropertyOrPseudoProperty
(String className, String packageName, String subset, String propName, EObject node)
{
return GenUtil.inVector(propName, getPropertiesAndPseudoProperties(className,packageName,subset,node));
}
/**
* @param className
* @param packageName
* @param propName
* @param node any ode n the mapping set
* @return true if the property is a property of the class,
*/
public static boolean hasProperty
(String className, String packageName,String propName, EObject node)
{
return GenUtil.inVector(propName, getProperties(className,packageName,node));
}
/**
* return a Hashtable of all pseudo-properties of any class
* (things which are converted into real properties in the class model)
* These are all the arguments of any 'in' conversions and
* results of any 'out' conversions, sor all subset mappings
* @param className qualfied name of the class
* @param node any node in the mapping set
* @return Hashtable of all pseudo-property names (key) and their mapping subsets (value)
* The value is not much use, since the same pseudo-property may be used for several subsets
* and only one will be recorded.
*/
public static Hashtable<String,String> getPseudoProperties(String className, NodeDef node)
{
MappedStructure ms = (MappedStructure) getModelRoot(node);
// store pseudo-properties in a Hashtable to avoid duplicates and note the subsets
Hashtable<String,String> ppSet = new Hashtable<String,String>();
if (ms.getMappingParameters() != null)
for (Iterator<ClassDetails> it = ms.getMappingParameters().getClassDetails().iterator();it.hasNext();)
{
ClassDetails cd = it.next();
if (cd.getQualifiedClassName().equals(className))
{
// find all results of 'out' conversions and arguments of 'in' conversions for the subset
for (Iterator<PropertyConversion> iw = cd.getPropertyConversions().iterator();iw.hasNext();)
{
PropertyConversion pc = iw.next();
if (pc.getSense().getLiteral().equals("out")) ppSet.put(pc.getResultSlot(),pc.getSubset());
else if (pc.getSense().getLiteral().equals("in"))
for (Iterator<ConversionArgument> ix = pc.getConversionArguments().iterator();ix.hasNext();)
{ppSet.put(ix.next().getPropertyName(),pc.getSubset());}
}
}
}
return ppSet;
}
/**
* get names of all pseudo-properties of a given class and subset
* @param className
* @param subset
* @param node any node in the mapped structure
* @return
*/
public static Vector<String> getPseudoProperties(String className, String subset, EObject node)
{
MappedStructure ms = (MappedStructure) getModelRoot(node);
Vector<String> ppNames = new Vector<String>();
if (ms.getMappingParameters() != null)
for (Iterator<ClassDetails> it = ms.getMappingParameters().getClassDetails().iterator();it.hasNext();)
{
ClassDetails cd = it.next();
if (cd.getQualifiedClassName().equals(className))
{
// find all results of 'out' conversions and arguments of 'in' conversions for the subset
for (Iterator<PropertyConversion> iw = cd.getPropertyConversions().iterator();iw.hasNext();)
{
PropertyConversion pc = iw.next();
if (pc.getSubset().equals(subset))
{
if (pc.getSense().getLiteral().equals("out")) ppNames.add(pc.getResultSlot());
else if (pc.getSense().getLiteral().equals("in"))
for (Iterator<ConversionArgument> ix = pc.getConversionArguments().iterator();ix.hasNext();)
{ppNames.add(ix.next().getPropertyName());}
}
}
}
}
return ppNames;
}
//---------------------------------------------------------------------------------------
// Paths in the mapped structure
//---------------------------------------------------------------------------------------
/**
*
* @param node a Node in the Mapped structure
* @param path an XPath
* @return true if the XPath is a relative path,
* and any 'parent' , 'child' or 'attribute' steps match the node names from the
* start node
*/
public static boolean isRelativePath(NodeDef node, Xpth path) throws MapperException
{
boolean isRPath = false;
try
{
if (!path.fromRoot()) // absolute paths fail
{
// 'stay here' is always a valid path
if (path.axis(0).equals("self"))
{
if (path.nodeName(0).equals("node()"))isRPath = true;
if (path.nodeName(0).equals(node.getName()))isRPath = true;
}
// ancestor step must be the only ascending step ('ancestor-or-self' is not yet allowed)
else if (path.axis(0).equals("ancestor"))
{
EObject parent = node.eContainer();
while ((!isRPath) && (parent instanceof ElementDef)) // fail when you reach MappedStructure
{
// if an ancestor name matches, check the descent from that ancestor
if (((ElementDef)parent).getName().equals(path.nodeName(0)))
{
// single-step ancestor paths are valid
if (path.size() == 1) isRPath = true;
// otherwise test the descending part
else isRPath = isDescendingPath((ElementDef)parent, path.removeOuterStep());
}
parent = parent.eContainer(); // keep trying ancestors
}
}
// parent step: check the containing element name
else if ((path.axis(0).equals("parent")) && (node.eContainer() instanceof ElementDef))
{
ElementDef parent = (ElementDef)node.eContainer();
// the node test 'node()' will match any node name
if ((parent.getName().equals(path.nodeName(0)))|
(path.nodeName(0).equals("node()")))
{
if (path.size() == 1) isRPath = true; // no more steps to check
else if (path.size() > 1)
{
// recursive check of remaining path
isRPath = isRelativePath(parent,path.removeOuterStep());
}
}
}
else if ((path.axis(0).equals("child")) && (node instanceof ElementDef))
{
isRPath = isDescendingPath((ElementDef)node, path);
}
else if (path.axis(0).equals("attribute"))
{
if (node instanceof ElementDef)
isRPath = (((ElementDef)node).getNamedAttribute(path.nodeName(0)) != null);
}
// other axes lead to failure
}
}
catch (XpthException ex) {return false;}
return isRPath;
}
/**
*
* @param node a Node in the Mapped structure
* @param path an XPath
*/
public static boolean isDescendingPath(ElementDef node, Xpth path) throws MapperException
{
boolean isDPath = false;
if ((path.axis(0).equals("descendant"))|(path.axis(0).equals("descendant-or-self")))
{
isDPath = true; // there is not much checking you can do after a descendant step
}
else if (path.axis(0).equals("child"))
{
// find a matching child element name
for (Iterator<ElementDef> it = node.getChildElements().iterator();it.hasNext();)
{
ElementDef child = it.next();
/* the node name must match; child::node() is not allowed */
if (child.getName().equals(path.nodeName(0)))
{
if (path.size() == 1) isDPath = true; // no more steps to check
else if (path.size() > 1)
{
// recursive step - check the remainder of the path against the child element
isDPath = isDescendingPath(child,path.removeOuterStep());
}
}
}
}
// must match an attribute name
else if ((path.axis(0).equals("attribute")) && (path.size() == 1))
{
for (Iterator<AttributeDef> it = node.getAttributeDefs().iterator();it.hasNext();)
if (it.next().getName().equals(path.nodeName(0))) isDPath = true;
}
return isDPath;
}
/**
*
* @param node a Node in the Mapped structure
* @param path an XPath
* @param mustbeUnique if true, the path must lead to at most one node
* @param isNonOptional if true, the path must lead to at least one node
* @return true if the XPath is a relative path,
* every step has an 'ancestor', 'parent','attribute' or 'child' axis,
* every node is named and matches the structure, and (when mustbeUnique is true)
* if the path leads to at most one node.
*/
public static boolean isRelativeDefinitePath(NodeDef node, Xpth path,
boolean mustBeUnique,
boolean isNonOptional)
{
boolean isRDPath = false;
try
{
/* indefinite paths (other than ancestor paths which are allowed)
* are filtered out by their 'descendant' steps. */
if (!path.fromRoot())
{
// 'stay here' is always a valid and unique path
if (path.axis(0).equals("self"))
{
if (path.nodeName(0).equals("node()"))isRDPath = true;
if (path.nodeName(0).equals(node.getName()))isRDPath = true;
}
// ancestor step must be the only ascending step ('ancestor-or-self' is not yet allowed)
else if (path.axis(0).equals("ancestor"))
{
EObject parent = node.eContainer();
while ((!isRDPath) && (parent instanceof ElementDef)) // fail when you reach MappedStructure
{
// if an ancestor name matches, check the descent from that ancestor
if (((ElementDef)parent).getName().equals(path.nodeName(0)))
{
// single-step ancestor paths are valid and unique
if (path.size() == 1) isRDPath = true;
// otherwise test the descending part
else isRDPath = isDescendingRelativeDefinitePath
((ElementDef)parent, path.removeOuterStep(), mustBeUnique,isNonOptional);
}
parent = parent.eContainer();
}
}
// parent step: check the containing element name, but uniqueness is guaranteed for the step
else if ((path.axis(0).equals("parent")) && (node.eContainer() instanceof ElementDef))
{
ElementDef parent = (ElementDef)node.eContainer();
// the node test 'node()' will match any node name
if ((parent.getName().equals(path.nodeName(0)))|
(path.nodeName(0).equals("node()")))
{
if (path.size() == 1) isRDPath = true; // no more steps to check
else if (path.size() > 1)
{
// recursive check of remaining path
isRDPath = isRelativeDefinitePath(parent,path.removeOuterStep(), mustBeUnique,isNonOptional);
}
}
}
else if ((path.axis(0).equals("child")) && (node instanceof ElementDef))
{
isRDPath = isDescendingRelativeDefinitePath((ElementDef)node, path, mustBeUnique,isNonOptional);
}
else if (path.axis(0).equals("attribute"))
{
if (node instanceof ElementDef)
isRDPath = (((ElementDef)node).getNamedAttribute(path.nodeName(0)) != null);
}
// other axes lead to failure
}
}
catch (XpthException ex) {return false;}
return isRDPath;
}
/**
*
* @param node a Node in the Mapped structure
* @param path an XPath
* @param mustbeUnique if true, the path must lead to at most one node
* @param isNonOptional if true, the path must lead to at least one node
* @return true if the XPath is a relative path, every step has the 'child' axis,
* every node is named and matches the structure, and (if mustbeUnique = true)
* the path leads to at most one node.
*/
public static boolean isDescendingRelativeDefinitePath(ElementDef node, Xpth path,
boolean mustBeUnique,
boolean isNonOptional)
{
boolean isDDPath = false;
try
{
if ((path.definite()) && (!path.fromRoot()))
{
if (path.axis(0).equals("child"))
{
// find a matching child element name
for (Iterator<ElementDef> it = node.getChildElements().iterator();it.hasNext();)
{
ElementDef child = it.next();
/* the node name must match, and possibly uniqueness and/or non-optionality may be required */
if ((child.getName().equals(path.nodeName(0))) &&
((!mustBeUnique)|(child.getMaxMultiplicity()== MaxMult.ONE)) &&
((!isNonOptional)|(child.getMinMultiplicity()== MinMult.ONE)))
{
if (path.size() == 1) isDDPath = true; // no more steps to check
else if (path.size() > 1)
{
// recursive step - check the remainder of the path against the child element
isDDPath = isDescendingRelativeDefinitePath
(child,path.removeOuterStep(),mustBeUnique,isNonOptional);
}
}
}
}
// an attribute must be the last step in a path, and is unique; match the name
else if ((path.axis(0).equals("attribute")) && (path.size() == 1))
{
for (Iterator<AttributeDef> it = node.getAttributeDefs().iterator();it.hasNext();)
{
AttributeDef ad = it.next();
if (ad.getName().equals(path.nodeName(0)) &&
((!isNonOptional)|(ad.getMinMultiplicity()== MinMult.ONE))) isDDPath = true;
}
}
// other axes, such as descendant, cause failure
}
}
catch (XpthException ex) {return false;}
return isDDPath;
}
/**
* @param node any Element in the mapped structure
* @param mustBeUnique
* @return a Vector of all definite relative paths from a node.
* If mustBeUnique is true, these can include only paths that lead to one node.
* Steps have only 'child' or 'parent' axes.
*/
public static Vector<Xpth> getRelativeDefinitePaths(NodeDef node, boolean mustBeUnique)
{
Vector<Xpth> paths = new Vector<Xpth>();
try {
// the 'stay here' path is allowed from any node
paths.add(new Xpth(getGlobalNamespaceSet(node),"."));
// find any descending paths from this node
if (node instanceof ElementDef)
{
Vector<Xpth> dPaths = getDRDPaths((ElementDef)node, null, null, mustBeUnique);
for (Iterator<Xpth> ix = dPaths.iterator();ix.hasNext();)
paths.add(ix.next());
}
// climb parents, adding paths to them and their descendants
Xpth nodePath = new Xpth(getGlobalNamespaceSet(node));
while (node.eContainer() instanceof ElementDef) // stops at the MappedStructure node
{
ElementDef parent = (ElementDef)node.eContainer();
Xpth parentPath = nodePath.addInnerStep("parent::" + parent.getName());
// exclude each node on the ascent, from the paths back down from its higher parents
Vector<Xpth> pPaths = getDRDPaths(parent, parentPath, node, mustBeUnique);
for (Iterator<Xpth> ix = pPaths.iterator();ix.hasNext();)
paths.add(ix.next());
node = parent; // to iterate up to ancestors
nodePath = parentPath; // to keep building a multiple 'parent::' path
}
}
catch (MapperException ex) {GenUtil.surprise(ex, "ModelUtil.getRelativeDefinitePaths");}
return paths;
}
/**
* get all descending definite relative paths from a node.
* If mustBeUnique is true, these can include only paths that lead to one node.
* Steps have only 'child' axes.
*/
public static Vector<Xpth> getDescendingRelativeDefinitePaths(ElementDef node, boolean mustBeUnique)
{
return getDRDPaths(node, null, null, mustBeUnique);
}
/*
* recursive finding of all descending relative definite paths from a node,
* adding them as inner steps to the path so far
* (if it is non-null ;otherwise starting a new path)
* If excludeChild is not null, do not include a path that goes back down to that child node.
*/
private static Vector<Xpth> getDRDPaths(ElementDef node, Xpth pathSoFar, NodeDef excludeChild, boolean mustBeUnique)
{
Vector<Xpth> paths = new Vector<Xpth>();
try {
// if non-null, the path to this node is part of the result set
if (pathSoFar != null) paths.add(pathSoFar);
// at the start of the recursion, we need an empty path to build on
else if (pathSoFar == null) pathSoFar = new Xpth(getGlobalNamespaceSet(node));
// recursion through child elements
for (Iterator<ElementDef> it = node.getChildElements().iterator();it.hasNext();)
{
ElementDef child = it.next();
// to avoid paths which go up and then come back down to the same node
boolean skipChild = ((excludeChild != null)&& (child.getName().equals(excludeChild.getName())));
// sometimes go only to unique child nodes
if ((!skipChild) && ((!mustBeUnique)|(child.getMaxMultiplicity() == MaxMult.ONE)))
{
Xpth extended = pathSoFar.addInnerStep("child::" + child.getName());
Vector<Xpth> childPaths = getDRDPaths(child, extended, null, mustBeUnique);
for (Iterator<Xpth> ix = childPaths.iterator();ix.hasNext();)
paths.add(ix.next());
}
}
// add paths for attributes
for (Iterator<AttributeDef> it = node.getAttributeDefs().iterator();it.hasNext();)
{
Xpth extended = pathSoFar.addInnerStep("attribute::" + it.next().getName());
paths.add(extended);
}
}
catch (MapperException ex) {GenUtil.surprise(ex, "ModelUtil.getDRDPaths");}
return paths;
}
/**
*
* @param start a Node in the mapped structure
* @param end another Node
* @return true if the shortest cross path from the start node to the end node
* can only result in one of the end node
*/
public static boolean uniqueTraverse(NodeDef start, NodeDef end)
{
boolean unique = true;
// find the trail of all nodes up from the start node to the root of the mapped structure
Vector<NodeDef> startAncestors = new Vector<NodeDef>();
EObject stanc = start;
while (stanc instanceof NodeDef)
{
startAncestors.add((NodeDef)stanc);
stanc = stanc.eContainer();
}
/* check if the end node or any of its ancestors is in this trail,
* failing if you encounter any multiple branch going up from the end */
boolean found = false;
EObject eanc = end;
while ((unique) && (!found) && (eanc instanceof NodeDef))
{
// if this node is in the ancestors of the start node, succeed
for (Iterator<NodeDef> it = startAncestors.iterator();it.hasNext();)
if (it.next().equals(eanc)) found = true;
// otherwise, if this is a multiple child of its parent, fail
if ((!found) && (eanc instanceof ElementDef)
&& (((ElementDef)eanc).getMaxMultiplicity() == MaxMult.UNBOUNDED)) unique = false;
// try the parent node
eanc = eanc.eContainer();
}
return unique;
}
/**
*
* @param m any mapping
* @return the Node of the mapped structure which contains it
*/
public static NodeDef mappingNode(Mapping m)
{
// Node/MappingSet/AssocMapping/AssocEndMapping
if (m instanceof AssocEndMapping) return (NodeDef)(m.eContainer().eContainer().eContainer());
// Node/MappingSet/Any other type of mapping
return (NodeDef)(m.eContainer().eContainer());
}
/**
* @param m any mapping
* @return true if the shortest path to its object mapping leads to a unique node
* @throws MapperException if there is no Object mapping
*/
public static boolean hasUniqueShortestPathToObjectMapping(Mapping m) throws MapperException
{
ObjMapping om = getObjectMapping(m);
return uniqueTraverse(mappingNode(m),mappingNode(om));
}
/**
* follow the XPath path from the node. Return true if the min or max cardinality is one
* (max if isMax = true; min if isMax = false)
* Throw a Mapper Exception if the path is invalid,
* or does not lead to nodes in the structure
*/
static private boolean cardinalityIsOne(NodeDef node, Xpth path, boolean isMax) throws MapperException
{
if (path.size() == 0) throw new MapperException("Cannot match empty path");
// 'self' step has min and max cardinality 1 if the node name matches
if (path.axis(0).equals("self"))
{
if (path.nodeName(0).equals("node()")) return true;
if (path.nodeName(0).equals(node.getName())) return true;
throw new MapperException("'self' step expected node '" + path.nodeName(0)
+ "' but found node '" + node.getName() + "'");
}
// 'parent' step does not alter min and max cardinality of the path
else if (path.axis(0).equals("parent"))
{
EObject p = node.eContainer();
if ((p instanceof NodeDef) && (path.nodeName(0).equals(((NodeDef)p).getName())))
{
if (path.size() == 1) return true;
else return cardinalityIsOne((NodeDef)p,path.removeOuterStep(),isMax);
}
else throw new MapperException("Failed to match 'parent' step '" + path.nodeName(0) + "'");
}
// 'ancestor' step does not alter min and max cardinality of the path
else if (path.axis(0).equals("ancestor"))
{
boolean found = false;
EObject p = node.eContainer();
//search up the tree to match the node name
while ((p instanceof NodeDef) && (!found))
{
NodeDef parent = (NodeDef)p;
found = (path.nodeName(0).equals(parent.getName()));
if (found)
{
if (path.size() == 1) return true;
else return cardinalityIsOne(parent,path.removeOuterStep(),isMax);
}
p = p.eContainer();
}
if (!found) throw new MapperException("Failed to match 'ancestor' step '" + path.nodeName(0) + "'");
}
// 'child' step can alter min and max cardinality of the path
else if (path.axis(0).equals("child"))
{
if (!(node instanceof ElementDef)) throw new MapperException("'child' step from Attribute");
for (Iterator<ElementDef> it = ((ElementDef)node).getChildElements().iterator();it.hasNext();)
{
ElementDef child = it.next();
if (path.nodeName(0).equals(child.getName()))
{
if (isMax && (child.getMaxMultiplicity()== MaxMult.UNBOUNDED)) return false;
else if (!isMax && (child.getMinMultiplicity()== MinMult.ZERO)) return false;
else if (path.size() == 1) return true;
else return cardinalityIsOne(child,path.removeOuterStep(),isMax);
}
}
throw new MapperException("Failed to match 'child' step '" + path.nodeName(0) + "'");
}
// 'attribute' step can alter min cardinality of the path
else if (path.axis(0).equals("attribute"))
{
if (!(node instanceof ElementDef)) throw new MapperException("'attribute' step from Attribute");
for (Iterator<AttributeDef> it = ((ElementDef)node).getAttributeDefs().iterator();it.hasNext();)
{
AttributeDef at = it.next();
if (path.nodeName(0).equals(at.getName()))
{
if (!isMax && (at.getMinMultiplicity()== MinMult.ZERO)) return false;
else if (path.size() == 1) return true;
else throw new MapperException("XPath cannot have steps after 'attribute' step");
}
}
throw new MapperException("Failed to match 'attribute' step '" + path.nodeName(0) + "'");
}
// other axes in steps are not supported
else throw new MapperException("Failed to match axis '" + path.axis(0) + "'");
return false; // to satisfy the compiler; but inaccessible
}
/**
* @param node a node in the mapped structure
* @param path a valid path from the node
* @return the max cardinality of the path; 1 or -1 (means unbounded)
* @throws MapperException if the path is invalid or does not lead to a node
*/
static public int getMaxCardinality(NodeDef node, Xpth path) throws MapperException
{
boolean isMax = true;
if (cardinalityIsOne(node,path, isMax)) return 1;
else return -1;
}
/**
* @param node a node in the mapped structure
* @param path a valid path from the node
* @return the min cardinality of the path; 1 or 0
* @throws MapperException if the path is invalid or does not lead to a node
*/
static public int getMinCardinality(NodeDef node, Xpth path) throws MapperException
{
boolean isMax = false;
if (cardinalityIsOne(node,path, isMax)) return 1;
else return 0;
}
//-------------------------------------------------------------------------------------
// Unique identifiers
//-------------------------------------------------------------------------------------
/** Return a Vector of vectors. Each one is a Vector
of property names which together form a unique identifier for
objects of the class - including those unique
identifiers it inherits.*/
public static Vector<Vector<String>> uniqueIdentifiers(String className, EPackage classModel)
{
Vector<Vector<String>> res = new Vector<Vector<String>>();
if (classModel != null)
{
for (Iterator<EClassifier> it = classModel.getEClassifiers().iterator();it.hasNext();)
{
EClassifier ec = it.next();
if ((ec instanceof EClass) && (ec.getName().equals(className)))
{
EClass ecc = (EClass)ec;
// find unique identifiers of the class itself
addIdentifiersForClass(ecc,res);
// find unique identifiers of any superclasses
for (Iterator<EClass> ix = ecc.getEAllSuperTypes().iterator();ix.hasNext();)
{
EClass supC = ix.next();
addIdentifiersForClass(supC,res);
}
}
}
}
return res;
}
private static void addIdentifiersForClass(EClass supC, Vector<Vector<String>> res)
{
for (Iterator<EAnnotation> iz = supC.getEAnnotations().iterator();iz.hasNext();)
{
EAnnotation ea = iz.next();
EMap<String,String> ed = ea.getDetails();
for (Iterator<String> ik = ed.keySet().iterator(); ik.hasNext();)
{
String key = ik.next();
if (key.startsWith("identifier"))
{
String ident = ed.get(key);
StringTokenizer st = new StringTokenizer(ident,", ");
Vector<String> idv = new Vector<String>();
while (st.hasMoreTokens()) idv.add(st.nextToken());
res.add(idv);
}
}
}
}
//--------------------------------------------------------------------------------------------
// annotations
//--------------------------------------------------------------------------------------------
private static String mifNamespaceURI = "urn:hl7-org:v3/mif2";
public static String mifNamespaceURI() {return mifNamespaceURI;}
private static String genModelURI = "http://www.eclipse.org/emf/2002/GenModel";
public static String genModelURI() {return genModelURI;}
/**
*
* @param toObj a Ecore model element to which an annotation is either to be added,
* or extended with a new String key and value
* @param key the key (new or possibly already existing)
* @param value the new value for the key
*/
public static void addMIFAnnotation(EModelElement toObj, String key, String value)
{
EAnnotation ann = toObj.getEAnnotation(mifNamespaceURI);
if (ann == null)
{
ann = EcoreFactory.eINSTANCE.createEAnnotation();
ann.setSource(mifNamespaceURI);
toObj.getEAnnotations().add(ann);
}
ann.getDetails().put(key, value);
}
/**
* copy MIF annotations from one EObject to another
* @param fromObj
* @param toObj
*/
public static void copyMifAnnotations(EModelElement fromObj,EModelElement toObj)
{
copySomeAnnotations(fromObj, toObj, mifNamespaceURI);
}
/**
* copy annotations with some namespace from one object to another
* @param fromObj
* @param toObj
* @param namespaceURI
*/
public static void copySomeAnnotations(EModelElement fromObj,EModelElement toObj, String namespaceURI)
{
EAnnotation ann = fromObj.getEAnnotation(namespaceURI);
if (ann != null)
{
EMap<String,String> details = ann.getDetails();
Hashtable<String,String> det = new Hashtable<String,String>();
for (Iterator<Entry<String,String>> it = details.iterator();it.hasNext();)
{
Entry<String,String> next = it.next();
det.put(next.getKey(), next.getValue());
}
EAnnotation ann2 = toObj.getEAnnotation(namespaceURI);
if (ann2 == null)
{
ann2 = EcoreFactory.eINSTANCE.createEAnnotation();
ann2.setSource(namespaceURI);
toObj.getEAnnotations().add(ann2);
}
for (Enumeration<String> en = det.keys(); en.hasMoreElements();)
{
String key = en.nextElement();
ann2.getDetails().put(key, det.get(key));
}
}
}
/**
*
* @param toObj
* @param key
* @param value
*/
public static void removeMIFAnnotation(EModelElement toObj, String key)
{
EAnnotation ann = toObj.getEAnnotation(mifNamespaceURI);
if (ann != null)
{
EMap<String,String> details = ann.getDetails();
if (details.get(key) != null)
{
details.removeKey(key);
if (details.size() == 0) toObj.getEAnnotations().remove(ann);
}
}
}
/**
* copy an ECore annotation from one object to another -
* maybe overwriting the values of existing annotations with the same
* source and the same key
* @param toObject
* @param note
*/
public static void copyAnnotation(EModelElement toObject, EAnnotation note)
{
String source = note.getSource();
// if the target does not have an annotation with this source, make one
EAnnotation existing = toObject.getEAnnotation(source);
if (existing == null)
{
existing = EcoreFactory.eINSTANCE.createEAnnotation();
existing.setSource(source);
toObject.getEAnnotations().add(existing);
}
// transfer values for all keys, overwriting if values already exist
for (Iterator<String> it = note.getDetails().keySet().iterator();it.hasNext();)
{
String key = it.next();
existing.getDetails().put(key, note.getDetails().get(key));
}
}
/**
* @param toObj an Ecore model element
* @param key the key to an annotation detail
* @return the value for that key, or null if there is none
*/
public static String getMIFAnnotation(EModelElement toObj, String key)
{
String value = null;
EAnnotation ann = toObj.getEAnnotation(mifNamespaceURI);
if (ann != null) value = ann.getDetails().get(key);
return value;
}
/**
*
* @param toObj an Ecore model element
* @return the details of the MIF annotation
*/
public static EMap<String,String> getMIFAnnotationDetails(EModelElement toObj)
{
EMap<String,String> details = new BasicEMap<String,String>();
EAnnotation ann = toObj.getEAnnotation(mifNamespaceURI);
if (ann != null) details = ann.getDetails();
return details;
}
/**
* @param el an EModelObject in an ecore model
* @param key the key of the annotation detail
* @return one detail of an annotation to an EObject, by key
*/
public static String getEAnnotationDetail(EModelElement el, String key)
{
String detail = null;
for (Iterator<EAnnotation> iz = el.getEAnnotations().iterator();iz.hasNext();)
{
EAnnotation ea = iz.next();
if (detail == null) detail = ea.getDetails().get(key);
}
return detail;
}
/**
* remove all EReferences from a class, leaving its EAttributes unchanged
* @param theClass
*/
static public void removeEReferences(EClass theClass)
{
Vector<EAttribute> atts = new Vector<EAttribute>();
for (Iterator<EAttribute> ia = theClass.getEAttributes().iterator();ia.hasNext();)
atts.add(ia.next());
theClass.getEStructuralFeatures().clear();
for (Iterator<EAttribute> ia = atts.iterator();ia.hasNext();)
theClass.getEStructuralFeatures().add(ia.next());
}
/**
* @param node any node in the mapping set
* @param className a class name
* @return true if the class is one of the parameter classes of the mapping set
* Appears not to be used
*/
public static boolean isParameterClassx(EObject node, String className)
{
boolean isPar = false;
MappedStructure root= getMappedStructure(node);
for (Iterator<ParameterClass> it = root.getParameterClasses().iterator(); it.hasNext();)
if (it.next().getClassName().equals(className)) isPar = true;
return isPar;
}
/**
* the association name is now calculated automatically by
* concatenating the association end names; or if they only differ
* by a final '_1' and '_2', by removing the final '_1' and '_2'
* @param role1 the role name of end 1 (the lexically first role name)
* @param role2 the role name of end 2 (lexically last role name)
* @return the association name
*/
public static String assocName(String r1, String r2)
{
String role1 = r1;
String role2 = r2;
// unexpected cases when either role is missing
if (role1 == null) return role2;
if (role2 == null) return role1;
// if either role is non-navigable (role name ""), the association name is the other role name
if (role1.equals(MappableAssociation.NON_NAVIGABLE_ROLE_NAME)) return role2;
if (role2.equals(MappableAssociation.NON_NAVIGABLE_ROLE_NAME)) return role1;
// reverse the roles if necessary, to ensure role1 is lexically first
if (role1.compareTo(role2) > 0)
{
String r = role1;
role1 = role2;
role2 = r;
}
String assocName = role1 + "|" + role2;
/* legacy case; for mapping sets which had association names but no role names,
* give them role names starting with the association name, ending in '_1' and '_2'
* and reconstruct the association name from them */
if ((role1.length() == role2.length()) &&
(role1.length() > 2) &&
(role1.endsWith("_1")) &&
(role2.endsWith("_2")))
{
String t1 = role1.substring(0,role1.length() - 2);
String t2 = role2.substring(0,role2.length() - 2);
if (t1.equals(t2)) assocName = t2;
}
return assocName;
}
/**
* from two association role names, return the role that will
* point to end 1 under the association naming and end convention
* @param r1 a role name
* @param r2 the inverse association role name
* @return the role which is lexically first
*/
public static String end1Role(String r1, String r2)
{
// if r2 is lexically first, return it
String role1 = r1;
if (r1.compareTo(r2) > 0) role1 = r2;
/* if either r1 or r2 is non-navigable (nonexistent in the Ecore model,
* the navigable role points to end 2 */
if (r2.equals(MappableAssociation.NON_NAVIGABLE_ROLE_NAME)) role1 = r2;
if (r1.equals(MappableAssociation.NON_NAVIGABLE_ROLE_NAME)) role1 = r1;
return role1;
}
/**
* from two association role names, return the role that will
* point to end 2 under the association naming and end convention
* @param r1 a role name
* @param r2 the inverse association role name
* @return the role which is lexically last
*/
public static String end2Role(String r1, String r2)
{
// if r2 is lexically first, return r1
String role2 = r2;
if (r1.compareTo(r2) > 0) role2 = r1;
/* if either r1 or r2 is non-navigable (nonexistent in the Ecore model,
* the non-navigable role points to end 1 */
if (r2.equals(MappableAssociation.NON_NAVIGABLE_ROLE_NAME)) role2 = r1;
if (r1.equals(MappableAssociation.NON_NAVIGABLE_ROLE_NAME)) role2 = r2;
return role2;
}
/**
* @param className
* @param packageName
* @return class name preceded by the package name and '.', if the package name is not empty
*/
public static String getQualifiedClassName(String className,String packageName)
{
String qName = className;
if ((packageName != null) && (!packageName.equals(""))) qName = packageName + "." + className;
return qName;
}
/**
* return an association name given only one role name, as in class1.role.class2.
* The association name is the role name if the reference has no opposite.
* @param class1
* @param role
* @param class2
* @param node
* @return the association name calculated automatically, or null if there is any problem
*/
public static String getAssociationName(String qualifiedClassName1,String role,
String qualifiedClassName2, EObject node)
{
String class1 = getBareClassName(qualifiedClassName1);
String packageName1 = getPackageName(qualifiedClassName1);
String class2 = getBareClassName(qualifiedClassName2);
String packageName2 = getPackageName(qualifiedClassName2);
EPackage package1 = getEPackage(packageName1,node);
EPackage package2 = getEPackage(packageName2,node);
return getAssociationName(class1, package1, role, class2, package2);
}
/**
* return an association name given only one role name, as in class1.role.class2.
* The association name is the role name if the reference has no opposite.
* @param class1
* @param role
* @param class2
* @param node
* @return the association name calculated automatically, or null if there is any problem
*/
public static String getAssociationName(String class1,EPackage package1, String role, String class2, EPackage package2)
{
String assocName = role; // remains so if there is no inverse or if it is non-navigable
if (role.equals(MappableAssociation.NON_NAVIGABLE_ROLE_NAME))
System.out.println("Cannot find an inverse role to '" +
MappableAssociation.NON_NAVIGABLE_ROLE_NAME + "' because there might be several");
String invRole = getInverseRole(class1, package1,role, class2, package2);
if ((invRole != null)&& (!(invRole.equals(MappableAssociation.NON_NAVIGABLE_ROLE_NAME))))
assocName = assocName(role,invRole);
return assocName;
}
/**
* @param qualifiedClassName1 the start class
* @param role the role name to get to the target class
* @param qualifiedClassName2 the target class
* @param node any node in the mapped structure referring to the class model
* @return the inverse role name, or null if there is any problem
*/
public static String getInverseRole(String qualifiedClassName1, String role, String qualifiedClassName2, EObject node)
{
String class1 = getBareClassName(qualifiedClassName1);
String packageName1 = getPackageName(qualifiedClassName1);
EPackage package1 = getEPackage(packageName1,node);
String class2 = getBareClassName(qualifiedClassName2);
String packageName2 = getPackageName(qualifiedClassName2);
EPackage package2 = getEPackage(packageName2,node);
return getInverseRole(class1, package1, role, class2, package2);
}
/**
* @param class1
* @param package1
* @param role
* @param class2
* @param package2
* @return
*/
public static String getInverseRole(String class1, EPackage package1,
String role, String class2, EPackage package2)
{
String invRole = MappableAssociation.NON_NAVIGABLE_ROLE_NAME;
if ((package1 == null)|(package2 == null)) return invRole;
EClass c1 = (EClass)package1.getEClassifier(class1);
EClass c2 = (EClass)package2.getEClassifier(class2);
if ((c1 == null)|(c2 == null)) return invRole;
else if (!(role.equals(MappableAssociation.NON_NAVIGABLE_ROLE_NAME)))
{
for (Iterator<EReference> it = c1.getEAllReferences().iterator(); it.hasNext();)
{
EReference er = it.next();
EClass c2s = er.getEReferenceType();
if ((er.getName()!= null) && (er.getName().equals(role)) && (c2s != null) && (c2s.isSuperTypeOf(c2)))
{
EReference inverse = er.getEOpposite();
if (inverse != null) // usual case
{
invRole= inverse.getName();
}
else // no EOpposite; non-navigable role
{
invRole = MappableAssociation.NON_NAVIGABLE_ROLE_NAME;
}
}
}
}
else if (role.equals(MappableAssociation.NON_NAVIGABLE_ROLE_NAME))
{
System.out.println("Cannot yet get the name of the inverse role to '"
+ MappableAssociation.NON_NAVIGABLE_ROLE_NAME + "'");
}
return invRole;
}
/**
* Extract a role name for end 0 or 1 from an association name, according to the automatic
* naming of associations. But does not support the legacy asscoaition naming,
* and in any case this method is not used - thus the wierd name
* @param assocName
* @param end01
* @return
*/
public static String roleNameX(String assocName, int end01) throws MapperException
{
if ((end01 < 0)|(end01 >1)) throw new MapperException ("Invalid end: " + end01);
StringTokenizer st = new StringTokenizer(assocName,"|");
if (st.countTokens() == 2) // usual case
{
String[] role = new String[2];
role[0] = st.nextToken();
role[1] = st.nextToken();
// if role[0] is lexically after role[1], reverse ends
if (role[0].compareTo(role[1]) > 0) return role[1-end01];
return role[end01];
}
else if (st.countTokens() == 1)
{
/* this method used to add '_1' or '_2' to the end to support the legacy
* convention or naming roles. Now it no longer does - it supports the new convention
* that when one end is non-navigable, the association name is the navigable
* role name, and the navigable role leads to end 1 of (1,2) i.e
* to end 0 of (0,1). But since this method is not used, it all does not matter.*/
if (end01 == 0) return assocName;
if (end01 == 1) return MappableAssociation.NON_NAVIGABLE_ROLE_NAME;
}
else throw new MapperException ("Invalid association name: " + assocName);
return ""; // to satisfy the compiler
}
//-------------------------------------------------------------------------------------------
// handling nested packages and qualified class names
//-------------------------------------------------------------------------------------------
/**
* get a named class from the ECore class model
* @param qualifiedClassName the class name, preceded by the package name and '.' if non-empty
* @return
*/
public static EClass getNamedClass(EPackage topPackage, String qualifiedClassName)
{
String packageName = getPackageName(qualifiedClassName);
EPackage thePackage = getNamedPackage(topPackage,packageName);
String className = getBareClassName(qualifiedClassName);
if (thePackage != null) return getEClass(thePackage, className);
else return null;
}
/**
* @param className
* @return the named class
*/
public static EClass getEClass(EPackage thePackage, String className)
{
if (thePackage == null) return null;
EClassifier ec = thePackage.getEClassifier(className);
if ((ec != null) && (ec instanceof EClass)) return (EClass)ec;
else return null;
}
/**
*
* @param theClass
* @param attName the name of an EAttribute, which may be inherited
* @return the EAttribute, or null if there is none of that name
*/
public static EAttribute getNamedAttribute(EClass theClass, String attName)
{
EAttribute att = null;
for (Iterator<EAttribute> it = theClass.getEAllAttributes().iterator();it.hasNext();)
{
EAttribute a = it.next();
if (a.getName().equals(attName)) att = a;
}
return att;
}
/**
*
* @param topPackage
* @param packageName name of the required package; must not be null
* @return the top package or a sub-package within it with the required name;
* null package name matches with "".
*/
public static EPackage getNamedPackage(EPackage topPackage, String packageName)
{
EPackage thePackage = null;
if (packageName.equals(topPackage.getName())) thePackage = topPackage;
if ((packageName.equals("")) && (topPackage.getName()== null))
thePackage = topPackage;
for (Iterator<EPackage> it = topPackage.getESubpackages().iterator();it.hasNext();)
{
EPackage child = it.next();
if (thePackage == null) thePackage = getNamedPackage(child,packageName);
}
return thePackage;
}
/**
* @param qualifiedClassName the class name, preceded by the package name and '.' if non-empty
* @return the package name, or "" if there is none
*/
public static String getPackageName(String qualifiedClassName)
{
String packageName = "";
StringTokenizer st = new StringTokenizer(qualifiedClassName,".");
// if there are any full stops, return the string before the first
if (st.countTokens() > 1) packageName = st.nextToken();
return packageName;
}
/**
* @param qualifiedClassName the class name, preceded by the package name and '.' if non-empty
* @return the package name, or "" if there is none
*/
public static String getBareClassName(String qualifiedClassName)
{
String cName = qualifiedClassName;
String packageName = getPackageName(qualifiedClassName);
// if there are any full stops, remove the package name and the first full stop
if (!packageName.equals("")) cName = qualifiedClassName.substring(packageName.length() + 1);
return cName;
}
/**
*
* @param theClass an EClass
* @return the class name, preceded by the package name and '.' if nonempty
*/
public static String getQualifiedClassName(EClass theClass)
{
String qName = theClass.getName();
String packageName = theClass.getEPackage().getName();
if ((packageName != null) && (!packageName.equals("")))
qName = packageName + "." + qName;
return qName;
}
/**
* @param theClass an EClass
* @return the outermost package, containing the class either directly
* or in a sub-package
*
*/
public static EPackage getTopPackage(EClass theClass)
{
EPackage direct = theClass.getEPackage();
while (direct.getESuperPackage() != null) direct = direct.getESuperPackage();
return direct;
}
/**
*
* @param pack a package
* @return a Hashtable whose key = qualified class name
* and value = a Vector of qualified class names of all classes that directly
* contain that class by a containment relation
*/
public static Hashtable<String,Vector<String>> parentTable(EPackage pack)
{
Hashtable<String,Vector<String>> pTable = new Hashtable<String,Vector<String>>();
for (Iterator<EClass> it = getAllClasses(pack).iterator(); it.hasNext();)
{
EClass next = it.next();
String parentName = getQualifiedClassName(next);
for (Iterator<EReference> ir = next.getEAllReferences().iterator();ir.hasNext();)
{
EReference ref = ir.next();
if (ref.isContainment())
{
EClassifier contained = ref.getEType();
if (contained instanceof EClass)
{
String childName = getQualifiedClassName((EClass)contained);
Vector<String> parents = pTable.get(childName);
if (parents == null) parents = new Vector<String>();
parents.add(parentName);
pTable.put(childName, parents);
}
}
}
}
return pTable;
}
/**
* @param thePackage a package
* @return a Vector of all classes in the package or its sub-packages to any depth
*/
public static Vector<EClass> getAllClasses(EPackage thePackage)
{
Vector<EClass> classes = new Vector<EClass>();
addClasses(classes,thePackage);
return classes;
}
private static void addClasses(Vector<EClass> classes, EPackage thePackage)
{
for (Iterator<EClassifier> it = thePackage.getEClassifiers().iterator();it.hasNext();)
{
EClassifier next = it.next();
if (next instanceof EClass) classes.add((EClass)next);
}
for (Iterator<EPackage> it = thePackage.getESubpackages().iterator();it.hasNext();)
addClasses(classes,it.next());
}
/**
*
* @param qualifiedClassName the package name (if not null) and class name of some class
* @param classModel the top package of the class model
* @return a Vector of qualified names of all subclasses, including itself, in any package
* @throws MapperException
*/
public static Vector<String> allSubclassNames(String qualifiedClassName, EPackage classModel) throws MapperException
{
EClass theClass = null;
String className = null;
String packageName = null;
/* if the class name contains '.', there must be a non-null package; if not,
* the first part of the class name would be taken as a package name. */
StringTokenizer st = new StringTokenizer(qualifiedClassName,".");
if (st.countTokens() == 1) className = st.nextToken();
else if (st.countTokens() > 1)
{
packageName = st.nextToken(); // before the first '.'
className = qualifiedClassName.substring(packageName.length()+1); // after
}
if (packageName== null) theClass = (EClass)classModel.getEClassifier(className);
else if (packageName.equals(classModel.getName())) theClass = (EClass)classModel.getEClassifier(className);
else
{
EPackage thePackage = getNamedPackage(classModel,packageName);
theClass = (EClass)thePackage.getEClassifier(className);
}
if (theClass == null) throw new MapperException("Cannot find class " + qualifiedClassName);
// find the names of all subclasses, including itself
Vector<EClass> inheritors = getAllSubClasses(theClass);
Vector<String> subclassNames = new Vector<String>();
for (Iterator<EClass> it = inheritors.iterator();it.hasNext();)
subclassNames.add(getQualifiedClassName(it.next()));
return subclassNames;
}
/**
*
* @param ec a class
* @return a Vector of all classes which inherit from the class,
* including the class itself
*/
public static Vector<EClass> getAllSubClasses(EClass ec)
{
Vector<EClass> inheritors = new Vector<EClass>();
EPackage ep = getTopPackage(ec);
for (Iterator<EClass> it = getAllClasses(ep).iterator(); it.hasNext();)
{
EClass next = it.next();
// isSuperTypeOf includes the class itself
if (ec.isSuperTypeOf(next)) inheritors.add(next);
}
return inheritors;
}
/**
*
* @param ec a class
* @return a Vector of all concrete classes which inherit from the class,
* including the class itself
*/
public static Vector<EClass> getAllConcreteSubClasses(EClass ec)
{
Vector<EClass> inheritors = new Vector<EClass>();
EPackage ep = getTopPackage(ec);
for (Iterator<EClass> it = getAllClasses(ep).iterator(); it.hasNext();)
{
EClass next = it.next();
// isSuperTypeOf includes the class itself
if ((ec.isSuperTypeOf(next)) && (!next.isAbstract())) inheritors.add(next);
}
return inheritors;
}
/**
*
* @param topPackage a package which may contain nested packages
* @param bareClassName an unqualified class name (with no package name)
* @return a list of all classes in the top package or its nested packages
* which have the required bare class name
*/
public static List<EClass> getAllNamedClasses(EPackage topPackage, String bareClassName)
{
Vector<EClass> classes = new Vector<EClass>();
addNamedClasses(topPackage,bareClassName,classes);
return classes;
}
/**
* recursive descent of nested packages, looking for all classes with a given name
* @param topPackage
* @param bareClassName
* @param classes
*/
private static void addNamedClasses(EPackage topPackage,String bareClassName,Vector<EClass> classes)
{
EClassifier named = topPackage.getEClassifier(bareClassName);
if ((named != null) && (named instanceof EClass)) classes.add((EClass)named);
for (Iterator<EPackage> it = topPackage.getESubpackages().iterator();it.hasNext();)
addNamedClasses(it.next(),bareClassName,classes);
}
//---------------------------------------------------------------------------------
// cloning EObjects
//---------------------------------------------------------------------------------
/**
* @param obj an object in the Mapper model
* @return a clone of any EObject in the Mapper model, including all of its EAttributes
* and recursively its contained EObjects if deep = true. Non-containment relations are not
* yet supported.
*/
public static EObject EClone(EObject obj, boolean deep)
{
if (obj == null) return null;
// clone the EObject
EObject clone = MapperFactory.eINSTANCE.create(obj.eClass());
// clone the values of all its EAttributes
for (Iterator<EAttribute> it = obj.eClass().getEAllAttributes().iterator();it.hasNext();)
{
EAttribute att = it.next();
clone.eSet(att, obj.eGet(att));
}
// for deep copy, clone all its containment EReferences, and their target EObjects
if (deep) for (Iterator<EReference> it = obj.eClass().getEAllReferences().iterator();it.hasNext();)
{
EReference ref = it.next();
if (ref.isContainment())
{
if (ref.getUpperBound() == 1)
{
Object target = obj.eGet(ref);
if ((target != null) && (target instanceof EObject))
clone.eSet(ref, EClone((EObject)target,deep));
}
else if (ref.getUpperBound() == -1)
{
Object targets = obj.eGet(ref);
if ((targets != null) && (targets instanceof EList<?>))
{
EList<EObject> cont = new BasicEList<EObject>();
EList<?> targetList = (EList<?>)targets;
for (Iterator<?> iw = targetList.iterator();iw.hasNext();)
{
Object target = iw.next();
if (target instanceof EObject) cont.add(EClone((EObject)target,deep));
}
clone.eSet(ref,cont);
}
}
}
}
return clone;
}
//------------------------------------------------------------------------------------------
// Making and saving Ecore models
//------------------------------------------------------------------------------------------
/**
* write out an Ecore package as a resource.
*/
public static void savePackage(String filePath, EPackage topPackage) throws MapperException
{
URI instanceURI = URI.createURI(filePath);
// create the resource that is to be filled with the model instance
ResourceSet resourceSet = new ResourceSetImpl();
// register the factory
if (instanceURI != null)
{
String extension = ".ecore";
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().
put(extension, new XMIResourceFactoryImpl());
}
Resource modelResource = resourceSet.createResource(instanceURI);
// add the package to the resource
modelResource.getContents().add(topPackage);
// save the resource
try {modelResource.save(null);}
catch (IOException ex)
{throw new MapperException("Failed to save EMF model resource: " + ex.getMessage());}
}
/**
* create a model object
* @param qualifiedClassName
* @param classModel
* @return
*/
public static EObject createModelObject(String qualifiedClassName, EPackage classModel) throws MapperException
{
String packageName = ModelUtil.getPackageName(qualifiedClassName);
EPackage thePackage = ModelUtil.getNamedPackage(classModel, packageName);
EClass theClass = ModelUtil.getNamedClass(classModel, qualifiedClassName);
if (theClass != null)
{
if (!theClass.isAbstract()) return thePackage.getEFactoryInstance().create(theClass);
else throw new MapperException("Abstract class " + qualifiedClassName);
}
else throw new MapperException("No EClass " + qualifiedClassName + " in package " + classModel.getNsPrefix());
}
//------------------------------------------------------------------------------------------
// Making and saving mapping sets
//------------------------------------------------------------------------------------------
/**
* save a mapping set
* @param resource a fully constructed mapping set
* @throws MapperException
*/
public static MappedStructure saveNewMappingSet(String location) throws MapperException
{
try{
URI mappingSetURI = URI.createURI(location);
Resource resource = makeNewMappingSet(mappingSetURI);
// Save the contents of the resource to the file system.
Map<Object, Object> options = new HashMap<Object, Object>();
options.put(XMLResource.OPTION_ENCODING, "UTF-8");
resource.save(options);
return (MappedStructure) resource.getContents().get(0);
}
catch (Exception ex) {throw new MapperException(ex.getMessage());}
}
/**
* @param fileURI the URI where a mapping set is to be located
* @return the mapping set resource (with no structure yet)
*/
public static Resource makeNewMappingSet(URI fileURI)
{
// Create a resource set and a resource for this file.
ResourceSet resourceSet = new ResourceSetImpl();
Resource resource = resourceSet.createResource(fileURI);
// Add the initial model object to the contents.
EObject rootObject = createInitialModel();
resource.getContents().add(rootObject);
return resource;
}
/**
* save a mapping set
* @param resource a fully constructed mapping set
* @throws MapperException
*/
public static void saveMappingSet(Resource resource) throws MapperException
{
try{
// Save the contents of the resource to the file system.
Map<Object, Object> options = new HashMap<Object, Object>();
options.put(XMLResource.OPTION_ENCODING, "UTF-8");
resource.save(options);
}
catch (Exception ex) {throw new MapperException(ex.getMessage());}
}
/**
* @return an initial empty mapping set
*/
public static MappedStructure createInitialModel() {
MappedStructure rootObject = MapperFactory.eINSTANCE.createMappedStructure();
GlobalMappingParameters globalObject = MapperFactory.eINSTANCE.createGlobalMappingParameters();
rootObject.setMappingParameters(globalObject);
return rootObject;
}
/**
* @param reps a Vector of objectReps
* @return the corresponding Vector of objectTokens
*/
public static Vector<objectToken> toTokens(Vector<objectRep> reps)
{
Vector<objectToken> toks = new Vector<objectToken>();
for (Iterator<objectRep> ir = reps.iterator(); ir.hasNext();) toks.add(ir.next());
return toks;
}
/**
* @param toks a Vector of objectTokens
* @return the Vector cast to objectReps
* @throws MapperException if any cannot be cast
*/
public static Vector<objectRep> toReps(Vector<objectToken> toks)
throws MapperException
{
Vector<objectRep> reps = new Vector<objectRep>();
try
{
for (Iterator<objectToken> it = toks.iterator(); it.hasNext();) reps.add((objectRep)it.next());
}
catch (Exception ex) {throw new MapperException("objectToken is not an objectRep");}
return reps;
}
/**
* @param tok an objectToken
* @return the token cast to an objectRep
* @throws MapperException if it cannot be cast
*/
public static objectRep toRep(objectToken tok) throws MapperException
{
objectRep rep = null;
try {rep = (objectRep)tok;}
catch (Exception ex) {throw new MapperException("objectToken is not an objectRep");}
return rep;
}
//----------------------------------------------------------------------------------------------------------
// tracing
//----------------------------------------------------------------------------------------------------------
public static void traceRefNames(EPackage thePackage, String className)
{
for (Iterator<EClassifier> it = thePackage.getEClassifiers().iterator();it.hasNext();)
{
EClassifier next = it.next();
if ((next.getName().equals(className)) && (next instanceof EClass))
traceRefNames((EClass)next);
}
}
public static void traceRefNames(EClass theClass)
{
String refNames = "";
for (Iterator<EReference> it = theClass.getEReferences().iterator(); it.hasNext();)
refNames = refNames + it.next().getName() + " ";
trace("Class " + theClass.getName() + " has associations " + refNames);
}
public static void traceAttNames(EClass theClass)
{
String refNames = "";
for (Iterator<EAttribute> it = theClass.getEAttributes().iterator(); it.hasNext();)
refNames = refNames + it.next().getName() + " ";
trace("Class " + theClass.getName() + " has attributes " + refNames);
}
/**
* RIM class structure
*/
public static String[] ActSubclasses =
{"Act","Procedure","Observation","PatientEncounter","SubstanceAdministration","Organizer","Supply"};
public static String[] RoleSubclasses = {"Role"};
public static String addSuffixToFileName(String fullLocation,String suffix) throws MapperException
{
String result = null;
String[] exts = {".mapper",".ecore"};
for (int i = 0; i < exts.length; i++)
{
if (fullLocation.endsWith(exts[i]))
{
int len = fullLocation.length() - exts[i].length();
result = fullLocation.substring(0,len) + "_" + suffix + exts[i];
}
}
if (result == null) throw new MapperException("Invalid file extension in location '" + fullLocation + "'");
return result;
}
private static void trace(String s) {System.out.println(s);}
}