/******************************************************************************* * Copyright (c) 2012, 2015 Willink Transformations 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: * 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) { return ResourceDialog.this.error(primaryMessage, detailMessage); } }; 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); 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); } }