/******************************************************************************* | |
* 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 2.0 | |
* which accompanies this distribution, and is available at | |
* https://www.eclipse.org/legal/epl-2.0/ | |
* | |
* SPDX-License-Identifier: EPL-2.0 | |
* | |
* 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); | |
} | |
} | |
} |