| /******************************************************************************* |
| * 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); |
| } |
| } |