blob: 048e84da0b348b2b0d090e846230706d3110ad31 [file] [log] [blame]
/*******************************************************************************
* 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);
}
}
}