/***************************************************************************** * Copyright (c) 2011-2012 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.service.types.helper.advice; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.gmf.runtime.common.core.command.CommandResult; import org.eclipse.gmf.runtime.common.core.command.CompositeCommand; import org.eclipse.gmf.runtime.common.core.command.ICommand; import org.eclipse.gmf.runtime.common.core.command.UnexecutableCommand; import org.eclipse.gmf.runtime.emf.core.util.EMFCoreUtil; import org.eclipse.gmf.runtime.emf.type.core.commands.ConfigureElementCommand; import org.eclipse.gmf.runtime.emf.type.core.edithelper.AbstractEditHelperAdvice; import org.eclipse.gmf.runtime.emf.type.core.requests.ConfigureRequest; import org.eclipse.gmf.runtime.emf.type.core.requests.CreateRelationshipRequest; import org.eclipse.gmf.runtime.emf.type.core.requests.DestroyDependentsRequest; import org.eclipse.gmf.runtime.emf.type.core.requests.DestroyElementRequest; import org.eclipse.gmf.runtime.emf.type.core.requests.DestroyReferenceRequest; import org.eclipse.gmf.runtime.emf.type.core.requests.MoveRequest; import org.eclipse.gmf.runtime.emf.type.core.requests.ReorientRelationshipRequest; import org.eclipse.gmf.runtime.emf.type.core.requests.SetRequest; import org.eclipse.gmf.runtime.notation.NotationPackage; import org.eclipse.gmf.runtime.notation.View; import org.eclipse.papyrus.infra.services.edit.service.ElementEditServiceUtils; import org.eclipse.papyrus.infra.services.edit.service.IElementEditService; import org.eclipse.papyrus.uml.service.types.Activator; import org.eclipse.papyrus.uml.service.types.element.UMLElementTypes; import org.eclipse.papyrus.uml.service.types.utils.ClassifierUtils; import org.eclipse.papyrus.uml.service.types.utils.ElementUtil; import org.eclipse.papyrus.uml.service.types.utils.RequestParameterConstants; import org.eclipse.papyrus.uml.service.types.utils.RequestParameterUtils; import org.eclipse.uml2.uml.Association; import org.eclipse.uml2.uml.Classifier; import org.eclipse.uml2.uml.Property; import org.eclipse.uml2.uml.Type; import org.eclipse.uml2.uml.UMLFactory; import org.eclipse.uml2.uml.UMLPackage; /** * Association edit helper advice. */ public class AssociationEditHelperAdvice extends AbstractEditHelperAdvice { /** * This method provides the source type provided as {@link ConfigureRequest} parameter. * * @return the target role */ protected Classifier getSourceOwnerType(ConfigureRequest req) { Classifier result = null; Object paramObject = req.getParameter(CreateRelationshipRequest.SOURCE); if(paramObject instanceof Classifier) { result = (Classifier)paramObject; } return result; } /** * This method provides the target type provided as {@link ConfigureRequest} parameter. * * @return the target role */ protected Classifier getTargetOwnerType(ConfigureRequest req) { Classifier result = null; Object paramObject = req.getParameter(CreateRelationshipRequest.TARGET); if(paramObject instanceof Classifier) { result = (Classifier)paramObject; } return result; } /** * Creates a new source {@link Property} from the targetType. * * @param targetType * the type of the {@link Property} * @return the new {@link Property} */ protected Property createSourceProperty(Type targetType) { Property sourceProperty = UMLFactory.eINSTANCE.createProperty(); sourceProperty.setType(targetType); sourceProperty.setName(targetType.getName().toLowerCase()); return sourceProperty; } /** * Creates a new target {@link Property} from the sourceType. * * @param sourceType * the type of the {@link Property} * @return the new {@link Property} */ protected Property createTargetProperty(Type sourceType) { Property targetProperty = UMLFactory.eINSTANCE.createProperty(); targetProperty.setType(sourceType); targetProperty.setName(sourceType.getName().toLowerCase()); return targetProperty; } /** * Add the source {@link Property} in the correct container. * * @param sourceEnd * the semantic end * @param owner * the end container * @param targetType * the target type * @param association * the association * @throws UnsupportedOperationException */ protected void addSourceInModel(final Property sourceEnd, Classifier owner, Classifier targetType, Association association) throws UnsupportedOperationException { boolean added = ClassifierUtils.addOwnedAttribute(owner, sourceEnd); if(!added) { throw new UnsupportedOperationException("Cannot add a Property on Classifier " + owner.getQualifiedName()); } } /** * Add the source {@link Property} in the correct container. * * @param targetEnd * the semantic end * @param owner * the end container * @param sourceType * the source type * @param association * the association * @throws UnsupportedOperationException */ protected void addTargetInModel(Property targetEnd, Classifier owner, Classifier sourceType, Association association) { boolean added = ClassifierUtils.addOwnedAttribute(owner, targetEnd); if(!added) { throw new UnsupportedOperationException("Cannot add a Property on Classifier " + owner.getQualifiedName()); } } /** * <pre> * {@inheritDoc} * * Complete the {@link Association} creation by: * adding its {@link Property} ends in the model * adding the UML Nature on the {@link Association}. * * </pre> */ @Override protected ICommand getBeforeConfigureCommand(final ConfigureRequest request) { final Association association = (Association)request.getElementToConfigure(); final Classifier sourceType = getSourceOwnerType(request); final Classifier targetType = getTargetOwnerType(request); if((sourceType == null) || (targetType == null)) { return UnexecutableCommand.INSTANCE; } return new ConfigureElementCommand(request) { protected CommandResult doExecuteWithResult(IProgressMonitor progressMonitor, IAdaptable info) throws ExecutionException { // Add UML Nature on the new Association ElementUtil.addNature(association, UMLElementTypes.UML_NATURE); // Create source and target ends Property sourceEnd = createSourceProperty(targetType); Property targetEnd = createTargetProperty(sourceType); // Add association ends references association.getMemberEnds().add(sourceEnd); association.getMemberEnds().add(targetEnd); // Add end properties in the model try { addSourceInModel(sourceEnd, sourceType, targetType, association); addTargetInModel(targetEnd, targetType, sourceType, association); } catch (Exception e) { Activator.log.error(e); return CommandResult.newCancelledCommandResult(); } return CommandResult.newOKCommandResult(association); } }; } /** * <pre> * {@inheritDoc} * * Add a command to destroy {@link Association} ends referenced by the {@link Association} * to delete. * * </pre> */ @Override protected ICommand getBeforeDestroyDependentsCommand(DestroyDependentsRequest req) { List<EObject> dependentsToDestroy = new ArrayList<EObject>(); List<EObject> dependentsToKeep = (req.getParameter(RequestParameterConstants.DEPENDENTS_TO_KEEP) != null) ? (List<EObject>)req.getParameter(RequestParameterConstants.DEPENDENTS_TO_KEEP) : new ArrayList<EObject>(); Association association = (Association)req.getElementToDestroy(); for(Property end : association.getMemberEnds()) { if (!dependentsToKeep.contains(end)) { dependentsToDestroy.add(end); } } // Return command to destroy dependents ends if(!dependentsToDestroy.isEmpty()) { return req.getDestroyDependentsCommand(dependentsToDestroy); } return super.getBeforeDestroyDependentsCommand(req); } /** * <pre> * {@inheritDoc} * * Add a command to destroy {@link Association} when only 1 end remains. * * </pre> */ @Override protected ICommand getBeforeDestroyReferenceCommand(DestroyReferenceRequest request) { ICommand gmfCommand = super.getBeforeDestroyReferenceCommand(request); Association association = (Association)request.getContainer(); if((request.getContainingFeature() == UMLPackage.eINSTANCE.getAssociation_MemberEnd()) && (association.getMemberEnds().contains(request.getReferencedObject()))) { Set<Property> ends = new HashSet<Property>(); ends.addAll(association.getMemberEnds()); ends.remove(request.getReferencedObject()); if(ends.size() <= 2) { DestroyElementRequest destroyRequest = new DestroyElementRequest(association, false); IElementEditService provider = ElementEditServiceUtils.getCommandProvider(association); if(provider != null) { ICommand destroyCommand = provider.getEditCommand(destroyRequest); gmfCommand = CompositeCommand.compose(gmfCommand, destroyCommand); } } } return gmfCommand; } /** * <pre> * {@inheritDoc} * * Add a command to related association end during re-orient. * * </pre> */ @Override protected ICommand getBeforeReorientRelationshipCommand(ReorientRelationshipRequest request) { ICommand gmfCommand = super.getBeforeReorientRelationshipCommand(request); MoveRequest moveRequest = null; SetRequest setTypeRequest = null; // Retrieve re-oriented association and add it to the list of re-factored elements Association association = (Association)request.getRelationship(); List<EObject> currentlyRefactoredElements = (request.getParameter(RequestParameterConstants.ASSOCIATION_REFACTORED_ELEMENTS) != null) ? (List<EObject>)request.getParameter(RequestParameterConstants.ASSOCIATION_REFACTORED_ELEMENTS) : new ArrayList<EObject>(); if(currentlyRefactoredElements.contains(association)) { // Abort - already treated return null; } else { currentlyRefactoredElements.add(association); request.getParameters().put(RequestParameterConstants.ASSOCIATION_REFACTORED_ELEMENTS, currentlyRefactoredElements); } // Retrieve property ends of the Association (assumed to be binary) Property semanticSource = association.getMemberEnds().get(0); Property semanticTarget = association.getMemberEnds().get(1); EObject modifiedPropertyType = null; if(request.getDirection() == ReorientRelationshipRequest.REORIENT_SOURCE) { if(!association.getOwnedEnds().contains(semanticSource)) { moveRequest = new MoveRequest(request.getNewRelationshipEnd(), semanticSource); } modifiedPropertyType = semanticTarget; setTypeRequest = new SetRequest(modifiedPropertyType, UMLPackage.eINSTANCE.getTypedElement_Type(), request.getNewRelationshipEnd()); } if(request.getDirection() == ReorientRelationshipRequest.REORIENT_TARGET) { if(!association.getOwnedEnds().contains(semanticTarget)) { moveRequest = new MoveRequest(request.getNewRelationshipEnd(), semanticTarget); } modifiedPropertyType = semanticSource; setTypeRequest = new SetRequest(modifiedPropertyType, UMLPackage.eINSTANCE.getTypedElement_Type(), request.getNewRelationshipEnd()); } if(moveRequest != null) { // Propagate parameters to the move request moveRequest.addParameters(request.getParameters()); IElementEditService provider = ElementEditServiceUtils.getCommandProvider(request.getNewRelationshipEnd()); if(provider != null) { ICommand moveCommand = provider.getEditCommand(moveRequest); gmfCommand = CompositeCommand.compose(gmfCommand, moveCommand); } } if(setTypeRequest != null) { // Propagate parameters to the set request setTypeRequest.addParameters(request.getParameters()); IElementEditService provider = ElementEditServiceUtils.getCommandProvider(modifiedPropertyType); if(provider != null) { ICommand setTypeCommand = provider.getEditCommand(setTypeRequest); gmfCommand = CompositeCommand.compose(gmfCommand, setTypeCommand); } } // Destroy inconsistent views of the association Set<View> viewsToDestroy = new HashSet<View>(); viewsToDestroy.addAll(getViewsToDestroy(association, request)); //return the command to destroy all these views if(!viewsToDestroy.isEmpty()) { DestroyDependentsRequest ddr = new DestroyDependentsRequest(request.getEditingDomain(), request.getRelationship(), false); ICommand destroyViewsCommand = ddr.getDestroyDependentsCommand(viewsToDestroy); gmfCommand = CompositeCommand.compose(gmfCommand, destroyViewsCommand); } if(gmfCommand != null) { gmfCommand.reduce(); } return gmfCommand; } /** * Returns all views referencing Association except the view currently re-oriented. * * @param association * the association referenced by views * @param request * the re-orient relationship request * @return the list of views to be destroy */ private Set<View> getViewsToDestroy(Association association, ReorientRelationshipRequest request) { Set<View> viewsToDestroy = new HashSet<View>(); // Find all views representing the Associations EReference[] refs = new EReference[]{ NotationPackage.eINSTANCE.getView_Element() }; @SuppressWarnings("unchecked") Collection<View> associationViews = EMFCoreUtil.getReferencers(association, refs); View currentlyReorientedView = RequestParameterUtils.getReconnectedEdge(request); viewsToDestroy.addAll(associationViews); viewsToDestroy.remove(currentlyReorientedView); return viewsToDestroy; } }