package com.openMap1.mapper.reader;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import java.util.Hashtable;
import java.util.Enumeration;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EFactory;
import org.eclipse.emf.ecore.EcoreFactory;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
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 com.openMap1.mapper.converters.LRAConverter;
import com.openMap1.mapper.core.ClassSet;
import com.openMap1.mapper.core.MapperException;
import com.openMap1.mapper.core.PropertyConversionException;
import com.openMap1.mapper.core.RunIssue;
import com.openMap1.mapper.core.notRepresentedException;
import com.openMap1.mapper.core.Xpth;
import com.openMap1.mapper.core.NamespaceSet;
import com.openMap1.mapper.util.ModelUtil;
import com.openMap1.mapper.util.Timer;
/**
* The interface implemented by this class has one method, to make a resource
* containing an instance of an EMF model, using an
* XML document which represents an instance of the model
* and a set of mappings which define how the XML instance
* represents the instance of the model
*
* @author robert worden
*
*/
public abstract class EMFInstanceFactoryImpl implements EMFInstanceFactory{
// if true, trace the steps of creating the EMF instance
private boolean tracing = false;
// if true, write out the model objects stored before traversing non-containment relations
private boolean writeModelObjects = false;
// if true, write out timing statistics for making the instance
private boolean timing = false;
protected Timer timer = null;
private XOReader xor;
/**
* @return The Ecore model for the Factory-generated instance.
* Compared to the input Ecore model, this model may have an extra class 'Container' (or 'Container_N')
* which has a containment association to every class not otherwise contained.
*/
public EPackage modelForInstance() {return classModel;}
protected EPackage classModel;
protected EFactory factory;
/**
* @return true if the Factory-generated instance has an extra class 'Container' (or 'Container_N')
* which has a containment association to every class not otherwise contained.
*/
public boolean hasAddedContainerClass() {return hasAddedContainerClass;}
private boolean hasAddedContainerClass = false;
/* for each qualified class name, store a Hashtable from objectToken key to represented EObject */
private Hashtable<String,Hashtable<Object,EObject>> savedModelObjects
= new Hashtable<String,Hashtable<Object,EObject>>();
/* record cases where traversal of a non-containment link reaches an object
* which has not been found by traversal of containment links.
* Outer key = class name; inner key = role traversed; Integer = number of occurrences. */
private Hashtable<String,Hashtable<String,Integer>> failedLookups;
/** if an extra container class gets added to the class model, this is its name */
private String containerClassName = "";
protected String NSPrefix = "generic";
/**
* Set the namespace prefix for the model
* @param NSPrefix
*/
public void setNsPrefix(String NSPrefix) {this.NSPrefix = NSPrefix;}
protected String NSUri = "http://www.com.openMap1.mapper/test";
/**
* Set the namespace URI for the model
* @param NSUri
*/
public void setNsUri(String NSUri) {this.NSUri = NSUri;}
public static URI DO_NOT_SAVE_URI() {return URI.createURI("noFile.ecore");}
//------------------------------------------------------------------------------------
// code dependent on the specific EMF model
//-----------------------------------------------------------------------------------
/** use the generated factory to create a model object with the right class name */
abstract public EObject createModelObject(String qualifiedClassName) throws MapperException;
//------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------
/** use the generated factory to convert a property from a String to the right type */
public static Object convertPropertyType(EAttribute att, String val) throws MapperException
{
String attType = att.getEAttributeType().getName();
if (attType.equals("EString")) return val;
else if (attType.equals("EInt"))
{
Integer i = new Integer(0);
try{i = new Integer(val);}
catch (Exception ex)
{System.out.println("Cannot convert value '" + val + "' to Integer; stored value 0");}
return i;
}
else if (attType.equals("EBoolean"))
{
Boolean b = new Boolean(false);
try{b = new Boolean(val);}
catch (Exception ex)
{System.out.println("Cannot convert value '" + val + "' to Boolean; stored value 'false'");}
return b;
}
else if (attType.equals("EFloat"))
{
Float f = new Float(0.0);
try{f = new Float(val);}
catch (Exception ex)
{System.out.println("Cannot convert value '" + val + "' to Float; stored value '0.0'");}
return f;
}
else throw new MapperException("Attribute '" + att.getName() + "' type not recognised: " + attType);
}
//----------------------------------------------------------------------------------------
// API called from the Menu command
//----------------------------------------------------------------------------------------
public void giveTimer(Timer timer)
{
this.timer = timer;
}
/**
* @param xor the reader which uses mappings to extract EMF model instance information
* @param modelInstanceURI the URI at which the new model instance resource is to be stored;
* cannot be null as the extension (e.g .ecore) is used to define what kind of model it is.
* if you do not wish to save the resource, use a URI like 'noFile.ecore'
* @param topobjectToken the objectToken of the instance which is to be the root of the Ecore tree
*/
public Resource createModelInstance(XOReader xor, URI modelInstanceURI, objectToken topobjectToken)
throws MapperException
{
// start a timer and give a copy to the XOReader
if (timer == null) timer = new Timer("");
timer.start(Timer.MAKE_EMF_INSTANCE);
xor.giveTimer(timer, false);
failedLookups = new Hashtable<String,Hashtable<String,Integer>>();
// create the resource that is to be filled with the model instance
ResourceSet resourceSet = new ResourceSetImpl();
// register the factory
if (modelInstanceURI != null)
{
String extension = modelInstanceURI.fileExtension();
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().
put(extension, new XMIResourceFactoryImpl());
}
else throw new MapperException("Null URI not allowed; to not save the Resource, use e.g. NoFile.ecore");
Resource modelResource = resourceSet.createResource(modelInstanceURI);
if (modelResource == null) System.out.println("Null model resource");
// restart the list of all runtime issues
allRunIssues = new Hashtable<String,RunIssue>();
// find the Ecore model that the new resource is to hold an instance of
this.xor = xor;
if (xor.classModel() == null)
{throw new MapperException("Cannot find class model");}
classModel = xor.classModel();
// find or create the EClass which is the container of all the others
EClass topClass = ModelUtil.getNamedClass(classModel, topobjectToken.className());
String topClassName = ModelUtil.getQualifiedClassName(topClass);
EObject rootObject = createModelObject(topClassName);
trace("Created top object of class '" + topClassName + "'");
// recursively fill out the attributes and containment relations of the root object
// System.out.println("Filling the model");
timer.start(Timer.FILL_OBJECT);
fillObject(rootObject,topobjectToken);
timer.stop(Timer.FILL_OBJECT);
// diagnostic look at the modelObjects stored
if (writeModelObjects) writeModelObjects(rootObject);
// recursively do all non-containment links for this object and the objects it contains
// System.out.println("Non containments");
timer.start(Timer.EMF_NON_CONTAINMENT);
doNonContainmentLinks(rootObject,topobjectToken);
timer.stop(Timer.EMF_NON_CONTAINMENT);
// add the root object to the resource
modelResource.getContents().add(rootObject);
// one-off test of Ecore models
// if (writeModelObjects) eCoreTest(rootObject);
// save the resource, unless 'noFile' has been used
if ((modelInstanceURI != null) && (!modelInstanceURI.toString().startsWith("noFile")))
try {modelResource.save(null);}
catch (IOException ex)
{throw new MapperException("Failed to save EMF model resource: " + ex.getMessage());}
/* note where traversing non-containment links led to objects not previously
* found by traversing containment links */
writeFailedLookups();
/* overwrite of dynamic Ecore models, putting a native Ecore model to the same location.
* This overcomes some strange glitch in Ecore models */
LRAConverter conv = new LRAConverter();
if (topClass.getName().equals("EPackage"))
{
/* this only does LRA special post-processing,
* converting some EReferences to EAttributes,
* if the top package name is 'lra' */
conv.saveAsEcore(rootObject,modelInstanceURI);
}
// report all timings
timer.stop(Timer.MAKE_EMF_INSTANCE);
if (timing) timer.report();
return modelResource;
}
//-------------------------------------------------------------------------------------------------------
// API for translation tests, when there may be an extra container object added at the root
//-------------------------------------------------------------------------------------------------------
/**
*
* @param xor the reader which uses mappings to extract EMF model instance information
* @param modelInstanceURI the URI at which the new model instance resource is to be stored;
* or null if it is not to be stored anywhere
* @param forceContainer: if forceContainer is true, the Factory will act as if
* there is a new root class,
* (called 'Container' or 'Container_N' to avoid class name clashes) in the EMF model.
* This class has a containment relation 'contains' to any class which is not contained
* in some other class. This is done so that all object instances can be shown in the EMF instance
* with a single root.
* <p>
* When forceContainer is false, the Factory will only add a new root class 'Container'
* if it is necessary - i.e. if there more than one 'root' class, and so there
* is no one class in the Ecore model which has
* a containment relation (direct or indirect) to all the other classes.
* <p>
* In this case, when the Factory does not introduce a new Container class
* it may fail and throw a MapperException if there is not
* exactly one instance of the top class of the ECore model represented in the XML.
* <p>
* Whether forceContainer is true or false, the Factory does not alter the stored EMF model;
* it uses an in-memory modified copy, and makes that publicly accessible.
*
*/
public Resource createModelInstanceInTranslationTest(XOReader xor, URI modelInstanceURI, boolean forceContainer)
throws MapperException
{
// start a timer and give a copy to the XOReader
if (timer == null) timer = new Timer("");
timer.start(Timer.MAKE_EMF_INSTANCE);
timer.start(Timer.EMF_PRELIMINARIES);
xor.giveTimer(timer, false);
failedLookups = new Hashtable<String,Hashtable<String,Integer>>();
// create the resource that is to be filled with the model instance
ResourceSet resourceSet = new ResourceSetImpl();
// register the factory
if (modelInstanceURI != null)
{
String extension = modelInstanceURI.fileExtension();
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().
put(extension, new XMIResourceFactoryImpl());
}
Resource modelResource = resourceSet.createResource(modelInstanceURI);
if (modelResource == null) System.out.println("Null model resource");
// restart the list of all runtime issues
allRunIssues = new Hashtable<String,RunIssue>();
// find the Ecore model that the new resource is to hold an instance of
this.xor = xor;
if (xor.classModel() == null)
{throw new MapperException("Cannot find class model");}
classModel = xor.classModel(); // it would be hard work to make a copy without stripping the classes out
// find all object mappings for objects not nested inside some other mapped object. Key = classSet name
Vector<ClassSet> previousOuters = xor.outerObjectClassSets();
// find or add the new single outer class, and make an instance of it
EClass topClass = addContainerClassIfNecessary(classModel,previousOuters,forceContainer);
EObject rootObject = createModelObject(ModelUtil.getQualifiedClassName(topClass));
/* get the objectTokens for all the classes that were outer
* before any container class was added. Key = ClassSet name. */
Hashtable<String,Vector<objectToken>> topobjectTokens = getTopObjects(previousOuters);
/* note the top objects in a lookup table modelObjects,
* later used for non-containment associations */
recordModelObjects(topobjectTokens);
timer.stop(Timer.EMF_PRELIMINARIES);
/* fill in the properties of all top instances, and recursively traverse their
* containment associations; and if a container class has been added to the class model,
* make each top object contained inside it */
for (Enumeration<String> en = topobjectTokens.keys();en.hasMoreElements();)
{
String classSetName = en.nextElement();
/* to hold all the objects of this class contained in the Container object
* FIXME - case when two classSets have the same class name; ELists should be merged */
EList<EObject> contList = new BasicEList<EObject>();
Vector<objectToken> oReps = topobjectTokens.get(classSetName);
if (oReps.size() > 0)
{
String qualifiedClassName = oReps.get(0).className();
for (Iterator<objectToken> ir = oReps.iterator(); ir.hasNext();)
{
objectToken oRep = ir.next();
EObject topObject = savedModelObjects.get(qualifiedClassName).get(oRep.objectKey());
// add this object to a containment list for objects of this class in the top 'Container' object
contList.add(topObject);
// recursively fill out the attributes and containment relations of this object
timer.start(Timer.FILL_OBJECT);
fillObject(topObject,oRep);
timer.stop(Timer.FILL_OBJECT);
// if there is no extra containment class, there should be one real object to be root
if (!hasAddedContainerClass) rootObject = topObject;
}
// store the top objects of this class in the correct feature of the container object
if (hasAddedContainerClass)
{
// find the correct containment association to get to an object of this class
boolean containmentFound = false;
String className = ModelUtil.getBareClassName(qualifiedClassName);
for (Iterator<EReference> iRef = topClass.getEAllReferences().iterator();iRef.hasNext();)
{
EReference er = iRef.next();
if (er.getEType().getName().equals(className))
{
rootObject.eSet(er, contList);
containmentFound = true;
}
}
if (!containmentFound)
throw new MapperException("Found no containment relation for objects of class '"
+ qualifiedClassName + "'");
}
}
}
/* fill in all the non-containment associations, after all model objects have been made. */
timer.start(Timer.EMF_NON_CONTAINMENT);
for (Enumeration<String> en = topobjectTokens.keys();en.hasMoreElements();)
{
String classSetName = en.nextElement();
Vector<objectToken> oReps = topobjectTokens.get(classSetName);
if (oReps.size() > 0)
{
String qualifiedClassName = oReps.get(0).className();
for (Iterator<objectToken> ir = oReps.iterator(); ir.hasNext();)
{
objectToken oRep = ir.next();
EObject topObject = savedModelObjects.get(qualifiedClassName).get(oRep.objectKey());
// recursively do all non-containment links for this object and the objects it contains
doNonContainmentLinks(topObject,oRep);
}
}
}
timer.stop(Timer.EMF_NON_CONTAINMENT);
// add the root object to the resource
modelResource.getContents().add(rootObject);
// save the resource
if (modelInstanceURI != null)
try {modelResource.save(null);}
catch (IOException ex)
{throw new MapperException("Failed to save EMF model resource: " + ex.getMessage());}
// clean up afterwards
removeContainerClassIfNecessary(classModel);
/* note where traversing non-containment links led to objects not previously
* found by traversing containment links */
writeFailedLookups();
return modelResource;
}
/**
* diagnostic write of the numbers of stored model objects, by class
*/
private void writeModelObjects(EObject rootObject)
{
System.out.println("\n\nModel objects found by class");
for (Enumeration<String> en = savedModelObjects.keys();en.hasMoreElements();)
{
String className = en.nextElement();
Hashtable<Object,EObject> forClass = savedModelObjects.get(className);
// find which EObjects have no container; or if they do, what their parent class is
int noContainer = 0;
String parentClassName = "nonexistent";
for (Enumeration<EObject> eo = forClass.elements(); eo.hasMoreElements();)
{
EObject next = eo.nextElement();
EObject parent = next.eContainer();
if (parent == null) noContainer++;
else parentClassName = parent.eClass().getName();
}
System.out.println(className + ": " + forClass.size() + "; " + noContainer
+ " without container; parent class " + parentClassName);
}
System.out.println("\n\nClass Tree");
writeTree(rootObject, 0);
}
/*
* write out the tree structure of an EMF model
*/
private void writeTree(EObject obj, int level)
{
String className = obj.eClass().getName();
System.out.println(level + ": " + className);
for (Iterator<EObject> it = obj.eContents().iterator();it.hasNext();)
writeTree(it.next(),level+1);
}
/**
* @param classModel an Ecore model, headed by an EPackage
* @return the EClass which is recommended as top class for Ecore
* instances of the model, in an annotation on the top EPackage object;
* or null if there is no such annotation
*/
public static EClass getRecommendedTopClass(EPackage classModel)
{
EClass recClass = null;
String className = ModelUtil.getEAnnotationDetail(classModel, "topClass");
if (className != null) recClass = ModelUtil.getNamedClass(classModel, className);
return recClass;
}
//-------------------------------------------------------------------------------------
// Main recursive methods to fill in the Ecore instance
//-------------------------------------------------------------------------------------
/**
* fill in all the properties of an EObject that you can get from the
* mappings, and fill in its containment associations -
* recursively filling in the contained objects.
* @param obj
* @param oRep
*/
private void fillObject(EObject obj, objectToken oRep) throws MapperException
{
String className = obj.eClass().getName();
String task = "Properties of " + className + "(" + oRep.cSet().stringForm() + ")";
trace(task);
// find values for all mapped properties of the class
for (Iterator<EAttribute> it = obj.eClass().getEAllAttributes().iterator();it.hasNext();)
{
EAttribute att = it.next();
String attName = att.getName();
// if the XML represents the value of this property, set it on the object
// temporarily forgive failure to convert properties
// the next call is very expensive, so just catch the exception silently in stead
boolean representsProp = true; // xor.representsProperty(oRep, attName);
if (representsProp) try
{
String propVal = xor.getPropertyValue(oRep, attName);
// assume that "" means nothing more than 'default' which is there already
if ((propVal != null) && (!propVal.equals("")))
{
obj.eSet(att, convertPropertyType(att,propVal));
trace(attName + ": " + propVal);
}
}
catch (PropertyConversionException ex)
{
int nature = RunIssue.RUN_XSLT_PROPERTY_CONVERSION;
noteRunIssue(nature, ex.getMessage());
}
catch (notRepresentedException ex)
{
/*
int nature = RunIssue.RUN_PROPERTY_REPRESENTED;
noteRunIssue(nature, ex.getMessage());
*/
}
}
task = "Containments of " + obj.eClass().getName() + "(" + oRep.cSet().stringForm() + ")";
trace(task);
// get all objects reached by mapped containment associations, and fill them in
for (Iterator<EReference> it = obj.eClass().getEAllReferences().iterator();it.hasNext();)
{
EReference ref = it.next();
EClass childSuperClass = (EClass)ref.getEType();
String roleName = ref.getName();
if (roleName == null) roleName = "null role name";
trace("Role name " + roleName);
if (ref.isContainment())
{
EList<EObject> cont = new BasicEList<EObject>();
// try all subclasses of the class at the other end of the association
List<EClass> scList = xor.ms().getAllSubClasses(childSuperClass);
for (Iterator<EClass> iw = scList.iterator(); iw.hasNext();)
{
EClass childClass = iw.next();
if (!childClass.isAbstract())
{
String childClassName = ModelUtil.getQualifiedClassName(childClass);
trace("Subclass " + childClassName);
/* the call to representsAssociationRole is so expensive that I now just
* catch the nonrepresented Exception silently. */
boolean representsAssoc = true; // xor.representsAssociationRole(oRep,roleName, childClassName);
if (representsAssoc) try
{
task = "following " + oRep.cSet().stringForm() + "." + roleName + "." + childClassName;
trace(task);
Vector<objectToken> childObjects =
xor.getAssociatedObjectTokens(oRep, childClassName, roleName);
if (childObjects.size() > 0) trace("found " + childObjects.size());
for (Iterator<objectToken> ix = childObjects.iterator();ix.hasNext();)
{
objectToken cRep = ix.next();
String childSubClassName = cRep.className();
trace("class of objectToken " + childSubClassName);
/* Avoid the same object being found twice through different superclasses in the
* association being followed */
if (childSubClassName.equals(childClassName))
{
EObject child = createModelObject(childSubClassName);
fillObject(child,cRep);
cont.add(child);
trace("adding class " + childSubClassName);
// store lookup for non-containment associations
Hashtable<Object,EObject> childObjTable = savedModelObjects.get(childSubClassName);
if (childObjTable == null) childObjTable = new Hashtable<Object,EObject>();
childObjTable.put(cRep.objectKey(),child);
savedModelObjects.put(childSubClassName, childObjTable);
}
}
}
catch (notRepresentedException ex) // now caught silently
{
/*
int nature = RunIssue.RUN_ASSOCIATION_REPRESENTED;
String problem = ("Containment association not represented: " +
thisCName + "." + roleName + "." + childClassName + " " +
ex.getMessage());
noteRunIssue(nature, problem);
*/
}
}
}
// set the reference feature just once for all subclasses of the association end class
if (ref.getUpperBound() == -1)
{
obj.eSet(ref, cont);
trace("setting single-valued " + ref.getName());
}
else if (ref.getUpperBound() == 1)
{
if (cont.size() > 0) obj.eSet(ref, cont.get(0));
trace("setting multi-valued " + ref.getName() + " with " + cont.size() + " values");
}
else trace("Failed to store child object");
}
}
}
/**
* find all non-containment links of an object which are mapped,
* and record them in the EMF model.
* Do the same for all contained objects.
* @param obj
* @param oRep
* @throws MapperException
*/
private void doNonContainmentLinks(EObject obj, objectToken oRep) throws MapperException
{
trace("Non-containments of " + obj.eClass().getName()
+ "(" + oRep.className() + ")");
/* look at all links of this object, containment or not */
for (Iterator<EReference> it = obj.eClass().getEAllReferences().iterator();it.hasNext();)
{
EReference ref = it.next();
boolean oppositeOfContainment = ((ref.getEOpposite() != null)
&& (ref.getEOpposite().isContainment()));
// Need we look at the opposite of the containment link which got to this object? I think not.
if ((!oppositeOfContainment) && (ref.getEType() != null))
{
EClass reffedSuperClass = (EClass)ref.getEType();
String thisCName = ModelUtil.getQualifiedClassName(obj.eClass());
String roleName = ref.getName();
// build up a list of referenced objects over all subclasses of the other end object
EList<EObject> reffedList = new BasicEList<EObject>();
// try all subclasses of the class at the other end of the association
List<EClass> scList = xor.ms().getAllSubClasses(reffedSuperClass);
for (Iterator<EClass> iw = scList.iterator(); iw.hasNext();)
{
String reffedClassName = ModelUtil.getQualifiedClassName(iw.next());
trace("following " + oRep.className() + "." + roleName + "." + reffedClassName);
// Check if this class has been retrieved by a containment association
Hashtable<Object,EObject> reffedTable = savedModelObjects.get(reffedClassName);
if (reffedTable != null)
{
// then next check is very expensive, so in stead, catch the exception silently
boolean representsAssoc = true; // xor.representsAssociationRole(oRep, roleName, reffedClassName);
// look at both containment and non-containment associations, doing different things for each
if (representsAssoc) try
{
Vector<objectToken> linkedObjects =
xor.getAssociatedObjectTokens(oRep, reffedClassName, roleName);
trace("Found: " + linkedObjects.size());
for (Iterator<objectToken> ix = linkedObjects.iterator();ix.hasNext();)
{
objectToken cRep = ix.next();
EObject reffed = reffedTable.get(cRep.objectKey());
if (reffed == null)
{
trace("Failed to find referenced object in " + reffedTable.size()
+ " objects of class " + reffedClassName);
recordFailedLookup(reffedClassName, (thisCName + "." + roleName));
}
else if (reffed != null)
{
reffedList.add(reffed);
/* if this is a containment association, do the non-containment
* links of the contained object */
if (ref.isContainment()) doNonContainmentLinks(reffed,cRep);
}
}
}
catch (notRepresentedException ex) // now caught silently, as it can happen
{
/*
int nature = RunIssue.RUN_PROPERTY_CONVERSION;
String problem = ("non-containment association not represented: " +
thisCName + "." + roleName + "." + reffedClassName + " " +
ex.getMessage());
noteRunIssue(nature, problem);
*/
}
}
}
/* if this is not a containment, and its opposite is not a containment
* (which will have been dealt with already) store the links on this object */
boolean oppositeIsContainment = ((ref.getEOpposite() != null) &&
(ref.getEOpposite().isContainment()));
if (!oppositeIsContainment && (!ref.isContainment()) && (reffedList.size() > 0))
{
trace("Noting " + reffedList.size() + " references " + roleName);
if (ref.getUpperBound() == -1) obj.eSet(ref, reffedList);
else if (ref.getUpperBound() == 1) obj.eSet(ref, reffedList.get(0));
// checks
else System.out.println("Failed to set non-containment " + ref.getName());
Object newValue = obj.eGet(ref);
if (newValue == null) System.out.println("Null value non-containment " + ref.getName());
else if (ref.getUpperBound() == -1)
{
EList<?> oList = (EList<?>)newValue;
if (oList.size() != reffedList.size())
System.out.println("Cardinality mismatch of non-containment " + ref.getName()
+ " " + oList.size() + " " + reffedList.size());
}
}
}
}
}
/**
* record occasions when an object found by traversing a
* non-containment association has not been previously found
* by traversing a containment relation
* @param className class of the object not found in lookup
* @param roleName role by which it was found
*/
private void recordFailedLookup(String className, String roleName)
{
Hashtable<String,Integer> failuresForClass = failedLookups.get(className);
if (failuresForClass == null) failuresForClass = new Hashtable<String,Integer>();
Integer failsForRole = failuresForClass.get(roleName);
if (failsForRole == null) failsForRole = new Integer(0);
failsForRole = new Integer(failsForRole.intValue() + 1);
failuresForClass.put(roleName, failsForRole);
failedLookups.put(className, failuresForClass);
}
private void writeFailedLookups()
{
for (Enumeration<String> en = failedLookups.keys();en.hasMoreElements();)
{
String className = en.nextElement();
Hashtable<String,Integer> failuresForClass = failedLookups.get(className);
for (Enumeration<String> ep = failuresForClass.keys();ep.hasMoreElements(); )
{
String roleName = ep.nextElement();
Integer count = failuresForClass.get(roleName);
System.out.println(count.intValue() + " failed lookups of class "
+ className + " found by association " + roleName);
}
}
}
/**
* @param previousOuters all the classes that were outer (which were not at the inner end of any
* containment relation) before any outer 'Container' class was added
*
* @return for every class that was outer in the un-modified class
* model, the key is the ClassSet name and the value is a Vector of objectTokens found.
*
* if hasAddedContainerClass = false, it is expected that there will be only one class
* and only one objectToken in that class.
*/
private Hashtable<String,Vector<objectToken>> getTopObjects(Vector<ClassSet> previousOuters)
throws MapperException
{
Hashtable<String,Vector<objectToken>> topObjects = new Hashtable<String,Vector<objectToken>>();
for (Iterator<ClassSet> io = previousOuters.iterator();io.hasNext();)
{
ClassSet cs = io.next();
String qualifiedClassName = cs.className();
Vector<objectToken> topClassInstances = new Vector<objectToken>();
// get all instances of the class and filter them by subset
Vector<objectToken> allTopClassInstances = xor.getAllLocalObjectTokens(qualifiedClassName);
for (Iterator<objectToken> it = allTopClassInstances.iterator();it.hasNext();)
{
objectToken oRep = it.next();
if (oRep.subset().equals(cs.subset())) topClassInstances.add(oRep);
}
topObjects.put(cs.stringForm(), topClassInstances);
}
return topObjects;
}
/**
* Make a copy of the Ecore class model so you can change it
* (by adding new classes and associations)
* without changing the original.
*
* To make it without ripping the classes out of the original, I would
* have to make copies of all the classes. It does not seem necessary
* to go to this length, as the changed class model is never persisted.
*
* @param originalModelthe original class model
* @return the copy that can be modified
*/
/* private EPackage copyClassModel(EPackage originalModel)
{
EPackage newPackage = EcoreFactory.eINSTANCE.createEPackage();
newPackage.setName(originalModel.getName());
newPackage.setNsPrefix(originalModel.getNsPrefix());
newPackage.setNsURI(originalModel.getNsURI());
EList<EClassifier> cont = new BasicEList<EClassifier>();
for (Iterator<EClassifier> it = originalModel.getEClassifiers().iterator(); it.hasNext();)
cont.add(it.next());
newPackage.eSet(EcorePackage.eINSTANCE.getEPackage_EClassifiers(), cont);
return newPackage;
} */
/**
* if necessary (because there is more than one outer class)
* or if forced to do so, add a new class 'Container' with
* a containment relation to all classes that were previously outer.
* @param classModel the class model
* @param forceContainer if true, you must add a new outer class
*/
private EClass addContainerClassIfNecessary(EPackage classModel, Vector<ClassSet> previousOuters, boolean forceContainer)
throws MapperException
{
if ((previousOuters.size() != 1)|forceContainer)
{
// create the new container class
EClass newClass = EcoreFactory.eINSTANCE.createEClass();
containerClassName = newClassName("Container",classModel);
newClass.setName(containerClassName);
/* give it a containment relation to all classes that were previously outer classes;
* avoid adding any class twice if it has more than one outer mapping */
Hashtable<String,String> classNames = new Hashtable<String,String>();
for (Iterator<ClassSet> io = previousOuters.iterator();io.hasNext();)
{
ClassSet cs = io.next();
if (classNames.get(cs.className()) == null)
{
classNames.put(cs.className(),"1");
EClass oc = ModelUtil.getNamedClass(classModel, cs.className());
if (oc == null) throw new MapperException("Cannot find mapped class '" + cs.className() + "'");
EReference eRef = EcoreFactory.eINSTANCE.createEReference();
eRef.setEType(oc);
eRef.setName("contains_" + oc.getName());
eRef.setUpperBound(-1); // means unbounded
eRef.setContainment(true);
newClass.getEStructuralFeatures().add(eRef);
}
}
// add the new class to the top package of the new class model, and record it has been done
classModel.getEClassifiers().add(newClass);
hasAddedContainerClass = true;
return newClass;
}
// when there is a unique top class and you are not forcing a container class...
else if ((previousOuters.size() == 1) && (!forceContainer))
{
for (Iterator<ClassSet> io = previousOuters.iterator();io.hasNext();)
{
ClassSet cs = io.next();
EClass oc = ModelUtil.getNamedClass(classModel, cs.className());
if (oc == null) throw new MapperException("Cannot find unique top mapped class '" + cs.className() + "'");
return oc;
}
}
return null; // for the compiler
}
/**
* set the class model attached to the XOReader back to its original state
* @param classModel
*/
private void removeContainerClassIfNecessary(EPackage classModel)
{
if (!(containerClassName).equals(""))
{
EClassifier ec = classModel.getEClassifier(containerClassName);
classModel.getEClassifiers().remove(ec);
}
}
/**
* If necessary ,put some suffix onto a class name to stop
* it clashing with any existing class name in the model
* @param className the proposed name
* @param classModel the class model
* @return the non-clashing name
*/
private String newClassName(String className,EPackage classModel)
{
String cName = className;
boolean clashes = true;
int index = 0;
while (clashes)
{
index++;
clashes = false;
for (Iterator<EClass> it = ModelUtil.getAllClasses(classModel).iterator();it.hasNext();)
if (it.next().getName().equals(cName)) clashes = true;
if (clashes) cName = className + "_" + index;
}
return cName;
}
/**
* Used only in translation test.
* build up the Hashtable (key - objectToken key; value = empty EObject)
* for all objects in the classes that were outer in the Ecore
* model before an outer Container class was added
* @param topobjectTokens
*/
private void recordModelObjects(Hashtable<String,Vector<objectToken>> topobjectTokens) throws MapperException
{
// iterate over all the classes that were previously outer
for (Enumeration<String> en = topobjectTokens.keys();en.hasMoreElements();)
{
String classSetName = en.nextElement();
Hashtable<Object,EObject> topObjects = new Hashtable<Object,EObject>();
Vector<objectToken> reps = topobjectTokens.get(classSetName);
String qualifiedClassName = null;
for (Iterator<objectToken> it = reps.iterator();it.hasNext();)
{
objectToken oRep = it.next();
qualifiedClassName = oRep.className();
EObject topObject = createModelObject(qualifiedClassName);
topObjects.put(oRep.objectKey(), topObject);
}
// if more than one subset of a top class is represented, collect all objectTokens
if (qualifiedClassName != null)
{
Hashtable<Object,EObject> previousTopObjects = savedModelObjects.get(qualifiedClassName);
if (previousTopObjects != null)
for (Enumeration<Object> eo = previousTopObjects.keys(); eo.hasMoreElements();)
{
Object key = eo.nextElement();
topObjects.put(key, previousTopObjects.get(key));
}
savedModelObjects.put(qualifiedClassName,topObjects);
}
}
}
//----------------------------------------------------------------------------------------
// recording runtime issues
//----------------------------------------------------------------------------------------
/**
* @return a summary of any exceptions thrown or reader problems encountered
* when making the instance, with duplicates removed.
* The RunIssues do not contain the XPaths in the source where the exception occurred.
*/
public List<RunIssue> runIssues()
{
Vector<RunIssue> issues = new Vector<RunIssue>();
for (Enumeration<RunIssue> en = allRunIssues.elements();en.hasMoreElements();)
issues.add(en.nextElement());
return issues;
}
protected Hashtable<String,RunIssue> allRunIssues;
/**
* note a runtime issue, with no XPath information,
* counting occurrences of duplicates
* @param nature constant defining the broad nature of the issue - see class RunIssue
* @param problem text description of the problem
*/
private void noteRunIssue(int nature, String problem)
{
RunIssue ri = new RunIssue(nature,"","",problem,new Xpth(new NamespaceSet()),0);
RunIssue existingIssue = allRunIssues.get(ri.key());
if (existingIssue != null) existingIssue.addOccurrence();
else allRunIssues.put(ri.key(),ri);
trace("Run issue " + nature + ": " + problem);
}
/**
* test of Ecore models; test that the subclass relationship is not circular
* */
@SuppressWarnings("unused")
private void eCoreTest(EObject rootObject)
{
System.out.println("\nTest the subclass relation is not circular");
if (rootObject instanceof EPackage)
{
for (Iterator<EClass>it = ModelUtil.getAllClasses((EPackage)rootObject).iterator();it.hasNext();)
{
EClass ec = it.next();
System.out.println("\nClass " + ec.getName());
String supers = "Immediate superclasses: ";
for (Iterator<EClass> is = ec.getESuperTypes().iterator();is.hasNext();)
supers = supers + is.next().getName() + " ";
System.out.println(supers);
supers = "All superclasses: ";
for (Iterator<EClass> is = ec.getEAllSuperTypes().iterator();is.hasNext();)
supers = supers + is.next().getName() + " ";
System.out.println(supers);
}
}
}
private void trace (String s) {if (tracing) System.out.println(s);}
}