| /******************************************************************************* |
| * Copyright (c) 2012, 2018 Willink Transformations and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v2.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v20.html |
| * |
| * Contributors: |
| * E.D.Willink - Initial API and implementation |
| * Obeo - Enable export and re-use CompleteOCL files for validation |
| *******************************************************************************/ |
| package org.eclipse.ocl.xtext.completeocl.ui.commands; |
| |
| import java.util.Arrays; |
| import java.util.Comparator; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.core.commands.AbstractHandler; |
| import org.eclipse.core.commands.ExecutionEvent; |
| import org.eclipse.core.commands.ExecutionException; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.emf.common.ui.dialogs.DiagnosticDialog; |
| import org.eclipse.emf.common.util.BasicDiagnostic; |
| import org.eclipse.emf.common.util.Diagnostic; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.provider.EcoreEditPlugin; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.resource.ResourceSet; |
| import org.eclipse.emf.edit.domain.EditingDomain; |
| import org.eclipse.emf.edit.domain.IEditingDomainProvider; |
| import org.eclipse.emf.edit.ui.action.LoadResourceAction.LoadResourceDialog; |
| import org.eclipse.emf.edit.ui.provider.ExtendedImageRegistry; |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.jface.dialogs.ErrorDialog; |
| import org.eclipse.jface.dialogs.IDialogConstants; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.viewers.LabelProvider; |
| import org.eclipse.ocl.pivot.internal.registry.CompleteOCLRegistry; |
| import org.eclipse.ocl.pivot.internal.resource.OCLAdapter; |
| import org.eclipse.ocl.pivot.internal.utilities.EnvironmentFactoryInternal; |
| import org.eclipse.ocl.pivot.internal.utilities.PivotUtilInternal; |
| import org.eclipse.ocl.pivot.utilities.ClassUtil; |
| import org.eclipse.ocl.xtext.base.ui.utilities.PDEUtils; |
| import org.eclipse.ocl.xtext.completeocl.ui.CompleteOCLUiModule; |
| import org.eclipse.ocl.xtext.completeocl.ui.messages.CompleteOCLUIMessages; |
| import org.eclipse.ocl.xtext.completeocl.utilities.CompleteOCLLoader; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.dnd.DND; |
| import org.eclipse.swt.dnd.DropTarget; |
| import org.eclipse.swt.dnd.DropTargetAdapter; |
| import org.eclipse.swt.dnd.DropTargetEvent; |
| import org.eclipse.swt.dnd.FileTransfer; |
| import org.eclipse.swt.dnd.Transfer; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.layout.FormAttachment; |
| import org.eclipse.swt.layout.FormData; |
| import org.eclipse.swt.widgets.Button; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.ISources; |
| import org.eclipse.ui.dialogs.ElementListSelectionDialog; |
| import org.eclipse.ui.handlers.HandlerUtil; |
| import org.eclipse.ui.part.ResourceTransfer; |
| import org.eclipse.xtext.resource.XtextResource; |
| import org.eclipse.xtext.ui.editor.XtextEditor; |
| import org.eclipse.xtext.ui.editor.model.IXtextDocument; |
| import org.eclipse.xtext.util.concurrent.IUnitOfWork; |
| |
| /** |
| * A LoadCompleteOCLResourceHandler supports the OCL->Load Document command. |
| * |
| * It provides a pop-up dialog with DND capability for a Complete OCL document to be installed in the |
| * ResourceSet associated with the invoking selection. |
| */ |
| public class LoadCompleteOCLResourceHandler extends AbstractHandler |
| { |
| protected class ResourceDialog extends LoadResourceDialog |
| { |
| public class URIDropTargetListener extends DropTargetAdapter |
| { |
| @Override |
| public void dragEnter(DropTargetEvent e) { |
| e.detail = DND.DROP_LINK; |
| } |
| |
| @Override |
| public void dragOperationChanged(DropTargetEvent e) { |
| e.detail = DND.DROP_LINK; |
| } |
| |
| @Override |
| public void drop(DropTargetEvent event) { |
| Object data = event.data; |
| if (data == null) { |
| event.detail = DND.DROP_NONE; |
| return; |
| } |
| if (data instanceof IResource[]) { |
| StringBuilder s = new StringBuilder(); |
| for (IResource resource : (IResource[])data) { |
| if (s.length() > 0) { |
| s.append(" "); |
| } |
| s.append(URI.createPlatformResourceURI(resource.getFullPath().toString(), true)); |
| } |
| uriField.setText(s.toString()); |
| } |
| else if (data instanceof String[]) { |
| StringBuilder s = new StringBuilder(); |
| for (String resource : (String[])data) { |
| if (s.length() > 0) { |
| s.append(" "); |
| } |
| s.append(URI.createFileURI(resource)); |
| } |
| uriField.setText(s.toString()); |
| } |
| else { |
| uriField.setText(((String) data)); |
| } |
| } |
| } |
| |
| /** |
| * Job scheduled on a worker thread to compute the editor text. |
| */ |
| protected class DeferredLoadDocumentJob extends Job |
| { |
| private final @NonNull List<URI> uris; |
| private boolean processedResourcesReturn = false; |
| |
| public DeferredLoadDocumentJob(@NonNull List<URI> uris) { |
| super("Deferred Load OCL Document"); |
| this.uris = uris; |
| } |
| |
| @Override |
| protected IStatus run(final IProgressMonitor monitor) { |
| processedResourcesReturn = processResources(); |
| Display.getDefault().asyncExec(new Runnable() |
| { |
| @Override |
| public void run() { |
| okPressed(); |
| } |
| }); |
| return Status.OK_STATUS; |
| } |
| |
| protected boolean processResources() { |
| OCLAdapter oclAdapter = OCLAdapter.getAdapter(resourceSet); |
| CompleteOCLLoader helper = new CompleteOCLLoader(oclAdapter.getEnvironmentFactory()) { |
| @Override |
| protected boolean error(@NonNull String primaryMessage, @Nullable String detailMessage) { |
| Display.getDefault().asyncExec(new Runnable() |
| { |
| @Override |
| public void run() { |
| ResourceDialog.this.error(primaryMessage, detailMessage); |
| } |
| }); |
| return false; |
| } |
| }; |
| |
| if (!helper.loadMetamodels()) { |
| return false; |
| } |
| // |
| // Load all the documents |
| // |
| for (URI oclURI : uris) { |
| assert oclURI != null; |
| try { |
| if (!helper.loadDocument(oclURI)) { |
| return false; |
| }; |
| } |
| catch (Throwable e) { |
| IStatus status = new Status(IStatus.ERROR, CompleteOCLUiModule.PLUGIN_ID, e.getLocalizedMessage(), e); |
| Display.getDefault().asyncExec(new Runnable() |
| { |
| @Override |
| public void run() { |
| ErrorDialog.openError(parent, "OCL->Load Document Failure", "Failed to load '" + oclURI + "'", status); |
| } |
| }); |
| return false; |
| } |
| } |
| helper.installPackages(); |
| return true; |
| } |
| |
| public boolean getProcessedResourcesReturn() { |
| return processedResourcesReturn; |
| } |
| } |
| |
| protected final Shell parent; |
| protected final @NonNull ResourceSet resourceSet; |
| private DropTarget target; |
| private Set<URI> registeredURIsForResourceSet; |
| private DeferredLoadDocumentJob job = null; |
| |
| protected ResourceDialog(Shell parent, EditingDomain domain, @NonNull ResourceSet resourceSet) { |
| super(parent, domain); |
| this.parent = parent; |
| this.resourceSet = resourceSet; |
| int shellStyle = getShellStyle(); |
| int newShellStyle = shellStyle & ~(SWT.APPLICATION_MODAL | SWT.PRIMARY_MODAL | SWT.SYSTEM_MODAL); |
| setShellStyle(newShellStyle); |
| } |
| |
| @Override |
| protected void configureShell(Shell shell) { |
| super.configureShell(shell); |
| shell.setText("Load Complete OCL Document"); |
| } |
| |
| @Override |
| protected Control createContents(Composite parent) { |
| Control control = super.createContents(parent); |
| int operations = /*DND.DROP_MOVE |*/ DND.DROP_COPY | DND.DROP_LINK; |
| target = new DropTarget(uriField, operations); |
| target.setTransfer(new Transfer[] {ResourceTransfer.getInstance(), FileTransfer.getInstance()}); |
| target.addDropListener(new URIDropTargetListener()); |
| return control; |
| } |
| |
| @Override |
| protected Control createDialogArea(Composite parent) { |
| Composite createDialogArea = (Composite) super.createDialogArea(parent); |
| |
| // Button composite is the first children and we are expected to retrieve it as such... |
| Composite buttonComposite = (Composite)createDialogArea.getChildren()[0]; |
| |
| Button browseRegisteredOCLFiles = new Button(buttonComposite, SWT.PUSH); |
| browseRegisteredOCLFiles.setText(CompleteOCLUIMessages.LoadCompleteOCLResource_BrowseRegisteredOCLFiles); |
| prepareBrowseRegisteredOCLFiles(browseRegisteredOCLFiles); |
| // registeredURIsForResourceSet = CompleteOCLRegistry.INSTANCE.getResourceURIs(resourceSet); |
| CompleteOCLRegistry registry = PDEUtils.createCompleteOCLRegistry(); |
| registeredURIsForResourceSet = registry.getResourceURIs(resourceSet); |
| if (registeredURIsForResourceSet.isEmpty()) { |
| browseRegisteredOCLFiles.setEnabled(false); |
| } else { |
| browseRegisteredOCLFiles.setEnabled(true); |
| } |
| |
| { |
| FormData data = new FormData(); |
| Control [] children = buttonComposite.getChildren(); |
| data.right = new FormAttachment(children[0], -CONTROL_OFFSET); |
| browseRegisteredOCLFiles.setLayoutData(data); |
| } |
| |
| Label helpLabel = new Label(createDialogArea, SWT.CENTER); |
| helpLabel.setText("You may Drag and Drop from an Eclipse or Operating System Explorer."); |
| { |
| FormData data = new FormData(); |
| data.top = new FormAttachment(uriField, 2 * CONTROL_OFFSET); // Separator is at 1 * CONTROL_OFFSET |
| data.left = new FormAttachment(0, CONTROL_OFFSET); |
| data.right = new FormAttachment(100, -CONTROL_OFFSET); |
| helpLabel.setLayoutData(data); |
| } |
| |
| return createDialogArea; |
| } |
| |
| private void prepareBrowseRegisteredOCLFiles(Button browseRegisteredOCLFiles) { |
| browseRegisteredOCLFiles.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent event) { |
| RegisteredOCLFilesDialog registeredOCLFilesDialog = new RegisteredOCLFilesDialog(getShell()); |
| registeredOCLFilesDialog.open(); |
| Object[] result = registeredOCLFilesDialog.getResult(); |
| if (result != null) { |
| StringBuffer uris = new StringBuffer(); |
| |
| for (int i = 0, length = result.length; i < length; i++) { |
| uris.append(result[i]); |
| uris.append(" "); |
| } |
| uriField.setText((uriField.getText() + " " + uris.toString()).trim()); |
| } |
| } |
| }); |
| } |
| |
| private class RegisteredOCLFilesDialog extends ElementListSelectionDialog |
| { |
| public RegisteredOCLFilesDialog(Shell parent) { |
| super(parent, new LabelProvider() { |
| @Override |
| public Image getImage(Object element) { |
| return ExtendedImageRegistry.getInstance().getImage(EcoreEditPlugin.INSTANCE.getImage("full/obj16/EPackage")); |
| } |
| }); |
| |
| setMultipleSelection(true); |
| setMessage(CompleteOCLUIMessages.LoadCompleteOCLResource_SelectRegisteredOCLFileURI); |
| setFilter("*"); |
| setTitle(CompleteOCLUIMessages.LoadCompleteOCLResource_OCLFileSelection_label); |
| setSize(100, 20); |
| } |
| |
| @Override |
| protected Control createDialogArea(Composite parent) |
| { |
| Composite result = (Composite)super.createDialogArea(parent); |
| URI[] uris = registeredURIsForResourceSet.toArray(new URI[registeredURIsForResourceSet.size()]); |
| Arrays.sort(uris, new URIComparator()); |
| setListElements(uris); |
| return result; |
| } |
| } |
| |
| /** |
| * Generate a popup to display a primaryMessage and optionally a detailMessage too. |
| */ |
| protected boolean error(@NonNull String primaryMessage, @Nullable String detailMessage) { |
| Shell shell = parent; |
| if (detailMessage != null) { |
| Diagnostic diagnostic = new BasicDiagnostic(Diagnostic.ERROR, "source", 0, detailMessage, null); |
| DiagnosticDialog.open(shell, title, primaryMessage, diagnostic); |
| } |
| else { |
| MessageDialog.openInformation(shell, title, primaryMessage); |
| } |
| return false; |
| } |
| |
| @Override |
| protected void okPressed() { |
| if (job == null) { |
| getButton(IDialogConstants.OK_ID).setEnabled(false); |
| getButton(IDialogConstants.CANCEL_ID).setEnabled(false); |
| @SuppressWarnings("null")@NonNull List<URI> urIs = getURIs(); |
| job = new DeferredLoadDocumentJob(urIs); |
| job.schedule(); |
| } |
| else { |
| getButton(IDialogConstants.OK_ID).setEnabled(true); |
| getButton(IDialogConstants.CANCEL_ID).setEnabled(true); |
| super.okPressed(); |
| job = null; |
| } |
| } |
| |
| @Override |
| public int open() { |
| try { |
| return super.open(); |
| } |
| catch (Throwable e) { |
| @NonNull String primaryMessage = String.valueOf(e.getMessage()); |
| error(primaryMessage, null); |
| return CANCEL; |
| } |
| finally { |
| if (target != null) { |
| target.dispose(); |
| target = null; |
| } |
| } |
| } |
| |
| @Override |
| protected boolean processResources() { |
| if (job != null) { |
| return job.getProcessedResourcesReturn(); |
| } |
| else { |
| return false; |
| } |
| } |
| |
| @Override |
| protected boolean processResource(Resource resource) { |
| throw new UnsupportedOperationException(); // Never happens since processResources overridden. |
| } |
| } |
| |
| public static class URIComparator implements Comparator<URI> { |
| @Override |
| public int compare(URI o1, URI o2) { |
| return o1.toString().compareTo(o2.toString()); |
| } |
| } |
| |
| @Override |
| public @Nullable Object execute(ExecutionEvent event) throws ExecutionException { |
| Object applicationContext = event.getApplicationContext(); |
| EditingDomain editingDomain = getEditingDomain(applicationContext); |
| ResourceSet resourceSet = getResourceSet(applicationContext); |
| // System.out.println("execute " + event); |
| Object shell = HandlerUtil.getVariable(applicationContext, ISources.ACTIVE_SHELL_NAME); |
| if (!(shell instanceof Shell)) { |
| return null; |
| } |
| if (resourceSet != null) { |
| ResourceDialog dialog = new ResourceDialog((Shell)shell, editingDomain, resourceSet); |
| dialog.open(); |
| } |
| return null; |
| } |
| |
| public static @Nullable EditingDomain getEditingDomain(@Nullable Object evaluationContext) { |
| Object o = HandlerUtil.getVariable(evaluationContext, ISources.ACTIVE_EDITOR_NAME); |
| if (!(o instanceof IEditorPart)) { |
| return null; |
| } |
| IEditingDomainProvider editor = ClassUtil.getAdapter((IEditorPart)o, IEditingDomainProvider.class); |
| if (editor == null) { |
| return null; |
| } |
| EditingDomain editingDomain = editor.getEditingDomain(); |
| if (editingDomain == null) { |
| return null; |
| } |
| return editingDomain; |
| } |
| |
| public static @Nullable ResourceSet getResourceSet(@Nullable Object evaluationContext) { |
| Object o = HandlerUtil.getVariable(evaluationContext, ISources.ACTIVE_EDITOR_NAME); |
| if (!(o instanceof IEditorPart)) { |
| return null; |
| } |
| IEditingDomainProvider editingDomainProvider = ClassUtil.getAdapter((IEditorPart)o, IEditingDomainProvider.class); |
| if (editingDomainProvider != null) { |
| EditingDomain editingDomain = editingDomainProvider.getEditingDomain(); |
| if (editingDomain == null) { |
| return null; |
| } |
| ResourceSet resourceSet = editingDomain.getResourceSet(); |
| return resourceSet; |
| } |
| XtextEditor xtextEditor = ClassUtil.getAdapter((IEditorPart)o, XtextEditor.class); |
| if (xtextEditor != null) { |
| IXtextDocument document = xtextEditor.getDocument(); |
| ResourceSet resourceSet = document.readOnly(new IUnitOfWork<ResourceSet, XtextResource>() { |
| @Override |
| public ResourceSet exec(@Nullable XtextResource xtextResource) { |
| if (xtextResource == null) { |
| return null; |
| } |
| EnvironmentFactoryInternal environmentFactory = PivotUtilInternal.findEnvironmentFactory(ClassUtil.nonNullState(xtextResource)); |
| if (environmentFactory != null) { |
| return environmentFactory.getResourceSet(); |
| } |
| else { |
| return xtextResource.getResourceSet(); |
| } |
| } |
| }); |
| return resourceSet; |
| } |
| return null; |
| } |
| |
| @Override |
| public void setEnabled(Object evaluationContext) { |
| // System.out.println("setEnabled " + evaluationContext); |
| setBaseEnabled(getResourceSet(evaluationContext) != null); |
| } |
| } |