blob: 51efdc6ab3e0ad7061eff0fa9570eb67c6aeb78b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013 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.acceleo.ui.interpreter.ocl;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import org.eclipse.acceleo.ui.interpreter.language.CompilationResult;
import org.eclipse.acceleo.ui.interpreter.language.InterpreterContext;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.Resource.Diagnostic;
import org.eclipse.ocl.examples.xtext.console.xtfo.EmbeddedXtextEditor;
import org.eclipse.ocl.pivot.Element;
import org.eclipse.ocl.pivot.ExpressionInOCL;
import org.eclipse.ocl.pivot.internal.context.EInvocationContext;
import org.eclipse.ocl.pivot.utilities.Pivotable;
import org.eclipse.ocl.pivot.values.Value;
import org.eclipse.ocl.xtext.essentialocl.utilities.EssentialOCLCSResource;
import org.eclipse.swt.widgets.Display;
import org.eclipse.xtext.parser.IParseResult;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.util.concurrent.IUnitOfWork;
/**
* This class allows adopters to compile the String representation of an OCL expression in an asynchronous
* way. The resulting {@link CompilationResult} will be the compiled {@link ExpressionInOCL}.
*
* @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
*/
public class OCLCompilationTask implements Callable<CompilationResult> {
/** Current interpreter context. */
private final InterpreterContext context;
/** Embedded editor containing the expression to compile. */
private final EmbeddedXtextEditor editor;
/**
* Instantiates our compilation task given the current interpreter context.
*
* @param context
* The current interpreter context.
* @param editor
* The current editor.
*/
public OCLCompilationTask(InterpreterContext context, EmbeddedXtextEditor editor) {
this.context = context;
this.editor = editor;
}
/**
* {@inheritDoc}
*
* @see java.util.concurrent.Callable#call()
*/
public CompilationResult call() throws Exception {
checkCancelled();
final boolean nothingToCompile = context.getExpression() == null
|| context.getExpression().length() == 0;
final boolean noContext = context.getTargetEObjects().isEmpty();
if (nothingToCompile || noContext) {
CompilationResult shortcut = null;
if (noContext) {
shortcut = new CompilationResult(new Status(IStatus.INFO, OCLInterpreterActivator.PLUGIN_ID,
"No context selected."));
}
return shortcut;
}
EObject evaluationTarget = context.getTargetEObjects().get(0);
ExpressionInOCL expressionInOCL = null;
CompilationResult compilationResult = null;
Resource resource = editor.getResource();
if (resource instanceof XtextResource) {
refreshEditor(evaluationTarget);
checkCancelled();
final IParseResult result = ((XtextResource)resource).getParseResult();
if (result != null && result.getRootASTElement() instanceof Pivotable) {
final Element pivotElement = ((Pivotable)result.getRootASTElement()).getPivot();
if (pivotElement instanceof ExpressionInOCL) {
expressionInOCL = (ExpressionInOCL)pivotElement;
}
}
}
if (expressionInOCL == null) {
compilationResult = new CompilationResult(new Status(IStatus.ERROR,
OCLInterpreterActivator.PLUGIN_ID, "Unexpected error while parsing expression."));
} else {
final IStatus status = parseProblems(resource);
compilationResult = new CompilationResult(expressionInOCL, status);
}
return compilationResult;
}
/**
* Checks the given resource for any error or warning and creates a MultiStatus containing, in order, one
* entry for each error and one entry for each warning.
*
* @param resource
* The resource which problems we need to check.
* @return A MultiStatus for this resource's problems if any, <code>null</code> if none.
*/
private IStatus parseProblems(Resource resource) {
final List<IStatus> problems = new ArrayList<IStatus>();
for (Diagnostic diagnostic : resource.getErrors()) {
problems.add(new Status(IStatus.ERROR, OCLInterpreterActivator.PLUGIN_ID, diagnostic.getMessage()));
}
for (Diagnostic diagnostic : resource.getWarnings()) {
problems.add(new Status(IStatus.WARNING, OCLInterpreterActivator.PLUGIN_ID, diagnostic
.getMessage()));
}
if (problems.isEmpty()) {
return null;
}
final MultiStatus status = new MultiStatus(OCLInterpreterActivator.PLUGIN_ID, 1,
"Problems encountered while compiling expression.", null);
for (IStatus child : problems) {
status.add(child);
}
return status;
}
/**
* This will refresh the editor and force a reparsing of the OCL expression it displays if needed (i.e. if
* the target EObject has changed since it was last refreshed).
*
* @param target
* The selected model element.
*/
private void refreshEditor(EObject target) {
final IUnitOfWork<Object, XtextResource> refresher = new DocumentRefresher(editor, target);
final Display display = Display.getDefault();
if (display != null) {
display.syncExec(new Runnable() {
public void run() {
editor.getDocument().modify(refresher);
}
});
} else {
editor.getDocument().modify(refresher);
}
}
/**
* Throws a new {@link CancellationException} if the current thread has been cancelled.
*/
private void checkCancelled() {
if (Thread.currentThread().isInterrupted()) {
throw new CancellationException();
}
}
/**
* An instance of this class will have the responsibility of setting the context classifier of an Xtext
* document and reparse the expression if needed.
*
* @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
*/
private static class DocumentRefresher implements IUnitOfWork<Object, XtextResource> {
/** Embedded editor containing the expression to compile. */
private final EmbeddedXtextEditor editor;
/** EObject which is to be set as the new context of this document. */
private final EObject target;
/**
* Instantiates a document refresher given its context.
*
* @param editor
* The editor which document will be refreshed.
* @param target
* The context EObject for the incoming parsing.
*/
public DocumentRefresher(EmbeddedXtextEditor editor, EObject target) {
this.editor = editor;
this.target = target;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.xtext.util.concurrent.IUnitOfWork#exec(Object)
*/
public Value exec(XtextResource resource) throws Exception {
EClassifier contextClassifier = target.eClass();
EssentialOCLCSResource csResource = (EssentialOCLCSResource)resource;
if (csResource != null) {
if (csResource.getParserContext() instanceof EInvocationContext) {
final EInvocationContext context = (EInvocationContext)csResource.getParserContext();
if (context.getClassContext().getETarget().equals(contextClassifier)) {
return null;
}
}
csResource.setParserContext(new EInvocationContext(editor.getEnvironmentFactory(), resource
.getURI(), contextClassifier, null));
csResource.reparse(editor.getDocument().get());
}
// unused return value
return null;
}
}
}