blob: a33be23e07890d463e8d9e6d42291f611d2d782e [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 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:
* Eugen Neufeld - initial API and implementation
*
*******************************************************************************/
package org.eclipse.emf.ecp.view.spi.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.ecp.view.internal.editor.controls.ECPSelectionStatusValidator;
import org.eclipse.emf.ecp.view.internal.editor.controls.ECPViewEditorTreeSelectionDialog;
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.ITreeContentProvider;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Shell;
/**
* 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
* @since 1.5
*/
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() {
final AdapterFactoryLabelProvider labelProvider = new AdapterFactoryLabelProvider(composedAdapterFactory);
final ECPViewEditorTreeSelectionDialog dialog = new ECPViewEditorTreeSelectionDialog(shell, labelProvider,
new EReferenceTreeContenProvider());
validator.setECPViewEditorTreeSelectionDialog(dialog);
dialog.setAllowMultiple(false);
dialog.setValidator(validator);
dialog.setInput(rootClass != null ? rootClass : EPackage.Registry.INSTANCE);
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);
/**
* Content provider for a tree showing all {@link EReference}s within a {@link EClass} or {@link EPackage}.
*
* @author Jonas Helming
*
*/
private final class EReferenceTreeContenProvider implements ITreeContentProvider {
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
// Do nothing (no input change expected/supported)
}
@Override
public void dispose() {
// Do Nothing, no listeners instanciated
}
@Override
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;
}
@Override
public Object getParent(Object element) {
// TODO Auto-generated method stub
return null;
}
@Override
public Object[] getElements(Object inputElement) {
return getChildren(inputElement);
}
@Override
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();
}
if (EPackage.Registry.class.isInstance(parentElement)) {
return EPackage.Registry.class.cast(parentElement).values().toArray();
}
if (EPackage.class.isInstance(parentElement)) {
final Set<Object> children = new LinkedHashSet<Object>();
children.addAll(EPackage.class.cast(parentElement).getESubpackages());
children.addAll(EPackage.class.cast(parentElement).getEClassifiers());
return children.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;
}
}
}