blob: 5ad5ab57c862f6809adca6ae4d85fa34d7b3a20d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 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.eef.ide.ui.ext.widgets.reference.internal;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.eef.common.api.utils.Util;
import org.eclipse.eef.core.api.EditingContextAdapter;
import org.eclipse.eef.ide.ui.ext.widgets.reference.api.IEEFExtReferenceViewerFilterProvider.ContextKind;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.edit.command.CommandParameter;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.provider.IEditingDomainItemProvider;
import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Tree;
/**
* This page is used to create a new EObject for the given reference.
*
* @author sbegaudeau
*/
public class EEFExtEObjectCreationPage extends WizardPage {
/**
* The target.
*/
private EObject target;
/**
* The EReference.
*/
private EReference eReference;
/**
* The editing context adapter.
*/
private EditingContextAdapter editingContextAdapter;
/**
* The {@link ComposedAdapterFactory} used to retrieve the label and image of the various EObjects visible in the
* user interface.
*/
private ComposedAdapterFactory composedAdapterFactory;
/**
* The combo viewer used to select the EClass of the element to create.
*/
private ComboViewer eClassInstanceComboViewer;
/**
* This listener will react to selection changes in the combo viewer.
*/
private ISelectionChangedListener eClassInstanceComboViewerListener;
/**
* The tree viewer used to select the container of the new EObject to create.
*/
private TreeViewer eContainerTreeViewer;
/**
* This listener will react to selection changes in the tree viewer.
*/
private ISelectionChangedListener eContainerTreeViewerListener;
/**
* The combo viewer used to select the containment reference to use for the creation of the EObject.
*/
private ComboViewer eContainementReferenceComboViewer;
/**
* This listener will react to selection changes in the combo viewer.
*/
private ISelectionChangedListener eContainmentReferenceComboViewerListener;
/**
* The constructor.
*
* @param target
* The target
* @param eReference
* The EReference
* @param editingContextAdapter
* The editing context adapter
*/
public EEFExtEObjectCreationPage(EObject target, EReference eReference, EditingContextAdapter editingContextAdapter) {
super(Messages.ReferenceCreationWizardPage_title);
this.target = target;
this.eReference = eReference;
this.editingContextAdapter = editingContextAdapter;
this.setTitle(Messages.ReferenceCreationWizardPage_title);
this.setDescription(Messages.ReferenceCreationWizardPage_description);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
*/
@Override
public void createControl(Composite parent) {
this.initializeDialogUnits(parent);
Composite control = new Composite(parent, SWT.NONE);
control.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
this.setControl(control);
GridLayout gridLayout = new GridLayout(2, false);
control.setLayout(gridLayout);
this.composedAdapterFactory = new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE);
this.composedAdapterFactory.addAdapterFactory(new ReflectiveItemProviderAdapterFactory());
if (this.eReference.isContainment()) {
this.createEObjectEClassComboViewer(control);
this.initializeContainmentInput(this.target, this.eReference);
} else {
this.createContainerTreeViewer(control);
this.createContainmentFeatureComboViewer(control);
this.createEObjectEClassComboViewer(control);
this.initializeNonContainmentInput();
}
this.determinePageCompletion();
}
/**
* Initializes the input of the page for a containment EReference.
*
* @param eObject
* The EObject to consider
* @param eContainementReference
* The containment EReference to consider
*/
private void initializeContainmentInput(EObject eObject, EReference eContainementReference) {
List<Object> values = new ArrayList<>();
Adapter adapter = this.composedAdapterFactory.adapt(eObject, IEditingDomainItemProvider.class);
if (adapter instanceof IEditingDomainItemProvider) {
IEditingDomainItemProvider itemProviderAdapter = (IEditingDomainItemProvider) adapter;
Collection<?> newChildDescriptors = itemProviderAdapter.getNewChildDescriptors(eObject, this.editingContextAdapter.getEditingDomain(),
null);
for (Object newChildDescriptor : newChildDescriptors) {
if (newChildDescriptor instanceof CommandParameter) {
CommandParameter commandParameter = (CommandParameter) newChildDescriptor;
Object value = commandParameter.getValue();
if (commandParameter.getEReference().equals(eContainementReference) && value instanceof EObject
&& this.eReference.getEReferenceType().isSuperTypeOf(((EObject) value).eClass())) {
values.add(commandParameter.getValue());
}
}
}
}
this.eClassInstanceComboViewer.setInput(values);
if (values.size() > 0) {
this.eClassInstanceComboViewer.setSelection(new StructuredSelection(values.get(0)));
} else {
this.eClassInstanceComboViewer.setSelection(new StructuredSelection());
}
}
/**
* Initializes the input of the page for a non containment EReference.
*/
private void initializeNonContainmentInput() {
this.eContainerTreeViewer.setInput(this.editingContextAdapter.getEditingDomain().getResourceSet());
}
/**
* Creates the tree viewer.
*
* @param parent
* The parent composite
*/
private void createContainerTreeViewer(Composite parent) {
Label label = new Label(parent, SWT.NONE);
label.setText(Messages.ReferenceCreationWizardPage_eContainerSelectionLabel);
label.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
this.eContainerTreeViewer = new TreeViewer(new Tree(parent, SWT.SINGLE | SWT.FULL_SELECTION | SWT.BORDER));
this.eContainerTreeViewer.setLabelProvider(new DelegatingStyledCellLabelProvider(
new AdapterFactoryLabelProvider.StyledLabelProvider(this.composedAdapterFactory, this.eContainerTreeViewer)));
this.eContainerTreeViewer.setContentProvider(new AdapterFactoryContentProvider(this.composedAdapterFactory));
this.eContainerTreeViewer.getTree().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
this.eContainerTreeViewer.setAutoExpandLevel(2);
List<ViewerFilter> viewFilters = EEFExtReferenceUIPlugin.getPlugin().getViewFilters(ContextKind.CONTAINER_SELECTION);
this.eContainerTreeViewer.setFilters(viewFilters.toArray(new ViewerFilter[viewFilters.size()]));
this.eContainerTreeViewerListener = new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
EEFExtEObjectCreationPage.this.handleEContainerSelectionChanged();
}
};
this.eContainerTreeViewer.addSelectionChangedListener(this.eContainerTreeViewerListener);
}
/**
* This methods is called when the selection changes in the EContainer tree viewer.
*/
protected void handleEContainerSelectionChanged() {
EObject eContainer = this.getEObject(this.eContainerTreeViewer);
if (eContainer != null) {
List<EReference> eReferences = this.getValidContainmentReferences(eContainer);
this.eContainementReferenceComboViewer.setInput(eReferences);
if (eReferences.size() > 0) {
this.eContainementReferenceComboViewer.setSelection(new StructuredSelection(eReferences.get(0)));
} else {
this.eContainementReferenceComboViewer.setSelection(new StructuredSelection());
this.eClassInstanceComboViewer.setSelection(new StructuredSelection());
}
} else {
this.eContainementReferenceComboViewer.setSelection(new StructuredSelection());
this.eClassInstanceComboViewer.setSelection(new StructuredSelection());
}
this.determinePageCompletion();
}
/**
* Returns the currently selected EObject in the given viewer.
*
* @param viewer
* The viewer
*
* @return The currently selected EObject in the given viewer, or <code>null</code> if the current selection is
* empty or not an EObject (for example an EResource)
*/
private EObject getEObject(StructuredViewer viewer) {
ISelection selection = viewer.getSelection();
if (selection instanceof IStructuredSelection) {
IStructuredSelection structuredSelection = (IStructuredSelection) selection;
Object object = structuredSelection.getFirstElement();
if (object instanceof EObject) {
return (EObject) object;
}
}
return null;
}
/**
* Returns a list of all the containment references of the given EObject which have a type compatible with the type
* of the current EReference.
*
* @param eObject
* The EObject
* @return A list of EReference
*/
private List<EReference> getValidContainmentReferences(EObject eObject) {
List<EReference> eReferences = new ArrayList<>();
List<EStructuralFeature> eAllStructuralFeatures = eObject.eClass().getEAllStructuralFeatures();
for (EStructuralFeature eStructuralFeature : eAllStructuralFeatures) {
if (eStructuralFeature instanceof EReference) {
EReference reference = (EReference) eStructuralFeature;
if (reference.isContainment() && reference.getEReferenceType().isSuperTypeOf(this.eReference.getEReferenceType())) {
eReferences.add(reference);
}
}
}
return eReferences;
}
/**
* Creates the combo viewer to display the possible containment EReferences to use to create the new EObject.
*
* @param parent
* The parent composite
*/
private void createContainmentFeatureComboViewer(Composite parent) {
Label label = new Label(parent, SWT.NONE);
label.setText(Messages.ReferenceCreationWizardPage_eContainerToUseLabel);
label.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
this.eContainementReferenceComboViewer = new ComboViewer(new Combo(parent, SWT.DROP_DOWN | SWT.READ_ONLY));
this.eContainementReferenceComboViewer.setLabelProvider(new AdapterFactoryLabelProvider(this.composedAdapterFactory));
this.eContainementReferenceComboViewer.setContentProvider(new ArrayContentProvider());
this.eContainementReferenceComboViewer.getCombo().setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
this.eContainmentReferenceComboViewerListener = new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
EEFExtEObjectCreationPage.this.handleEContainmentReferenceSelectionChange();
}
};
this.eContainementReferenceComboViewer.addSelectionChangedListener(this.eContainmentReferenceComboViewerListener);
}
/**
* This methods is called when the selection changes in the containment EReference combo viewer.
*/
private void handleEContainmentReferenceSelectionChange() {
EObject eContainer = this.getEObject(this.eContainerTreeViewer);
if (eContainer != null) {
EObject eContainmentReference = this.getEObject(this.eContainementReferenceComboViewer);
if (eContainmentReference instanceof EReference) {
this.initializeContainmentInput(eContainer, (EReference) eContainmentReference);
}
}
this.determinePageCompletion();
}
/**
* Creates the combo viewer used to display the EClass of the possible EObjects that can be created.
*
* @param parent
* The parent composite
*/
private void createEObjectEClassComboViewer(Composite parent) {
Label label = new Label(parent, SWT.NONE);
label.setText(Messages.ReferenceCreationWizardPage_eClassToCreateLabel);
label.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
this.eClassInstanceComboViewer = new ComboViewer(new Combo(parent, SWT.DROP_DOWN | SWT.READ_ONLY));
this.eClassInstanceComboViewer.setLabelProvider(new AdapterFactoryLabelProvider(this.composedAdapterFactory) {
@Override
public String getText(Object object) {
String result = super.getText(object);
if (Util.isBlank(result) && object instanceof EObject) {
AdapterFactoryLabelProvider labelProvider = new AdapterFactoryLabelProvider(composedAdapterFactory);
result = labelProvider.getText(((EObject) object).eClass());
}
return result;
}
});
this.eClassInstanceComboViewer.setContentProvider(new ArrayContentProvider());
this.eClassInstanceComboViewer.getCombo().setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
this.eClassInstanceComboViewerListener = new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
EEFExtEObjectCreationPage.this.determinePageCompletion();
}
};
this.eClassInstanceComboViewer.addSelectionChangedListener(this.eClassInstanceComboViewerListener);
}
/**
* Determines if the page is complete or not.
*/
private void determinePageCompletion() {
this.setMessage(null);
boolean isPageComplete = false;
if (this.eReference.isContainment()) {
isPageComplete = this.isCompleteViewer(true, this.eClassInstanceComboViewer, Messages.ReferenceCreationWizardPage_missingEClassToCreate);
} else {
String message = MessageFormat.format(Messages.ReferenceCreationWizardPage_missingEContainer,
this.eReference.getEReferenceType().getName());
isPageComplete = this.isCompleteViewer(true, this.eContainerTreeViewer, message);
isPageComplete = this.isCompleteViewer(isPageComplete, this.eContainementReferenceComboViewer,
Messages.ReferenceCreationWizardPage_missingContainmentEReference);
isPageComplete = this.isCompleteViewer(isPageComplete, this.eClassInstanceComboViewer,
Messages.ReferenceCreationWizardPage_missingEClassToCreate);
}
this.setPageComplete(isPageComplete);
}
/**
* Verifies if the given viewer is complete and if not, set the given error message.
*
* @param isCurrentlyComplete
* The currently completion status
* @param viewer
* The viewer
* @param errorMessage
* The error message
* @return <code>true</code> if the wizard is currently complete and the viewer too, <code>false</code> otherwise
*/
private boolean isCompleteViewer(boolean isCurrentlyComplete, StructuredViewer viewer, String errorMessage) {
boolean isComplete = isCurrentlyComplete;
if (isCurrentlyComplete) {
boolean isViewerComplete = this.getEObject(viewer) != null;
isComplete = isComplete && isViewerComplete;
if (!isViewerComplete) {
this.setMessage(errorMessage, ERROR);
}
}
return isComplete;
}
/**
* Creates the EObject.
*
* @param monitor
* The {@link IProgressMonitor}
*/
public void performFinish(IProgressMonitor monitor) {
EObject eObject = this.getEObject(this.eClassInstanceComboViewer);
if (eObject != null) {
if (this.eReference.isContainment() && this.eReference.isMany()) {
this.performFinishMultiValuedContainmentReference(eObject);
} else if (this.eReference.isContainment() && !this.eReference.isMany()) {
this.performFinistMonoValuedContainmentReference(eObject);
} else if (!this.eReference.isContainment() && this.eReference.isMany()) {
this.performFinistMultiValuedNonContainmentReference(eObject);
} else if (!this.eReference.isContainment() && !this.eReference.isMany()) {
this.performFinistMonoValuedNonContainmentReference(eObject);
}
}
}
/**
* Performs the creation of the object for a multi-valued containment reference. In this case, the operation is
* quite simple, we will just retrieve the collection containing the values of the reference and add the new
* element, value, at the end.
*
* @param value
* The element to create
*/
private void performFinishMultiValuedContainmentReference(EObject value) {
this.eContainmentAdd(this.target, this.eReference, value);
}
/**
* Performs the creation of the object for a mono-valued containment reference. In this case, the easiest one, we
* will just create set the value for the reference.
*
* @param value
* The element to create
*/
private void performFinistMonoValuedContainmentReference(EObject value) {
this.target.eSet(this.eReference, value);
}
/**
* Performs the creation of the object for a multi-valued reference. In this case, we will create the object in a
* mono or multi-valued containment reference and then add it to the multi-valued reference of our current object.
*
* @param value
* The element to create
*/
private void performFinistMultiValuedNonContainmentReference(EObject value) {
EObject eContainer = this.getEObject(this.eContainerTreeViewer);
EObject eContainmentReferenceEObject = this.getEObject(this.eContainementReferenceComboViewer);
if (eContainer != null && eContainmentReferenceEObject instanceof EReference) {
EReference eContainmentReference = (EReference) eContainmentReferenceEObject;
if (eContainmentReference.isMany()) {
this.eContainmentAdd(eContainer, (EReference) eContainmentReferenceEObject, value);
} else {
eContainer.eSet(eContainmentReference, value);
}
this.eContainmentAdd(this.target, this.eReference, value);
}
}
/**
* Performs the creation of the object for a mono-valued reference. In this case, we will create the object in a
* mono or multi-valued containment reference and then add it to the mono-valued reference of our current object.
*
* @param value
* The element to create
*/
private void performFinistMonoValuedNonContainmentReference(EObject value) {
EObject eContainer = this.getEObject(this.eContainerTreeViewer);
EObject eContainmentReferenceEObject = this.getEObject(this.eContainementReferenceComboViewer);
if (eContainer != null && eContainmentReferenceEObject instanceof EReference) {
EReference eContainmentReference = (EReference) eContainmentReferenceEObject;
if (eContainmentReference.isMany()) {
this.eContainmentAdd(eContainer, (EReference) eContainmentReferenceEObject, value);
} else {
eContainer.eSet(eContainmentReference, value);
}
this.target.eSet(this.eReference, value);
}
}
/**
* Adds the given eObject to the value of the given EReference for the given container EObject.
*
* @param eContainer
* The container
* @param eContainmentReference
* The containment EReference
* @param eObject
* The EObject
*/
private void eContainmentAdd(EObject eContainer, EReference eContainmentReference, EObject eObject) {
Object value = eContainer.eGet(eContainmentReference);
if (value instanceof EList<?>) {
// sbegaudeau: Yes I know, @SuppressWarnings are bad but we really need this one in order to prevent the
// creation of a new list which would fire, as a side effect, a modification notification for all the
// siblings of the newly created object. We do not want anyone to believe that those objects have been
// modified
@SuppressWarnings("unchecked")
EList<EObject> objects = (EList<EObject>) value;
objects.add(eObject);
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.jface.dialogs.DialogPage#dispose()
*/
@Override
public void dispose() {
super.dispose();
this.composedAdapterFactory.dispose();
this.eClassInstanceComboViewer.removeSelectionChangedListener(this.eClassInstanceComboViewerListener);
}
}