Eclipse-PyUML/pyUml/src/pyUML/views/SynchronizeModelByView.java

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();
}
}
}
}