package com.openMap1.mapper.writer;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.impl.EcoreFactoryImpl;
import com.openMap1.mapper.core.MapperException;
import com.openMap1.mapper.reader.objectToken;
import com.openMap1.mapper.util.ModelUtil;
/**
* objectGetter from an EMF model instance.
*
* @author robert
*
*/
public class EMFObjectGetter extends SimpleObjectGetter implements objectGetter {
private EObject instance;
/* lookup table from EObjects to their objectTokens;
* used when storing non-containment relations */
private Hashtable<EObject,objectToken> addedObjects;
/**
*
* @param classModel
* @param instance
* @throws MapperException
*/
public EMFObjectGetter(EPackage classModel, EObject instance) throws MapperException
{
super(classModel);
this.instance = instance;
captureInstance();
}
/**
* capture the instance of the ECore class model in the internal structures
* of the SimpleObjectGetter, so it can be provided to an XMLWriter on demand
* @throws MapperException
*/
private void captureInstance() throws MapperException
{
// initialise the lookup from EObjects to objectTokens
addedObjects = new Hashtable<EObject,objectToken>();
// add the objects, properties and containment relations
captureHierarchy(instance,null,null);
// add the non-containment relations
captureNonContainmentRefsInTree(instance);
}
/**
* recursive capture of all objects, properties and containment
* relations in the class model instance
* @param obj the current object
* @param outerObj if non-null, the outer object containing this object
* @param ref if non-null, the containment relation from the outer object
* @throws MapperException
*/
private void captureHierarchy(EObject obj, objectToken outerTok, EReference ref) throws MapperException
{
// add this object to the saved model, and record it in the lookup table
String className = ModelUtil.getQualifiedClassName(obj.eClass());
objectToken oTok = addObject(className);
addedObjects.put(obj,oTok);
// if this object is contained in an outer object, record the containment association
if (outerTok != null) addLink(outerTok,ref.getName(),oTok);
// record all attributes of this object that have non-null values
for (Iterator<EAttribute> ia = obj.eClass().getEAllAttributes().iterator();ia.hasNext();)
{
EAttribute att = ia.next();
Object attVal = obj.eGet(att);
if (attVal != null) setProperty(oTok,att.getName(),stringValue(att,attVal));
}
// descend the tree of containment associations
for (Iterator<EReference> ir = obj.eClass().getEAllReferences().iterator(); ir.hasNext();)
{
EReference reff = ir.next();
if (reff.isContainment())
{
Object target = obj.eGet(reff);
if ((reff.getUpperBound() == 1) && (target != null))
{
EObject child = (EObject)target;
captureHierarchy(child,oTok,reff);
}
else if ((reff.getUpperBound() == -1) && (target instanceof List<?>))
{
for (Iterator<?> ic = ((List<?>)target).iterator();ic.hasNext();)
{
EObject child = (EObject)ic.next();
captureHierarchy(child,oTok,reff);
}
}
}
}
}
/**
* convert the value of any EAttribute of an EObject to a string
* @param att
* @param attValue
* @return
*/
private String stringValue(EAttribute att, Object attValue)
{
String val = "";
EDataType type = att.getEAttributeType();
String typeName = type.getName();
EcoreFactoryImpl fac = new EcoreFactoryImpl();
if (typeName.equals("EInt"))
val = fac.convertEIntToString(type, attValue);
else if (typeName.equals("EString"))
val = fac.convertEStringToString(type, attValue);
// ... etc; add more as needed
else {System.out.println("Cannot yet handle Ecore type " + typeName);}
return val;
}
// recursive descent capturing all non-containment associations
private void captureNonContainmentRefsInTree(EObject obj) throws MapperException
{
// capture non-containment refs of this object
captureNonContainmentRefs(obj);
// descend the tree of containment associations
if (obj != null) for (Iterator<EReference> ir = obj.eClass().getEAllReferences().iterator(); ir.hasNext();)
{
EReference reff = ir.next();
if (reff.isContainment())
{
Object target = obj.eGet(reff);
if (reff.getUpperBound() == 1)
{
EObject child = (EObject)target;
captureNonContainmentRefsInTree(child);
}
else if ((reff.getUpperBound() == -1) && (target instanceof List<?>))
{
for (Iterator<?> ic = ((List<?>)target).iterator();ic.hasNext();)
{
EObject child = (EObject)ic.next();
captureNonContainmentRefsInTree(child);
}
}
}
}
}
/**
* capture all the non-containment association links of this object
* @param obj
* @throws MapperException
*/
private void captureNonContainmentRefs(EObject obj) throws MapperException
{
if (obj != null)
{
for (Iterator<EReference> ir = obj.eClass().getEAllReferences().iterator(); ir.hasNext();)
{
EReference reff = ir.next();
if (!reff.isContainment())
{
Object target = obj.eGet(reff);
if (reff.getUpperBound() == 1)
{
EObject other = (EObject)target;
captureOneNonContainmentRef(obj,reff,other);
}
else if ((reff.getUpperBound() == -1) && (target instanceof List<?>))
{
for (Iterator<?> ic = ((List<?>)target).iterator();ic.hasNext();)
{
EObject other = (EObject)ic.next();
captureOneNonContainmentRef(obj,reff,other);
}
}
}
}
}
}
/**
* capture one non-containment link
* @param obj
* @param reff
* @param other
*/
private void captureOneNonContainmentRef(EObject obj,EReference reff,EObject other)
throws MapperException
{
objectToken oTok = addedObjects.get(obj);
if (oTok == null)
throw new MapperException("Cannot find object token of class " + obj.eClass().getName());
objectToken otherTok = addedObjects.get(other);
if (otherTok == null)
throw new MapperException("Cannot find object token of class " + other.eClass().getName());
/* this captures the link and its inverse link.
* Therefore each link is captured twice; this does not matter as they
* are in Hashtables, where Elements can be written a second time with no effect. */
addLink(oTok, reff.getName(), otherTok);
}
}