blob: fb6c6ba0232b2fba2ab477e38063f3150a81c1f2 [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2016 CEA LIST.
*
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* CEA LIST - Initial API and implementation
*
*****************************************************************************/
package org.eclipse.papyrus.uml.refactoring.mutation.ui;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.emf.transaction.util.TransactionUtil;
import org.eclipse.gmf.runtime.common.core.command.ICommand;
import org.eclipse.gmf.runtime.emf.type.core.ElementTypeRegistry;
import org.eclipse.gmf.runtime.emf.type.core.IClientContext;
import org.eclipse.gmf.runtime.emf.type.core.IElementType;
import org.eclipse.gmf.runtime.emf.type.core.requests.CreateElementRequest;
import org.eclipse.gmf.runtime.emf.type.core.requests.CreateRelationshipRequest;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.papyrus.infra.core.services.ServiceException;
import org.eclipse.papyrus.infra.newchild.CreationMenuRegistry;
import org.eclipse.papyrus.infra.newchild.elementcreationmenumodel.CreationMenu;
import org.eclipse.papyrus.infra.newchild.elementcreationmenumodel.Folder;
import org.eclipse.papyrus.infra.services.edit.context.TypeContext;
import org.eclipse.papyrus.infra.services.edit.service.ElementEditServiceUtils;
import org.eclipse.papyrus.infra.services.edit.service.IElementEditService;
import org.eclipse.papyrus.refactoring.Activator;
import org.eclipse.papyrus.refactoring.helper.AbstractSelectedElementsTransformation;
import org.eclipse.papyrus.refactoring.refactoringOnElement.ITransformationOnElement;
import org.eclipse.papyrus.refactoring.util.PapyrusRefactoringUtils;
import org.eclipse.papyrus.uml.refactoring.mutation.helper.MutationTransformation;
import org.eclipse.papyrus.uml.refactoring.mutation.messages.Messages;
import org.eclipse.papyrus.uml.refactoring.mutation.providers.ElemenetTypeContentProvider;
import org.eclipse.papyrus.uml.refactoring.mutation.providers.ElementTypeLabelProvider;
import org.eclipse.papyrus.uml.service.types.element.UMLElementTypes;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.FilteredTree;
import org.eclipse.ui.dialogs.PatternFilter;
import org.eclipse.uml2.uml.DirectedRelationship;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Relationship;
/**
* Class used to propose the next type of the mutated element through a new wizard page and {@link AbstractSelectedElementsTransformation} used to mutate the selected elements.
*
*/
@SuppressWarnings("restriction")
public class MutationRefactoring extends AbstractSelectedElementsTransformation {
/** The {@link TreeViewer} that displays the UML Meta-Classes in the UI. */
private TreeViewer fNewTypeTreeViewer;
/** The {@link FilteredTree} to help the user to find the UML Meta-class he is looking for. */
private FilteredTree fNewMetaClassTree;
/** The {@link Composite} for this UI. */
private Composite mutationComposite;
public MutationRefactoring() {
super(Messages.MUTATIONREFACTORING_PAGETITLE);
}
@Override
public void createControl(Composite parent) {
mutationComposite = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout();
layout.marginHeight = 0;
layout.marginWidth = 0;
layout.numColumns = 1;
mutationComposite.setLayout(layout);
mutationComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
setControl(mutationComposite);
initElementTypesViewer();
}
/**
* Initiate and display {@link #fNewTypeTreeViewer}.
*/
private void initElementTypesViewer() {
Label newMetaClassLabel = new Label(mutationComposite, SWT.NONE);
newMetaClassLabel.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false, 1, 1));
newMetaClassLabel.setText(Messages.MUTATIONREFACTORING_SELECTIONLABEL);
fNewMetaClassTree = new FilteredTree(mutationComposite, SWT.RIGHT | SWT.V_SCROLL | SWT.H_SCROLL, new PatternFilter(), true);
fNewTypeTreeViewer = fNewMetaClassTree.getViewer();
GridData listViewerGridData = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1);
listViewerGridData.heightHint = 300;
fNewMetaClassTree.setLayoutData(listViewerGridData);
fNewTypeTreeViewer.setComparator(new ViewerComparator());
Map<IElementType, EObject> umlTypesInput = new HashMap<>();
// EObject modelRoot = PapyrusRefactoringUtils.getUMLRoot(fModelSet);
// Model model = modelRoot instanceof Model ? (Model) modelRoot : null;
// if (model != null) {
// for (Profile profile : model.getAllAppliedProfiles()) {
// // TODO retrieve the elementTypes according to the applied profiles
// }
// }
ElementTypeRegistry registry = ElementTypeRegistry.getInstance();
EObject elementToMutate = getElementToMutate();
EObject elementToMutateParent = elementToMutate.eContainer() != null ? elementToMutate.eContainer() : elementToMutate;
IElementType relationshipType = UMLElementTypes.RELATIONSHIP;
IClientContext context = null;
try {
context = TypeContext.getContext(elementToMutate);
} catch (ServiceException e) {
Activator.log.error(e);
}
if (context == null) {
return;
}
IElementType elementToMutateType = registry.getElementType(elementToMutate, context);
boolean elementToMutateIsRelationship = Arrays.asList(elementToMutateType.getAllSuperTypes()).contains(relationshipType);
Set<IElementType> relationshipTypes = new HashSet<>();
Set<IElementType> nodeTypes = new HashSet<>();
for (Folder folder : CreationMenuRegistry.getInstance().getRootFolder()) {
// System.err.println(folder.getLabel() + ", " + folder.eContents().size());
for (EObject eObject : folder.eContents()) {
if (eObject instanceof CreationMenu) {
CreationMenu creationMenu = (CreationMenu) eObject;
IElementType menuElementType = registry.getType(creationMenu.getElementType().getIdentifier());
if (!context.includes(menuElementType)) {
continue;
}
if (menuElementType == null) {
continue;
}
if (Arrays.asList(menuElementType.getAllSuperTypes()).contains(relationshipType)) {
relationshipTypes.add(menuElementType);
} else {
nodeTypes.add(menuElementType);
}
}
}
}
if (elementToMutateIsRelationship) {
umlTypesInput = getRelationshipTypes(elementToMutateParent, elementToMutate, relationshipTypes);
} else {
umlTypesInput = getNodeTypes(elementToMutateParent, elementToMutate, nodeTypes);
}
umlTypesInput.remove(elementToMutateType);
// System.err.println("finalMenu, " + umlTypesInput.size());
fNewTypeTreeViewer.setLabelProvider(new ElementTypeLabelProvider());
fNewTypeTreeViewer.setContentProvider(new ElemenetTypeContentProvider());
fNewTypeTreeViewer.setInput(umlTypesInput);
}
/**
* Retrieves all the possible relationship that can be created from the selected element's context
*
* @param elementToMutateParent
* The relationship's container
* @param elementToMutate
* The relationship
* @param relationshipTypes
* The possible relationship types the are declared in the registry
* @return
* The filtered relationships to display in the viewer
*/
private Map<IElementType, EObject> getRelationshipTypes(EObject elementToMutateParent, EObject elementToMutate, Set<IElementType> relationshipTypes) {
Map<IElementType, EObject> filteredTypes = new HashMap<>();
Relationship relationshipToMutate = (Relationship) elementToMutate;
Element relationshipSource = null;
Element relationshipTarget = null;
if (relationshipToMutate instanceof DirectedRelationship) {
// System.err.println("DirectedRelationship");
// System.err.println(relationshipToMutate.getRelatedElements());
relationshipSource = ((DirectedRelationship) relationshipToMutate).getSources().get(0);
relationshipTarget = ((DirectedRelationship) relationshipToMutate).getTargets().get(0);
} else {
// System.err.println("Relationship");
// System.err.println(relationshipToMutate.getRelatedElements());
// This is to check the validity of the create command, hence the inverted ends are not that problematic
relationshipSource = relationshipToMutate.getRelatedElements().get(1);
relationshipTarget = relationshipToMutate.getRelatedElements().get(0);
}
// System.err.println(relationshipSource);
// System.err.println(relationshipTarget);
IElementEditService parentProvider = ElementEditServiceUtils.getCommandProvider(elementToMutateParent);
IElementEditService sourceProvider = ElementEditServiceUtils.getCommandProvider(relationshipSource);
if (parentProvider == null && sourceProvider == null) {
return null;
}
for (IElementType elementType : relationshipTypes) {
if (parentProvider != null) {
ICommand createGMFCommandParent = null;
TransactionalEditingDomain editingDomainParent = TransactionUtil.getEditingDomain(elementToMutateParent);
CreateRelationshipRequest relationshipParentRequest = new CreateRelationshipRequest(editingDomainParent, elementToMutateParent, relationshipSource, relationshipTarget, elementType);
createGMFCommandParent = parentProvider.getEditCommand(relationshipParentRequest);
if (createGMFCommandParent != null && createGMFCommandParent.canExecute()) {
filteredTypes.putIfAbsent(elementType, relationshipSource);
}
}
if (sourceProvider != null) {
ICommand createGMFCommandSource = null;
TransactionalEditingDomain editingDomainSource = TransactionUtil.getEditingDomain(relationshipSource);
CreateRelationshipRequest relationshipRequestSource = new CreateRelationshipRequest(editingDomainSource, relationshipSource, relationshipSource, relationshipTarget, elementType);
createGMFCommandSource = parentProvider.getEditCommand(relationshipRequestSource);
if (createGMFCommandSource != null && createGMFCommandSource.canExecute()) {
filteredTypes.putIfAbsent(elementType, relationshipSource);
}
}
}
return filteredTypes;
}
/**
* Retrieves all the possible nodes that can be created from the element's context
*
* @param elementToMutateParent
* The element's parent
* @param elementToMutate
* The element
* @param nodeTypes
* The possible nodes that are declared in the registry
* @return
* The filtered nodes to be displayed in the viewer
*/
private Map<IElementType, EObject> getNodeTypes(EObject elementToMutateParent, EObject elementToMutate, Set<IElementType> nodeTypes) {
Map<IElementType, EObject> filteredTypes = new HashMap<>();
IElementEditService provider = ElementEditServiceUtils.getCommandProvider(elementToMutateParent);
if (provider == null) {
return null;
}
for (IElementType elementType : nodeTypes) {
ICommand createGMFCommand = null;
TransactionalEditingDomain editingDomain = TransactionUtil.getEditingDomain(elementToMutate);
CreateElementRequest elementRequest = new CreateElementRequest(editingDomain, elementToMutateParent, elementType);
createGMFCommand = provider.getEditCommand(elementRequest);
if (createGMFCommand == null || !createGMFCommand.canExecute()) {
continue;
}
filteredTypes.putIfAbsent(elementType, elementToMutateParent);
}
return filteredTypes;
}
@Override
public ITransformationOnElement getTransformationOnElement() {
Entry selectedEntry = getTypeSelected(fNewTypeTreeViewer.getSelection());
// The null condition is already checked by the finalConditions method
IElementType selectedType = (IElementType) selectedEntry.getKey();
EObject associatedContainer = selectedEntry.getValue() != null ? (EObject) selectedEntry.getValue() : null;
MutationTransformation mutation = new MutationTransformation(fModelSet, getMetaClassToMutate(),
selectedType, associatedContainer);
return mutation;
}
@Override
/**
* Check that a meta-class is selected by the user in the {@link #fNewMetaClassTreeViewer}.
*/
public RefactoringStatus checkFinalConditions() {
RefactoringStatus status = new RefactoringStatus();
if (getTypeSelected(fNewTypeTreeViewer.getSelection()) == null) {
status.addFatalError(Messages.MUTATIONREFACTORING_SELECTIONLABEL);
}
return status;
}
/**
* Verify that the operation can proceed and is not lacking parameters
*
* @param selection
* The selected Type to transform the object into
* @return
* The {@link IElementType} that represents the new UML element.
*/
private Entry<IElementType, EObject> getTypeSelected(ISelection selection) {
if (selection instanceof IStructuredSelection) {
Object selectedElement = ((IStructuredSelection) selection).getFirstElement();
if (selectedElement instanceof Entry) {
Entry selectedEntry = (Entry) selectedElement;
Object selectedType = selectedEntry.getKey();
Object associatedContainer = selectedEntry.getValue();
if (selectedType instanceof IElementType) {
if (associatedContainer instanceof EObject) {
return selectedEntry;
}
// We can't simply resolve the provided container therefore fall back on the default transformation
selectedEntry.setValue(null);
return selectedEntry;
}
}
}
return null;
}
/**
* Retrieves the EClass of the element to mutate
*
* @return
*/
// We have checked in the checkIntialConditions method that all selected elements are instances of the same
// UML metaClass, so to get the EClass to mutate we just need to get the EClass of one element of the selected
// elements list.
private EClass getMetaClassToMutate() {
EObject elementToMutate = getElementToMutate();
if (elementToMutate != null) {
return elementToMutate.eClass();
}
return null;
}
/**
* Retrieves the EObject linked to the element to mutate
*
* @return
*/
private EObject getElementToMutate() {
if (fModelSet != null) {
Iterator<EObject> it = PapyrusRefactoringUtils.getSelectedElements(fModelSet).iterator();
if (it.hasNext()) {
return it.next();
}
}
return null;
}
@Override
/**
* Check that at least one element is selected,and that all selected elements are instances of the same UML metaClass.
*/
public RefactoringStatus checkInitialConditions() {
RefactoringStatus status = new RefactoringStatus();
HashSet<EClass> eClassSelectedList = new HashSet<>();
if (PapyrusRefactoringUtils.getSelectedElements(fModelSet).size() == 0) {
status.addFatalError(Messages.MUTATIONREFACTORING_SELECTIONWARNING_EMPTY);
} else {
for (EObject element : PapyrusRefactoringUtils.getSelectedElements(fModelSet)) {
eClassSelectedList.add(element.eClass());
}
if (eClassSelectedList.size() != 1) {
status.addFatalError(Messages.MUTATIONREFACTORING_SELECTIONWARNING_UNIFORM);
}
}
return status;
}
@Override
public String getName() {
return Messages.MUTATIONREFACTORING_PAGETITLE;
}
@Override
public void performHelp() {
// TODO Write the help
// http://help.eclipse.org/mars/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fextension-points%2Forg_eclipse_help_contexts.html
PlatformUI.getWorkbench().getHelpSystem().displayHelp("help context id");
}
}