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

385 lines
12 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.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
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.Class;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Generalization;
import org.eclipse.uml2.uml.Model;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.ParameterableElement;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Relationship;
import org.eclipse.uml2.uml.Type;
import org.eclipse.uml2.uml.resource.UMLResource;
import pyUML.backend.EclipseHelperMethods;
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 SynchronizeViews implements IObjectActionDelegate{
private String pathToModel;;
private String pathToView;
//private Model model;
private Model viewModel;
private Resource[] modelResource = {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) {
SynchronizeViews sv = new SynchronizeViews();
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();
// 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
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 =
initModel(this.pathToModel, this.xmiModelDict, this.modelXmiDict, this.modelResource);
this.viewModel = initModel(this.pathToView, this.xmiViewDict, this.viewXmiDict, null);
}
/**
* 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 associations / attributes/methods from
// View to Model.
// We cannot just copy the whole class, because this would
// destroy the associations/generalizations etc.
// Also, we have to make sure to delete Attributes/methods in
// the model if they were deleted in the code.
for (Relationship rel : viewClass.getRelationships()) {
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();
}
}
}
// 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;
}
}
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();
}
}
}
}