/*****************************************************************************
* 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.customization.properties.modelelement;
import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
import org.eclipse.core.databinding.observable.value.ValueDiff;
import org.eclipse.emf.common.command.CompoundCommand;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EFactory;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.edit.command.CreateChildCommand;
import org.eclipse.emf.edit.command.RemoveCommand;
import org.eclipse.emf.edit.command.SetCommand;
import org.eclipse.emf.edit.domain.EditingDomain;
/**
* An observable value for manipulating reflective properties
* (Based on a key/value pair). The modifications are based on EMF
* commands.
*
* @see GenericAttributeModelElement
*
* @author Camille Letavernier
*/
public class GenericAttributeObservable extends AbstractObservableValue {
private EObject source;
private EditingDomain domain;
private EStructuralFeature createIn;
private EFactory createFrom;
private EClass createAsValue;
private EClass createAsReference;
private String propertyPath;
/**
*
* Constructor.
*
* @param source
* The EObject being edited
* @param domain
* The Editing domain on which the commands will be executed
* @param createIn
* The Feature in which the value will be set
* @param createFrom
* The Factory used to instantiate the new value
* @param createAsValue
* The EClass that will be instantiated for the new value, if it is an attribute
* @param createAsReference
* The EClass that will be instantiated for the new value, if it is a reference
* @param propertyPath
* The value of the "name" attribute (Which is the "key" of the property)
*/
public GenericAttributeObservable(EObject source, EditingDomain domain, EStructuralFeature createIn, EFactory createFrom, EClass createAsValue, EClass createAsReference, String propertyPath) {
this.source = source;
this.domain = domain;
this.createIn = createIn;
this.createFrom = createFrom;
this.createAsValue = createAsValue;
this.createAsReference = createAsReference;
this.propertyPath = propertyPath;
}
public Object getValueType() {
return Object.class;
}
@Override
protected Object doGetValue() {
EObject attribute = findAttribute();
if(attribute == null) {
return null;
}
Object result = attribute.eGet(attribute.eClass().getEStructuralFeature("value")); //$NON-NLS-1$
return result;
}
/**
* Browse the existing attributes in the given feature, and returns the
* one with the same name, if it exists, or null otherwise.
*
* @return The attribute being edited, if it already exists, or null otherwise
*/
@SuppressWarnings("unchecked")
protected EObject findAttribute() {
EList<? extends EObject> allAttributes = (EList<? extends EObject>)source.eGet(createIn);
for(EObject attribute : allAttributes) {
if(createAsValue.isInstance(attribute) || createAsReference.isInstance(attribute)) {
if(attribute.eGet(attribute.eClass().getEStructuralFeature("name")).equals(propertyPath)) { //$NON-NLS-1$
return attribute;
}
}
}
return null;
}
@SuppressWarnings("unchecked")
@Override
public void doSetValue(final Object value) {
final Object oldValue = doGetValue();
if(value != null && value.equals(oldValue)) {
return;
}
EObject attribute = findAttribute();
EList<? extends EObject> collection = (EList<? extends EObject>)source.eGet(createIn);
CompoundCommand command = new CompoundCommand(String.format("Set %s value", propertyPath)) {
@Override
public boolean prepare() {
//Only test the first command's canExecute(), as the following ones depend on the execution of the first one
//Can we use a StrictCompoundCommand here ?
if(commandList.isEmpty()) {
return true;
}
return commandList.get(0).canExecute();
}
};
if(value == null || value.equals("")) { //$NON-NLS-1$
if(attribute != null) {
RemoveCommand rCommand = new RemoveCommand(domain, collection, attribute);
command.append(rCommand);
}
} else {
if(attribute == null) {
if(value instanceof String) {
attribute = createFrom.create(createAsValue);
} else {
attribute = createFrom.create(createAsReference);
}
CreateChildCommand crCommand = new CreateChildCommand(domain, source, createIn, attribute, null);
command.append(crCommand);
EStructuralFeature feature = attribute.eClass().getEStructuralFeature("name"); //$NON-NLS-1$
SetCommand setCommand = new SetCommand(domain, attribute, feature, propertyPath);
command.append(setCommand);
}
EStructuralFeature feature = attribute.eClass().getEStructuralFeature("value"); //$NON-NLS-1$
SetCommand setCommand = new SetCommand(domain, attribute, feature, value);
command.append(setCommand);
}
domain.getCommandStack().execute(command);
ValueDiff diff = new ValueDiff() {
@Override
public Object getOldValue() {
return oldValue;
}
@Override
public Object getNewValue() {
return value;
}
};
fireValueChange(diff);
}
@Override
protected void fireValueChange(ValueDiff diff) {
super.fireValueChange(diff);
}
@Override
protected void fireChange() {
super.fireChange();
}
}