package com.openMap1.mapper.util;
import java.util.Iterator;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Hashtable;
import java.util.Vector;
import org.w3c.dom.Element;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EcoreFactory;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.common.util.URI;
import com.openMap1.mapper.core.MapperException;
/**
* Class to create an EMF Ecore class model from a DAML model.
*
* @author robert
*
*/
public class ClassModelFromDAML extends ClassModelMaker {
private String filePath;
/* We need to pre-store the class superclass relation because we do not know
* what order the classes will be declared in.
* For each class name, store a Vector of its superclasses. */
private Hashtable<String, Vector<String>> classToSupers = new Hashtable<String, Vector<String>>();
private Hashtable<String,EDataType> dataTypes = new Hashtable<String,EDataType>();
public ClassModelFromDAML(URI uri){
/* The URI may be a resource URI or a file URI; normalize it to the latter, and
* then remove the String 'file:' from the normalised uri */
filePath = FileUtil.editURIConverter().normalize(uri).toFileString();
}
/**
* create a EMF ecore model from the DAML file with location set in the constructor,
* and return its EPackage root
* @return
*/
public EObject getRootOfUMLModel() throws MapperException
{
EObject ecoreRoot = null;
Element damlRoot = XMLUtil.getRootElement(filePath);
if (damlRoot != null) ecoreRoot = makeUMLModel(damlRoot);
return ecoreRoot;
}
/**
* Create an EMF ecore model from a DAML file as emitted by XMuLator
* @param damlRoot
* @return
*/
private EObject makeUMLModel(Element damlRoot)
{
classToSupers = new Hashtable<String, Vector<String>>();
dataTypes = new Hashtable<String,EDataType>();
EPackage ecoreRoot = null;
if (XMLUtil.getLocalName(damlRoot).equals("RDF"))
{
for (Iterator<Element> it = XMLUtil.childElements(damlRoot).iterator();it.hasNext();)
{
Element el = it.next();
String elType = XMLUtil.getLocalName(el);
//
if (elType.equals("Ontology"))
{
ecoreRoot = EcoreFactory.eINSTANCE.createEPackage();
ecoreRoot.setName("unnamed");
}
else if ((elType.equals("Class")) && (ecoreRoot != null)) try
{
handleClass(ecoreRoot, el);
}
catch (Exception ex) {System.out.println("Exception handling class");}
else if (elType.equals("DatatypeProperty")) try
{
handleProperty(ecoreRoot,el);
}
catch (Exception ex) {System.out.println("Exception handling property");}
else if (elType.equals("ObjectProperty")) try
{
handleAssociation(ecoreRoot, el);
}
catch (Exception ex) {System.out.println("Exception handling association");}
else show("Unrecognised DAML Element: " + elType);
}
// link classes with their superclasses
if (ecoreRoot != null)
for (Enumeration<String> en = classToSupers.keys(); en.hasMoreElements();)
{
String cName = en.nextElement();
EClass c = (EClass)ecoreRoot.getEClassifier(cName);
Vector<String> superNames = classToSupers.get(cName);
if ((superNames != null) && (c != null))
for (Iterator<String> iv = superNames.iterator(); iv.hasNext();)
{
EClass superC = (EClass)ecoreRoot.getEClassifier(iv.next());
if (superC != null) c.getESuperTypes().add(superC);
}
}
}
else show("Root element of DAML file is not an 'RDF' element");
return ecoreRoot;
}
private void handleClass(EPackage ecoreRoot, Element el)
{
String className = XMLUtil.getText(XMLUtil.firstNamedChild(el,"label"));
EClass theClass = EcoreFactory.eINSTANCE.createEClass();
theClass.setName(className);
ecoreRoot.getEClassifiers().add(theClass);
Vector<String> supers = new Vector<String>();
for (Iterator<Element> iu = XMLUtil.namedChildElements(el,"subClassOf").iterator(); iu.hasNext();)
{
String superName = iu.next().getAttribute("rdf:resource");
supers.addElement(superName);
}
classToSupers.put(className,supers);
}
private void handleProperty(EPackage ecoreRoot, Element el)
{
String propName = XMLUtil.getText(XMLUtil.firstNamedChild(el,"label"));
String className = XMLUtil.firstNamedChild(el,"domain").getAttribute("rdf:resource");
String type = XMLUtil.firstNamedChild(el,"range").getAttribute("rdf:resource");
// the value of 'type' is usually 'http://www.w3.org/2001/XMLSchema#string'
EDataType dt = getEDataType(ecoreRoot, type);
// assume all classes appear in the DAML file before any property, so the owning class can be found
EClass c = (EClass)ecoreRoot.getEClassifier(className);
if (c == null) {show("Cannot find class '" + className + "' for property '" + propName + "'");}
else
{
EAttribute ea = EcoreFactory.eINSTANCE.createEAttribute();
ea.setName(propName);
ea.setEType(dt);
c.getEStructuralFeatures().add(ea);
}
}
/**
* Ensure the EPackage contains each required EDatatype only once
* @param type the full type name from the DAML file
* @return the EDataType created for it
*/
private EDataType getEDataType(EPackage ecoreRoot, String type)
{
// if the EDataType has already been made, just return it
EDataType dt = dataTypes.get(type);
// otherwise, make it and return it
if (dt == null)
{
// find what Ecore data type name is most appropriate
String eTypeName = "EString"; // fallback if type name not recognised
StringTokenizer st = new StringTokenizer(type,"#");
String shortType = "";
while (st.hasMoreTokens()) {shortType = st.nextToken();} // i.e the type name after the last "#"
if (shortType.equals("string")) eTypeName = "EString";
else {System.out.println("New type name: " + shortType);}
// make the EDataType and add it to the package
dt = EcoreFactory.eINSTANCE.createEDataType();
dt.setName(eTypeName);
ecoreRoot.getEClassifiers().add(dt);
// record it, so it won't be made again
dataTypes.put(type,dt);
}
return dt;
}
private void handleAssociation(EPackage ecoreRoot, Element el)
{
String assocName = XMLUtil.getText(XMLUtil.firstNamedChild(el,"label"));
Element endEl1 = XMLUtil.firstNamedChild(el,"domain");
Element endEl2 = XMLUtil.firstNamedChild(el,"range");
String className1 = endEl1.getAttribute("rdf:resource");
String maxCardinality1 = endEl1.getAttribute("cardinality");
String className2 = endEl2.getAttribute("rdf:resource");
String maxCardinality2 = endEl2.getAttribute("cardinality");
// assume all classes appear in the DAML file before any associations, so both end classes can be found
EClass c1 = (EClass)ecoreRoot.getEClassifier(className1);
EClass c2 = (EClass)ecoreRoot.getEClassifier(className2);
if (c1 == null) {show("Cannot find class '" + className1 + "' for association '" + assocName + "'");}
else if (c2 == null) {show("Cannot find class '" + className2 + "' for association '" + assocName + "'");}
else
{
// DAML does not define role names, so make them from the association name
EReference r1 = EcoreFactory.eINSTANCE.createEReference();
r1.setName(assocName + "_1");
r1.setUpperBound(upperBound(maxCardinality1));
/* Class EReference has no method 'setEReferenceType' analogous to 'getEReferenceType',
* trying to set it reflectively gives a null pointer. Set it by setEType.
* This is because EReferenceType is derived feature, presumably derived from EType by
* a class cast. */
r1.setEType(c2);
c1.getEStructuralFeatures().add(r1);
EReference r2 = EcoreFactory.eINSTANCE.createEReference();
r2.setName(assocName + "_2");
r2.setUpperBound(upperBound(maxCardinality2));
r2.setEType(c1);
c2.getEStructuralFeatures().add(r2);
r1.setEOpposite(r2);
r2.setEOpposite(r1);
}
}
/**
* upper bound is 1 if cardinality is specified as 1;
* otherwise (cardinality unspecified) it is -1.
* @param cardinality
* @return
*/
private int upperBound(String cardinality)
{
if (cardinality.equals("1")) return 1;
return -1;
}
private void show(String s) {System.out.println(s);}
}