blob: c0ed86a96a4a559b19ed2ab1638ce030393cdd24 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2005 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.wst.common.internal.emfworkbench.integration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.eclipse.emf.common.command.AbstractCommand;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.command.CommandStack;
import org.eclipse.emf.common.util.Enumerator;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.edit.command.AddCommand;
import org.eclipse.emf.edit.command.RemoveCommand;
import org.eclipse.emf.edit.command.SetCommand;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.wst.common.internal.emf.utilities.ExtendedEcoreUtil;
/**
* Insert the type's description here. Creation date: (4/6/2001 3:40:35 PM)
*
* @author: Administrator
*/
public class ModelModifier {
private static final String SET_PATTERN = "Set {0}"; //$NON-NLS-1$
private static final String ADD_PATTERN = "Add {0}"; //$NON-NLS-1$
private static final String REMOVE_PATTERN = "Remove {0}"; //$NON-NLS-1$
private static final String DEFAULT_COMMAND_LABEL = "Command"; //$NON-NLS-1$
private EditingDomain editingDomain;
private List helpers;
private List extendedHelpers;
protected List additionalCommands;
protected int status;
public static final int NO_VALUE_CHANGE = 0;
public static final int VALUE_CHANGE = 1;
public static final int ERROR = 2;
/**
* J2EEModelModifier constructor comment.
*/
public ModelModifier() {
super();
}
/**
* J2EEModelModifier constructor comment.
*
* @param aDomain
* EditingDomain
*/
public ModelModifier(EditingDomain aDomain) {
setEditingDomain(aDomain);
}
/**
* Add
*
* @aHelper to the list of helper that will be executed.
*/
public void addHelper(ModifierHelper aHelper) {
if (aHelper != null && !getHelpers().contains(aHelper))
getHelpers().add(aHelper);
}
public void addAdditionalCommand(Command aCommand) {
if (aCommand != null && !getAdditionalCommands().contains(aCommand))
additionalCommands.add(aCommand);
}
/**
* Return true if this modifier can create a command that will perform the necessary operation.
*/
public boolean canExecuteCommand() {
return getEditingDomain() != null;
}
public int executeWithStatus() {
try {
execute();
return status;
} finally {
status = -1;
}
}
/**
* Execute this modifier using the recording mechanism of the StructedTextUndoManager. If this
* modifier cannot record, try to execute using the CommandStack (if it can execute commands).
* Return true if the execution was attempted.
*
* @see canExecuteCommand()
* @see canRecord()
* @see run()
*/
public boolean execute(ModifierHelper aHelper) {
addHelper(aHelper);
return execute();
}
/**
* Execute this modifier using the recording mechanism of the StructedTextUndoManager. If this
* modifier cannot record, try to execute using the CommandStack (if it can execute commands).
* Return true if the execution was attempted.
*
* @see canExecuteCommand()
* @see canRecord()
* @see run()
*/
public boolean execute(List someHelpers) {
setHelpers(someHelpers);
return execute();
}
/**
* Execute this modifier by creating a Command that is executed on the CommandStack. If this
* modifier cannot execute commands, the execution will not take place. Return true if the
* execution was attempted.
*
* @see canExecuteCommand()
*/
public boolean execute() {
boolean result = false;
if (canExecuteCommand()) {
try {
Command command = createCommand();
result = command != null;
if (result) {
getCommandStack().execute(command);
}
} finally {
release();
}
} else {
setStatus(ERROR);
}
return result;
}
protected CommandStack getCommandStack() {
if (getEditingDomain() != null)
return getEditingDomain().getCommandStack();
return null;
}
/**
* Insert the method's description here. Creation date: (4/6/2001 2:53:17 PM)
*
* @return EditingDomain
*/
public EditingDomain getEditingDomain() {
return editingDomain;
}
/**
* Insert the method's description here. Creation date: (4/10/2001 8:46:35 AM)
*
* @return J2EEModifierHelper
*/
public ModifierHelper getFirstHelper() {
if (helpers != null && getHelpers().size() > 0)
return (ModifierHelper) getHelpers().get(0);
return null;
}
/**
* Insert the method's description here. Creation date: (4/10/2001 8:46:35 AM)
*
* @return java.util.List
*/
public java.util.List getHelpers() {
if (helpers == null)
helpers = new ArrayList();
return helpers;
}
public java.util.List getAdditionalCommands() {
if (additionalCommands == null)
additionalCommands = new ArrayList();
return additionalCommands;
}
/**
* Release all model artifacts.
*/
protected void release() {
setEditingDomain(null);
setHelpers(null);
}
/**
* Insert the method's description here. Creation date: (4/6/2001 2:53:17 PM)
*
* @param newEditingDomain
* EditingDomain
*/
public void setEditingDomain(EditingDomain newEditingDomain) {
editingDomain = newEditingDomain;
}
/**
* Insert the method's description here. Creation date: (4/10/2001 8:46:35 AM)
*
* @param newHelpers
* java.util.List
*/
public void setHelpers(java.util.List newHelpers) {
helpers = newHelpers;
}
protected void setStatus(int statusCode) {
if (statusCode > status)
status = statusCode;
}
/**
* Return an AddCommand that will be executed by a CommandStack.
*/
protected Command createAddCommand(ModifierHelper aHelper) {
Object value = getValue(aHelper);
Command command = null;
if (valueChanged(aHelper.getOwner(), aHelper.getFeature(), value, false)) {
command = AddCommand.create(getEditingDomain(), aHelper.getOwner(), aHelper.getFeature(), value);
((AbstractCommand) command).setLabel(createCommandLabel(ADD_PATTERN, aHelper.getFeature()));
setStatus(VALUE_CHANGE);
} else {
setStatus(NO_VALUE_CHANGE);
}
return command;
}
/**
* Return a Command that will be executed by a CommandStack. The default is to return null.
* Subclasses should override this method.
*/
public Command createCommand() {
Command chainedCommand = createCommand(null, getHelpers());
if (null == chainedCommand && additionalCommands != null && additionalCommands.isEmpty()) {
setStatus(ERROR);
return null;
}
chainedCommand = appendAdditionalCommands(chainedCommand);
return chainedCommand;
}
protected Command createCommand(Command chainedCommand, List helpersArg) {
if (null == extendedHelpers) {
extendedHelpers = new ArrayList();
}
if (!helpersArg.isEmpty()) {
Iterator it = helpersArg.iterator();
Command nextCommand = null;
while (it.hasNext()) {
nextCommand = createCommand((ModifierHelper) it.next());
if (chainedCommand == null)
chainedCommand = nextCommand;
else if (nextCommand != null)
chainedCommand = chainedCommand.chain(nextCommand);
}
}
if (!extendedHelpers.isEmpty()) {
List copy = new ArrayList();
copy.addAll(extendedHelpers);
extendedHelpers.clear();
chainedCommand = createCommand(chainedCommand, copy);
}
return chainedCommand;
}
protected Command appendAdditionalCommands(Command chainedCommand) {
if (additionalCommands != null && !additionalCommands.isEmpty()) {
Command command;
for (int i = 0; i < additionalCommands.size(); i++) {
command = (Command) additionalCommands.get(i);
if (chainedCommand == null)
chainedCommand = command;
else
chainedCommand = chainedCommand.chain(command);
}
}
return chainedCommand;
}
/**
* Return a Command that will be executed by a CommandStack.
*/
protected Command createCommand(ModifierHelper aHelper) {
if (aHelper == null)
return null;
Command command1, command2;
ModifierHelper ownerHelper = aHelper.getOwnerHelper();
if (aHelper.shouldUnsetValue() && ownerHelper != null)
return null; //we are unsetting a value on an owner that does not exist so do not
// create the owner
command1 = createCommand(ownerHelper);
command2 = primCreateCommand(aHelper);
if (command1 != null) {
if (command2 == null)
command2 = command1;
else
command2 = command2.chain(command1);
}
return command2;
}
protected String createCommandLabel(String aPattern, EStructuralFeature feature) {
String replacement = feature == null ? DEFAULT_COMMAND_LABEL : feature.getName();
return java.text.MessageFormat.format(aPattern, new String[]{replacement});
}
/**
* Return a Command that will be executed by a CommandStack. The default is to return null.
* Subclasses should override this method.
*/
protected Command createManyCommand(ModifierHelper aHelper) {
if (aHelper.shouldUnsetValue())
return createRemoveCommand(aHelper);
return createAddCommand(aHelper);
}
protected EObject createObjectFromHelper(ModifierHelper aHelper) {
return aHelper.createNewObjectFromFeature();
}
/**
* Used to remove an object on unset. Makes the object a proxy
* on execute and redo commands. Undo will remove the proxy.
*/
public class ProxyWrappingCommand extends AbstractCommand {
protected Command baseCommand = null;
protected EObject eObject = null;
protected Resource resource = null;
public ProxyWrappingCommand(Command baseCommand, EObject eObject) {
this.baseCommand = baseCommand;
this.eObject = eObject;
this.resource = eObject.eResource();
}
public boolean canExecute() {
return baseCommand.canExecute();
}
public void execute() {
ExtendedEcoreUtil.becomeProxy(eObject, resource);
baseCommand.execute();
}
public boolean canUndo() {
return baseCommand.canUndo();
}
public void undo() {
baseCommand.undo();
ExtendedEcoreUtil.removeProxy(eObject, resource);
}
public void redo() {
ExtendedEcoreUtil.becomeProxy(eObject, resource);
baseCommand.redo();
}
public Collection getResult() {
return baseCommand.getResult();
}
public Collection getAffectedObjects() {
return baseCommand.getAffectedObjects();
}
public String getLabel() {
return baseCommand.getLabel();
}
public String getDescription() {
return baseCommand.getDescription();
}
public void dispose() {
super.dispose();
baseCommand.dispose();
}
}
/**
* Return a Remove Command that will be executed by a CommandStack.
*/
protected Command createRemoveCommand(ModifierHelper aHelper) {
Object value = getValue(aHelper);
Command command = null;
EStructuralFeature feature = aHelper.getFeature();
if (valueChanged(aHelper.getOwner(), feature, value, true)) {
if (isValueEqual(aHelper, value)) {
command = RemoveCommand.create(getEditingDomain(), aHelper.getOwner(), feature, (Collection) value);
} else {
command = RemoveCommand.create(getEditingDomain(), aHelper.getOwner(), feature, value);
}
((AbstractCommand) command).setLabel(createCommandLabel(REMOVE_PATTERN, feature));
setStatus(VALUE_CHANGE);
} else {
setStatus(NO_VALUE_CHANGE);
}
return command;
}
private boolean isValueEqual(ModifierHelper aHelper, Object value) {
return aHelper.getOwner().eGet(aHelper.getFeature()) == value;
}
/**
* Return a SetCommand that will be executed by a CommandStack.
*/
protected Command createSingleCommand(ModifierHelper aHelper) {
Object value = getValue(aHelper);
Command command = null;
if (valueChanged(aHelper.getOwner(), aHelper.getFeature(), value, aHelper.shouldUnsetValue())) {
command = SetCommand.create(getEditingDomain(), aHelper.getOwner(), aHelper.getFeature(), value);
((AbstractCommand) command).setLabel(createCommandLabel(SET_PATTERN, aHelper.getFeature()));
setStatus(VALUE_CHANGE);
} else {
setStatus(NO_VALUE_CHANGE);
}
return command;
}
protected Object createValueFromHelper(ModifierHelper aHelper) {
EObject newObject = createObjectFromHelper(aHelper);
setNewObjectAttributes(newObject, aHelper);
return newObject;
}
protected boolean enumValueChanged(EObject anObject, EStructuralFeature aFeature, Object aValue) {
if (!anObject.eIsSet(aFeature))
return true;
Enumerator existingEnumerator = (Enumerator) anObject.eGet(aFeature);
Enumerator newEnumerator = getEnumerator(aFeature, aValue);
return existingEnumerator != newEnumerator;
}
private Enumerator getEnumerator(EStructuralFeature aFeature, Object aValue) {
if (aValue instanceof Enumerator)
return (Enumerator) aValue;
EEnum anEnum = (EEnum) aFeature.getEType();
EEnumLiteral literal = null;
if (aValue instanceof String)
literal = anEnum.getEEnumLiteral((String) aValue);
else if (aValue instanceof Integer)
literal = anEnum.getEEnumLiteral(((Integer) aValue).intValue());
if (literal != null)
return literal.getInstance();
return null;
}
protected Object getValue(ModifierHelper aHelper) {
if (aHelper.mustCreateValue()) {
Object value = createValueFromHelper(aHelper);
aHelper.setValue(value);
}
return aHelper.getValue();
}
protected boolean manyValueChanged(EObject anObject, EStructuralFeature aFeature, Object aValue, boolean isUnset) {
List list = (List) anObject.eGet(aFeature);
if (isUnset)
return list.contains(aValue) || (list == aValue && !list.isEmpty());
return !list.contains(aValue);
}
/**
* Return a Command that will be executed by a CommandStack. The default is to return null.
* Subclasses should override this method.
*/
protected Command primCreateCommand(ModifierHelper aHelper) {
Command command = doCreateCommand(aHelper);
if (aHelper.shouldUnsetValue()) {
Object value = aHelper.getValue();
if (value instanceof EObject && !((EObject) value).eIsProxy()) {
command = new ProxyWrappingCommand(command, (EObject) value);
}
}
return command;
}
protected Command doCreateCommand(ModifierHelper aHelper) {
if (!aHelper.isComplete()) {
setStatus(ERROR);
return null;
}
Command command = null;
if (aHelper.getFeature().isMany())
command = createManyCommand(aHelper);
else
command = createSingleCommand(aHelper);
if (null != command) {
List localHelpers = ModifierHelperRegistry.getInstance().getHelpers(aHelper);
if (null != localHelpers) {
extendedHelpers.addAll(localHelpers);
}
}
return command;
}
/**
* Run using
*
* @aHelper. This will set a MOF attibute value to the owner of the helper.
*/
protected void primRun(ModifierHelper aHelper) {
if (aHelper.isComplete()) {
Object value = getValue(aHelper);
if (valueChanged(aHelper.getOwner(), aHelper.getFeature(), value, aHelper.shouldUnsetValue()))
setObjectAttribute(aHelper.getOwner(), aHelper.getFeature(), value, aHelper.shouldUnsetValue());
}
}
/**
* The default is to do nothing. Subclasses should override this method if they are using
* recordable commands. The implementation of this method should update the MOF model directly.
* Any modification will be recorded.
*/
public void run() {
if (!getHelpers().isEmpty()) {
Iterator it = getHelpers().iterator();
while (it.hasNext())
run((ModifierHelper) it.next());
}
}
/**
* Run using
*
* @aHelper's ownerHelper first before running with
* @aHelper.
*/
protected void run(ModifierHelper aHelper) {
if (aHelper != null) {
run(aHelper.getOwnerHelper());
primRun(aHelper);
}
}
protected void setNewObjectAttributes(EObject anObject, ModifierHelper aHelper) {
HashMap attributes = aHelper.getAttributes();
Iterator it = attributes.keySet().iterator();
EStructuralFeature feature;
Object value = null;
while (it.hasNext()) {
feature = (EStructuralFeature) it.next();
value = attributes.get(feature);
setObjectAttribute(anObject, feature, value, false);
}
}
protected void setObjectAttribute(EObject anObject, EStructuralFeature aFeature, Object aValue, boolean shouldUnsetValue) {
if (aFeature.isMany())
setObjectManyAttribute(anObject, aFeature, aValue, shouldUnsetValue);
else
setObjectSingleAttribute(anObject, aFeature, aValue, shouldUnsetValue);
}
protected void setObjectEnumAttribute(EObject anObject, EStructuralFeature aFeature, Object aValue) {
Enumerator enumerator = getEnumerator(aFeature, aValue);
anObject.eSet(aFeature, enumerator);
}
protected void setObjectManyAttribute(EObject anObject, EStructuralFeature aFeature, Object aValue, boolean shouldUnsetValue) {
List list = (List) anObject.eGet(aFeature);
if (shouldUnsetValue)
list.remove(aValue);
else
list.add(aValue);
}
protected void setObjectSingleAttribute(EObject anObject, EStructuralFeature aFeature, Object aValue, boolean shouldUnsetValue) {
if (shouldUnsetValue)
anObject.eUnset(aFeature);
else if (aFeature.getEType() instanceof EEnum)
setObjectEnumAttribute(anObject, aFeature, aValue);
else
anObject.eSet(aFeature, aValue);
}
protected boolean singleValueChanged(EObject anObject, EStructuralFeature aFeature, Object aValue, boolean isUnset) {
if (aFeature.getEType() instanceof EEnum)
return enumValueChanged(anObject, aFeature, aValue);
Object existingValue = anObject.eGet(aFeature);
if (existingValue == null && aValue == null)
return false;
if (existingValue != null && !existingValue.equals(aValue))
return true;
if (aValue != null && !aValue.equals(existingValue))
return true;
return false;
}
protected boolean valueChanged(EObject anObject, EStructuralFeature aFeature, Object aValue, boolean isUnset) {
if (aFeature.isMany())
return manyValueChanged(anObject, aFeature, aValue, isUnset);
return singleValueChanged(anObject, aFeature, aValue, isUnset);
}
}