/***************************************************************************** * Copyright (c) 2010 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: * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation *****************************************************************************/ package org.eclipse.papyrus.uml.tools.databinding; import java.util.ArrayList; import java.util.List; import org.eclipse.core.databinding.observable.ChangeEvent; import org.eclipse.core.databinding.observable.Diffs; import org.eclipse.core.databinding.observable.IChangeListener; import org.eclipse.core.databinding.observable.IObservable; import org.eclipse.core.databinding.observable.list.IObservableList; import org.eclipse.core.databinding.observable.value.AbstractObservableValue; import org.eclipse.emf.common.command.Command; import org.eclipse.emf.common.command.UnexecutableCommand; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.databinding.EMFProperties; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.edit.domain.EditingDomain; import org.eclipse.gmf.runtime.common.core.command.ICommand; import org.eclipse.gmf.runtime.emf.type.core.requests.SetRequest; import org.eclipse.papyrus.commands.wrappers.GMFtoEMFCommandWrapper; import org.eclipse.papyrus.infra.services.edit.service.ElementEditServiceUtils; import org.eclipse.papyrus.infra.services.edit.service.IElementEditService; import org.eclipse.papyrus.infra.tools.databinding.AggregatedObservable; import org.eclipse.papyrus.uml.tools.Activator; import org.eclipse.uml2.uml.Artifact; import org.eclipse.uml2.uml.Association; import org.eclipse.uml2.uml.DataType; import org.eclipse.uml2.uml.Interface; import org.eclipse.uml2.uml.Property; import org.eclipse.uml2.uml.Signal; import org.eclipse.uml2.uml.StructuredClassifier; import org.eclipse.uml2.uml.Type; import org.eclipse.uml2.uml.UMLPackage; /** * An ObservableValue for manipulating the UML Owner property. * The owner property is a virtual property, represented as an enumeration, * which can have two values : Association (Owned by Association) or Classifier * (Owned by Classifier) * * This value can be determined by the following query : * if self.association.ownedEnd->contains(self) then 'Association' else 'Classifier' endif * * This value doesn't make sense for n-ary associations, when n > 2. * * @author Camille Letavernier */ //TODO : To be refactored //This observable should rely on Papyrus UML Tools public class OwnerObservableValue extends AbstractObservableValue implements IChangeListener, AggregatedObservable, CommandBasedObservableValue { private Property memberEnd; private EditingDomain domain; private String currentValue; private final IObservableList navigableEndsObservableList; /** * Owned by classifier */ public static String CLASSIFIER = "Classifier"; //$NON-NLS-1$ /** * Owned by association */ public static String ASSOCIATION = "Association"; //$NON-NLS-1$ /** * Constructor. * * @param source * The EObject (Property) which the ownership is being edited * @param domain * The Editing Domain on which the commands will be executed */ public OwnerObservableValue(EObject source, EditingDomain domain) { this.memberEnd = (Property)source; this.domain = domain; navigableEndsObservableList = EMFProperties.list(UMLPackage.eINSTANCE.getAssociation_NavigableOwnedEnd()).observe(memberEnd.getAssociation()); navigableEndsObservableList.addChangeListener(this); } public void handleChange(ChangeEvent event) { fireValueChange(Diffs.createValueDiff(currentValue, doGetValue())); } public Object getValueType() { return String.class; } @Override protected String doGetValue() { return memberEnd.getAssociation().getOwnedEnds().contains(memberEnd) ? "Association" : "Classifier"; //$NON-NLS-1$ //$NON-NLS-2$ } @Override protected void doSetValue(Object value) { Command command = getCommand(value); domain.getCommandStack().execute(command); } @Override public synchronized void dispose() { super.dispose(); navigableEndsObservableList.removeChangeListener(this); navigableEndsObservableList.dispose(); } public Command getCommand(Object value) { if(value instanceof String) { String owner = (String)value; boolean isOwnedByAssociation = ASSOCIATION.equals(owner); Association association = memberEnd.getAssociation(); if(association.getMemberEnds().size() > 2) { Activator.log.warn("Cannot change End owner for n-ary associations"); //$NON-NLS-1$ return UnexecutableCommand.INSTANCE; } //Classifier classifier = memberEnd.getClass_(); //EStructuralFeature ownedEndFeature = association.eClass().getEStructuralFeature(UMLPackage.ASSOCIATION__OWNED_END); Command command = null; if(isOwnedByAssociation) { //Owned by Association IElementEditService provider = ElementEditServiceUtils.getCommandProvider(association); if(provider != null) { EStructuralFeature feature = UMLPackage.eINSTANCE.getAssociation_OwnedEnd(); List<Property> attributeList = new ArrayList<Property>(); attributeList.addAll(association.getOwnedEnds()); attributeList.add(memberEnd); //association.eSet(feature, attributeList); SetRequest request = new SetRequest(association, feature, attributeList); ICommand createGMFCommand = provider.getEditCommand(request); command = new GMFtoEMFCommandWrapper(createGMFCommand); } } else { //Owned by Classifier Type ownerType; List<Type> ownerList = association.getEndTypes(); if(ownerList.get(0).equals(memberEnd.getType()) && ownerList.size() > 1) { ownerType = ownerList.get(1); } else { ownerType = ownerList.get(0); } EStructuralFeature ownedAttributeFeature = getFeatureForType(ownerType); if(ownedAttributeFeature != null) { List<Property> attributeList = new ArrayList<Property>(); attributeList.addAll((EList<Property>)ownerType.eGet(ownedAttributeFeature)); attributeList.add(memberEnd); IElementEditService provider = ElementEditServiceUtils.getCommandProvider(ownerType); if(provider != null) { SetRequest request = new SetRequest(ownerType, ownedAttributeFeature, memberEnd); ICommand createGMFCommand = provider.getEditCommand(request); command = new GMFtoEMFCommandWrapper(createGMFCommand); } } } if(command != null) { this.currentValue = owner; } else { Activator.log.warn("Cannot modify the memberEnd owner"); } return command; } return UnexecutableCommand.INSTANCE; } private EStructuralFeature getFeatureForType(Type type) { if(type instanceof StructuredClassifier) { return UMLPackage.eINSTANCE.getStructuredClassifier_OwnedAttribute(); } if(type instanceof Interface) { return UMLPackage.eINSTANCE.getInterface_OwnedAttribute(); } if(type instanceof DataType) { return UMLPackage.eINSTANCE.getDataType_OwnedAttribute(); } if(type instanceof Artifact) { return UMLPackage.eINSTANCE.getArtifact_OwnedAttribute(); } if(type instanceof Signal) { return UMLPackage.eINSTANCE.getSignal_OwnedAttribute(); } //Unknown type : we try to find the feature reflexively Activator.log.warn("Unknown type : " + type.eClass().getName()); EStructuralFeature feature = type.eClass().getEStructuralFeature("ownedAttribute"); //$NON-NLS-1$ if(feature == null) { Activator.log.warn("Cannot find a valid feature for type " + type.eClass().getName()); } return feature; } public AggregatedObservable aggregate(IObservable observable) { try { return new AggregatedPapyrusObservableValue(domain, this, observable); } catch (IllegalArgumentException ex) { return null; //The observable cannot be aggregated } } public boolean hasDifferentValues() { return false; } }