513 lines
17 KiB
Java
Executable File
513 lines
17 KiB
Java
Executable File
package pyUML.views;
|
|
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Vector;
|
|
|
|
import org.eclipse.core.resources.IProject;
|
|
import org.eclipse.core.runtime.Path;
|
|
import org.eclipse.emf.common.util.EList;
|
|
import org.eclipse.emf.common.util.URI;
|
|
import org.eclipse.emf.ecore.EClass;
|
|
import org.eclipse.emf.ecore.EObject;
|
|
import org.eclipse.emf.ecore.impl.EClassImpl;
|
|
import org.eclipse.emf.ecore.resource.Resource;
|
|
import org.eclipse.jface.action.IAction;
|
|
import org.eclipse.jface.dialogs.MessageDialog;
|
|
import org.eclipse.jface.viewers.ISelection;
|
|
import org.eclipse.jface.viewers.TreeSelection;
|
|
import org.eclipse.ui.IObjectActionDelegate;
|
|
import org.eclipse.ui.IWorkbenchPage;
|
|
import org.eclipse.ui.IWorkbenchPart;
|
|
import org.eclipse.ui.PlatformUI;
|
|
import org.eclipse.uml2.uml.Association;
|
|
import org.eclipse.uml2.uml.BehavioredClassifier;
|
|
import org.eclipse.uml2.uml.Class;
|
|
import org.eclipse.uml2.uml.Classifier;
|
|
import org.eclipse.uml2.uml.Element;
|
|
import org.eclipse.uml2.uml.Generalization;
|
|
import org.eclipse.uml2.uml.Interface;
|
|
import org.eclipse.uml2.uml.Model;
|
|
import org.eclipse.uml2.uml.Package;
|
|
import org.eclipse.uml2.uml.PackageableElement;
|
|
import org.eclipse.uml2.uml.ParameterableElement;
|
|
import org.eclipse.uml2.uml.Property;
|
|
import org.eclipse.uml2.uml.Realization;
|
|
import org.eclipse.uml2.uml.Relationship;
|
|
import org.eclipse.uml2.uml.TemplateParameter;
|
|
import org.eclipse.uml2.uml.Type;
|
|
import org.eclipse.uml2.uml.resource.UMLResource;
|
|
|
|
import pyUML.backend.GlobalConstants;
|
|
import pyUML.backend.UMLToolsHelperMethods;
|
|
|
|
|
|
|
|
/**
|
|
* This is an action class where the global UML model
|
|
* is to be updated by a view.
|
|
* Classes cannot be copied, because this would destroy all
|
|
* model relationships of the class.
|
|
* New classes in a view are ignored, because it is not known where
|
|
* they are to be inserted in the model.
|
|
*
|
|
* At the moment, class attributes and methods are *not* synchronized
|
|
* with the mail model.
|
|
*
|
|
*/
|
|
public class SynchronizeModelByView implements IObjectActionDelegate{
|
|
private String pathToModel;;
|
|
private String pathToView;
|
|
private Model model;
|
|
private Model viewModel;
|
|
private Resource[] viewResource = {null};
|
|
private Map<String, EObject> xmiModelDict;
|
|
private Map<EObject, String> modelXmiDict;
|
|
private Map<String, EObject> xmiViewDict;
|
|
private Map<EObject, String> viewXmiDict;
|
|
private IProject project;
|
|
|
|
|
|
public static void synchronizeGlobalModel(String pathToView, IProject project) {
|
|
SynchronizeModelByView sv = new SynchronizeModelByView();
|
|
sv.project = project;
|
|
sv.synchronizeGlobalModel(pathToView);
|
|
}
|
|
|
|
/**
|
|
* Updates the global model by changes made by the given view.
|
|
* Class deletions are removed from the view, not from the model.
|
|
*
|
|
* New classes/packages are ignored, because it is not known
|
|
* where they should be inserted.
|
|
*
|
|
* Other changes are updated in the global model.
|
|
*
|
|
* @param pathToModel
|
|
* @param pathToView
|
|
* @return
|
|
*/
|
|
public boolean synchronizeGlobalModel(String pathToView) {
|
|
String pathToModel = this.project.getLocation()
|
|
.append(GlobalConstants.getPyUmlDir())
|
|
.append(this.project.getName()+".uml").toOSString();
|
|
|
|
//init models
|
|
this.pathToModel = pathToModel;
|
|
this.pathToView = pathToView;
|
|
initModels();
|
|
|
|
|
|
String viewName = new Path(pathToView).lastSegment().replaceAll(GlobalConstants.getViewUmlExtension(), "");
|
|
|
|
Package viewPackage = null;
|
|
|
|
viewPackage = this.viewModel.getNestedPackage("View_"+viewName);
|
|
|
|
if (viewPackage == null) {
|
|
MessageDialog.openError(null, "Error synchronizing View",
|
|
"The Model could not be synchronized by the view, because\n" +
|
|
"The View package View_"+viewName+" could not\n" +
|
|
"be found.");
|
|
return false;
|
|
}
|
|
List<PackageableElement> elList = new Vector<PackageableElement>();
|
|
elList.addAll(viewPackage.getPackagedElements());
|
|
for (PackageableElement el : elList) {
|
|
// find parent package in model
|
|
if (el instanceof Type) {
|
|
Type packagedType = (Type) el;
|
|
|
|
Type includingType = packagedType;
|
|
|
|
if (packagedType instanceof Association) {
|
|
Association as = (Association) packagedType;
|
|
includingType = as.getEndTypes().get(0);
|
|
}
|
|
|
|
String xmi_id = this.viewXmiDict.get(includingType);
|
|
if (xmi_id == null || (! this.xmiModelDict.containsKey(xmi_id)))
|
|
continue;
|
|
|
|
Type modelType = (Type) this.xmiModelDict.get(xmi_id);
|
|
Package parentPackage = modelType.getPackage();
|
|
String modelPackageXmiId= this.modelXmiDict.get(parentPackage);
|
|
if (modelPackageXmiId == null)
|
|
continue;
|
|
Package newViewPackage = (Package) this.xmiViewDict.get(modelPackageXmiId);
|
|
if (newViewPackage == null)
|
|
continue;
|
|
|
|
packagedType.setPackage(newViewPackage);
|
|
|
|
} else if (el instanceof ParameterableElement) {
|
|
ParameterableElement parEl = (ParameterableElement) el;
|
|
String xmi_id = this.viewXmiDict.get(el);
|
|
ParameterableElement modelParEl = (ParameterableElement) this.xmiModelDict.get(xmi_id);
|
|
Realization r;
|
|
TemplateParameter parentParEl = modelParEl.getOwningTemplateParameter();
|
|
String modelParElXmiId=this.modelXmiDict.get(parentParEl);
|
|
if (modelParElXmiId == null)
|
|
continue;
|
|
TemplateParameter newViewParEl = (TemplateParameter) this.xmiViewDict.get(modelParElXmiId);
|
|
if (newViewParEl == null)
|
|
continue;
|
|
parEl.setOwningTemplateParameter(newViewParEl);
|
|
}
|
|
}
|
|
viewPackage.destroy();
|
|
|
|
/*
|
|
// synchronize by all child classes
|
|
try{
|
|
List<Element> elementCopy = new Vector<Element>();
|
|
elementCopy.addAll(this.viewModel.getOwnedElements());
|
|
for (Element child : elementCopy) {
|
|
if (child instanceof Class)
|
|
synchronizeClass((Class)child);
|
|
}
|
|
} catch (Throwable t) {
|
|
t.printStackTrace();
|
|
}
|
|
*/
|
|
// save model
|
|
this.viewResource[0].setURI(URI.createFileURI(project.getLocation().append(GlobalConstants.getPyUmlDir()).append(project.getName()+".uml").toOSString()));
|
|
try {
|
|
this.viewResource[0].save(null);
|
|
}catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
return true;
|
|
//return saveModel();
|
|
}
|
|
|
|
/**
|
|
* initializes the Model of the view and the main model
|
|
*/
|
|
private void initModels() {
|
|
// save all open editors
|
|
IWorkbenchPage page =
|
|
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
|
|
page.saveAllEditors(false);
|
|
|
|
// initialize XMI dictionaries as empty -> they are filled, if a
|
|
// model already exists
|
|
this.modelXmiDict = new HashMap<EObject, String>();
|
|
this.xmiModelDict = new HashMap<String, EObject>();
|
|
this.viewXmiDict = new HashMap<EObject, String>();
|
|
this.xmiViewDict = new HashMap<String, EObject>();
|
|
|
|
// load view and model
|
|
// this.model =
|
|
this.model= initModel(this.pathToModel, this.xmiModelDict, this.modelXmiDict, null);
|
|
if (this.model == null)
|
|
return;
|
|
this.viewModel = initModel(this.pathToView, this.xmiViewDict, this.viewXmiDict, this.viewResource);
|
|
if (this.viewModel == null)
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* loads a .uml model (main model or view)
|
|
* and fills the given dictionaries
|
|
* @param pathToModel
|
|
* @param xmiModelDict
|
|
* @param modelXmiDict
|
|
* @param modelResource
|
|
* @return
|
|
*/
|
|
private Model initModel(String pathToModel, Map<String, EObject> xmiModelDict,
|
|
Map<EObject, String> modelXmiDict, Resource[] modelResource) {
|
|
|
|
// look for model file
|
|
Model model;
|
|
Resource resource;
|
|
File modelFile = new File(pathToModel);
|
|
if (! modelFile.exists()) {
|
|
MessageDialog.openError(null, "Error synchronizing Model by View",
|
|
"Model can not be found: "+pathToModel);
|
|
return null;
|
|
}
|
|
// read Model
|
|
EObject diagramRoot = UMLToolsHelperMethods.loadUMLDiagram(pathToModel);
|
|
model = (Model) diagramRoot;
|
|
resource = model.eResource();
|
|
|
|
// get model -> xmi-id dictionary
|
|
UMLResource r = (UMLResource) resource;
|
|
modelXmiDict.putAll(r.getEObjectToIDMap());
|
|
|
|
// create reverse dict, so that xmi_id can be the key
|
|
for (EObject modelObject : modelXmiDict.keySet()) {
|
|
String xmi_id = modelXmiDict.get(modelObject);
|
|
xmiModelDict.put(xmi_id, modelObject);
|
|
}
|
|
if (modelResource != null)
|
|
modelResource[0] = resource;
|
|
return model;
|
|
}
|
|
|
|
|
|
/**
|
|
* Synchronizes a model class by a view class
|
|
* @param viewClass
|
|
*/
|
|
private void synchronizeClass(Class viewClass) {
|
|
String classXmiId = this.viewXmiDict.get(viewClass);
|
|
if (! xmiModelDict.containsKey(classXmiId)) {
|
|
MessageDialog.openWarning(null, "Error saving View",
|
|
"You created a new class \""+ viewClass.getName() +
|
|
"\" in the view.\n" +
|
|
"This is not supported, because it is not " +
|
|
"known \nwhere to put the class in the original " +
|
|
"model.\n" +
|
|
"The inserted class will be ignored.");
|
|
return;
|
|
|
|
}
|
|
|
|
Class modelClass = (Class) xmiModelDict.get(classXmiId);
|
|
Package parentPackage = (Package) modelClass.getOwner();
|
|
|
|
// destroy all generalizations and associations in model,
|
|
// if both ends are present in view and association is no more
|
|
// present in view
|
|
|
|
for (Relationship rel : modelClass.getRelationships()) {
|
|
if (rel instanceof Association) {
|
|
String modelRelationXmiID = this.modelXmiDict.get(rel);
|
|
if (modelRelationXmiID != null && ! this.xmiViewDict.containsKey(modelRelationXmiID)) {
|
|
rel.destroy();
|
|
}
|
|
}
|
|
}
|
|
|
|
// now, copy all changes from
|
|
// View to Model.
|
|
// Also, we have to make sure to delete Attributes/methods in
|
|
// the model if they were deleted in the code.
|
|
|
|
List<Relationship> relList = new Vector<Relationship>();
|
|
relList.addAll(viewClass.getRelationships());
|
|
System.out.println(viewClass.getRelationships());
|
|
System.out.println(viewClass.getSourceDirectedRelationships());
|
|
|
|
for (Relationship rel : relList) {
|
|
if (rel instanceof Association) {
|
|
Association assoc = (Association) rel;
|
|
|
|
EList<Element> elList = rel.getRelatedElements();
|
|
|
|
Type connectedElement = (Type) elList.get(0);
|
|
|
|
Property end1 = (Property) rel.getOwnedElements().get(0);
|
|
Property end2 = (Property) (rel.getOwnedElements().size() > 1 ? rel.getOwnedElements().get(1) : end1);
|
|
|
|
Type secondElement = (Type) connectedElement;
|
|
if (elList.size() > 1) {
|
|
secondElement = (Type) elList.get(1);
|
|
}
|
|
|
|
// if source (first) element is not current class,
|
|
// switch first and second element
|
|
// to be sure element to be connected by this class is second.
|
|
if (! (connectedElement == viewClass)) {
|
|
Type tmpElement = secondElement;
|
|
secondElement = connectedElement;
|
|
connectedElement = tmpElement;
|
|
|
|
Property tmpProp = end1;
|
|
end1=end2;
|
|
end2=tmpProp;
|
|
}
|
|
|
|
String secondElXmiID = this.viewXmiDict.get(secondElement);
|
|
// continue if connected element is not present in model
|
|
// -> this relationship can not be handled!
|
|
if (! this.xmiModelDict.containsKey(secondElXmiID)) {
|
|
continue;
|
|
}
|
|
|
|
Type connectedElementInModel = (Type) this.xmiModelDict.get(secondElXmiID);
|
|
|
|
if (rel.getOwnedElements().size() == 0) {
|
|
continue;
|
|
}
|
|
|
|
// get old association, if possible
|
|
Relationship modelRelationship = null;
|
|
if (this.xmiModelDict.containsKey(this.viewXmiDict.get(rel))) {
|
|
modelRelationship = (Relationship) this.xmiModelDict.get(this.viewXmiDict.get(rel));
|
|
}
|
|
|
|
|
|
boolean changed = true;
|
|
if (modelRelationship != null) {
|
|
changed = false;
|
|
Property modelEnd2 = (Property) modelRelationship.getOwnedElements().get(0);
|
|
Property modelEnd1 = (Property) (modelRelationship.getOwnedElements().size() > 1 ? modelRelationship.getOwnedElements().get(1) : modelEnd2);
|
|
|
|
// update classes that take part in this association
|
|
modelEnd1.setType(modelClass);
|
|
modelEnd2.setType(connectedElementInModel);
|
|
|
|
// look if anything changed in association; if yes, move view association
|
|
// to model
|
|
if ( modelEnd1.isNavigable() != end1.isNavigable()
|
|
|| modelEnd1.isNavigable() != end1.isNavigable()
|
|
|| modelEnd2.isNavigable() != end2.isNavigable()
|
|
|| ! modelEnd1.getAggregation().equals(end1.getAggregation())
|
|
|| ! modelEnd2.getAggregation().equals(end2.getAggregation())
|
|
|| ! modelEnd1.getName().equals(end1.getName())
|
|
|| ! modelEnd2.getName().equals(end2.getName())
|
|
|| modelEnd1.getLower() != end1.getLower()
|
|
|| modelEnd2.getLower() != end2.getLower()
|
|
|| modelEnd1.getUpper() != end1.getUpper()
|
|
|| modelEnd2.getUpper() != end2.getUpper()
|
|
|| ! this.modelXmiDict.get(modelEnd1.getType()).equals(this.viewXmiDict.get(end1.getType()))
|
|
|| ! this.modelXmiDict.get(modelEnd2.getType()).equals(this.viewXmiDict.get(end2.getType())))
|
|
changed = true;
|
|
}
|
|
|
|
if (changed) {
|
|
assoc.setPackage(parentPackage);
|
|
|
|
end1.setType(modelClass);
|
|
end2.setType(connectedElementInModel);
|
|
//modelClass.createAssociation(end1.isNavigable(), end1.getAggregation(), end1.getName(), end1.getLower(), end1.getUpper(), connectedElementInModel, end2.isNavigable(), end2.getAggregation(), end2.getName(), end2.getLower(), end2.getUpper());
|
|
|
|
if (modelRelationship != null)
|
|
modelRelationship.destroy();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Directed Associations are hidden in attributes
|
|
propertyLoop:
|
|
for (Property att : viewClass.getAllAttributes()) {
|
|
if (att.getAssociation() != null) {
|
|
Association as = att.getAssociation();
|
|
String asXmiId = this.viewXmiDict.get(as);
|
|
Type modelType = null;
|
|
String typeXmiId = this.viewXmiDict.get(att.getType());
|
|
if (this.xmiModelDict.containsKey(typeXmiId))
|
|
modelType = (Type) this.xmiModelDict.get(typeXmiId);
|
|
else
|
|
continue propertyLoop;
|
|
|
|
Property modelProp = null;
|
|
|
|
|
|
// try to find model property -> property with same association
|
|
for (Property prop : modelClass.getAllAttributes()) {
|
|
if (prop.getAssociation()!= null &&
|
|
this.xmiModelDict.get(prop.getAssociation()).equals(asXmiId))
|
|
modelProp = prop;
|
|
}
|
|
|
|
if (modelProp != null){
|
|
// Property is present, check if everything is correct;
|
|
modelProp = (Property) this.xmiModelDict.get(asXmiId);
|
|
modelProp.setName(att.getName());
|
|
} else {
|
|
//create this Property
|
|
modelProp = modelClass.createOwnedAttribute(att.getName(), modelType);
|
|
}
|
|
|
|
as.setPackage(parentPackage);
|
|
//for (Element e : as.getOwnedElements();
|
|
modelProp.setOwningAssociation(as);
|
|
modelProp.setType(modelType);
|
|
modelProp.setLower(att.getLower());
|
|
modelProp.setUpper(att.getUpper());
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// delete all generalizations in model not present in view
|
|
List<Generalization> tmpGens= new Vector<Generalization>();
|
|
tmpGens.addAll(modelClass.getGeneralizations());
|
|
for (Generalization general: tmpGens) {
|
|
String modelSupXmiID = this.modelXmiDict.get(general.getGeneral());
|
|
Classifier viewSuperClass = (Classifier) this.xmiViewDict.get(modelSupXmiID);
|
|
|
|
boolean generalIsPresentInView = false;
|
|
for (Generalization viewSuper : viewClass.getGeneralizations()) {
|
|
if (viewSuper.getGeneral() == viewSuperClass) {
|
|
generalIsPresentInView = true;
|
|
break;
|
|
}
|
|
}
|
|
if (! generalIsPresentInView) {
|
|
general.destroy();
|
|
}
|
|
}
|
|
|
|
// copy all generalizations of classes
|
|
for (Generalization general: viewClass.getGeneralizations()) {
|
|
String superClassXmiID = viewXmiDict.get(general.getGeneral());
|
|
Classifier modelSuperClass = (Classifier) xmiModelDict.get(superClassXmiID);
|
|
|
|
boolean generalIsPresentInModel = false;
|
|
for (Generalization modelSuper : modelClass.getGeneralizations()) {
|
|
if (modelSuper.getGeneral() == modelSuperClass) {
|
|
generalIsPresentInModel = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (! xmiViewDict.containsKey(superClassXmiID))
|
|
continue;
|
|
|
|
if (! generalIsPresentInModel) {
|
|
modelClass.createGeneralization(modelSuperClass);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Saves the edited model .uml file
|
|
* @return
|
|
*/
|
|
private boolean saveModel() {
|
|
/*try {
|
|
this.modelResource[0].save(null);
|
|
EclipseHelperMethods.updateFile(pathToModel, project);
|
|
return true;
|
|
} catch (IOException e) {
|
|
MessageDialog.openError(null, "Error saving model",
|
|
"The model cannot be saved. Reason:\n" + e.getMessage());
|
|
return false;
|
|
}*/
|
|
return true;
|
|
}
|
|
|
|
public void setActivePart(IAction action, IWorkbenchPart targetPart) {
|
|
// Auto-generated method stub
|
|
}
|
|
|
|
public void run(IAction action) {
|
|
this.synchronizeGlobalModel(this.pathToView);
|
|
|
|
}
|
|
|
|
public void selectionChanged(IAction action, ISelection selection) {
|
|
// save selected uml view
|
|
if (selection instanceof TreeSelection) {
|
|
TreeSelection ts = (TreeSelection) selection;
|
|
if (ts.getFirstElement() instanceof org.eclipse.core.internal.resources.File) {
|
|
org.eclipse.core.internal.resources.File selectedFile = (org.eclipse.core.internal.resources.File) ts.getFirstElement();
|
|
this.pathToView = selectedFile.getLocation().toOSString();
|
|
this.project = selectedFile.getProject();
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|