/*******************************************************************************
* Copyright (c) 2008, 2012 Obeo.
* 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:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.emf.eef.runtime.ui.widgets.referencestable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.eef.runtime.impl.utils.EEFUtils;
import org.eclipse.emf.eef.runtime.ui.widgets.settings.EEFEditorSettings;
/**
* @author <a href="mailto:goulwen.lefur@obeo.fr">Goulwen Le Fur</a>
*/
public class ReferencesTableSettings implements EEFEditorSettings {
protected EObject source;
protected EReference[] features;
/**
* @param source
* @param path
*/
public ReferencesTableSettings(EObject source, EReference... features) {
super();
this.source = source;
this.features = features;
}
/**
* @return the source
*/
public EObject getSource() {
return source;
}
/**
* @param source
* the source to set
*/
public void setSource(EObject source) {
this.source = source;
}
/**
* @return the last reference of the settings.
*/
public EReference getLastReference() {
return features[features.length - 1];
}
/**
* @return the type of the last feature
*/
public EClassifier getEType() {
return features[features.length - 1].getEType();
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.eef.runtime.ui.widgets.settings.EEFEditorSettings#isAffectingFeature(org.eclipse.emf.ecore.EStructuralFeature)
*/
public boolean isAffectingFeature(EStructuralFeature feature) {
return Arrays.asList(features).contains(feature);
}
/**
* {@inheritDoc}
* @see org.eclipse.emf.eef.runtime.ui.widgets.settings.EEFEditorSettings#isAffectingEvent(org.eclipse.emf.common.notify.Notification)
*/
public boolean isAffectingEvent(Notification notification) {
return true;
}
/**
* {@inheritDoc}
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < features.length; i++) {
EReference ref = features[i];
builder.append(ref.getName());
if (i < (features.length - 1)) {
builder.append(" > ");
}
}
return builder.toString();
}
/************************************************************************************************
* * getElements() * *
************************************************************************************************/
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.eef.runtime.ui.widgets.settings.EEFEditorSettings#getValue()
*/
public Object[] getValue() {
if (((EClass)features[0].eContainer()).isInstance(source)) {
Object value1 = ((EObject)source).eGet(features[0]);
if (value1 != null) {
if (features.length == 1) {
return features[0].isMany() ? ((List<EObject>)value1).toArray() : new Object[] {value1};
} else {
if (features[0].isMany()) {
List<Object> result = new ArrayList<Object>();
for (EObject elem : ((List<EObject>)value1)) {
if (features[1].isMany())
result.addAll((List<EObject>)elem.eGet(features[1]));
else {
EObject value2 = (EObject)elem.eGet(features[1]);
result.add(value2 == null ? "" : value2);
}
}
return result.toArray();
} else {
if (features[1].isMany()) {
return ((List)((EObject)value1).eGet(features[1])).toArray();
} else {
Object value2 = ((EObject)value1).eGet(features[1]);
return new Object[] {value2 == null ? "" : value2};
}
}
}
}
}
return new Object[0];
}
/************************************************************************************************
* * Add via ModelNavigation * *
************************************************************************************************/
/**
* Add a new value following a list of StructualFeatures to a given EObject
*
* @param newValue
* the value to add
*/
public void addToReference(EObject newValue) {
Object value1 = source.eGet(features[0]);
if (features[0].isMany()) {
addFirstMany((List<EObject>)value1, newValue);
} else /* ref is Single */{
addFirstSingle((EObject)value1, newValue);
}
}
/**
* This method add newValue to the managed reference(s) if the first reference in the path is a multiple
* reference
*
* @param ref1Values
* @param newValue
*/
protected void addFirstMany(List<EObject> ref1Values, EObject newValue) {
if (features.length > 1) {
if (features[1].isMany()) {
addFirstManySecondMany(ref1Values, newValue);
} else {
addFirstManySecondSingle(ref1Values, newValue);
}
} else {
// There is only one multiple reference in the path, we simply add
// the new value to
// the existing values
((List<EObject>)ref1Values).add(newValue);
}
}
/**
* @param ref1Values
* @param newValue
*/
protected void addFirstManySecondMany(List<EObject> ref1Values, EObject newValue) {
throw new IllegalStateException(
"Ambigous case - Cannot process ModelNavigation with more than one multiple reference");
}
/**
* @param newValue
* @param ref2
*/
protected void addFirstManySecondSingle(List<EObject> ref1Values, EObject newValue) {
EObject intermediate = EcoreUtil.create(features[0].getEReferenceType());
((EObject)intermediate).eSet(features[1], newValue);
ref1Values.add(intermediate);
}
/**
* This method add newValue to the managed reference(s) if the first reference in the path is a single
* reference
*
* @param ref1Value
* @param newValue
*/
protected void addFirstSingle(EObject ref1Value, EObject newValue) {
if (features.length > 1) {
if (features[1].isMany()) {
addFirstSingleSecondMany(ref1Value, newValue);
} else {
addFirstSingleSecondSingle(ref1Value, newValue);
}
} else {
// There is only one single reference in the path, we simply add the
// new value to
// the existing values. Must be an error ?
source.eSet(features[0], newValue);
}
}
/**
* @param ref1Value
* @param newValue
*/
protected void addFirstSingleSecondMany(EObject ref1Value, EObject newValue) {
if (ref1Value == null) {
ref1Value = EcoreUtil.create(features[0].getEReferenceType());
// WARNING: Cannot be an abstract class
source.eSet(features[0], ref1Value);
}
((List<EObject>)ref1Value.eGet(features[1])).add(newValue);
}
/**
* @param ref1Value
* @param newValue
*/
protected void addFirstSingleSecondSingle(EObject ref1Value, EObject newValue) {
throw new IllegalStateException(
"Ambigous case - Cannot process ModelNavigation without multiple reference");
}
/************************************************************************************************
* * Set via ModelNavigation * *
************************************************************************************************/
/**
* Add a new value following a list of StructualFeatures to a given EObject
*
* @param newValues
* the value to add
*/
public void setToReference(List<EObject> newValues) {
Object value1 = source.eGet(features[0]);
if (features[0].isMany()) {
setFirstMany((List<EObject>)value1, newValues);
} else /* ref is Single */{
setFirstSingle((EObject)value1, newValues);
}
}
/**
* This method add newValue to the managed reference(s) if the first reference in the path is a multiple
* reference
*
* @param ref1Values
* @param newValues
*/
protected void setFirstMany(List<EObject> ref1Values, List<EObject> newValues) {
if (features.length > 1) {
EReference ref2 = features[1];
if (ref2.isMany()) {
setFirstManySecondMany(ref1Values, newValues);
} else {
setFirstManySecondSingle(ref1Values, newValues);
}
} else {
// There is only one multiple reference in the path, we simply add
// the new value to
// the existing values
source.eSet(features[0], newValues);
}
}
/**
* @param ref1Values
* @param newValues
*/
protected void setFirstManySecondMany(List<EObject> ref1Values, List<EObject> newValues) {
throw new IllegalStateException(
"Ambigous case - Cannot process ModelNavigation with more than one multiple reference");
}
/**
* @param ref1Values
* @param newValues
*/
protected void setFirstManySecondSingle(List<EObject> ref1Values, List<EObject> newValues) {
List<EObject> todo = new ArrayList<EObject>(newValues);
List<EObject> toremove = new ArrayList<EObject>();
// First, we check the existing values. Mainly, we create a list of
// elements
// to remove (i.e. not in the new values.
for (EObject ref1Value : ref1Values) {
EObject ref2Value = (EObject)ref1Value.eGet(features[1]);
if (todo.contains(ref2Value))
todo.remove(ref2Value);
else
toremove.add(ref1Value);
}
// We remove those we have detected to remove
for (EObject eObject : toremove) {
ref1Values.remove(eObject);
}
// Finally we have those we don't have found in the existing values
for (EObject eObject : todo) {
EObject intermediate = EcoreUtil.create(features[0].getEReferenceType());
intermediate.eSet(features[1], eObject);
ref1Values.add(intermediate);
}
}
/**
* This method add newValue to the managed reference(s) if the first reference in the path is a single
* reference
*
* @param ref1Value
* @param newValues
* @param ref
*/
protected void setFirstSingle(EObject ref1Value, List<EObject> newValues) {
if (features.length > 1) {
if (features[1].isMany()) {
setFirstSingleSecondMany(ref1Value, newValues);
} else {
setFirstSingleSecondSingle(features[1], newValues);
}
} else {
// There is only one single reference in the path, we simply add the
// new value to
// the existing values. Must be an error ?
source.eSet(features[0], newValues);
}
}
/**
* @param ref1Value
* @param newValues
*/
protected void setFirstSingleSecondMany(EObject ref1Value, List<EObject> newValues) {
if (ref1Value == null) {
ref1Value = EcoreUtil.create(features[0].getEReferenceType());
// WARNING: Cannot be an abstract class
source.eSet(features[0], ref1Value);
}
ref1Value.eSet(features[1], newValues);
}
/**
* @param value2
* @param newValues
*/
protected void setFirstSingleSecondSingle(Object value2, List<EObject> newValues) {
throw new IllegalStateException(
"Ambigous case - Cannot process ModelNavigation without multiple reference");
}
/************************************************************************************************
* * Remove via ModelNavigation * *
************************************************************************************************/
/**
* Remove a value following a list of StructualFeatures to a given EObject
* @param valueToRemove the value to remove
*/
public void removeFromReference(EObject valueToRemove) {
EReference ref = features[0];
Object value1 = source.eGet(ref);
if (ref.isMany()) {
removeFirstMany((List<EObject>)value1, valueToRemove);
} else /* ref is Single */{
removeFirstSingle((EObject)value1, valueToRemove);
}
}
protected void removeFirstMany(List<EObject> value1, EObject valueToRemove) {
if (features.length > 1) {
EReference ref2 = features[1];
if (ref2.isMany()) {
removeFirstManySecondMany(value1, valueToRemove);
} else { /* ref2 is Single */
removeFirstManySecondSingle(value1, valueToRemove);
}
} else {
// EcoreUtil.delete if the reference is containment.
if (features[0].isContainment()) {
EcoreUtil.delete(valueToRemove);
} else {
value1.remove(valueToRemove);
}
}
}
protected void removeFirstManySecondMany(List<EObject> ref1Values, EObject valueToRemove) {
throw new IllegalStateException(
"Ambigous case - Cannot process ModelNavigation with more than one multiple reference");
}
protected void removeFirstManySecondSingle(List<EObject> value1, EObject valueToRemove) {
EObject elemToRemove = null;
for (EObject elem : value1) {
EObject elem2 = (EObject)((EObject)elem).eGet(features[1]);
if (elem2 != null && elem2.equals(valueToRemove)) {
elemToRemove = elem;
}
}
if (features[0].isContainment()) {
EcoreUtil.delete(elemToRemove);
} else {
value1.remove(elemToRemove);
}
}
protected void removeFirstSingle(EObject value1, EObject valueToRemove) {
if (features.length > 1) {
Object value2 = value1.eGet(features[1]);
if (features[1].isMany()) {
removeFirstSingleSecondMany((List<EObject>)value2, valueToRemove);
} else { /* ref2 is Single */
removeFirstSingleSecondSingle();
}
} else {
// EcoreUtil.delete if the reference is containment.
if (features[0].isContainment()) {
EcoreUtil.delete(valueToRemove);
} else {
source.eUnset(features[0]);
}
}
}
protected void removeFirstSingleSecondMany(List<EObject> value2, EObject valueToRemove) {
if (features[1].isContainment()) {
EcoreUtil.delete(valueToRemove);
} else {
value2.remove(valueToRemove);
}
}
protected void removeFirstSingleSecondSingle() {
if (features[0].isContainment()) {
EcoreUtil.delete((EObject) source.eGet(features[0]));
} else {
source.eUnset(features[0]);
}
}
/**
* Defines if the given value is already contained in the given path
*
* @param toCheck
* the element to check
*/
public boolean contains(EObject toCheck) {
EReference ref = features[0];
Object value1 = source.eGet(ref);
if (ref.isMany()) {
if (features.length > 1) {
EReference ref2 = features[1];
if (ref2.isMany()) {
for (EObject elem : (List<EObject>)value1) {
List<EObject> value2 = (List<EObject>)((EObject)elem).eGet(ref2);
if (value2.contains(toCheck))
return true;
}
return false;
} else { /* ref2 is Single */
EObject elemToRemove = null;
for (EObject elem : (List<EObject>)value1) {
EObject elem2 = (EObject)((EObject)elem).eGet(ref2);
if (elem2 != null && elem2.equals(toCheck)) {
return true;
}
}
return false;
}
} else {
return ((List<EObject>)value1).contains(toCheck);
}
} else /* ref is Single */{
if (features.length > 1) {
EReference ref2 = features[1];
Object value2 = ((EObject)value1).eGet(ref2);
if (ref2.isMany()) {
return ((List<EObject>)value2).contains(toCheck);
} else { /* ref2 is Single */
return value2.equals(toCheck);
}
} else {
return value1.equals(toCheck);
}
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.eef.runtime.ui.widgets.settings.EEFEditorSettings#choiceOfValues(org.eclipse.emf.common.notify.AdapterFactory)
*/
public Object choiceOfValues(AdapterFactory adapterFactory) {
// FIXME: choiceOfValues should be called with the adapterFactory in
// parameter
if (features.length == 1)
return EEFUtils.choiceOfValues(source, features[0]);
else {
if (features.length > 1) {
EObject tmp = EcoreUtil.create((EClass)features[0].getEType());
source.eResource().getContents().add(tmp);
Object result = EEFUtils.choiceOfValues(tmp, features[1]);
EcoreUtil.delete(tmp);
return result;
}
}
return null;
}
/************************************************************************************************
* * Move via ModelNavigation * *
************************************************************************************************/
/**
* Move an element to the new index
*
* @param newValue
* the value to add
*/
public void move(int newIndex, EObject element) {
Object value1 = source.eGet(features[0]);
if (features[0].isMany()) {
moveFirstMany((EList<EObject>)value1, newIndex, element);
} else /* ref is Single */{
moveFirstSingle((EObject)value1, newIndex, element);
}
}
/**
* This method move an element to the managed reference(s) if the first reference in the path is a multiple
* reference
*
* @param ref1Values
* @param newValue
*/
protected void moveFirstMany(EList<EObject> ref1Values, int newIndex, EObject element) {
if (features.length > 1) {
if (features[1].isMany()) {
moveFirstManySecondMany(ref1Values, newIndex, element);
} else {
moveFirstManySecondSingle(ref1Values, newIndex, element);
}
} else {
// There is only one multiple reference in the path, we simply add
// the new value to
// the existing values
((EList<EObject>)ref1Values).move(newIndex, element);
}
}
/**
* @param ref1Values
* @param newValue
*/
protected void moveFirstManySecondMany(EList<EObject> ref1Values, int newIndex, EObject element) {
throw new IllegalStateException(
"Ambigous case - Cannot process ModelNavigation with more than one multiple reference");
}
/**
* @param newValue
* @param ref2
*/
protected void moveFirstManySecondSingle(EList<EObject> ref1Values, int newIndex, EObject element) {
for (EObject eObject : ref1Values) {
if (eObject.eGet(features[1]).equals(element)) {
ref1Values.move(newIndex, eObject);
return;
}
}
}
/**
* This method add newValue to the managed reference(s) if the first reference in the path is a single
* reference
*
* @param ref1Value
* @param newValue
*/
protected void moveFirstSingle(EObject ref1Value, int newIndex, EObject element) {
if (features.length > 1) {
if (features[1].isMany()) {
moveFirstSingleSecondMany(ref1Value, newIndex, element);
} else {
moveFirstSingleSecondSingle(ref1Value, newIndex, element);
}
} else {
// There is only one single reference in the path, we simply add the
// new value to
// the existing values. Must be an error ?
throw new IllegalStateException("Ambigous case - Cannot process move for ModelNavigation with one single reference");
}
}
/**
* @param ref1Value
* @param newValue
*/
protected void moveFirstSingleSecondMany(EObject ref1Value, int newIndex, EObject element) {
((EList<EObject>)ref1Value.eGet(features[1])).move(newIndex, element);
}
/**
* @param ref1Value
* @param newValue
*/
protected void moveFirstSingleSecondSingle(EObject ref1Value, int newIndex, EObject element) {
throw new IllegalStateException(
"Ambigous case - Cannot process ModelNavigation without multiple reference");
}
}