package com.openMap1.mapper.health.actions;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.Vector;
import org.eclipse.core.resources.IFile;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.EMap;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EModelElement;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcoreFactory;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPart;
import com.openMap1.mapper.actions.MakeITSMappingsAction;
import com.openMap1.mapper.core.MapperException;
import com.openMap1.mapper.util.EclipseFileUtil;
import com.openMap1.mapper.util.FileUtil;
import com.openMap1.mapper.util.GenUtil;
import com.openMap1.mapper.util.ModelUtil;
import com.openMap1.mapper.views.WorkBenchUtil;
import com.openMap1.mapper.health.cda.OCLExpression;
/**
* this class imports an MDHT CDA class model, to create and store a CDA class model
* in a form suitable for attaching to a mapping set and doing message simplification
* @author Robert
*
*/
public class ImportMDHTModel implements IObjectActionDelegate{
public IWorkbenchPart targetPart; // where this action was invoked from
public ISelection selection;
protected boolean tracing = true;
/* key = package name. Element - package in the MDHT model */
private Hashtable<String,EPackage> mdhtPackages;
/* key = superclass 'id' = <package name>|<class name> .
* element = a Hashtable with key= MDHT EClass of the subclass, element = "1" */
private Hashtable<String,Hashtable<EClass,String>> mdhtSubClasses;
/* key = class name; value = class in cda package of constrained class model;
* used to check we do not add two classes with the same name from different MDHT model packages */
private Hashtable<String,EClass> addedClasses;
/* top package of the constrained model */
private EPackage topPackage;
/* package of the new constrained model in which non-data type classes are all made */
private EPackage constrainedCDAPackage;
/* package of the new constrained model in which data type classes are all made */
private EPackage constrainedDatatypesPackage;
/* strings used as keys of annotations in unresolved associations, to identify the target class */
private String MDHTCLASS = "mdhtClass";
private String MDHTPACKAGE = "mdhtPackage";
private String UML_GENMODEL_SOURCE = "http://www.eclipse.org/uml2/1.1.0/GenModel";
private String MDHT_ANNOTATION_SOURCE = "http://www.openhealthtools.org/mdht/uml/cda/annotation";
private String EXTENDED_METADATA_SOURCE = "http:///org/eclipse/emf/ecore/util/ExtendedMetaData";
// key of an annotation on EReferences, to be used by the wrapper class
private String CDA_NAME = "CDA_Name";
// calculated from the set of all MDHT packages, by subtraction of those with no constraints
private Vector<String> templatedPackages = new Vector<String>();
// initial value for a count
private int oclConstraintNumber = 0;
/* file to be read containing extra permissions for templated classes to appear, in the form
* [qualified owner class name, XPath, qualified permitted class name] */
private String extraPermissionsFileName = "extraPermissions.csv";
/* file to be written out containing all permissions read in or inferred from OCL constraints, in the form
* [qualified owner class name, XPath, qualified permitted class name, OCL constraint names] */
private String allPermissionsFileName = "allPermissions.csv";
/* file to be written out containing all OCL constraints read, and whether they were
* successfully parsed */
private String constraintsFileName = "allConstraints.csv";
/* file containing template id combinations of sections to be included in the
* mapped class model view, in the form:
* [qualified class name, set of template ids, section class name] */
private String sectionFilterFileName = "sectionFilter.csv";
/* file containing names, template ids and superclasses of classes with no permissions */
private String noPermissionsFileName= "classesWithNoPermissions.csv";
/* key = qualified class name of MDHT class. Value = a set of unique String arrays with three elements -
* a path, the name of the constrained CDA class it reaches, and a list of constraints.*/
private Hashtable<String,Hashtable<String,String[]>> allPermittedPaths = new Hashtable<String,Hashtable<String,String[]>>();
/* used to write out a csv file of classes which have no permissions */
private Vector<String[]> classesWithNoPermissions;
// header row for the csv file of classes with no permissions
private String[] classesWithNoPermissionsHeader = {"Class","TemplateId","SuperClasses"};
// header row for file of all OCL constraints
private String[] constraintsFileHeader = {"Class","Constraint","Status","Number","OCL"};
/* used to write out a csv file of template permissions used (read in or inferred from OCL constraints) */
private Vector<String[]> templatePermissions;
// header row for the csv file of all template permissions
private String[] templatePermissionsHeader = {"Owning_Class","CDA_Path","Permitted_Class","Owning_TemplateId","PermittedTemplateId","OCL_Constraints"};
// trim the full list of ordered packages according to what packages are in scope
private String[] packageOrder()
{
// size the array
int size = 0;
for (int i = 0; i < fullPackageOrder.length; i++) if (mdhtPackages.get(fullPackageOrder[i]) != null) size++;
String[] packageOrder = new String[size];
// fill the array
size = 0;
for (int i = 0; i < fullPackageOrder.length; i++) if (mdhtPackages.get(fullPackageOrder[i]) != null) {packageOrder[size] = fullPackageOrder[i];size++;}
return packageOrder;
}
private String[] fullPackageOrder = {"consol","ccd","ihe","hitsp"};
// packages which have no template constraints
private String[] nonTemplatedPackages= {"cda","rim","datatypes"};
/**
* when this is false, the only classes which can inherit template ids from their superclasses
* (and thus have more than one template id) are subclasses of ClinicalDocument.
* In CCDA only this node appears to have more than one template id
*/
private boolean inheritTemplateIds = false;
/**
* the MDHT consolidated CDA model has a widespread fixed value for 'contextConductionInd'
* which is 'implies' . Most example messages I see have the value 'true'.
* This constant determines the fixed value - or if null, implies there is no fixed value
*/
private String fixedContextConductionInd = "true";
/**
* if this is true, remove from the model the original CDA associations from which
* the templated associations were made.
* For open templates (the US norm) leave the original CDA associations in.
*/
private boolean makeClosedTemplates = false;
//----------------------------------------------------------------------------------------------
// Main run method
//----------------------------------------------------------------------------------------------
/**
*
*/
public void run(IAction action) {
try
{
// Open the MDHT model
EPackage mdhtEntryPackage = getECoreModel();
if (mdhtEntryPackage == null) return;
URI EcoreURI = mdhtEntryPackage.eResource().getURI();
String absoluteEcoreLocation = FileUtil.removeFilePrefix(EcoreURI.toString()); // remove 'file:/'
String resourceECoreLocation = FileUtil.resourceLocation(absoluteEcoreLocation);
trace("Entry MDHT Package: " + mdhtEntryPackage.getName());
// find all MDHT packages which this model depends on
findAllMDHTPackages(mdhtEntryPackage);
EPackage mdhtCDAPackage = mdhtPackages.get("cda");
if (mdhtCDAPackage == null) throw new MapperException("Cannot find CDA package");
EPackage mdhtDatatypesPackage = mdhtPackages.get("datatypes");
if (mdhtDatatypesPackage == null) throw new MapperException("Cannot find data types package");
trace("\n*** Found " + mdhtPackages.size() + " mdht packages");
// find MDHT subclass relations
findMDHTSubclasses();
// summariseMDHTSubclasses();
trace("\n*** Found MDHT subclass relations.");
// find all relevant OCL annotations, in all packages that have them
Vector<String[]> OCLRows = new Vector<String[]>();
OCLRows.add(constraintsFileHeader);
for (Enumeration<String> en = mdhtPackages.keys();en.hasMoreElements();)
{
String pName = en.nextElement();
if (!GenUtil.inArray(pName, nonTemplatedPackages))
{
trace("*** Reading OCL annotations in package " + pName);
EPackage pack = mdhtPackages.get(pName);
findOCLAnnotations(pack,OCLRows);
}
}
// write out all the OCL annotations found
String OCLFileLocation = getCSVFileLocation(resourceECoreLocation, constraintsFileName);
trace("*** writing OCL constraints to " + OCLFileLocation);
IFile oclFile = EclipseFileUtil.getFile(OCLFileLocation);
EclipseFileUtil.writeCSVFile(OCLRows, oclFile);
// extend the OCL permissions, by reading from a file, and by subclass extension
trace("\n**** Extending OCL permissions");
extendOCLPermissions(absoluteEcoreLocation,resourceECoreLocation);
// make the packages of the constrained CDA model
topPackage = EcoreFactory.eINSTANCE.createEPackage();
topPackage.setName("constrainedCDAModel");
// ensure this class model will be viewed tree-like, as if it were an RMIM in the class model view
ModelUtil.addMIFAnnotation(topPackage, "RMIM", "true");
constrainedCDAPackage = EcoreFactory.eINSTANCE.createEPackage();
constrainedCDAPackage.setName("CDA");
constrainedDatatypesPackage = EcoreFactory.eINSTANCE.createEPackage();
constrainedDatatypesPackage.setName("datatypes");
topPackage.getESubpackages().add(constrainedCDAPackage);
topPackage.getESubpackages().add(constrainedDatatypesPackage);
trace("\n*** Made packages of constrained model.");
// for checks that we don't add the same class twice
addedClasses = new Hashtable<String,EClass>();
// import all datatype classes, and resolve their associations
importDatatypes(mdhtDatatypesPackage);
trace("\n*** Imported data type classes.");
// import all classes in the CDA package and all their subclasses, with CDA associations
importCDAClasses(constrainedCDAPackage);
trace("*** Imported CDA classes and subclasses.");
// mark the entry class for the model
String entryClassName = markEntryClass(mdhtEntryPackage);
trace("\n*** Marked entry class '" + entryClassName + "'");
// apply OCL constraints to classes, making associations to their subclasses
trace("\n*** Apply OCL constraints.");
applyOCLConstraints();
// attach target classes properly to associations
resolveAllAssociations(constrainedCDAPackage);
trace("\n*** Attached target classes to associations.");
// for closed templates, prune CDA associations that now have templated versions
if (makeClosedTemplates) // generally should be false for US realm
{
prune_CDA_Associations(constrainedCDAPackage);
trace("\n*** Pruned CDA associations");
}
// sort out the strange 'typeId' association from the top ClinicalDocument node
sortTypeId();
// move classes to packages based on section templates
moveClassesToPackages(entryClassName);
// find isolated classes, that have no association to them per se
trace("\n*** Finding Isolated classes.");
findIsolatedClasses(resourceECoreLocation);
// trace("\n*** Listing subclass relations.");
// showSubclassRelations();
// filter sections to those manually chosen in a csv file
trace("\n*** Filtering sections.");
filterSections(absoluteEcoreLocation);
// Save the new model
String[] modelExts= {};
String modelPath = FileUtil.getFilePathFromUser(targetPart,modelExts,"Select location for Imported Model",true);
if (!modelPath.equals(""))
{
ModelUtil.savePackage(FileUtil.resourceLocation(modelPath), topPackage);
WorkBenchUtil.showMessage("Completed", "Saved imported Ecore model " + FileUtil.getFileName(modelPath));
trace("*** Saved constrained model.");
}
else WorkBenchUtil.showMessage("Cancelled","No Ecore model saved");
}
catch (Exception ex)
{
WorkBenchUtil.showMessage("Error", ex.getMessage());
ex.printStackTrace();
}
}
//---------------------------------------------------------------------------------------------------------------
// Sorting out packages
//---------------------------------------------------------------------------------------------------------------
/**
* populate the Hashtable of all MDHT packages
* @param startPackage
*/
private void findAllMDHTPackages(EPackage startPackage) throws MapperException
{
mdhtPackages = new Hashtable<String,EPackage>() ;
templatedPackages = new Vector<String>();
addPackages(startPackage);
for (Enumeration<String> en = mdhtPackages.keys(); en.hasMoreElements();)
{
String packName = en.nextElement();
trace("MDHT package: " + packName);
if (!GenUtil.inArray(packName, nonTemplatedPackages)) templatedPackages.add(packName);
}
}
/**
* add all MDHT packages, by recording the package of every class in the initial package,
* all of its superclasses, and all the classes they are associated to
* @param mdhtPackage
*/
private void addPackages(EPackage mdhtPackage) throws MapperException
{
for (Iterator<EClassifier> it = mdhtPackage.getEClassifiers().iterator();it.hasNext();)
{
EClassifier next = it.next();
if (next instanceof EClass)
{
addPackagesFromSuperClasses((EClass)next);
}
}
}
/**
*
* @param theClass
* @throws MapperException
*/
private void addPackagesFromSuperClasses(EClass theClass) throws MapperException
{
addPackages(theClass);
for (Iterator<EClass> iu = theClass.getESuperTypes().iterator();iu.hasNext();)
{
EClassifier nextSuper = iu.next();
if (nextSuper.getName() == null) trace("A superclass of " + theClass.getName()
+ " in package " + theClass.getEPackage().getName() + " is has a null name");
if ((nextSuper != null) && (nextSuper.getName() != null)
&& (nextSuper instanceof EClass)) addPackagesFromSuperClasses((EClass)nextSuper);
}
}
/**
*
* @param theClass
*/
private void addPackages(EClass theClass) throws MapperException
{
if (theClass == null) throw new MapperException("Null class");
addOwnPackage(theClass);
for (Iterator<EStructuralFeature> iu = theClass.getEStructuralFeatures().iterator();iu.hasNext();)
{
EStructuralFeature feature = iu.next();
if (feature instanceof EReference)
{
// trace("Following ref " + feature.getName());
EClass reffedClass = (EClass)((EReference)feature).getEType();
if (reffedClass == null) throw new MapperException("No target class for association "
+ feature.getName() + " from class " + theClass.getName());
addOwnPackage(reffedClass);
}
}
}
private void addOwnPackage(EClass theClass) throws MapperException
{
EPackage thePackage = theClass.getEPackage();
if (thePackage == null) throw new MapperException("No package for class " + theClass.getName());
if (thePackage.getName() == null) throw new MapperException("No name for package of class " + theClass.getName());
mdhtPackages.put(thePackage.getName(), thePackage);
}
//---------------------------------------------------------------------------------------------------------------
// Recording which MDHT classes are subclasses of other classes
//---------------------------------------------------------------------------------------------------------------
/**
* record all the proper subclasses of any class
*/
private void findMDHTSubclasses()
{
mdhtSubClasses = new Hashtable<String,Hashtable<EClass,String>>();
// loop over all MDHT packages
for (Enumeration<String> en = mdhtPackages.keys(); en.hasMoreElements();)
{
String packageName = en.nextElement();
EPackage thePackage = mdhtPackages.get(packageName);
// loop over all classes in the package
for (Iterator<EClassifier> it = thePackage.getEClassifiers().iterator();it.hasNext();)
{
EClassifier next = it.next();
if (next instanceof EClass)
{
EClass theClass = (EClass)next;
// loop over all superclasses of the class
for (Iterator<EClass> iu = theClass.getEAllSuperTypes().iterator();iu.hasNext();)
{
EClassifier nextSuper = iu.next();
if (nextSuper instanceof EClass)
{
// record the class as a subclass of its superclass
EClass superC = (EClass)nextSuper;
if ((!theClass.getName().equals(superC.getName()))
&& (superC.getName() != null)&& (superC.getEPackage() != null))
{
String superId = superC.getEPackage().getName() + "|" + superC.getName();
Hashtable<EClass,String> subs = mdhtSubClasses.get(superId);
if (subs == null) subs = new Hashtable<EClass,String>();
subs.put(theClass,"1");
mdhtSubClasses.put(superId, subs);
}
}
}
}
}
}
}
/**
* write a summary of how many subclasses there are for each CDA class
*/
@SuppressWarnings("unused")
private void summariseMDHTSubclasses()
{
for (Enumeration<String> en = mdhtSubClasses.keys(); en.hasMoreElements();)
{
String classKey = en.nextElement();
Hashtable<EClass,String> subs = mdhtSubClasses.get(classKey);
trace(classKey + ": " + subs.size());
}
}
//---------------------------------------------------------------------------------------------------------------
// Reading OCL annotations
//---------------------------------------------------------------------------------------------------------------
/**
*
* @param theClass
* @return
*/
private Hashtable<String,String[]> getOCLPermissions(EClass mdhtClass)
{
Hashtable<String,String[]> constraints = allPermittedPaths.get(ModelUtil.getQualifiedClassName(mdhtClass));
if (constraints == null) constraints = new Hashtable<String,String[]>();
return constraints;
}
/**
* list relevant OCL annotations, which mention classes in the leaf package (eg ccd)
* @param mdhtPackage
*/
private void findOCLAnnotations(EPackage mdhtPackage,Vector<String[]> OCLRows) throws MapperException
{
trace("*** finding constraints from OCL annotations in package " + mdhtPackage.getName());
for (Iterator<EClassifier> it = mdhtPackage.getEClassifiers().iterator();it.hasNext();)
{
EClassifier next = it.next();
if (next instanceof EClass)
{
EClass theClass = (EClass)next;
boolean writeConstraints = false;
Hashtable<String,String[]> oclPermissions = findOCLPermissions(theClass,writeConstraints, OCLRows);
String qualifiedClassName = ModelUtil.getQualifiedClassName(theClass);
allPermittedPaths.put(qualifiedClassName, oclPermissions);
}
}
}
/**
*
* @param theClass
* @throws MapperException
*/
private Hashtable<String,String[]> findOCLPermissions(EClass theClass, boolean writeConstraint,Vector<String[]> OCLRows) throws MapperException
{
String packageName = theClass.getEPackage().getName();
String className = theClass.getName();
Hashtable<String,String[]> permittedPaths = new Hashtable<String,String[]>();
for (Iterator<EOperation> iu = theClass.getEOperations().iterator();iu.hasNext();)
{
EOperation op = iu.next();
String constraintName = op.getName();
EAnnotation ann = op.getEAnnotation(UML_GENMODEL_SOURCE);
if (ann != null)
{
String OCLText = ann.getDetails().get("body");
String[] OCLRow = new String[5];
OCLRow[0] = className;
OCLRow[1] = constraintName;
OCLRow[2] = "read";
OCLRow[3] = "-";
OCLRow[4] = OCLText;
if (OCLText != null) try
{
OCLExpression expr = new OCLExpression(packageName,className,templatedPackages,OCLText,constraintName,false);
if (expr.parseThisOCL())
{
oclConstraintNumber++;
OCLRow[2] = new Integer(expr.constraintPaths().size()).toString() + " paths";
OCLRow[3] = new Integer(oclConstraintNumber).toString();
OCLRow[4] = expr.OCLText();
resolveConstraintPaths(theClass,expr,permittedPaths);
}
else OCLRow[2] = "no parse";
}
catch (Exception ex) {OCLRow[2] = "Exception: " + ex.getMessage();}
OCLRows.add(OCLRow);
}
}
return permittedPaths;
}
/**
*
* @param mdhtPackage
* @param theClass
* @param expr
* @throws MapperException
*/
private void resolveConstraintPaths(EClass theClass,OCLExpression expr,Hashtable<String,String[]> permittedPaths) throws MapperException
{
for (Iterator<String[]> it = expr.getAllConstraints().iterator();it.hasNext();)
{
// resolve any ambiguous paths in the constraint, and check that the path can be followed
String[] constraint = it.next();
String ambiguousPath = constraint[0];
String targetClassPackageName = constraint[1];
EPackage targetClassPackage = mdhtPackages.get(targetClassPackageName);
String targetClassName = constraint[2];
String constraintName = constraint[3];
EClass targetClass = (EClass)targetClassPackage.getEClassifier(targetClassName);
String truePath = pathToClass(theClass,ambiguousPath,targetClass);
if (truePath == null) throw new MapperException("Cannot follow path " + ambiguousPath + " from class "
+ theClass.getName() + " to class " + targetClass.getName());
// store the constraint without duplicates in the class
addPermission(truePath,ModelUtil.getQualifiedClassName(targetClass),constraintName,permittedPaths);
}
}
/**
* resolve an ambiguous path into the actual path that can be followed, sorting out two issues:
* (1) the choice between 'entry' and 'entryRelationship' steps depends if the parent class is
* a section or an entry
* (2) the last step may be the ambiguous 'clinicalStatement', which needs to be resolved to act or procedure etc.
* @param parentClass
* @param ambiguousPath a path of association names, which may be ambiguous because of a last 'clinicalStatement' step
* @param childClass
* @return the unambiguous path, or null if it cannot be followed
*/
private String pathToClass(EClass parentClass, String ambiguousPath, EClass childClass)
{
EClass currentClass = parentClass;
boolean hasPath = true;
String path = "";
StringTokenizer steps = new StringTokenizer(ambiguousPath,".");
String step = "";
// as long as you can follow steps, transcribe them to the true path
while (steps.hasMoreTokens() && hasPath)
{
step = steps.nextToken();
// resolve the 'entry or entryRelationship or component' choice
if (step.equals("entryOrEntryRelationship"))
{
if (isSubClass(parentClass,"Section")) step = "entry";
else if (isSubClass(parentClass,"Organizer")) step = "component";
else step = "entryRelationship";
}
EStructuralFeature feat = currentClass.getEStructuralFeature(step);
if ((feat != null) && (feat instanceof EReference))
{
EReference ref = (EReference)feat;
currentClass = (EClass)ref.getEType();
path = path + step + ".";
}
else hasPath = false;
}
// the last step may be the ambiguous 'clinicalStatement' which cannot be followed; if so, find out what step it really is
if (step.equals("clinicalStatement"))
{
for (Iterator<EStructuralFeature> it = currentClass.getEAllStructuralFeatures().iterator();it.hasNext();)
{
EStructuralFeature feat = it.next();
if (feat instanceof EReference)
{
EClass foundTarget = (EClass)((EReference)feat).getEType();
step = feat.getName();
if ((ModelUtil.isSubClass(childClass, foundTarget))
&& (GenUtil.inArray(step, OCLExpression.CLINICAL_STATEMENT_VALUES))) return (path + step);
}
}
}
// the result of following the path should be a superclass of the target class
if ((hasPath) && (ModelUtil.isSubClass(childClass, currentClass))) return path;
return null;
}
/**
*
* @param theClass
* @return true if the class is a subclass of CDA class Section.
*/
private boolean isSubClass(EClass theClass, String superName)
{
boolean sectionClone = false;
for (Iterator<EClass> it = theClass.getEAllSuperTypes().iterator();it.hasNext();)
{
EClass superC = it.next();
if (superC.getName().equals(superName)) sectionClone = true;
}
return sectionClone;
}
//---------------------------------------------------------------------------------------------------------------
// Extending OCL permissions, by reading a file, then making subclass extensions
//---------------------------------------------------------------------------------------------------------------
private void extendOCLPermissions(String EcoreLocation, String resourceECoreLocation) throws MapperException
{
trace("OCL path permissions before extension: " + totalPermissions());
// read a csv file of extra permissions
String extraPermissionFileLocation = getCSVFileLocation(EcoreLocation, extraPermissionsFileName);
Vector<String> permissionFileLines = new Vector<String>();
try {
permissionFileLines = FileUtil.textLines(extraPermissionFileLocation);
}
catch (Exception ex) {throw new MapperException("Cannot open permissions file at '" + extraPermissionFileLocation + "': " + ex.getMessage());}
// extend with permissions from a csv file
for (int i = 1; i < permissionFileLines.size(); i++) // miss out the header row
{
String line = permissionFileLines.get(i);
StringTokenizer st = new StringTokenizer(line,",");
if (st.countTokens() == 3)
{
String masterClassName = st.nextToken();
String path = st.nextToken();
String permittedClassName = st.nextToken();
EClass masterClass = getNamedMDHTClass(masterClassName);
if (masterClass == null) throw new MapperException("Cannot find MDHT master class " + masterClassName);
EClass permittedClass = getNamedMDHTClass(permittedClassName);
if (permittedClass == null) throw new MapperException("Cannot find MDHT permitted class " + permittedClassName);
Hashtable<String,String[]> permissions = getOCLPermissions(masterClass);
String origin = "from file row " + new Integer(i).toString();
addPermission(path,permittedClassName,origin,permissions);
allPermittedPaths.put(masterClassName, permissions);
}
else throw new MapperException("Invalid line in permissions csv file: '" + line + "'");
}
trace("OCL path permissions after extension from file: " + totalPermissions());
extendPermissionsBySubClasses();
trace("OCL path permissions after extension by subclasses: " + totalPermissions());
showAllPermissions(resourceECoreLocation);
}
/**
* used for all additions to the permissions data; store a string array of dimension 2 with no duplicates
* @param path
* @param permittedClassName
* @param permissions a table of permissions for the owner class. key = <path>_$<permitted class> ;
* value array = [path,permitted class,constraints].(If > 1 constraint, separated by ';')
*/
private void addPermission(String path,String permittedClassName,String constraintName, Hashtable<String,String[]> permissions)
{
String[] cons = new String[3];
// ensure the path has no final '.'
String p = path;
if (p.endsWith(".")) p = p.substring(0, p.length()-1);
cons[0] = p;
cons[1] = permittedClassName;
cons[2] = constraintName;
String key = cons[0] + "_$" + cons[1];
// if there is an existing permission with this permitted class and path, collect all the constraint names which led to it
String[] oldCons = permissions.get(key);
if (oldCons != null) cons[2] = oldCons[2] + ";" + constraintName;
permissions.put(key, cons);
}
/**
*
* @return the permissions csv file is in the top MDHT folder - i.e two folders
* above the folder holding the mdht ecore file which was right-clicked
*/
private String getCSVFileLocation(String EcoreLocation, String fileName)
{
StringTokenizer st = new StringTokenizer(EcoreLocation,"/");
int steps = st.countTokens();
// miss out the last two folders and file name, i.e the last 3 steps
String path = "";
for (int i = 0; i < steps -3; i++) {path = path + st.nextToken() + "/";}
return path + fileName;
}
/**
* count the total number of permission paths and classes
* @return
*/
private int totalPermissions()
{
int permissions = 0;
for(Enumeration<String> en = allPermittedPaths.keys();en.hasMoreElements();)
{
String className = en.nextElement();
Hashtable<String,String[]> paths = allPermittedPaths.get(className);
permissions = permissions + paths.size();
}
return permissions;
}
/**
*
* @param EcoreLocation
* @throws MapperException
*/
private void showAllPermissions(String resourceECoreLocation) throws MapperException
{
// initialise the csv file of inferred template permissions to be written out
templatePermissions = new Vector<String[]>();
templatePermissions.add(templatePermissionsHeader);
String allPermissionsFileLocation = getCSVFileLocation(resourceECoreLocation, allPermissionsFileName);
// work through in package order, writing permissions in each package to the csv file and console
for (int i = 0; i < packageOrder().length; i++) writeAllPermissionsInPackage(i);
// write out the csv file
trace("writing csv file to " + allPermissionsFileLocation);
IFile csvFile = EclipseFileUtil.getFile(allPermissionsFileLocation);
EclipseFileUtil.writeCSVFile(templatePermissions, csvFile);
}
/**
* write all permissions form classes in a package to a csv file
* @param packageNumber
*/
private void writeAllPermissionsInPackage(int packageNumber) throws MapperException
{
String packageName = packageOrder()[packageNumber];
// trace("----- All permissions in package " + packageName);
for(Enumeration<String> en = allPermittedPaths.keys();en.hasMoreElements();)
{
String className = en.nextElement();
StringTokenizer st = new StringTokenizer(className,".");
if (st.nextToken().equals(packageName))
{
String line = "Class " + className + ": ";
Hashtable<String,String[]> paths = allPermittedPaths.get(className);
// only write lines for classes that have some permissions to contain other classes
if (paths.size() > 0)
{
for (Enumeration<String[]> ep = paths.elements();ep.hasMoreElements();)
{
// build up the line to be written to the console (several permissions per line)
String[] cons = ep.nextElement();
line = line + "[" + cons[0] + "," + cons[1] + "," + cons[2] + "]";
// write a new line to the csv file (one permission per line)
String[] csvLine = new String[6];
csvLine[0] = className; // the containing class name
csvLine[1] = cons[0]; // the path
csvLine[2] = cons[1]; // the permitted class
csvLine[3] = getTemplateId(className); // the containing template id
csvLine[4] = getTemplateId(cons[1]); // the permitted template id
csvLine[5] = cons[2]; // constraint names
templatePermissions.add(csvLine);
}
// trace(line);
}
}
}
}
/**
*
* @param mdhtClassName
* @return
* @throws MapperException
*/
private String getTemplateId(String mdhtClassName) throws MapperException
{
String templateId = "";
EClass mdhtClass = getNamedMDHTClass(mdhtClassName);
EAnnotation ann = mdhtClass.getEAnnotation(MDHT_ANNOTATION_SOURCE);
if (ann != null)
{
String template = ann.getDetails().get("templateId.root");
if (template != null) templateId = template;
}
return templateId;
}
/**
*
*/
private void extendPermissionsBySubClasses() throws MapperException
{
for (int fromPackageNo = 0; fromPackageNo < packageOrder().length;fromPackageNo++)
{
EPackage fromPackage = mdhtPackages.get(packageOrder()[fromPackageNo]);
for (int toPackageNo = fromPackageNo; toPackageNo < packageOrder().length; toPackageNo++)
{
EPackage toPackage = mdhtPackages.get(packageOrder()[toPackageNo]);
if ((fromPackage != null) && (toPackage != null))
extendPermissionsBySubClasses(fromPackage,toPackage);
}
}
}
/**
*
* @param fromPackage
* @param toPackage
*/
private void extendPermissionsBySubClasses(EPackage fromPackage,EPackage toPackage) throws MapperException
{
for (Iterator<EClassifier> it = fromPackage.getEClassifiers().iterator();it.hasNext();)
{
EClassifier next = it.next();
if (next instanceof EClass)
{
EClass fromClass = (EClass)next;
Hashtable<String,String[]> permittedPaths = getOCLPermissions(fromClass);
if (permittedPaths.size() > 0)
{
Vector<EClass> ownerSubclasses = getSubclassesInPackage(fromClass,toPackage);
extendPermissionsBySubClasses(fromClass,ownerSubclasses,permittedPaths,toPackage);
}
}
}
}
/**
*
* @param fromClass
* @param ownerSubclasses
* @param permittedPaths
*/
private void extendPermissionsBySubClasses(EClass fromClass,
Vector<EClass> ownerSubclasses, Hashtable<String,String[]> permittedPaths, EPackage toPackage) throws MapperException
{
for (Enumeration<String[]> en = permittedPaths.elements();en.hasMoreElements();)
{
String[] cons = en.nextElement();
String path = cons[0];
String className = cons[1];
String constraintName = cons[2];
EClass target = getNamedMDHTClass(cons[1]);
Vector<EClass> subTargets = getSubclassesInPackage(target,toPackage);
// there are no subclasses of the target class; add permissions for subclasses of the owner class, to the one target class
if (subTargets.size() == 0)
{
for (int i = 0; i < ownerSubclasses.size();i++)
{
EClass ownerSubclass = ownerSubclasses.get(i);
Hashtable<String,String[]> subPaths = getOCLPermissions(ownerSubclass);
addPermission(path,className,constraintName,subPaths);
allPermittedPaths.put(ModelUtil.getQualifiedClassName(ownerSubclass), subPaths);
}
}
// if there are target subclasses, add a permission for every owner subclass and every target subclass
else if (subTargets.size() > 0)
{
for (int i = 0; i < ownerSubclasses.size();i++)
{
EClass ownerSubclass = ownerSubclasses.get(i);
Hashtable<String,String[]> subPaths = getOCLPermissions(ownerSubclass);
for (int j = 0; j < subTargets.size();j++)
{
EClass subTarget = subTargets.get(j);
addPermission(path,ModelUtil.getQualifiedClassName(subTarget),constraintName,subPaths);
}
allPermittedPaths.put(ModelUtil.getQualifiedClassName(ownerSubclass), subPaths);
}
}
}
}
/**
*
* @param fromClass
* @param toPackage
* @return
*/
private Vector<EClass> getSubclassesInPackage(EClass fromClass,EPackage toPackage)
{
Vector<EClass> subclasses = new Vector<EClass>();
for (Iterator<EClassifier> it = toPackage.getEClassifiers().iterator();it.hasNext();)
{
EClassifier next = it.next();
if (next instanceof EClass)
{
EClass subClass = (EClass)next;
boolean isSubclass = false;
for (Iterator<EClass> iu = subClass.getESuperTypes().iterator();iu.hasNext();)
if (iu.next().equals(fromClass)) isSubclass = true;
if (isSubclass) subclasses.add(subClass);
}
}
return subclasses;
}
//---------------------------------------------------------------------------------------------------------------
// Importing classes from an MDHT package to a constrained model package
//---------------------------------------------------------------------------------------------------------------
/**
* Revised version
* @param cdaPackage
*/
private void importCDAClasses(EPackage cdaPackage) throws MapperException
{
/* iterate over all packages in the MDHT model, except the RIM or datatypes package(we don't need RIM classes, and 'Act' causes problems)
* and the data types package (done already) */
for (Enumeration<String> en = mdhtPackages.keys();en.hasMoreElements();)
{
String mdhtPackageName = en.nextElement();
if ((!mdhtPackageName.equals("rim")) && (!mdhtPackageName.equals("datatypes")))
{
trace("*** Importing from MDHT package " + mdhtPackageName);
EPackage mdhtPackage = mdhtPackages.get(mdhtPackageName);
// iterate over all classes in the package, importing every one
for (Iterator<EClassifier> it = mdhtPackage.getEClassifiers().iterator();it.hasNext();)
{
EClassifier next = it.next();
if (next instanceof EClass)
{
EClass theClass = (EClass)next;
// trace("Importing class " + theClass.getName());
// import the class in the CDA package
@SuppressWarnings("unused")
EClass importedClass = addImportedClass(cdaPackage,theClass,false);
}
}
}
}
}
/* If this is true, add the MDHT package name to the class name generated in the ecore model */
private boolean addMDHTPackageName = false;
/**
*
* @param toPackage the package in the constrained model, to which a class may be added
* @param fromClass the class in the MDHT model which may be added
* @param keepInheritance if false, give the imported class inherited features so it no longer needs to inherit them
* @return
*/
private EClass addImportedClass(EPackage toPackage, EClass fromClass, boolean keepInheritance) throws MapperException
{
// all imported classes have their original package name in the class name, except data type and cda classes
String className = fromClass.getName();
String mdhtPackageName = fromClass.getEPackage().getName();
if ((!mdhtPackageName.equals("datatypes")) && (!mdhtPackageName.equals("cda")) && (addMDHTPackageName))
className = className + "_" + mdhtPackageName;
className = newUniqueName(className);
// make the bare class, with annotations to say what MDHT class it comes from
EClass newClass = addBareClass(toPackage, fromClass, className);
// pick up all attributes and associations, including inherited ones if keepInheritance = false
EList<EStructuralFeature> features = fromClass.getEAllStructuralFeatures();
if (keepInheritance) features = fromClass.getEStructuralFeatures();
for (Iterator<EStructuralFeature> it = features.iterator();it.hasNext();)
{
EStructuralFeature feature = it.next();
if (feature instanceof EAttribute) addAttribute(newClass, (EAttribute)feature);
else if (feature instanceof EReference) addUnresolvedAssociation(newClass, (EReference)feature, (EClass)((EReference)feature).getEType());
}
return newClass;
}
/**
*
* @param className
* @return a new name for the class that has not been used before
*/
private String newUniqueName(String className)
{
int index = 0;
String newName = className;
while (addedClasses.get(newName) != null)
{
index++;
newName = className + "_" + index;
}
return newName;
}
/**
*
* @param toPackage
* @param fromClass
* @return the new EClass
* @throws MapperException if one with that name has already been made
*/
private EClass addBareClass(EPackage toPackage, EClass fromClass, String className) throws MapperException
{
EClass newClass = EcoreFactory.eINSTANCE.createEClass();
newClass.setName(className);
String rimClassName = rimClassName(fromClass);
if (rimClassName != null) ModelUtil.addMIFAnnotation(newClass, "RIM Class",rimClassName);
ModelUtil.addMIFAnnotation(newClass, MDHTCLASS, fromClass.getName());
ModelUtil.addMIFAnnotation(newClass, MDHTPACKAGE, fromClass.getEPackage().getName());
if (toPackage.getEClassifier(className) != null) throw new MapperException("Duplicate class with name " + className);
toPackage.getEClassifiers().add(newClass);
addedClasses.put(className, newClass);
transferAnnotations(fromClass,newClass);
return newClass;
}
/**
*
* @param fromClass
* @param newClass
* @throws MapperException
*/
private void transferAnnotations(EClass fromClass,EClass newClass) throws MapperException
{
// decide whether template ids are to be inherited from a superclass
boolean inheritTemplateIdsHere = inheritTemplateIds; // usually false
// the one case where two templatIds appear under one node - the head of the document
if (fromClass.getName().equals("GeneralHeaderConstraints")) inheritTemplateIdsHere = true;
EAnnotation note = fromClass.getEAnnotation(MDHT_ANNOTATION_SOURCE);
if (note != null)
{
EMap<String,String> details = note.getDetails();
for (Iterator<String> ik = details.keySet().iterator(); ik.hasNext();)
{
String key = ik.next();
String value = details.get(key);
// special label/keys for template constraints
if (key.equals("templateId.root"))
{
String templateLabel = getNextTemplateLabel(newClass); // 'template' or 'template_1' etc.
// only in special cases (ClinicalDocument node) add template_1 etc. annotations
if ((templateLabel.equals("template"))||inheritTemplateIdsHere)
ModelUtil.addMIFAnnotation(newClass,templateLabel , value);
}
// ignore these annotations
else if (key.startsWith("constraints")) {}
else if (key.equals("contextDependent")) {}
else if (fromClass.getName().equals("ProcedureActivity")) {} // strange class in MDHT model
// path to a constrained value
else
{
String newKey = "constraint:" + followPath(fromClass,key);
ModelUtil.addMIFAnnotation(newClass, newKey, value);
}
}
}
// transfer annotations from superclasses in templated packages
for (Iterator<EClass> it = fromClass.getESuperTypes().iterator(); it.hasNext();)
{
EClass superFromClass = it.next();
if (GenUtil.inVector(superFromClass.getEPackage().getName(),templatedPackages))
transferAnnotations(superFromClass,newClass);
}
}
/**
*
* @param newClass
* @return a new template label that has not been used already for this class,
* in the sequence 'template', 'template_1', 'template_2' etc.
*/
private String getNextTemplateLabel(EClass newClass)
{
String label = "template";
if (ModelUtil.getMIFAnnotation(newClass, label) == null) return label;
int i = 1;
label = "template_" + i;
while (ModelUtil.getMIFAnnotation(newClass, label) != null)
{
i++;
label = "template_" + i;
}
return label;
}
/**
*
* @param fromClass
* @param path a path of form step1.step2.step3
* @return a path of form step1/step2/@step3
* @throws MapperException if the path cannot be followed from the class
*/
private String followPath(EClass fromClass,String path) throws MapperException
{
String newPath = "";
StringTokenizer steps = new StringTokenizer(path,".");
EClass currentClass = fromClass;
while(steps.hasMoreTokens())
{
String step = steps.nextToken();
EStructuralFeature feat = currentClass.getEStructuralFeature(step);
// all steps except the last are associations
if ((steps.hasMoreTokens()) && (feat != null) && (feat instanceof EReference))
{
newPath = newPath + step + "/";
currentClass = (EClass)((EReference)feat).getEType();
}
// the last step is always an attribute
else if ((!steps.hasMoreTokens()) && (feat != null) && (feat instanceof EAttribute))
{
newPath = newPath + "@" + step;
}
// turn off path checking for data type 'ANY;
else if (currentClass.getName().equals("ANY"))
{
newPath = newPath + "@" + step;
}
else throw new MapperException("Cannot follow annotation path '" + path + "' from class '" + fromClass.getName() + "' at step '" + step + "'");
}
return newPath;
}
/**
*
* @param el
* @return from the annotation on an imported class or feature, get the mdht class it was derived from
*/
private EClass getMDHTClass(EModelElement el)
{
EClass mdhtClass = null;
String thePackage = ModelUtil.getMIFAnnotation(el, MDHTPACKAGE);
String theClass = ModelUtil.getMIFAnnotation(el, MDHTCLASS);
if ((thePackage != null) && (theClass != null))
{
EPackage mdhtPackage = mdhtPackages.get(thePackage);
if (mdhtPackage != null) mdhtClass = (EClass)mdhtPackage.getEClassifier(theClass);
}
return mdhtClass;
}
/**
*
* @param mdhtClass
* @return
*/
private EClass getImportedClass(EClass mdhtClass)
{
EClass result = null;
String mdhtClassName = mdhtClass.getName();
// find the right package to look in
EPackage packageToSearch = constrainedCDAPackage;
if (mdhtClass.getEPackage().getName().equals("datatypes")) packageToSearch = constrainedDatatypesPackage;
// check all classes in the package
for (Iterator<EClassifier> it = packageToSearch.getEClassifiers().iterator();it.hasNext();)
{
EClassifier next = it.next();
String nextName = next.getName();
if ((next instanceof EClass)
&& (nextName.startsWith(mdhtClassName)) // class name may have been altered by adding '_1' etc.
&& ((nextName.length() - mdhtClassName.length()) < 3) // avoid major templated extensions of MDHT class names
&& (getMDHTClass(next) != null)
&& (mdhtClass.equals(getMDHTClass(next))))
result = (EClass) next;
}
return result;
}
/**
*
* @param newClass
* @param att
* @return
*/
private EAttribute addAttribute(EClass newClass, EAttribute att) throws MapperException
{
EAttribute newAtt = EcoreFactory.eINSTANCE.createEAttribute();
// set the attribute name, type and lower bound
newAtt.setName(att.getName());
newAtt.setLowerBound(att.getLowerBound());
newAtt.setEType(EcorePackage.eINSTANCE.getEString());
// annotate fixed values of the attribute got from its owning MDHT Class
String fixedValue = null;
EObject container = att.eContainer();
if ((container != null) && (container instanceof EClass))
fixedValue = getOCLFixedValue((EClass)container,att.getName());
/* annotate any fixed value of the attribute, taken from an annotation on the imported class like
<details key="constraint:@moodCode" value="EVN"/> */
String annotationKey = "constraint:@" + att.getName();
if (fixedValue == null) fixedValue = ModelUtil.getMIFAnnotation(newClass, annotationKey);
// may want to remove or replace the value 'implies' for contextConductionInd?
if ((newAtt.getName().equals("contextConductionInd")) && (fixedValue != null)) fixedValue = fixedContextConductionInd;
if (fixedValue != null) ModelUtil.addMIFAnnotation(newAtt, "fixed value", fixedValue);
// attach the attribute
newClass.getEStructuralFeatures().add(newAtt);
return newAtt;
}
/**
* Process an annotation whose details are of the form
* <details key="body" value="self.classCode=vocab::ActClinicalDocument::DOCCLIN"/>
* @param theClass the EClass with the annotation
* @param attName that attribute name, e.g 'classCode'
* @return the fixed value, e.g. 'DOCCLIN'
*/
private String getOCLFixedValue(EClass theClass, String attName) throws MapperException
{
String fixedValue = null;
for (Iterator<EOperation> it = theClass.getEOperations().iterator();it.hasNext();)
{
EOperation op = it.next();
// the EOperation must match the attribute name
if (op.getName().equals(attName))
{
EAnnotation ann = op.getEAnnotation(UML_GENMODEL_SOURCE);
if (ann != null)
{
String value = ann.getDetails().get("body");
if (value != null)
{
String errorMessage = "**** Unexpected OCL string '" + value
+ "' for attribute '" + attName + "' of class " + theClass.getName();
boolean expectedForm = false;
StringTokenizer sides = new StringTokenizer(value,"= ");
int count = sides.countTokens();
/* expressions like 'self.isMoodCodeDefined() implies self.moodCode=vocab::ActMood::EVN' , possibly with 'not' at the front
* Ignore everything up to and including 'implies' */
if (count == 5) sides.nextToken(); // consume any initial 'not'
if (count == 4)
{
sides.nextToken();
@SuppressWarnings("unused")
String implies = sides.nextToken();
}
// expressions like 'self.moodCode=vocab::ActMood::EVN'
if ((count == 2)|(count == 4)|(count == 5))
{
String lhs =sides.nextToken();
if (lhs.startsWith("self."))
{
if (!lhs.startsWith("self." + attName))
trace("LHS " + lhs + " for attribute " + attName);
String rhs = sides.nextToken();
StringTokenizer st = new StringTokenizer(rhs,":'");
int tokens = st.countTokens();
if (tokens == 1)
{
fixedValue = st.nextToken();
expectedForm = true;
}
else if ((tokens == 3) && (st.nextToken().equals("vocab")))
{
st.nextToken();
fixedValue = st.nextToken();
expectedForm = true;
}
}
}
if (!expectedForm) trace(errorMessage);
}
}
}
}
return fixedValue;
}
/**
*
* @param newClass
* @return a new text content attribute, added to the class
*/
private EAttribute addTextAttribute(EClass newClass)
{
EAttribute newAtt = EcoreFactory.eINSTANCE.createEAttribute();
newAtt.setName("textContent");
newAtt.setLowerBound(0);
newAtt.setEType(EcorePackage.eINSTANCE.getEString());
newClass.getEStructuralFeatures().add(newAtt);
return newAtt;
}
/**
* An unresolved association is one that does not yet have a target class.
* Add an EReference to a class; but do not yet set its type to the target class,
* because the target class may not exist in the constrained model.
* In stead, annotate the EReference with the MDHT class and package of the target
* class, to be resolved later whan all target classes exist in the constrained model
* @param theClass
* @param ref Reference in the MDHT model that the new EReferenceis based on
* @param mdhtTarget
* @return the new unresolved EReference
*/
private EReference addUnresolvedAssociation(EClass theClass, EReference ref, EClass mdhtTarget) throws MapperException
{
String location = " at EReference " + ref.getName() + " from class " + theClass.getName();
String errorMessage = "";
if (mdhtTarget == null) errorMessage = ("Null MDHT class" + location);
if (mdhtTarget.getName() == null) errorMessage = ("No name for MDHT class" + location);
if (mdhtTarget.getEPackage() == null) errorMessage = ("No package for MDHT class " + mdhtTarget.getName() + location);
EReference newRef = EcoreFactory.eINSTANCE.createEReference();
newRef.setName(ref.getName());
if (errorMessage.equals(""))
{
newRef.setLowerBound(ref.getLowerBound());
newRef.setUpperBound(ref.getUpperBound());
newRef.setContainment(ref.isContainment());
ModelUtil.addMIFAnnotation(newRef, MDHTCLASS, mdhtTarget.getName());
ModelUtil.addMIFAnnotation(newRef, MDHTPACKAGE, mdhtTarget.getEPackage().getName());
theClass.getEStructuralFeatures().add(newRef);
}
else WorkBenchUtil.showMessage("MDHT class error", errorMessage);
return newRef;
}
/**
* Add a revolved association, i.e one which has a target class
* @param theClass class to add the association to
* @param ref association in the MDHT model that this association is based on
* @param newName new name for this association - more complex than the MDHT association name
* @param cdaName CDA name of the association - to be put in an annotation
* @param target taregt class
* @return
*/
private EReference addAssociation(EClass theClass, EReference ref, String newName, String cdaName, EClass target)
{
EReference newRef = EcoreFactory.eINSTANCE.createEReference();
newRef.setName(newName);
newRef.setLowerBound(ref.getLowerBound());
newRef.setUpperBound(ref.getUpperBound());
newRef.setContainment(ref.isContainment());
newRef.setEType(target);
if (cdaName != null) ModelUtil.addMIFAnnotation(newRef, CDA_NAME, cdaName);
theClass.getEStructuralFeatures().add(newRef);
return newRef;
}
/**
* @param fromClass
* @return the name of the RIM class it is a clone of, or null if it is a datatype class
*/
private String rimClassName(EClass fromClass)
{
return packageSuperClassName(fromClass,"rim");
}
/**
*
* @param fromClass
* @param packageName
* @return the name of the class in the package which this class is a subclass of
*/
private String packageSuperClassName(EClass fromClass, String packageName)
{
if (fromClass.getEPackage().getName().equals(packageName)) return fromClass.getName();
for (Iterator<EClass> it = fromClass.getESuperTypes().iterator();it.hasNext(); )
{
String pClassName = packageSuperClassName(it.next(),packageName);
if (pClassName != null) return pClassName;
}
return null;
}
/**
* mark the entry class for the model - the class derived from an MDHT class
* in the specified package, which is the subclass of "ClinicalDocument"
* selected by the user
* @param mdhtPackage
*/
private String markEntryClass(EPackage mdhtPackage) throws MapperException
{
String entryClassName = null;
Vector<String> entryClassNames = new Vector<String>();
Vector<EClass> entryClasses = new Vector<EClass>();
// iterate over all classes in the imported Ecore model, derived from any MDHT package
for (Iterator<EClassifier> it = constrainedCDAPackage.getEClassifiers().iterator(); it.hasNext();)
{
EClass theClass = (EClass)it.next();
EClass mdhtClass = getMDHTClass(theClass);
// classes derived from an MDHT class in the originally selected package
if ((mdhtClass != null) && (mdhtPackage.getName().equals(mdhtClass.getEPackage().getName())))
{
// build up a list of classes derived from an MDHT class which is a subclass of ClinicalDocument
for (Iterator<EClass> ix = mdhtClass.getEAllSuperTypes().iterator();ix.hasNext();)
{
if (ix.next().getName().equals("ClinicalDocument"))
{
entryClassNames.add(mdhtClass.getName());
entryClasses.add(theClass);
}
}
}
}
// allow the user to choose an entry class
int chosen = WorkBenchUtil.chooseOneString("Chooose an entry class", targetPart, entryClassNames);
if ((chosen > -1) && (chosen < entryClassNames.size()))
{
EClass theClass = entryClasses.get(chosen);
// rename the entry class so it has no package suffix
entryClassName = entryClassNames.get(chosen);
theClass.setName(entryClassName);
// add annotations to the class, to make it the entry class with the correct wrapper class
ModelUtil.addMIFAnnotation(theClass, "entry", "true");
ModelUtil.addMIFAnnotation(theClass, "wrapperClass", MakeITSMappingsAction.CDAWrapperClass);
}
else throw new MapperException("Selected no subclass of ClinicalDocument as entry class");
return entryClassName;
}
//---------------------------------------------------------------------------------------------------
// Adding association from OCL constraints
//---------------------------------------------------------------------------------------------------
/**
*
*/
private void applyOCLConstraints() throws MapperException
{
// loop over all cda classes that have been imported - avoiding concurrent mods, as we will add to the list
Vector<EClass> importedClasses = ModelUtil.getAllClasses(constrainedCDAPackage);
for (Iterator<EClass> it = importedClasses.iterator();it.hasNext();)
{
EClass importedClass = it.next();
EClass mdhtClass = getMDHTClass(importedClass); // every imported class has an MDHT class at this stage.
// find paths defined by OCL permissions on this class
Hashtable<String,String[]> permissions = getOCLPermissions(mdhtClass);
for (Enumeration<String[]> en = permissions.elements();en.hasMoreElements();)
{
String[] constraint = en.nextElement();
String path = constraint[0];
String qualifiedMDHTClassName = constraint[1];
EClass permittedMDHTClass = getNamedMDHTClass(qualifiedMDHTClassName);
EClass endClass = getImportedClass(permittedMDHTClass);
String constraintName = constraint[2];
if (endClass == null) trace("Cannot find constrained class '" + constraint[1] + "'");
else applyOCLConstraint(importedClass,path,endClass,constraintName);
}
}
}
/**
*
* @param qualifiedClassName
* @return
* @throws MapperException
*/
EClass getNamedMDHTClass(String qualifiedClassName) throws MapperException
{
String packageName = ModelUtil.getPackageName(qualifiedClassName);
EPackage mdhtPackage = mdhtPackages.get(packageName);
if (mdhtPackage == null) throw new MapperException("Cannot find MDHT package " + packageName);
String className = ModelUtil.getBareClassName(qualifiedClassName);
EClass permittedMDHTClass = (EClass)mdhtPackage.getEClassifier(className);
return permittedMDHTClass;
}
/**
*
* @param importedClass
* @param path
* @param endClass
* @throws MapperException
*/
private void applyOCLConstraint(EClass importedClass,String path,EClass endClass,String constraintName) throws MapperException
{
boolean fromFile = (constraintName.startsWith("from"));
// trace("From class " + importedClass.getName() + " OCL path " + path + " to class " + endClass.getName());
EClass[] MDHTClass = new EClass[20];
EReference[] MDHTRef = new EReference[20];
MDHTClass[0] = getMDHTClass(importedClass); // every imported class has an MDHT class at this stage.
// follow the association path, putting classes and EReferences in arrays
int depth = 0;
int unalteredAssociations = 0; // number of associations on the path down to the first one you will add
StringTokenizer steps = new StringTokenizer(path,".");
while (steps.hasMoreTokens())
{
String step = steps.nextToken();
EStructuralFeature feat = MDHTClass[depth].getEStructuralFeature(step);
if ((feat != null) && (feat instanceof EReference))
{
MDHTRef[depth] = (EReference) feat;
MDHTClass[depth + 1] = (EClass)MDHTRef[depth].getEType();
depth++; // final value of depth is path length
}
else throw new MapperException("Cannot follow OCL path '" + path + "' at step " + step);
}
if (fromFile) trace("Path check from " + importedClass.getName() + " via path " + path + " to " + endClass.getName() + ": " + MDHTClass[depth].getName());
// case when the last step of the path is from an ActRelationship or Participation
if ((depth > 1) && (isActRelationshipOrParticipation(MDHTClass[depth - 1])))
{
unalteredAssociations = depth - 2;
EClass branchClass = getImportedClass(MDHTClass[depth - 2]); // imported class which the new association is to be added to
EReference topAssoc = MDHTRef[depth -2];
String newTopAssocName = topAssoc.getName() + "_" + endClass.getName();
if (fromFile) {trace("Branch class: " + branchClass.getName() + "; New top association: " + newTopAssocName);}
/* only add a new association and class to this branch class if it has not been added already, for a different
* ancestor class at the beginning of the path. */
if (branchClass.getEStructuralFeature(newTopAssocName) == null)
{
EClass topModelClass = MDHTClass[depth - 1]; // class which the new class is to be modelled on
EReference bottomAssoc = MDHTRef[depth -1];
String newClassName = topModelClass.getName() + "_" + endClass.getName();
// if an ActRelationship or Participation of this name does not exist already, make it and link it to the end class
EClass newClass = (EClass)constrainedCDAPackage.getEClassifier(newClassName);
if (newClass == null)
{
// make the new ActRelationship or Participation class
newClass = addBareClass(constrainedCDAPackage, topModelClass, newClassName);
// pick up its attributes, and associations to data type classes
for (Iterator<EStructuralFeature> it = topModelClass.getEAllStructuralFeatures().iterator();it.hasNext();)
{
EStructuralFeature feature = it.next();
if (feature instanceof EAttribute) addAttribute(newClass, (EAttribute)feature);
else if (feature instanceof EReference)
{
EReference ref = (EReference)feature;
EClass target = (EClass)ref.getEType();
if (target.getEPackage().getName().equals("datatypes"))
addUnresolvedAssociation(newClass, ref, target);
}
}
// link in the end class by a new association, and annotate the association for use by the CDA wrapper class
String bottomAssocName = bottomAssoc.getName() + "_" + endClass.getName();
@SuppressWarnings("unused")
EReference newbottomRef = addAssociation(newClass, bottomAssoc, bottomAssocName, bottomAssoc.getName(),endClass);
if (fromFile) {trace("New class " + newClassName + "; New bottom association: " + bottomAssocName);}
}
// link the new or found class to the branch class by an association, and annotate the association for use by the CDA wrapper class
@SuppressWarnings("unused")
EReference newTopRef = addAssociation(branchClass, topAssoc, newTopAssocName, topAssoc.getName(), newClass);
}
boolean hasTheAssoc = (branchClass.getEStructuralFeature(newTopAssocName) != null);
if (fromFile) trace("Class " + branchClass.getName() + " has association " + newTopAssocName + "? " + hasTheAssoc);
}
// simple case of linking in the end class by a new association
else if (!isActRelationshipOrParticipation(MDHTClass[depth - 1]))
{
unalteredAssociations = depth - 1;
EClass branchClass = getImportedClass(MDHTClass[depth - 1]); // imported class which the new association is to be added to
EReference topAssoc = MDHTRef[depth -1];
String topAssocName = topAssoc.getName() + "_" + endClass.getName();
@SuppressWarnings("unused")
EReference newTopRef = addAssociation(branchClass, topAssoc, topAssocName, topAssoc.getName(),endClass);
}
/* if any associations were passed on to the path down to those added, rename them
* and annotate with their CDA name for the wrapper class. */
for (int d = 0; d < unalteredAssociations;d++)
{
EClass impClass = getImportedClass(MDHTClass[d]);
EReference ref = (EReference)impClass.getEStructuralFeature(MDHTRef[d].getName());
if ((ref != null) && (ModelUtil.getMIFAnnotation(ref, CDA_NAME)== null))
{
String cdaName = ref.getName();
String newName = cdaName + "_T";
ref.setName(newName);
ModelUtil.addMIFAnnotation(ref, CDA_NAME, cdaName);
}
}
}
//-----------------------------------------------------------------------------------------
// Attaching target classes to associations
//-----------------------------------------------------------------------------------------
/** attach target classes to associations */
private void resolveAllAssociations(EPackage thePackage) throws MapperException
{
for (Iterator<EClassifier> it = thePackage.getEClassifiers().iterator(); it.hasNext();)
{
EClass theClass = (EClass)it.next();
for (Iterator<EStructuralFeature> iu = theClass.getEStructuralFeatures().iterator(); iu.hasNext();)
{
EStructuralFeature feature = iu.next();
if (feature instanceof EReference)
{
EReference ref = (EReference)feature;
// check if the association needs to be resolved; and do so
String tClass = ModelUtil.getMIFAnnotation(ref, MDHTCLASS);
String tPackage = ModelUtil.getMIFAnnotation(ref, MDHTPACKAGE);
if ((tClass != null) && (tPackage != null))
{
ModelUtil.removeMIFAnnotation(ref, MDHTCLASS);
ModelUtil.removeMIFAnnotation(ref, MDHTPACKAGE);
EClass targetClass = null;
// names of imported classes are not changed in the datatypes package
if (tPackage.equals("datatypes"))
{
targetClass = (EClass)constrainedDatatypesPackage.getEClassifier(tClass);
}
// haven't yet worked out how to handle this case of target class 'ecore.EStringToStringMapEntry'
else if (tPackage.equals("ecore"))
{}
// in other packages, find the imported class whose name may have changed
else
{
String qualifiedName = tPackage + "." + tClass;
EClass mdhtClass = getNamedMDHTClass(qualifiedName);
targetClass = getImportedClass(mdhtClass);
}
if (targetClass == null) trace("Cannot find target class "
+ tClass + " for association " + ref.getName() + " of class " + theClass.getName());
else ref.setEType(targetClass);
}
}
}
}
}
//-----------------------------------------------------------------------------------------
// Removing CDA Associations that have templated versions
//-----------------------------------------------------------------------------------------
/**
* remove all CDA associations which have had templated associations made from them
* @param thePackage
* @throws MapperException
*/
private void prune_CDA_Associations(EPackage thePackage) throws MapperException
{
for (Iterator<EClassifier> it = thePackage.getEClassifiers().iterator(); it.hasNext();)
{
EClass theClass = (EClass)it.next();
// names of CDA associations that have been templated
Hashtable<String,String> renamedRefs = new Hashtable<String,String>();
// pre-stored list of refs, because we are going to modify it
Vector<EStructuralFeature> refs = new Vector<EStructuralFeature>();
// make table of refs to remove, and pre-store list of all refs
for (Iterator<EStructuralFeature> iu = theClass.getEStructuralFeatures().iterator(); iu.hasNext();)
{
EStructuralFeature feature = iu.next();
if (feature instanceof EReference)
{
refs.add(feature);
String cdaName = ModelUtil.getMIFAnnotation(feature, CDA_NAME);
if (cdaName != null) renamedRefs.put(cdaName, "1");
}
}
// remove refs
for (int i = 0; i < refs.size();i++)
{
EStructuralFeature feat = refs.get(i);
if ((feat instanceof EReference) && (renamedRefs.get(feat.getName()) != null)) theClass.getEStructuralFeatures().remove(feat);
}
}
}
//-----------------------------------------------------------------------------------------
// Move classes to packages depending on which section template they occur in
//-----------------------------------------------------------------------------------------
/**
* Move all constrained CDA classes to packages, depending on their section template
* @param entryClassName
*/
private void moveClassesToPackages(String entryClassName) throws MapperException
{
EClass entryClass = (EClass)constrainedCDAPackage.getEClassifier(entryClassName);
if (entryClass == null) throw new MapperException("Cannot find entry class " + entryClassName);
String entryPackageName = "cdaHeader";
moveClassToPackage(entryClass,entryPackageName);
moveChildClassesToPackages(entryClass,entryPackageName,entryPackageName);
}
/**
* recursive descent of the class tree, moving classes to new packages
* @param theClass
* @param packageName
* @param entryPackageName passed down the recursion unchanged
*/
private void moveChildClassesToPackages(EClass theClass,String packageName,String entryPackageName) throws MapperException
{
String nextPackageName = packageName;
for (Iterator<EStructuralFeature> it = theClass.getEStructuralFeatures().iterator(); it.hasNext();)
{
EStructuralFeature next = it.next();
if (next instanceof EReference)
{
EReference ref = (EReference)next;
// only allow one change of package name from the top, at a new section (not an observation, etc.)
if (packageName.equals(entryPackageName))
{
// if the association name is 'component_ProblemSection' make the package name 'problemSection'
if ((ref.getName().startsWith("component_")) && (ref.getName().length() > "component_T".length()))
nextPackageName = GenUtil.initialLowerCase(ref.getName().substring("component_".length()));
// do not make separate packages for these associations
if (nextPackageName.endsWith("EntriesOptional"))
nextPackageName = nextPackageName.substring(0,nextPackageName.length() - "EntriesOptional".length());
}
EClass nextClass = (EClass)ref.getEType();
// stop the recursion if this class has already been moved, or is in the datatypes package
if (moveClassToPackage(nextClass,nextPackageName)) moveChildClassesToPackages(nextClass,nextPackageName, entryPackageName);
}
}
}
/**
*
* @param theClass
* @param packageName
*/
private boolean moveClassToPackage(EClass theClass, String packageName) throws MapperException
{
boolean moved = false;
if (theClass == null) throw new MapperException("Moving null class to package " + packageName);
if (theClass.getEPackage() == null) throw new MapperException("Class '" + theClass.getName() + "' starts in no package, when moving to to package " + packageName);
// only move a class to a new package if it is in the initial CDA package it was made in (and not in the datatypes package)
if (theClass.getEPackage().equals(constrainedCDAPackage))
{
// put 'Text' class in data types package
if (theClass.getName().equals("Text")) packageName="datatypes";
EPackage newPackage = findOrMakePackage(packageName);
// adding the class to the new package should remove it from its old package
newPackage.getEClassifiers().add(theClass);
moved = true;
}
return moved;
}
/**
* find or make a package with a given name
* @param packageName
* @return the package
*/
private EPackage findOrMakePackage(String packageName)
{
EPackage thePackage = null;
for (Iterator<EPackage> it = topPackage.getESubpackages().iterator();it.hasNext();)
{
EPackage next = it.next();
if (next.getName().equals(packageName)) thePackage = next;
}
if (thePackage == null)
{
thePackage = EcoreFactory.eINSTANCE.createEPackage();
thePackage.setName(packageName);
topPackage.getESubpackages().add(thePackage);
}
return thePackage;
}
//-----------------------------------------------------------------------------------------
// Importing data types
//-----------------------------------------------------------------------------------------
/**
*
*/
private void importDatatypes(EPackage mdhtDatatypesPackage) throws MapperException
{
// first pass; add data type classes with EAttributes, and EReferences not yet resolved
for (Iterator<EClassifier> it = mdhtDatatypesPackage.getEClassifiers().iterator();it.hasNext();)
{
EClassifier next = it.next();
if (next instanceof EClass)
{
EClass dtClass = (EClass) next;
// no inheritance in data type classes yet
EClass newClass = addImportedClass(constrainedDatatypesPackage,dtClass,false);
// if this class has mixed data type, give it a textContent attribute
if (isMixedDataType(dtClass)) addTextAttribute(newClass);
}
}
// second pass; resolve target classes of EReferences, now that all data type classes have been imported
for (Iterator<EClassifier> it = constrainedDatatypesPackage.getEClassifiers().iterator();it.hasNext();)
{
EClass next = (EClass)it.next();
for (Iterator<EStructuralFeature> iu = next.getEStructuralFeatures().iterator();iu.hasNext();)
{
EStructuralFeature feature = iu.next();
if (feature instanceof EReference)
{
EReference ref = (EReference)feature;
String targetClassName = ModelUtil.getMIFAnnotation(ref, MDHTCLASS);
EClass targetClass = (EClass)constrainedDatatypesPackage.getEClassifier(targetClassName);
ref.setEType(targetClass);
}
}
}
// third pass not used ; assert the same inheritance as for MDHT data type classes
/*
for (Iterator<EClassifier> it = constrainedDatatypesPackage.getEClassifiers().iterator();it.hasNext();)
{
EClass next = (EClass)it.next();
EClass mdhtClass = getMDHTClass(next);
for (Iterator<EClass> iu = mdhtClass.getESuperTypes().iterator();iu.hasNext();)
{
EClass mdhtSuperClass = iu.next();
EClass dtSuperClass = getImportedClass(mdhtSuperClass);
if (dtSuperClass == null) throw new MapperException("Cannot find data type class '" + mdhtSuperClass.getName() + "'");
next.getESuperTypes().add(dtSuperClass);
}
}
*/
// finally ensure that the 'ANY' data type class has every EAttribute or EReference that any other data type class has
extendANYClass();
}
/**
*
* @param dtClass
* @return
*/
private boolean isMixedDataType(EClass dtClass)
{
boolean mixed = false;
EAnnotation ann = dtClass.getEAnnotation(EXTENDED_METADATA_SOURCE);
if ((ann != null) && ("mixed".equals(ann.getDetails().get("kind")))) mixed = true;
return mixed;
}
/**
*
* @param dtClass
* @return
*/
@SuppressWarnings("unused")
private boolean inheritsMixedDataType(EClass dtClass)
{
boolean inherits = false;
for (Iterator<EClass> it = dtClass.getEAllSuperTypes().iterator();it.hasNext();)
if (isMixedDataType(it.next())) inherits = true;
return inherits;
}
/**
* Ensure the ANY data type class has every attribute and association that any other class has,
* except the boring association of AD and EN
*/
private void extendANYClass() throws MapperException
{
EClass anyClass = (EClass)constrainedDatatypesPackage.getEClassifier("ANY");
if (anyClass == null) throw new MapperException("Cannot find 'ANY' datatype class");
// set up Hashtables to avoid duplicate associations and EReferences
Hashtable<String,EAttribute> allAttributes = new Hashtable<String,EAttribute>();
Hashtable<String,EReference> allReferences = new Hashtable<String,EReference>();
// pick up attributes and associations from all data type classes except AD and EN (boring long list, never used)
String[] avoidNames = {"ANY","AD","EN"};
for (Iterator<EClassifier> it = constrainedDatatypesPackage.getEClassifiers().iterator();it.hasNext();)
{
EClassifier next = it.next();
if ((next instanceof EClass) && (!GenUtil.inArray(next.getName(), avoidNames)))
{
EClass dtClass = (EClass) next;
for (Iterator<EStructuralFeature> iu = dtClass.getEStructuralFeatures().iterator();iu.hasNext();)
{
EStructuralFeature feature = iu.next();
if ((feature instanceof EReference) && (allReferences.get(feature.getName()) == null))
{
EReference ref = (EReference)feature;
addAssociation(anyClass, ref, ref.getName(), null, (EClass)ref.getEType());
allReferences.put(ref.getName(),ref);
}
else if ((feature instanceof EAttribute) && (allAttributes.get(feature.getName()) == null))
{
EAttribute att = (EAttribute)feature;
addAttribute(anyClass, att);
allAttributes.put(att.getName(), att);
}
}
}
}
}
/**
* The association 'typeId' points to a class 'InfrastructureRootTypeId' (a subclass of II, in MDHT)
* which has two strange attributes 'redefinedRoot' and 'redefinedExtension' deriving from the MDHT cda.ecore.
* Remove them, but make the fixed value of 'redefinedRoot' be a fixed value of 'root'
*/
private void sortTypeId() throws MapperException
{
EClass infraRoot = (EClass)constrainedCDAPackage.getEClassifier("InfrastructureRootTypeId");
if (infraRoot != null)
{
EStructuralFeature redefined = infraRoot.getEStructuralFeature("redefinedRoot");
EStructuralFeature root = infraRoot.getEStructuralFeature("root");
String fixedRoot = ModelUtil.getMIFAnnotation(redefined, "fixed value");
ModelUtil.addMIFAnnotation(root, "fixed value", fixedRoot);
infraRoot.getEStructuralFeatures().remove(redefined);
}
}
//-----------------------------------------------------------------------------------------
// Finding classes that have no association pointing to them
//-----------------------------------------------------------------------------------------
private void findIsolatedClasses(String resourceECoreLocation) throws MapperException
{
Hashtable<String,EClass> classesPointedTo = new Hashtable<String,EClass>();
Hashtable<String,String> classesInConstraints = new Hashtable<String,String>();
// initialise the data for the csv file to be written out
classesWithNoPermissions = new Vector<String[]>();
classesWithNoPermissions.add(classesWithNoPermissionsHeader);
String noPermissionsFileLocation = getCSVFileLocation(resourceECoreLocation, noPermissionsFileName);
// first pass; find all classes with an association pointing to them
for (Iterator<EClassifier> it = constrainedCDAPackage.getEClassifiers().iterator();it.hasNext();)
{
EClass next = (EClass)it.next();
EClass mdhtClass = this.getMDHTClass(next);
Hashtable<String,String[]> constraints = getOCLPermissions(mdhtClass);
for (Enumeration<String[]> en = constraints.elements();en.hasMoreElements();)
{
String[] constraint = en.nextElement();
classesInConstraints.put(constraint[1], "1");
}
for (Iterator<EStructuralFeature> iu = next.getEStructuralFeatures().iterator(); iu.hasNext();)
{
EStructuralFeature feature = iu.next();
if (feature instanceof EReference)
{
EClassifier target = ((EReference)feature).getEType();
if (target != null) classesPointedTo.put(target.getName(), (EClass)target);
}
}
}
// second pass; write out classes without any association pointing to them
int totalClasses = 0;
int isolatedClasses = 0;
for (Enumeration<String> ep = mdhtPackages.keys();ep.hasMoreElements();)
{
String packName = ep.nextElement();
EPackage mdhtPackage = mdhtPackages.get(packName);
if (GenUtil.inVector(packName, templatedPackages))
{
// trace("");
// trace("---- Unlinked classes in package " + packName);
for (Iterator<EClassifier> it = mdhtPackage.getEClassifiers().iterator();it.hasNext();)
{
EClassifier nc = it.next();
if (nc instanceof EClass)
{
EClass mdhtClass = (EClass)nc;
EClass next = getImportedClass(mdhtClass);
totalClasses++;
if ((next != null) && (classesPointedTo.get(next.getName()) == null))
{
String[] csvRow = new String[3];
String className = mdhtClass.getEPackage().getName() + "." + mdhtClass.getName();
String line = ("Not linked: " + className + " ");
csvRow[0] = className;
csvRow[1] = getTemplateId(className);
String supers = "";
for (Iterator<EClass> iu = mdhtClass.getESuperTypes().iterator();iu.hasNext();)
{
EClass superC = iu.next();
String pName = superC.getEPackage().getName() + "." + superC.getName();
line = line + "[" + pName + "]";
supers = supers + pName;
if (iu.hasNext()) supers = supers + "; ";
}
csvRow[2] = supers;
classesWithNoPermissions.add(csvRow);
// trace(line);
isolatedClasses++;
}
}
}
}
}
// write out the csv file
trace("writing csv file to " + noPermissionsFileLocation);
IFile csvFile = EclipseFileUtil.getFile(noPermissionsFileLocation);
EclipseFileUtil.writeCSVFile(classesWithNoPermissions, csvFile);
trace("Total classes: " + totalClasses + "; isolated classes: " + isolatedClasses);
trace("Classes in constraints: " + classesInConstraints.size());
trace("Classes linked to: " + classesPointedTo.size());
}
/**
*
*/
@SuppressWarnings("unused")
private void showSubclassRelations()
{
for (Enumeration<String> en = mdhtPackages.keys();en.hasMoreElements();)
{
String packageName = en.nextElement();
if (GenUtil.inVector(packageName, templatedPackages))
{
trace("----- Classes in package " + packageName);
Hashtable<String,String> referencedPackages = new Hashtable<String,String>();
EPackage mdhtPackage = mdhtPackages.get(packageName);
for (Iterator<EClassifier> it = mdhtPackage.getEClassifiers().iterator();it.hasNext();)
{
EClassifier next = it.next();
if (next instanceof EClass)
{
String line = "Class " + next.getName() + ": ";
EClass theClass = (EClass)next;
for (Iterator<EClass> iu = theClass.getESuperTypes().iterator();iu.hasNext();)
{
EClass superC = iu.next();
String pName = superC.getEPackage().getName();
referencedPackages.put(pName, "1");
line = line + "[" + pName + "," + superC.getName() + "]";
}
trace(line);
}
}
String packageSummary = "Package " + packageName + " depends on packages ";
for (Enumeration<String> eg = referencedPackages.keys();eg.hasMoreElements();)
packageSummary = packageSummary + eg.nextElement() + ",";
trace(packageSummary);
}
}
}
//-----------------------------------------------------------------------------------------
// Filtering of included sections from a csv file
//-----------------------------------------------------------------------------------------
/**
*
* @param EcoreLocation
*/
private void filterSections(String EcoreLocation)
{
// read a csv file of required sections
String sectionFilterFileLocation = "";
Vector<String> sectionFilterFileLines = new Vector<String>();
try {
sectionFilterFileLocation = getCSVFileLocation(EcoreLocation, sectionFilterFileName);
sectionFilterFileLines = FileUtil.textLines(sectionFilterFileLocation);
filterSections(sectionFilterFileLines);
}
catch (Exception ex) {WorkBenchUtil.showMessage("Warning","Error reading section filter file at "
+ sectionFilterFileLocation + "': " + ex.getMessage() + "; so all sections are included.");}
}
/**
*
* @param sectionFilterFileLines
* @throws MapperException
*/
private void filterSections(Vector<String> sectionFilterFileLines) throws MapperException
{
Hashtable<String,EClass> owningClasses = getOwningClasses(sectionFilterFileLines);
trace("Owning classes: " + owningClasses.size());
for(Enumeration<String> en = owningClasses.keys();en.hasMoreElements();)
{
String owningClassName = en.nextElement();
EClass theClass = owningClasses.get(owningClassName);
filterClass(owningClassName,theClass,sectionFilterFileLines);
}
}
/**
*
* @param owningClassName
* @param theClass
* @param sectionFilterFileLines
*/
private void filterClass(String owningClassName,EClass theClass,Vector<String> sectionFilterFileLines) throws MapperException
{
// empty list of EAttributes and EReferences for the class
EList<EStructuralFeature> newFeatures = new BasicEList<EStructuralFeature>();
// build up the list, including all EAttributes and only some EReferences
for (Iterator<EStructuralFeature> it = theClass.getEStructuralFeatures().iterator(); it.hasNext();)
{
EStructuralFeature feature = it.next();
if (feature instanceof EAttribute) {newFeatures.add(feature);}
else if (feature instanceof EReference)
{
EReference ref = (EReference)feature;
EClass target = (EClass)ref.getEType();
EClass mdhtClass = getMDHTClass(target);
/* if there is an equivalent MDHT class, whose name has not been altered on import, do not filter */
if ((mdhtClass != null) && (target.getName().equals(mdhtClass.getName())))
{newFeatures.add(feature);}
else
{
boolean retained = false;
/* find child nodes in templated packages; one of these must match in template ids
* for the association to be retained. */
for (Iterator<EStructuralFeature> iu = target.getEStructuralFeatures().iterator();iu.hasNext();)
{
EStructuralFeature f = iu.next();
if (f instanceof EReference)
{
EReference r = (EReference)f;
EClass sect = (EClass)r.getEType();
if (matchesTemplates(sect,owningClassName,sectionFilterFileLines)) retained = true;
}
}
if (retained) newFeatures.add(feature);
}
}
}
// reset the whole list of EAttributes and EReferences for the class
theClass.eSet(EcorePackage.eINSTANCE.getEClass_EStructuralFeatures(), newFeatures);
}
/**
*
* @param sect
* @param parentClassName
* @param sectionFilterFileLines
* @return true if the templates on the class match those in any line of the file, with the correct parent class name
*/
private boolean matchesTemplates(EClass sect,String parentClassName,Vector<String> sectionFilterFileLines)
{
boolean matches = false;
// try to match the section against any file line with the required class name
for (int i = 1; i < sectionFilterFileLines.size();i++)
{
StringTokenizer st = new StringTokenizer(sectionFilterFileLines.get(i),",");
String cName = st.nextToken();
if (parentClassName.equals(cName))
{
String templates = st.nextToken();
if (matchesTemplates(sect,templates)) matches = true;
}
}
// message(parentClassName + "; " + sect.getName() + ": " + matches);
return matches;
}
/**
*
* @param sect a class in the imported Ecore class model
* @param templates String of template ids, separated by ';' and ' '
* @return true if the set of template ids on the annotations of the class exactly matches
* the templates in String 'templates'
*/
private boolean matchesTemplates(EClass sect,String templates)
{
boolean matches = false;
EAnnotation ann = sect.getEAnnotation(ModelUtil.mifNamespaceURI());
// no match if the class has no template annotations
if (ann != null)
{
Hashtable<String,String> sectionTemplates = new Hashtable<String,String>();
// collect all template ids from the class annotations
EMap<String,String> details = ann.getDetails();
for (Iterator<String> it = details.keySet().iterator();it.hasNext();)
{
String key = it.next();
// template annotations have keys 'template' , 'template_1', etc
if (key.startsWith("template"))
{
String templateId = details.get(key);
sectionTemplates.put(templateId, "1");
}
}
// can only match if there is a matching number of template ids, separated by ';' and ' '
StringTokenizer ids = new StringTokenizer(templates,"; ");
if (ids.countTokens() == sectionTemplates.size())
{
matches = true;
while (ids.hasMoreTokens())
if (sectionTemplates.get(ids.nextToken()) == null) matches = false;
}
}
return matches;
}
/**
*
* @param sectionFilterFileLines
* @return a Hashtable of all the classes mentioned in the first column of the csv file
* @throws MapperException
*/
private Hashtable<String,EClass> getOwningClasses(Vector<String> sectionFilterFileLines) throws MapperException
{
Hashtable<String,EClass> owningClasses = new Hashtable<String,EClass>();
for (int i = 1; i < sectionFilterFileLines.size();i++)
{
String line = sectionFilterFileLines.get(i);
StringTokenizer st = new StringTokenizer(line,",");
if (st.countTokens() != 3) throw new MapperException("Invalid line in csv file: '" + line + "'");
String className = st.nextToken();
EClass mdhtClass = getNamedMDHTClass(className);
if (mdhtClass == null) throw new MapperException("Cannot find MDHT class: '" + className + "'");
EClass importedClass = this.getImportedClass(mdhtClass);
if (importedClass == null) throw new MapperException("Cannot find imported class: '" + className + "'");
owningClasses.put(className, importedClass);
}
return owningClasses;
}
//-----------------------------------------------------------------------------------------
// Plumbing and trivia
//-----------------------------------------------------------------------------------------
/**
*
* @param theClass
*
* @return true if the class is an ActRelationship clone or a Participation clone
*/
private boolean isActRelationshipOrParticipation(EClass mdhtClass)
{
boolean isBranch = false;
for (Iterator<EClass> it = mdhtClass.getESuperTypes().iterator();it.hasNext();)
{
String superClassName = it.next().getName();
if (superClassName.equals("ActRelationship")) isBranch = true;
if (superClassName.equals("Participation")) isBranch = true;
}
return isBranch;
}
@Override
public void setActivePart(IAction action, IWorkbenchPart targetPart) {
this.targetPart = targetPart;
}
@Override
public void selectionChanged(IAction action, ISelection selection) {
this.selection = selection;
}
/**
*
* @return either the ecore file which was right-clicked, or
* if a mapper file was right-clicked, an ecore file chosen by the user
* @throws MapperException
*/
protected EPackage getECoreModel() throws MapperException
{
String resourceLocation = FileUtil.removeFilePrefix(getFilePath(selection));
// if the user right-clicked an ecore file, return it
if (resourceLocation.endsWith(".ecore")) return FileUtil.getClassModel(resourceLocation);
// if the user right-clicked a mapper file, ask for the location of an ecore file
if (resourceLocation.endsWith(".mapper"))
{
String[] exts = {"*.ecore"};
String loc = FileUtil.getFilePathFromUser(targetPart, exts, "Select MDHT Ecore model", false);
resourceLocation = FileUtil.forwardSlashForm(loc);
}
if (resourceLocation != null) return FileUtil.getClassModel(resourceLocation);
// if the user cancelled
return null;
}
/**
* @param selection the object the user right-clicked to get this action
* @return the file path to it
*/
public String getFilePath(ISelection selection)
{
String path = "not found";
if (selection instanceof IStructuredSelection)
{
Object el = ((IStructuredSelection)selection).getFirstElement();
if (el instanceof IFile)
{
IFile file = (IFile)el;
path = file.getLocationURI().toString();
}
}
return path;
}
protected void message(String s)
{
System.out.println(s);
}
protected void trace(String s)
{
if (tracing) System.out.println(s);
}
}