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 xmiModelDict; private Map modelXmiDict; private Map xmiViewDict; private Map 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 elList = new Vector(); 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 elementCopy = new Vector(); 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(); this.xmiModelDict = new HashMap(); this.viewXmiDict = new HashMap(); this.xmiViewDict = new HashMap(); // 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 xmiModelDict, Map 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 relList = new Vector(); 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 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 tmpGens= new Vector(); 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(); } } } }