package com.openMap1.mapper.views;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.Vector;
import java.io.IOException;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.graphics.Image;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.dialogs.SaveAsDialog;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EModelElement;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.ENamedElement;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
import org.eclipse.emf.edit.ui.util.EditUIUtil;
import org.eclipse.xsd.XSDNamedComponent;
import com.openMap1.mapper.presentation.MapperEditor;
import com.openMap1.mapper.presentation.QueryEditor;
import com.openMap1.mapper.presentation.MapperActionBarContributor;
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.util.Timer;
import com.openMap1.mapper.mapping.MDLBase;
import com.openMap1.mapper.mapping.objectMapping;
import com.openMap1.mapper.util.messageChannel;
import com.openMap1.mapper.util.SystemMessageChannel;
import com.openMap1.mapper.actions.CopySimplificationsAction;
import com.openMap1.mapper.actions.ImportSimplificationsAction;
import com.openMap1.mapper.actions.MakeEcoreMappingsAction;
import com.openMap1.mapper.actions.MergeModelsAction;
import com.openMap1.mapper.actions.PasteSimplificationsAction;
import com.openMap1.mapper.actions.RemoveSimplificationsAction;
import com.openMap1.mapper.core.MapperException;
import com.openMap1.mapper.MappedStructure;
import com.openMap1.mapper.ParameterClass;
/**
* Shows a tree-structured view of the mapped class model, and the mappings.
*
* Usually the tree structure is the class hierarchy.
* For HL7 RMIM applications, it is the RMIM tree.
*
* @author robert
*
*/
public class ClassModelView extends ViewPart implements ISelectionChangedListener{
private boolean tracing = false;
private boolean writeMappingsColumn = true;
private Timer timer;
private TreeViewer viewer;
private ArrayList<EObject> modelRoot = new ArrayList<EObject>();
private ArrayList<LabelledEClass> rmimRoot = new ArrayList<LabelledEClass>();
/* record the class tree children of each named class in the model, including 'Object'
* These are the classes which have that class as 'main' superclass - only for purposes
* of tree display. Multiple inheritance of attributes and associations is not ignored. */
private Hashtable<String,ArrayList<EClass>> childVanillaClasses;
/** the package which is the root of the model */
public EPackage ecoreRoot() {return ecoreRoot;}
private EPackage ecoreRoot;
private URI classModelURI = null;
/** the URI address of the stored class model */
public URI classModelURI() {return classModelURI;}
private URI mappingSetURI = null;
/** the URI address of the stored mapping set, if the view is currently linked to one */
public URI mappingSetURI() {return mappingSetURI;}
public void setMappingSetURI(URI uri) {mappingSetURI = uri; queryURI = null;}
private URI queryURI = null;
/** the URI address of the stored query, if the view is currently linked to one */
public URI queryURI() {return queryURI;}
public void setQueryURI(URI uri) {queryURI = uri; mappingSetURI = null;}
private MapperEditor mapperEditor = null;
public MapperEditor mapperEditor() {return mapperEditor;}
private MDLBase mdlBase = null;
public LabelledEClass topLabelledEClass() {return topLabelledEClass;}
private LabelledEClass topLabelledEClass;
private Hashtable<String,Vector<String>> parentTable;
//---------------------------------------------------------------------------------------------
// constructor and initialisation
//---------------------------------------------------------------------------------------------
public ClassModelView() {
}
/**
* Callback to create the viewer and initialize it.
*/
public void createPartControl(Composite parent) {
trace("creating part control");
viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
// this column shows the tree , and gets its label provider from the viewer
TreeViewerColumn tv1 = new TreeViewerColumn(viewer,SWT.LEFT);
tv1.getColumn().setWidth(300);
tv1.getColumn().setText("Class");
TreeViewerColumn tv2 = null;
if (writeMappingsColumn)
{
tv2 = new TreeViewerColumn(viewer,SWT.LEFT);
tv2.getColumn().setWidth(300);
tv2.getColumn().setText("Mappings/Templates");
tv2.setLabelProvider(new MappingLabelProvider());
}
// set up this part to be a selection provider for editors and other views
getSite().setSelectionProvider(viewer);
//make this view listen to its own selections, to remember what was selected
viewer.addPostSelectionChangedListener(this);
if (classModelURI != null) try{
// set up the class model and connect the viewer to it
EObject root = FileUtil.getEMFModelRoot(classModelURI);
if (!(root instanceof EPackage)) throw new MapperException("Class model root is not an EPackage");
ecoreRoot = (EPackage)root;
// set up the appropriate content provider and label provider for the class model
setupViewer((EPackage)root, classModelURI);
/* if at the previous shutdown, the class model view was connected to a mapper editor,
* re-connect it to the same one. */
if (mappingSetURI != null)
{
MapperEditor me = WorkBenchUtil.getMapperEditor(mappingSetURI.toString());
if ((me != null) && (root != null))
viewer.addPostSelectionChangedListener((MapperActionBarContributor)me.getActionBarContributor());
}
/* if at the previous shutdown, the class model view was connected to a query editor,
* re-connect it to the same one. */
if (queryURI != null)
{
QueryEditor qe = WorkBenchUtil.getQueryEditor(queryURI.toString());
if ((qe != null) && (root != null))
connectToQueryEditor(qe);
}
}
catch (Exception ex)
{
System.out.println("Failed to open class model for class model view, at location '"
+ classModelURI.toString() + "'");
}
// make the menu actions
makeActions();
contributeToActionBars();
}
/* connect to the Attribute and Association Views, if you can;
* 'false' means do not force creation; Eclipse objects to the potential recursion */
private void connectToFeatureViews()
{
AttributeView atv = WorkBenchUtil.getAttributeView(false);
if (atv != null) connectToAttributeView(atv);
AssociationView asv = WorkBenchUtil.getAssociationView(false);
if (asv != null) connectToAssociationView(asv);
}
private void setupViewer(EPackage ecoreRoot, URI uri)
{
trace("Set up viewer");
// viewing a class model as an RMIM tree
if (isRMIMRoot(ecoreRoot))
{
setRMIMClassModel(ecoreRoot, uri);
setupRMIMViewer(viewer);
}
// viewing a class model grouped by packages
else if (groupInPackages(ecoreRoot))
{
setPackagedClassModel(ecoreRoot,uri);
setupPackagedViewer(viewer);
}
// viewing a class model as an inheritance hierarchy
else
{
setVanillaClassModel(ecoreRoot,uri);
setupVanillaViewer(viewer);
}
// connect to the Associations and Attributes views
connectToFeatureViews();
}
/**
* Set up the TreeViewer with the appropriate content provider and label provider,
* for a vanilla class model; the tree structure follows the inheritance structure
* @param viewer
*/
private void setupVanillaViewer(TreeViewer viewer)
{
trace("set up vanilla viewer");
viewer.setContentProvider(new ClassModelViewContentProvider());
viewer.setLabelProvider(new ClassModelViewLabelProvider());
viewer.getTree().setHeaderVisible(true);
viewer.getTree().setLinesVisible(true);
}
/**
* Set up the TreeViewer with the appropriate content provider and label provider,
* for a class model to be viewed by package
* @param viewer
*/
private void setupPackagedViewer(TreeViewer viewer)
{
trace("set up packaged viewer");
viewer.setContentProvider(new PackagedViewContentProvider());
viewer.setLabelProvider(new PackagedViewLabelProvider());
viewer.getTree().setHeaderVisible(true);
viewer.getTree().setLinesVisible(true);
}
/**
* Set up the TreeViewer with the appropriate content provider and label provider,
* for an RMIM class model; the tree structure follows the RMIM associations
* @param viewer
*/
private void setupRMIMViewer(TreeViewer viewer)
{
trace("set up RMIM viewer");
viewer.setContentProvider(new RMIMViewContentProvider());
viewer.setLabelProvider(new RMIMViewLabelProvider());
viewer.getTree().setHeaderVisible(true);
viewer.getTree().setLinesVisible(true);
}
/**
*
* @param root
* @return true if the class model is to be shown in an RMIM hierarchy view
*/
public static boolean isRMIMRoot(EObject root)
{
if ((root != null) && (root instanceof EPackage))
return (ModelUtil.getMIFAnnotation((EPackage)root,"RMIM") != null);
return false;
}
/* replaced by calls to ModelUtil.getMIFAnnotation
private String getAnnotation(EModelElement eo, String key)
{
String mifNamespaceURI = "urn:hl7-org:v3/mif2";
String value = null;
EAnnotation ann = eo.getEAnnotation(mifNamespaceURI);
if (ann != null) value = ann.getDetails().get(key);
return value;
}
*/
/**
*
* @param eo an EMF model object - root of the package tree
* @return true if the classes are to be viewed grouped by packages,
* rather than by an inheritacne hierarchy
*/
private Boolean groupInPackages(EModelElement eo)
{
boolean group = false;
EAnnotation ann = eo.getEAnnotation("urn:com.openMap1.mapper");
String value = null;
if (ann != null) value = ann.getDetails().get("viewByPackages");
if (value != null)group = (value.equals("true"));
return group;
}
/**
* do the startup activities when this view is associated with a mapping set
* and its active mapper editor
* @param MapperEditor me the active mapper editor
* @param EObject umlRoot the root of the UML model being shown in this view
* @param String path the path to the UML model file, for use in the view title
*/
public void initiateForMapperEditor(MapperEditor me, EObject root, String classModelURIString)
{
trace("initiate for mapper editor");
if ((root != null) && (root instanceof EPackage))
{
ecoreRoot = (EPackage)root;
URI uri = URI.createURI(classModelURIString);
setupViewer(ecoreRoot,uri);
setContentDescriptionFromURL(classModelURIString);
setMappingSetURI(EditUIUtil.getURI(me.getEditorInput()));
// hook up the mapper editor so it can listen to selections in this class model view
viewer.addPostSelectionChangedListener((MapperActionBarContributor)me.getActionBarContributor());
// record the editor
mapperEditor = me;
mdlBase = null;
viewer.refresh();
}
else if (root == null)
{
//System.out.println("Class Model Root is null in ClassModelView.initiateForMapperEditor" );
}
else if (!(root instanceof EPackage))
{
System.out.println("Class Model Root is not an EPackage, but a "
+ root.eClass().getName());
return;
}
}
/**
* do the startup activities when this view is associated with a query
* and its active query editor
* @param QueryEditor qe the active query editor
* @param EObject umlRoot the root of the UML model being shown in this view
* @param String path the path to the query file, for use in the view title
*/
public void initiateForQueryEditor(QueryEditor qe, EObject root, String classModelURIString)
{
trace("initiate for query editor");
if (timer == null) timer = new Timer("Class model view");
timer.start(Timer.COMPILE);
if ((root != null) && (root instanceof EPackage))
{
this.ecoreRoot = (EPackage)root;
URI uri = URI.createURI(classModelURIString);
setupViewer(ecoreRoot,uri);
setContentDescriptionFromURL(classModelURIString);
setQueryURI(EditUIUtil.getURI(qe.getEditorInput()));
connectToQueryEditor(qe);
mapperEditor = null; // sever any previous link to a mapping set
mdlBase = null;
viewer.refresh();
}
else if (root == null)
{
System.out.println("Class Model Root is null in ClassModelView.initiateForQueryEditor" );
}
else if (!(root instanceof EPackage))
{
System.out.println("Ecore Class Model Root is not an EPackage, but a "
+ root.eClass().getName());
return;
}
}
/**
* hook up the query editor so it can listen to selections in this class model view
* @param qe
*/
public void connectToQueryEditor(QueryEditor qe)
{
viewer.addPostSelectionChangedListener(qe);
mapperEditor = null; // sever any previous link to a mapping set
}
/**
* hook up the Attribute View so it can listen to selections in this class model view
* @param qe
*/
public void connectToAttributeView(AttributeView av)
{viewer.addPostSelectionChangedListener(av);}
/**
* hook up the Association View so it can listen to selections in this class model view
* @param qe
*/
public void connectToAssociationView(AssociationView av)
{viewer.addPostSelectionChangedListener(av);}
/**
* Strip out a file name from a URL, and use it as the description of the view
*/
public void setContentDescriptionFromURL(String url)
{super.setContentDescription(FileUtil.getFileName(url));}
//---------------------------------------------------------------------------------------------
// Setting up a class model to be viewed in packages for the content provider and label provider
//---------------------------------------------------------------------------------------------
/**
* record a UML class model in the form used by the ViewContentProvider -
* in the Hashtable of 'main' subclasses.
* This method assumes the class model is in a single package
*
* @param root the root object of the UML model, assumed to be an EPackage
*/
public void setPackagedClassModel(EPackage root, URI classModelURI)
{
trace("Set packaged class model");
this.classModelURI = classModelURI;
modelRoot = new ArrayList<EObject>();
modelRoot.add(root);
/* ensure the viewer is expecting the right kind of content provider and label provider,
* and give it the root of the tree. */
setupPackagedViewer(viewer);
viewer.setInput(modelRoot);
}
//---------------------------------------------------------------------------------------------
// content provider and label provider for a class model to be viewed by package
//---------------------------------------------------------------------------------------------
/*
* The provider (= adapter) which adapts the model (in this case an EMF model)
* to the viewer (in this case a TreeViewer).
* There are two parts - a content provider which provides the tree structure
* and a label provider which provides the icons and text
*/
class PackagedViewContentProvider implements IStructuredContentProvider,
ITreeContentProvider {
// called by viewer.setInput(Object) but need do nothing
public void inputChanged(Viewer v, Object oldInput, Object newInput)
{}
public void dispose() {trace("packaged view dispose");}
public Object[] getElements(Object parent) {
// the only child of modelroot is the EPackage object
if (parent.equals(modelRoot)) {
return arrayOf(modelRoot);
}
// for all other objects (including the package) use getChildren
return getChildren(parent);
}
/**
* child EObjects:
* For a package, the first children are its sub-packages; next children are the classes in it
* For any class, the there are no children
* For any other EObject, get the EObject child nodes
*/
public Object [] getChildren(Object parent) {
ArrayList<EObject> result = new ArrayList<EObject>();
if (parent instanceof EPackage)
{
EPackage pack = (EPackage)parent;
// sub-packages are the first children
for (Iterator<EPackage> ip = pack.getESubpackages().iterator();ip.hasNext();)
result.add(ip.next());
// next follow classes in the package
for (Iterator<EClassifier> ic = pack.getEClassifiers().iterator();ic.hasNext();)
{
EClassifier ec = ic.next();
if (ec instanceof EClass) result.add(ec);
}
}
return arrayOf(result);
}
/**
* get the parent object of any object in the tree. For EClasses, this is the
* its containing package. for packages, the parent is its superpackage
*/
public Object getParent(Object child) {
EObject parent = null;
if (child instanceof EClass)
{
parent = ((EClass)child).getEPackage();
}
else if (child instanceof EPackage)
{
parent = ((EPackage)child).getESuperPackage();
if (parent == null) parent = ecoreRoot;
}
return parent;
}
public boolean hasChildren(Object parent) {
return(getChildren(parent).length > 0);
}
} // end of class PackagedViewContentProvider
class PackagedViewLabelProvider extends LabelProvider implements ITableLabelProvider{
public String getText(Object obj) {
if (obj instanceof EObject)
return getTextLabel((EObject)obj);
return "not an EObject";
}
public String getColumnText(Object obj, int columnIndex) {
switch(columnIndex){
case 0: return getText(obj);
case 1: {
if (obj instanceof EClass)
return getObjectMappingText((EClass)obj);
}
}
return ("");
}
public Image getColumnImage(Object obj, int columnIndex) {
switch(columnIndex){
case 0: return getImage(obj);
case 1: return null;
}
return null;
}
public Image getImage(Object obj) {
Image im = null;
// default 'Folder' image if type is not recognised (eg packages)
String imageKey = ISharedImages.IMG_OBJ_FOLDER;
im = PlatformUI.getWorkbench().getSharedImages().getImage(imageKey);
// images for Ecore classes
if (obj instanceof EClass)
{im = FileUtil.getImage("Class");}
return im;
}
private String getTextLabel(EObject eo)
{
if (eo instanceof ENamedElement) // for named parts of Ecore models: classes and packages
{
return ((ENamedElement)eo).getName();
}
else return eo.eClass().getName();
}
}
//---------------------------------------------------------------------------------------------
// Setting up a vanilla class model for the content provider and label provider
//---------------------------------------------------------------------------------------------
/**
* record a UML class model in the form used by the ViewContentProvider -
* in the Hashtable of 'main' subclasses.
* This method assumes the class model is in a single package
*
* @param root the root object of the UML model, assumed to be an EPackage
*/
public void setVanillaClassModel(EPackage root, URI classModelURI)
{
trace("Set vanilla class model");
this.classModelURI = classModelURI;
childVanillaClasses = new Hashtable<String,ArrayList<EClass>>();
modelRoot = new ArrayList<EObject>();
modelRoot.add(root);
addClasses(root);
/* ensure the viewer is expecting the right kind of content provider and label provider,
* and give it the root of the tree. */
setupVanillaViewer(viewer);
viewer.setInput(modelRoot);
}
/**
* add the classes in a package to the model, and recursively add
* the classes in all its sub-packages
* @param aPackage
*/
private void addClasses(EPackage aPackage)
{
// add the classes in this package
for (Iterator<EObject> it = aPackage.eContents().iterator(); it.hasNext();)
{
EObject child = it.next();
// record each Class under its superclasses, or under class Object
if (child instanceof EClass) recordVanillaClass((EClass)child);
// ignore other things in the package - imports etc.
else {}
}
//do the same recursively for its sub-packages
for (Iterator<EPackage> it = aPackage.getESubpackages().iterator();it.hasNext();)
addClasses(it.next());
}
/**
* record each class under its first superclass, or under class 'Object' if it has none
* @param c
*/
private void recordVanillaClass(EClass c)
{
if (c.getESuperTypes().size() == 0)
{
addVanillaClassToSubClasses("Object",c);
}
// if the class has more than one superclass, choose the main superclass that it inherits most from
else addVanillaClassToSubClasses(getMainSuperClass(c).getName(),c);
}
/**
* When an EClass has more than one superclass, the 'main' superclass,
* which will be its parent in the tree diagram of the mapped class model, is the
* superclass with the largest number of features (i.e from which which it inherits most features).
* When there is a tie for number of features, the class whose name is lexically first wins.
* @param c the EClass whose main superclass is sought
* @return the main EClass, or null if there are no superclasses
*/
private EClass getMainSuperClass(EClass c)
{
EClass best = null;
for (Iterator<EClass> it = c.getESuperTypes().iterator();it.hasNext();)
{
EClass current = it.next();
if (best == null) {best = current;}
else if (current.getFeatureCount() > best.getFeatureCount()) {best = current;}
else if ((current.getFeatureCount() == best.getFeatureCount())
&&(current.getName().compareTo(best.getName()) < 0)) {best = current;}
// compareTo: The result is a negative integer if this String object lexicographically precedes the argument string
}
return best;
}
/**
* record a class under one of its superclasses
* @param superClass
* @param c
*/
private void addVanillaClassToSubClasses(String superClass,EClass c)
{
ArrayList<EClass> subs = childVanillaClasses.get(superClass);
if (subs == null) subs = new ArrayList<EClass>();
ArrayList<EClass> newSubs = addAClass(subs,c);
childVanillaClasses.put(superClass,newSubs);
}
/**
* @param subs existing list of subclasses
* @param c new subclass
* @return enlarged list of subclasses, preserving alphabetical order of class names
*/
private ArrayList<EClass> addAClass(ArrayList<EClass> subs, EClass c)
{
ArrayList<EClass> newSubs = new ArrayList<EClass>();
String cName = c.getName();
boolean placed = false; // new class has not yet been added
for (Iterator<EClass> it = subs.iterator();it.hasNext();)
{
EClass next = it.next();
// if the new class comes before the current class and has not been added, add it once
if ((next.getName().compareTo(cName) > 0) && (!placed))
{
newSubs.add(c);
placed = true;
}
// add the current class in any case
newSubs.add(next);
}
// if the new class is last, add it
if (!placed) newSubs.add(c);
return newSubs;
}
//---------------------------------------------------------------------------------------------
// content provider and label provider for a vanilla class model
//---------------------------------------------------------------------------------------------
/*
* The provider (= adapter) which adapts the model (in this case an EMF model)
* to the viewer (in this case a TreeViewer).
* There are two parts - a content provider which provides the tree structure
* and a label provider which provides the icons and text
*/
class ClassModelViewContentProvider implements IStructuredContentProvider,
ITreeContentProvider {
// called by viewer.setInput(Object) but need do nothing
public void inputChanged(Viewer v, Object oldInput, Object newInput)
{}
public void dispose() {trace("vanilla dispose");}
public Object[] getElements(Object parent) {
// the only child of modelroot is the EPackage object
if (parent.equals(modelRoot)) {
return arrayOf(modelRoot);
}
// for all other objects (including the package) use getChildren
return getChildren(parent);
}
/**
* child EObjects:
* For the package, the children are the classes with no superclass
* For any class, the children are (a) the subclasses, (b) the association markers (c) the properties
* For any other EObject, get the EObject child nodes
*/
public Object [] getChildren(Object parent) {
ArrayList<EObject> result = new ArrayList<EObject>();
String className = "NoClass"; // there will be no entry in subClasses map for this class name
// direct children of the package are the subclasses of 'Object' = classes with no superclass
if (parent instanceof EPackage) {className = "Object";}
else if (parent instanceof EClass) {className = ((EClass)parent).getName();}
if (childVanillaClasses.get(className) != null)
for (Iterator<EClass> it = childVanillaClasses.get(className).iterator(); it.hasNext();)
{result.add(it.next());}
return arrayOf(result);
}
/**
* get the parent object of any object in the tree. For EClasses, this is the
* 'main' superclass, or the package if there are no superclasses
*/
public Object getParent(Object child) {
EObject parent = null;
if (child instanceof EClass)
{
parent = getMainSuperClass((EClass)child);
if (parent == null) parent = ecoreRoot;
}
return parent;
}
public boolean hasChildren(Object parent) {
return(getChildren(parent).length > 0);
}
} // end of class ViewContentProvider
private EObject[] arrayOf (ArrayList<EObject> aList) {return (EObject[])aList.toArray(new EObject[aList.size()]);}
class ClassModelViewLabelProvider extends LabelProvider implements ITableLabelProvider{
public String getText(Object obj) {
if (obj instanceof EObject)
return getTextLabel((EObject)obj);
return "not an EObject";
}
public String getColumnText(Object obj, int columnIndex) {
switch(columnIndex){
case 0: return getText(obj);
case 1: {
if (obj instanceof EClass)
return getObjectMappingText((EClass)obj);
}
}
return ("");
}
public Image getColumnImage(Object obj, int columnIndex) {
switch(columnIndex){
case 0: return getImage(obj);
case 1: return null;
}
return null;
}
public Image getImage(Object obj) {
Image im = null;
// default 'Folder' image if type is not recognised (eg packages)
String imageKey = ISharedImages.IMG_OBJ_FOLDER;
im = PlatformUI.getWorkbench().getSharedImages().getImage(imageKey);
// images for Ecore classes
if (obj instanceof EClass)
{im = FileUtil.getImage("Class");}
return im;
}
private String getTextLabel(EObject eo)
{
if (eo instanceof ENamedElement) // for named parts of Ecore models: classes and packages
{
return ((ENamedElement)eo).getName();
}
else if (eo instanceof XSDNamedComponent) // for XML schemas
{
return eo.eClass().getName() + ": " + ((XSDNamedComponent)eo).getName();
}
else return eo.eClass().getName();
}
}
//---------------------------------------------------------------------------------------------
// setup for an RMIM class model
//---------------------------------------------------------------------------------------------
/**
* find the LabelledEClass which is the root of the class tree of the RMIM
*/
public static LabelledEClass getRootLabelledEClass(EPackage topPackage)
{
LabelledEClass rootLabelledEClass = null;
/* find the one EClass marked as the entry class;
* iterate over all nested packages to find it. */
for (Iterator<EPackage> ip = topPackage.getESubpackages().iterator();ip.hasNext();)
{
EPackage rmimPackage = ip.next();
// Usually there is on package inside the top package. Iterate over classes in any packages found
for (Iterator<EClassifier> ic = rmimPackage.getEClassifiers().iterator(); ic.hasNext();)
{
EClassifier ec = ic.next();
if ((ec instanceof EClass) && (ModelUtil.getMIFAnnotation(ec, "entry") != null))
{
rootLabelledEClass = new LabelledEClass((EClass)ec, null, null);
}
}
}
/* if the class is not found in any packages below the top package (possibly because there are no such packages)
* look for the class directly in the top package. */
if (rootLabelledEClass == null)
{
// iterate over classes in the top package
for (Iterator<EClassifier> ic = topPackage.getEClassifiers().iterator(); ic.hasNext();)
{
EClassifier ec = ic.next();
if ((ec instanceof EClass) && (ModelUtil.getMIFAnnotation(ec, "entry") != null))
{
rootLabelledEClass = new LabelledEClass((EClass)ec, null, null);
}
}
}
return rootLabelledEClass;
}
/**
* @param topPackage the root package of the RMIM class model,
* which has a single layer of sub-packages below it
*/
public void setRMIMClassModel(EPackage topPackage, URI classModelURI)
{
timer = new Timer("Class model view");
this.classModelURI = classModelURI;
rmimRoot = new ArrayList<LabelledEClass>();
trace("Setting RMIM " + classModelURI);
parentTable = ModelUtil.parentTable(topPackage);
topLabelledEClass = getRootLabelledEClass(topPackage);
rmimRoot.add(topLabelledEClass);
/* ensure the viewer is expecting the right kind of content provider and label provider,
* and give it the root of the tree. */
setupRMIMViewer(viewer);
viewer.setInput(rmimRoot);
/*
Vector<String> noDups = new Vector<String>();
int maximum = 100000000;
int dataTypeDepth = 0;
int start = 0;
int leafCount = countLeafNodes(start,dataTypeDepth,maximum,topLabelledEClass.eClass(),noDups);
System.out.println("Leaf nodes in class model at '" + classModelURI.toString() + "': " + leafCount);
*/
}
/**
* count leaf nodes in the RMIM class tree, truncating any recursion,
* ignoring text content of elements, and ignoring occurrences of data type ANY
* @param start the count up to this point
* @param maximum a maximum it will not count beyond, to avoid memory overflow
* @param theClass class whose subtree is being added to the count
* @param noDups to truncate recursion whenever the same class name is encountered twice
* @return
*/
@SuppressWarnings("unused")
private int countLeafNodes(int start,int dataTypeDepth, int maximum, EClass theClass,Vector<String> noDups)
{
// control depth into data type classes
int MAX_DATATYPE_DEPTH = 1;
int nextDTDepth = dataTypeDepth;
if (theClass.getEPackage().getName().equals("datatypes"))nextDTDepth++;
if (nextDTDepth > MAX_DATATYPE_DEPTH) return start;
// control recursion of RMIM classes
Vector<String> nextNoDups = new Vector<String>();
for (Iterator<String> it = noDups.iterator();it.hasNext();)nextNoDups.add(it.next());
nextNoDups.add(theClass.getName());
// attribute leaf nodes of this class
int count = start + theClass.getEAttributes().size();
// leaf nodes of classes in subtrees
for (Iterator<EReference> ir = theClass.getEAllReferences().iterator();ir.hasNext();)
{
EReference ref = ir.next();
EClassifier ec = ref.getEType();
if ((ec instanceof EClass) && (ref.isContainment()) && (count < maximum))
{
EClass next = (EClass)ec;
String name = next.getName();
if ((!GenUtil.inVector(name, nextNoDups)) && (!name.equals("ANY")))
count = countLeafNodes(count,nextDTDepth,maximum, next,nextNoDups);
}
}
return count;
}
//---------------------------------------------------------------------------------------------
// content provider and label provider for an RMIM class model
//---------------------------------------------------------------------------------------------
/*
* The provider (= adapter) which adapts the model (in this case an EMF model)
* to the viewer (in this case a TreeViewer).
* There are two parts - a content provider which provides the tree structure
* and a label provider which provides the icons and text
*/
class RMIMViewContentProvider implements IStructuredContentProvider,
ITreeContentProvider {
// called by viewer.setInput(Object) but need do nothing
public void inputChanged(Viewer v, Object oldInput, Object newInput)
{}
public void dispose() {trace("RMIM dispose");}
public Object[] getElements(Object parent) {
// the only child of rmimRoot is the EPackage object
if (parent.equals(rmimRoot)) {
return array2Of(rmimRoot);
}
// for all other objects (including the package) use getChildren
return getChildren(parent);
}
/**
* child LabelledEClass objects:
* For the package, the children are the RMIM root class
* For any other LabelledEClass, get the LabelledEClass nodes for the classes linked by RMIM associations
*/
public Object [] getChildren(Object parent) {
ArrayList<LabelledEClass> children = new ArrayList<LabelledEClass>();
if (parent instanceof LabelledEClass)
children = ((LabelledEClass)parent).getChildren();
return array2Of(children);
}
private LabelledEClass[] array2Of(ArrayList<LabelledEClass> aList)
{return (LabelledEClass[])aList.toArray(new LabelledEClass[aList.size()]);}
/**
* get the parent object of any object in the tree.
*/
public Object getParent(Object child) {
LabelledEClass parent = null;
if (child instanceof LabelledEClass)
{
parent = ((LabelledEClass)child).parent();
if (parent == null)
{
trace("null parent");
// parent = (LabelledEClass)child; // ??
}
}
return parent;
}
public boolean hasChildren(Object parent) {
return(getChildren(parent).length > 0);
}
} // end of class RMIMViewContentProvider
class RMIMViewLabelProvider extends LabelProvider implements ITableLabelProvider{
public String getColumnText(Object obj, int columnIndex) {
switch(columnIndex){
case 0: return getText(obj);
case 1: {
if (obj instanceof LabelledEClass)
return getObjectMappingText((LabelledEClass)obj);
}
}
return ("");
}
public Image getColumnImage(Object obj, int columnIndex) {
switch(columnIndex){
case 0: return getImage(obj);
case 1: return null;
}
return null;
}
// label an RMIM class by the association leading to it, followed by the class name
public String getText(Object obj) {
if (obj instanceof LabelledEClass)
{
LabelledEClass lc = (LabelledEClass)obj;
String name = lc.eClass().getName();
if (lc.associationName() != null) name = lc.associationName() + "." + name;
EPackage CMETPackage = lc.eClass().getEPackage();
String CMETName = ModelUtil.getEAnnotationDetail(CMETPackage, "name");
if ((CMETName != null) && (!CMETName.equals("")))
name = name + " (" + CMETName + ")";
return name;
}
return "not a LabelledEClass";
}
public Image getImage(Object obj) {
Image im = null;
// default 'Folder' image if type is not recognised
String imageKey = ISharedImages.IMG_OBJ_FOLDER;
im = PlatformUI.getWorkbench().getSharedImages().getImage(imageKey);
// images for RMIM classes and data type classes
if (obj instanceof LabelledEClass)
{
EClass theClass = ((LabelledEClass)obj).eClass();
String RIMClass = ModelUtil.getMIFAnnotation(theClass,"RIM Class");
String imageName = "Class" + getColour(RIMClass); // "ClassGreen", "ClassRed", etc.
String packageName = theClass.getEPackage().getName();
if (packageName.equals("datatypes")) imageName = "DataType";
im = FileUtil.getImage(imageName);
}
return im;
}
/**
* @param RIMClass
* @return a string for the colour which clones of that class have in an RMIM diagram
*/
private String getColour(String RIMClass)
{
String colour = "";
if (RIMClass != null)
{
if (GenUtil.inArray(RIMClass, redClasses)) colour = "Red";
if (GenUtil.inArray(RIMClass, pinkClasses)) colour = "Pink";
if (GenUtil.inArray(RIMClass, blueClasses)) colour = "Blue";
if (GenUtil.inArray(RIMClass, yellowClasses)) colour = "Yellow";
if (GenUtil.inArray(RIMClass, greenClasses)) colour = "Green";
}
return colour;
}
}
// HL7 colouring of RIM classes
static String[] redClasses = {"Act", "ControlAct",
"Observation","DiagnosticImage","PublicHealthCase",
"Supply", "Diet", "DeviceTask",
"FinancialContract","InvoiceElement","FinancialTransaction","Account",
"PatientEncounter","SubstanceAdministration","WorkingList","Exposure",
"Procedure"};
static String[] pinkClasses = {"ActRelationship"};
static String[] blueClasses = {"Participation","ManagedParticipation"};
static String[] yellowClasses = {"Role", "RoleLink","Patient",
"LicensedEntity","QualifiedEntity","Access","Employee"};
static String[] greenClasses = {"Entity","Place","Person",
"LivingSubject","NonPersonLivingSubject","Organization",
"Material","Device","ManufacturedMaterial","Container"};
//----------------------------------------------------------------------------------------
// Label Provider for mapping columns
//----------------------------------------------------------------------------------------
/**
* @return MDLBase the mappings in the mapping set being edited,
* with fast retrieval methods
*/
public MDLBase mdlBase()
{
// if there is no current Mapper editor, return no MDLBase
if (mapperEditor == null) return null;
if (mdlBase == null) refreshMDLBase();
else if (mdlBase != null)
{
// if the mappings have been altered since the MDBase was last refreshed, refresh it now
if (!WorkBenchUtil.mappingRoot(mapperEditor).classModelViewIsRefreshed())
refreshMDLBase();
}
return mdlBase;
}
private void refreshMDLBase()
{
messageChannel mc = new SystemMessageChannel();
try
{
mdlBase = new MDLBase(WorkBenchUtil.mappingRoot(mapperEditor),mc);
// record that the MDLBase has been refreshed
WorkBenchUtil.mappingRoot(mapperEditor).setClassModelViewIsRrefreshed(true);
}
catch (MapperException ex) {GenUtil.surprise(ex, "ClassModelView.mdlBase");}
}
/**
* @param ec a class
* @return a text description of the object mappings to the class and its
* subclasses, of the form (S)paths where S is the number of object mappings to
* its proper subclasses, and 'paths' is the concatenated paths of the mappings to the class itself.
*/
private String getObjectMappingText(EClass ec)
{
String end = "; ";
String text = "";
if (mdlBase() != null) try
{
String className = ModelUtil.getQualifiedClassName(ec);
for (Iterator<objectMapping> it = mdlBase().objectMappings(className).iterator(); it.hasNext();)
{
objectMapping om = it.next();
String path = om.nodePath().stringForm();
if (om.hasSubset()) path = path + "(" + om.subset() + ")";
text = text + path + end;
}
int mappingsToSubclasses = 0;
for (Iterator<EClass> it = mdlBase().ms().getAllSubClasses(ec).iterator(); it.hasNext();)
{
EClass ed = it.next();
if ((ec.isSuperTypeOf(ed)) && (ec != ed))
{
String subclassName = ModelUtil.getQualifiedClassName(ed);
mappingsToSubclasses =
mappingsToSubclasses + mdlBase().objectMappings(subclassName).size();
}
}
if (mappingsToSubclasses > 0) text = "[" + mappingsToSubclasses + "] " + text;
}
catch (MapperException ex) {}
if (text.endsWith(end)) text = text.substring(0,text.length() - end.length());
return text;
}
/**
* @param lec a labelled EClass
* @return a text description of the object mappings to this occurrence of the class
* at this point of the RMIM tree;
* or its template id if it has one
*/
private String getObjectMappingText(LabelledEClass lec)
{
String path= "";
EClass ec = lec.eClass();
LabelledEClass parent = lec.parent();
String className = ModelUtil.getQualifiedClassName(ec);
// modification to show template ids in stead of a mapping, if there are any
String templateId = ModelUtil.getMIFAnnotation(ec, "template");
if (templateId != null)
{
boolean found = true;
int index = 1;
// find all annotations with keys 'template_1','template_2', etc.
while (found)
{
String nextId = ModelUtil.getMIFAnnotation(ec, "template_" + new Integer(index).toString());
if (nextId != null) templateId = templateId + "; " + nextId;
else found = false;
index++;
}
return templateId;
}
/* a class may only show some mapping text if (a) it is the parameter class
* of the mapping set or (b) it is the root class or (c) its parent has
* a mapping (already determined, as the parent node is visible)*/
boolean eligible = ((isParameterClass(lec))||(parent == null)||
((parent != null) && (parent.isMapped())));
if ((mdlBase() != null) && eligible) try
{
// don't carry on looking after you have found the right object mapping
boolean isLinkedToParent = false;
// there may be many object mappings to this class, most of which are elsewhere in the tree
for (Iterator<objectMapping> it = mdlBase().objectMappings(className).iterator(); it.hasNext();)
{
objectMapping om = it.next();
if (!isLinkedToParent)
{
// top of the RMIM tree, or top (parameter) class of an imported mapping set
if ((isParameterClass(lec))|(parent == null))
path = setTheObjectMappingText(lec, om);
/* for any class not at the top of the RMIM tree, its parent class must be
* mapped, and the named association to this class must also be mapped
* with the correct subsets. */
else
{
String parentClass= ModelUtil.getQualifiedClassName(parent.eClass());
String parentSubset = parent.getMappedSubset();
isLinkedToParent = (mdlBase().representsAssociationRoleLocally(parentClass, parentSubset,
lec.associationName(), className,om.subset()));
if (isLinkedToParent) path = setTheObjectMappingText(lec, om);
}
}
}
}
catch (Exception ex) {} // odd null pointers can occur when mappings have been deleted
return path;
}
private String setTheObjectMappingText(LabelledEClass lec, objectMapping om)
{
String path = om.nodePath().stringForm();
if (om.hasSubset()) path = path + "(" + om.subset() + ")";
lec.setObjectMappingText(path);
lec.setMappedSubset(om.subset());
return path;
}
/** *
* @param lec
* @return true if the EClass is the parameter class of the mapping set
*/
private boolean isParameterClass(LabelledEClass lec)
{
boolean isParamClass = false;
if (mapperEditor != null)
{
MappedStructure ms = WorkBenchUtil.mappingRoot(mapperEditor);
if (ms.getParameterClasses().size() > 0)
{
ParameterClass pc = ms.getParameterClasses().get(0);
String paramClassName = pc.getQualifiedClassName();
String mappedClassName = ModelUtil.getQualifiedClassName(lec.eClass());
isParamClass = (mappedClassName.equals(paramClassName));
}
}
return isParamClass;
}
/**
*
* @return if the mapping set has a parameter class, return the
* qualified parameter class name; otherwise return null
*/
private String parameterClassName()
{
String paramClassName = null;
if (mapperEditor != null)
{
MappedStructure ms = WorkBenchUtil.mappingRoot(mapperEditor);
if (ms.getParameterClasses().size() > 0)
{
ParameterClass pc = ms.getParameterClasses().get(0);
paramClassName = pc.getQualifiedClassName();
}
}
return paramClassName;
}
/**
* label provider for the mappings column of the class model or RMIM model
* @author robert
*
*/
class MappingLabelProvider extends ColumnLabelProvider{
public MappingLabelProvider()
{
}
public String getText(Object element)
{
String text = "";
System.out.println("getting text from label provider");
// view of a vanilla class model
if ((element instanceof EClass) && (mdlBase() != null))
{
EClass ec = (EClass)element;
text = getObjectMappingText(ec);
}
// view of an RMIM class model
else if ((element instanceof LabelledEClass) && (mdlBase() != null))
{
EClass ec = ((LabelledEClass)element).eClass();
text = getObjectMappingText(ec);
}
return text;
}
}
//----------------------------------------------------------------------------------------
// Actions for the pull-down menu
//----------------------------------------------------------------------------------------
private Action saveClassModelChangesAction;
private Action saveClassModelAsAction;
private Action showLastSelectedClassAction;
private MakeEcoreMappingsAction makeEcoreMappingsAction;
private ImportSimplificationsAction importSimplificationsAction;
private CopySimplificationsAction copySimplificationsAction;
private PasteSimplificationsAction pasteSimplificationsAction;
private RemoveSimplificationsAction removeSimplificationsAction;
private MergeModelsAction mergeModelsAction;
private void contributeToActionBars() {
IActionBars bars = getViewSite().getActionBars();
fillLocalPullDown(bars.getMenuManager());
}
private void fillLocalPullDown(IMenuManager manager) {
manager.add(importSimplificationsAction);
manager.add(copySimplificationsAction);
manager.add(pasteSimplificationsAction);
manager.add(removeSimplificationsAction);
manager.add(mergeModelsAction);
manager.add(saveClassModelChangesAction);
manager.add(saveClassModelAsAction);
manager.add(showLastSelectedClassAction);
manager.add(makeEcoreMappingsAction);
}
/**
* make the actions for the pulldown menu in the Mapped Class Model view
*/
private void makeActions() {
saveClassModelChangesAction = new Action() {
public void run() {
FileUtil.saveResource(ecoreRoot().eResource());
}
};
saveClassModelChangesAction.setText("Save Changes in Class Model");
saveClassModelChangesAction.setToolTipText("Save simplification annotation changes in class model");
saveClassModelChangesAction.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().
getImageDescriptor(ISharedImages.IMG_OBJS_INFO_TSK));
saveClassModelAsAction = new Action() {
public void run() {
doWriteClassModel();
}
};
saveClassModelAsAction.setText("Save Class Model as..");
saveClassModelAsAction.setToolTipText("Write out a class model to be used to generate Java classes");
saveClassModelAsAction.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().
getImageDescriptor(ISharedImages.IMG_OBJS_INFO_TSK));
showLastSelectedClassAction = new Action() {
public void run() {
String path = EclipseFileUtil.getSelectedClassPath(classModelURI.toString());
TreePath pathToLastClass = getLabelledTreePath(path);
if (pathToLastClass != null)
{
viewer.setSelection(new TreeSelection(pathToLastClass),true); // make it visible
}
}
};
showLastSelectedClassAction.setText("Show Last Selected Class");
showLastSelectedClassAction.setToolTipText("Expand the tree to show the class model last selected by the user");
showLastSelectedClassAction.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().
getImageDescriptor(ISharedImages.IMG_OBJS_INFO_TSK));
makeEcoreMappingsAction = new MakeEcoreMappingsAction(this);
importSimplificationsAction = new ImportSimplificationsAction();
copySimplificationsAction = new CopySimplificationsAction();
pasteSimplificationsAction = new PasteSimplificationsAction();
removeSimplificationsAction = new RemoveSimplificationsAction();
mergeModelsAction = new MergeModelsAction();
}
/**
* run the action to write the class model as an ecore file, for later code generation
*/
private void doWriteClassModel()
{
SaveAsDialog saveAsDialog = new SaveAsDialog(getSite().getShell());
saveAsDialog.open();
IPath path = saveAsDialog.getResult();
if (path != null) {
// use the platform URI, not the absolute one, and Eclipse will see the resource
URI resURI = URI.createPlatformResourceURI(path.toString(),true);
saveAtURI(ecoreRoot,resURI,"ecore");
}
}
/**
* Save an Ecore model at a given URI
* @param model an Ecore model
* @param resURI the URI it is to be saved at
*/
public void saveAtURI(EPackage model,URI resURI,String extension)
{
ResourceSet resourceSet = new ResourceSetImpl();
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().
put(extension, new XMIResourceFactoryImpl());
Resource resource = resourceSet.createResource(resURI);
resource.getContents().add(model);
try {
resource.save(null);
}
catch (IOException ex)
{
System.out.println("Exception saving ecore resource at "
+ resURI.toString() + ": " + ex.getMessage());
}
}
/**
* Passing the focus request to the viewer's control.
*/
public void setFocus() {
viewer.getControl().setFocus();
}
//------------------------------------------------------------------------------------------
// saving and restoring the state of the view
//------------------------------------------------------------------------------------------
private static final String TAG_MAPPING_SET_URI = "mappingSetURI";
private static final String TAG_CLASS_MODEL_URI = "instanceURI";
private static final String TAG_QUERY_URI = "queryURI";
public void saveState(IMemento memento)
{
super.saveState(memento);
if (classModelURI() != null)
memento.putString(TAG_CLASS_MODEL_URI, classModelURI().toString());
// at most one of mappingSetURI and queryURI will be non-null
if (mappingSetURI() != null)
memento.putString(TAG_MAPPING_SET_URI, mappingSetURI().toString());
if (queryURI() != null)
memento.putString(TAG_QUERY_URI, queryURI().toString());
}
/**
* re-initialise the model from its state at last closedown
* Note the viewer does not exist when this runs.
*/
public void init(IViewSite site, IMemento memento) throws PartInitException
{
super.init(site, memento);
if (memento != null)
{
String cmURI = memento.getString(TAG_CLASS_MODEL_URI);
if (cmURI != null) classModelURI = URI.createURI(cmURI);
// at most one of mappingSetURI and queryURI will be non-null
String msURI = memento.getString(TAG_MAPPING_SET_URI);
if (msURI != null) setMappingSetURI(URI.createURI(msURI));
String quURI = memento.getString(TAG_QUERY_URI);
if (quURI != null) setQueryURI(URI.createURI(quURI));
}
}
//----------------------------------------------------------------------------------------
// Expanding the tree view to show a mapped class
//----------------------------------------------------------------------------------------
/**
* @param qualifiedClassName the package name and class name of the class whose mapping has been selected
*/
public void showMappedClass(String qualifiedClassName,String subset)
{
// RMIM case; find the named LabelledEClass and expand to it
if ((isRMIMRoot(ecoreRoot)) && (subset != null))
{
TreePath startPath = TreePath.EMPTY.createChildPath(topLabelledEClass);
if (parameterClassName() != null) startPath = extendToParameterClass(startPath,parameterClassName());
TreePath namedPath = findLabelledPath(qualifiedClassName,subset,startPath);
if (namedPath != null)
{
viewer.setSelection(new TreeSelection(namedPath),true); // make it visible
}
else {System.out.println("Null tree path");}
}
// non-RMIM case; find the named EClass and expand to it
if (!(isRMIMRoot(ecoreRoot)))
{
EClass namedClass = ModelUtil.getNamedClass(ecoreRoot, qualifiedClassName);
if (namedClass != null)
{
viewer.expandToLevel(namedClass, 0);
viewer.setSelection(new StructuredSelection(namedClass));
}
}
}
/**
* recursive descent of a tree of mapped LabelledEClasses,
* looking for a class with the required class name and subset
* @param className
* @param subset
* @param path
*/
private TreePath findLabelledPath(String className, String subset, TreePath path)
{
if (path == null) return null;
LabelledEClass lc = (LabelledEClass)path.getLastSegment();
// find out if the class is mapped, and if so, set its subset
getObjectMappingText(lc);
/* only try mapped classes and their mapped descendants
* (usually any mapped class has all mapped ancestors) */
if (lc.getMappedSubset() != null)
{
String cName = ModelUtil.getQualifiedClassName(lc.eClass());
if ((subset.equals(lc.getMappedSubset())) && (className.equals(cName)))
return path;
else for (Iterator<LabelledEClass> it = lc.getChildren().iterator();it.hasNext();)
{
TreePath childPath = path.createChildPath(it.next());
TreePath targetPath = findLabelledPath(className, subset, childPath);
if (targetPath != null) return targetPath;
}
}
return null;
}
/**
* @param startPath a TreePath with one step - the entry class of the RMIM
* @param parameterClassName name of the parameter class of the mapping set
* @return a TreePath to one example of the parameter class (there may be several)
* found by a tree search cut off at repeated class names
*/
private TreePath extendToParameterClass(TreePath startPath,String parameterClassName)
{
TreePath currentPath = startPath;
// from parents of each class, find a path of parent classes to the top class
Vector<String> parentsToRoot = new Vector<String>();
parentsToRoot.add(parameterClassName);
String topClassName = ModelUtil.getQualifiedClassName(topLabelledEClass.eClass());
parentsToRoot = extendToAncestorClass(parentsToRoot,topClassName);
// use these to construct a TreePath of LabelledEClass objects
if (parentsToRoot != null)
{
for (int i = 1; i < parentsToRoot.size(); i++)
{
LabelledEClass current = (LabelledEClass)currentPath.getLastSegment();
boolean found = false;
for (Iterator<LabelledEClass> ic = current.getChildren().iterator();ic.hasNext();)
{
LabelledEClass child = ic.next();
if (ModelUtil.getQualifiedClassName(child.eClass()).equals(parentsToRoot.get(i)))
{
found = true;
currentPath = currentPath.createChildPath(child);
}
}
if (!found) return null;
}
return currentPath;
}
return null;
}
private Vector<String> extendToAncestorClass(Vector<String>parentsToRoot,String className)
{
if (parentsToRoot.get(0).equals(className)) return parentsToRoot;
else
{
Vector<String> parents = parentTable.get(parentsToRoot.get(0));
for (Iterator<String> ip = parents.iterator(); ip.hasNext();)
{
String parent = ip.next();
if (!GenUtil.inVector(parent, parentsToRoot))
{
Vector<String> newTrail = new Vector<String>();
newTrail.add(parent);
for (Iterator<String> is = parentsToRoot.iterator();is.hasNext();)
newTrail.add(is.next());
Vector<String> result = extendToAncestorClass(newTrail,className);
if (result != null) return result;
}
}
}
return null;
}
/**
*
* @param path
* @return the treePath of LabelledEClasses got by following a string path,
* or null if it cannot be followed
*/
private TreePath getLabelledTreePath(String path)
{
TreePath result = null;
if (path != null)
{
StringTokenizer steps = new StringTokenizer(path,"/");
String topName = steps.nextToken(); // check the first step, which should be the name of the top class
if (topName.equals(topLabelledEClass.eClass().getName()))
{
result = TreePath.EMPTY.createChildPath(topLabelledEClass);
LabelledEClass currentClass = topLabelledEClass;
while ((steps.hasMoreTokens()) && (currentClass != null))
{
String step = steps.nextToken();
currentClass = currentClass.getNamedAssocChild(step);
if (currentClass != null) result = result.createChildPath(currentClass);
else result = null;
}
}
}
return result;
}
//-----------------------------------------------------------------------------------------------------------------
// Listening for the selection of a new class in this View, for copy and paste of simplification annotations,
// and also to reopen the class model at the last selected class
//-----------------------------------------------------------------------------------------------------------------
private LabelledEClass selectedLabelledEClass = null;
/** get the node that has been selected, for copy and paste of simplification annotations */
public LabelledEClass getSelectedLabelledEClass() {return selectedLabelledEClass;}
private LabelledEClass copiedLabelledEClass = null;
/** set the node that has been copied, for later paste of simplification annotations */
public void setCopiedLabelledEClass(LabelledEClass copied) {copiedLabelledEClass= copied;}
/** get the node that has been copied, for later paste of simplification annotations */
public LabelledEClass getCopiedLabelledEClass() {return copiedLabelledEClass;}
/**
* remember the node selected in this view, as a local variable
* and remember the path to it as a local variable and in an XML file in the workspace
*/
public void selectionChanged(SelectionChangedEvent event)
{
ISelection selection = event.getSelection();
if (selection instanceof IStructuredSelection && ((IStructuredSelection)selection).size() == 1) {
Object object = ((IStructuredSelection)selection).getFirstElement();
// RMIM class model view
if (object instanceof LabelledEClass)
{
selectedLabelledEClass = (LabelledEClass)object;
String path = selectedLabelledEClass.getPath();
try {EclipseFileUtil.saveClassPath(classModelURI().toString(), path);}
catch (MapperException ex) {trace("Exception saving path to selected class:" + ex.getMessage());}
}
}
}
//----------------------------------------------------------------------------------------
// Trivia
//----------------------------------------------------------------------------------------
private void trace(String s)
{ if (tracing) System.out.println(s);}
}