/******************************************************************************* * Copyright (c) 2005, 2012 IBM Corporation and others. * 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.bpel.ui.commands; import java.util.Iterator; import java.util.List; import org.eclipse.bpel.ui.util.ModelHelper; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.wst.wsdl.ExtensibilityElement; import org.eclipse.wst.wsdl.ExtensibleElement; /** * Sets an extension property of a model object. E.g. BPEL+ uses extensions to * add properties to existing BPEL model types. * * Similarly to SetCommand, subclasses need to implement get() and set() in terms * of the particular extension property they set. They must also implement * createExtension() to create an extension of the proper type, and * isTargetExtensionUnused() to determine if the extension contains no meaningful * information. * * Using these methods, SetExtensionCommand will automatically create the extension * when it doesn't exist (removing it again on undo). It will also remove extensions * which become unused (restoring them on undo). * * Lastly, SetExtensionCommand touches the target model object's extension list as * part of every operation (even if it doesn't add or remove the extension), so that * Adapters listening to the target model object are notified. Avoiding the need to * listen to the specific extension is helpful because the lifetimes of the extension * objects themselves vary depending on the values stored in them. */ public abstract class SetExtensionCommand extends SetCommand { protected ExtensibilityElement oldExt, newExt; protected ExtensibilityElement targetExt; protected EClass extClass; public SetExtensionCommand(EObject target, EClass extClass, Object newValue) { super(target, newValue); this.extClass = extClass; } public ExtensibilityElement createExtension() { return (ExtensibilityElement)extClass.getEPackage().getEFactoryInstance().create(extClass); } /** * Returns true if the extension object is currently meaningless and should be * removed. The default behaviour is to return true iff all properties (except * for internal ExtensionImpl properties) have their default values. */ public boolean isTargetExtensionUnused() { for (Iterator it = targetExt.eClass().getEAllStructuralFeatures().iterator(); it.hasNext(); ) { EStructuralFeature feature = (EStructuralFeature)it.next(); // TODO: this is a hack! there must be a better way if (feature.isTransient() && "elementType".equals(feature.getName())) continue; //$NON-NLS-1$ Object defaultValue = feature.getDefaultValue(); if (defaultValue == null) { if (targetExt.eGet(feature) != null) return false; } else { if (!defaultValue.equals(targetExt.eGet(feature))) return false; } } return true; } @Override public void doExecute() { targetExt = oldExt = ModelHelper.getExtensibilityElement(fTarget, extClass); if (targetExt != null) { fOldValue = get(); } targetExt = newExt = (oldExt==null)? createExtension() : oldExt; // Cause a touch *before* the set as well as after... // necessary for automatic undo/redo to work correctly here. List eeList = ((ExtensibleElement)fTarget).getEExtensibilityElements(); if (newExt == oldExt) { // NOTE: the following line deliberately causes a touch in the containing // object. This reduces the need for everybody to put adapters on the // extension objects (having them on the extended object usually suffices). if (oldExt != null) eeList.set(eeList.indexOf(oldExt), newExt); } if (targetExt != null) { set(fNewValue); } if (isTargetExtensionUnused()) newExt = null; if (newExt == oldExt) { // NOTE: the following line deliberately causes a touch in the containing // object. This reduces the need for everybody to put adapters on the // extension objects (having them on the extended object usually suffices). if (oldExt != null) eeList.set(eeList.indexOf(oldExt), newExt); } else { if (oldExt != null) eeList.remove(oldExt); if (newExt != null) eeList.add(newExt); } } }