blob: d4e25c61a239010df93d74823cceb7269424c1f2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011-2013 EclipseSource Muenchen GmbH 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:
* Eugen Neufeld - initial API and implementation
*
*******************************************************************************/
package org.eclipse.emf.ecp.view.editor.controls;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.edit.command.ChangeCommand;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.dialogs.ElementTreeSelectionDialog;
import org.eclipse.ui.dialogs.ISelectionStatusValidator;
/**
* A ReferenceCommand allowing to select an {@link EStructuralFeature} using a dialog.
*
* @author Eugen Neufeld
*
* @param <T> type of the {@link EStructuralFeature} which can be selected
*/
public abstract class AbstractFilteredReferenceCommand<T extends EStructuralFeature> extends ChangeCommand {
private final Shell shell;
private final ComposedAdapterFactory composedAdapterFactory;
private final EClass rootClass;
private final ECPSelectionStatusValidator validator;
private final boolean allowMultiReferences;
/**
* Constructor for the AbstractFilteredReferenceCommand.
*
* @param notifier the Notifier for the {@link ChangeCommand}
* @param composedAdapterFactory the {@link ComposedAdapterFactory} for the LabelProvider
* @param shell the {@link Shell} to use in the dialog
* @param rootClass the {@link EClass} which is the root of the view
* @param validator the {@link ECPSelectionStatusValidator} to use when a selection was done
* @param allowMultiReferences whether multi references are allowed during the selection
*/
public AbstractFilteredReferenceCommand(Notifier notifier, ComposedAdapterFactory composedAdapterFactory,
Shell shell, EClass rootClass, ECPSelectionStatusValidator validator, boolean allowMultiReferences) {
super(notifier);
this.shell = shell;
this.composedAdapterFactory = composedAdapterFactory;
this.rootClass = rootClass;
this.validator = validator;
this.allowMultiReferences = allowMultiReferences;
}
private Class<?> returnedClass() {
final ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
return (Class<?>) parameterizedType.getActualTypeArguments()[0];
}
@Override
protected void doExecute() {
if (rootClass == null) {
return;
}
final AdapterFactoryLabelProvider labelProvider = new AdapterFactoryLabelProvider(composedAdapterFactory);
final ECPViewEditorTreeSelectionDialog dialog = new ECPViewEditorTreeSelectionDialog(shell, labelProvider,
getContentProvider(rootClass));
validator.setECPViewEditorTreeSelectionDialog(dialog);
dialog.setAllowMultiple(false);
dialog.setValidator(validator);
dialog.setInput(rootClass);
dialog.setMessage("Select a " + returnedClass().getSimpleName());
dialog.setTitle("Select a " + returnedClass().getSimpleName());
final int result = dialog.open();
if (Window.OK == result) {
final Object selection = dialog.getFirstResult();
if (returnedClass().isInstance(selection)) {
final T selectedFeature = (T) selection;
final List<EReference> bottomUpPath = new ArrayList<EReference>();
final TreePath path = dialog.getTreePath();
for (int i = 0; i < path.getSegmentCount() - 1; i++) {
final Object o = path.getSegment(i);
if (EReference.class.isInstance(o)) {
bottomUpPath.add((EReference) o);
}
}
setSelectedValues(selectedFeature, bottomUpPath);
}
}
labelProvider.dispose();
}
/**
* Template method which is called so that the selected element can be saved.
*
* @param selectedFeature the {@link EStructuralFeature} to set
* @param bottomUpPath the path to the feature
*/
protected abstract void setSelectedValues(T selectedFeature, List<EReference> bottomUpPath);
private ITreeContentProvider getContentProvider(EClass rootClass) {
return new ITreeContentProvider() {
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
// TODO Auto-generated method stub
}
public void dispose() {
// TODO Auto-generated method stub
}
public boolean hasChildren(Object element) {
if (EPackage.class.isInstance(element)) {
return true;
}
if (EClass.class.isInstance(element)) {
final EClass eClass = (EClass) element;
final boolean hasReferences = !eClass.getEAllReferences().isEmpty();
final boolean hasAttributes = !eClass.getEAllAttributes().isEmpty();
return hasReferences || hasAttributes;
}
if (EReference.class.isInstance(element)) {
final EReference eReference = (EReference) element;
return eReference.isMany() && !allowMultiReferences ? false : hasChildren(eReference
.getEReferenceType());
}
return false;
}
public Object getParent(Object element) {
// TODO Auto-generated method stub
return null;
}
public Object[] getElements(Object inputElement) {
return getChildren(inputElement);
}
public Object[] getChildren(Object parentElement) {
if (EClass.class.isInstance(parentElement)) {
final EClass eClass = (EClass) parentElement;
final Set<Object> result = getElementsForEClass(eClass);
return result.toArray();
}
if (EReference.class.isInstance(parentElement)) {
final EReference eReference = (EReference) parentElement;
final Set<Object> result = getElementsForEClass(eReference.getEReferenceType());
return result.toArray();
}
return null;
}
private Set<Object> getElementsForEClass(EClass eClass) {
final Set<Object> result = new LinkedHashSet<Object>();
if (eClass.isAbstract() || eClass.isInterface()) {
// find eClasses which are not abstract
for (final EClassifier eClassifier : eClass.getEPackage().getEClassifiers()) {
if (eClass != eClassifier && EClass.class.isInstance(eClassifier)
&& eClass.isSuperTypeOf((EClass) eClassifier)) {
result.add(eClassifier);
}
}
}
else {
result.addAll(eClass.getEAllReferences());
result.addAll(eClass.getEAllAttributes());
}
return result;
}
};
}
/**
* ElementTreeSelectionDialog which also stores the selection along the tree.
*
* @author Eugen Neufeld
*
*/
private static class ECPViewEditorTreeSelectionDialog extends ElementTreeSelectionDialog {
private TreePath treePath;
/**
* Default constructor.
*
* @param parent the {@link Shell} for creating the dialog
* @param labelProvider the {@link ILabelProvider} for the tree
* @param contentProvider the {@link ITreeContentProvider} for the tree
*/
public ECPViewEditorTreeSelectionDialog(Shell parent, ILabelProvider labelProvider,
ITreeContentProvider contentProvider) {
super(parent, labelProvider, contentProvider);
}
public TreePath getTreePath() {
if (getTreeViewer() != null) {
final TreeSelection selection = (TreeSelection) getTreeViewer().getSelection();
if (!selection.isEmpty()) {
treePath = selection.getPaths()[0];
}
}
return treePath;
}
// /**
// * {@inheritDoc}
// *
// * @see org.eclipse.ui.dialogs.ElementTreeSelectionDialog#computeResult()
// */
// @Override
// protected void computeResult() {
// treePath = ((TreeSelection) getTreeViewer().getSelection()).getPaths()[0];
// super.computeResult();
// }
}
static abstract class ECPSelectionStatusValidator implements ISelectionStatusValidator {
private ECPViewEditorTreeSelectionDialog dialog;
private void setECPViewEditorTreeSelectionDialog(ECPViewEditorTreeSelectionDialog dialog) {
this.dialog = dialog;
}
protected TreePath getTreePath() {
return dialog.getTreePath();
}
}
}