ac.soton.eventb.statemachines.transformation.TranslateAction.java Source code

Java tutorial

Introduction

Here is the source code for ac.soton.eventb.statemachines.transformation.TranslateAction.java

Source

/**
 * Copyright (c) 2010
 * University of Southampton.
 * 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
 * 
 */
package ac.soton.eventb.statemachines.transformation;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
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.common.util.WrappedException;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.transaction.Transaction;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gmf.runtime.common.core.command.CommandResult;
import org.eclipse.gmf.runtime.diagram.ui.resources.editor.parts.DiagramDocumentEditor;
import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand;
import org.eclipse.gmf.runtime.emf.core.util.EMFCoreUtil;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.m2m.qvt.oml.BasicModelExtent;
import org.eclipse.m2m.qvt.oml.ExecutionContextImpl;
import org.eclipse.m2m.qvt.oml.ExecutionDiagnostic;
import org.eclipse.m2m.qvt.oml.ModelExtent;
import org.eclipse.m2m.qvt.oml.TransformationExecutor;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.handlers.HandlerUtil;
import org.eventb.emf.core.context.ContextPackage;
import org.eventb.emf.core.machine.Machine;
import org.eventb.emf.core.machine.MachinePackage;
import org.rodinp.core.RodinCore;
import org.rodinp.core.RodinDBException;

import ac.soton.eventb.emf.diagrams.generator.Activator;
import ac.soton.eventb.emf.diagrams.generator.actions.ValidateAction;
import ac.soton.eventb.statemachines.State;
import ac.soton.eventb.statemachines.Statemachine;
import ac.soton.eventb.statemachines.TranslationKind;

/**
 * Translate action handler.
 * Translates iUML-B to Event-B using QVT transformation script.
 * 
 * cfs (04/01/12) : Use editing domain from diagram editor instead of creating a new one.
 * 
 * @author vitaly
 *
 */
public class TranslateAction extends AbstractHandler {

    /* (non-Javadoc)
     * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
     */
    @Override
    public Object execute(ExecutionEvent event) throws ExecutionException {

        IEditorPart editor = HandlerUtil.getActiveEditorChecked(event);
        if (editor instanceof DiagramDocumentEditor) {
            final DiagramDocumentEditor diagramDocumentEditor = (DiagramDocumentEditor) editor;

            if (diagramDocumentEditor.getDiagram().getElement() instanceof Statemachine) {
                final Statemachine statemachine = (Statemachine) diagramDocumentEditor.getDiagram().getElement();

                // save before transformation
                if (editor.isDirty())
                    editor.doSave(new NullProgressMonitor());

                // first validate, then transform
                if (ValidateAction.validate(diagramDocumentEditor)) {

                    final StatemachineTransformationCommand generateCommand = new StatemachineTransformationCommand(
                            diagramDocumentEditor.getDiagramEditPart().getEditingDomain(), statemachine);

                    if (generateCommand.canExecute()) {
                        // run with progress
                        ProgressMonitorDialog dialog = new ProgressMonitorDialog(
                                diagramDocumentEditor.getSite().getShell());
                        try {
                            dialog.run(true, true, new IRunnableWithProgress() {
                                public void run(IProgressMonitor monitor) {
                                    monitor.beginTask("Translating to Event-B ...", IProgressMonitor.UNKNOWN);
                                    try {
                                        generateCommand.execute(monitor, diagramDocumentEditor);
                                    } catch (ExecutionException e) {
                                        Activator.logError("Statemachine transformation failed", e);
                                    }
                                    monitor.done();
                                }
                            });
                        } catch (InvocationTargetException e) {
                            TransformationPlugin.getDefault().logError("Transformation failed", e);
                            return null;
                        } catch (InterruptedException e) {
                            TransformationPlugin.getDefault().logError("Transformation interrupted", e);
                            return null;
                        }

                        // error feedback
                        if (false == generateCommand.getCommandResult().getStatus().isOK())
                            MessageDialog.openError(editor.getSite().getShell(), "Translation Information",
                                    "Translation encountered problems.\n\nSee log for details.");
                    }
                }
            }
        }
        return null;
    }

    /**
     * Transformation command class.
     * 
     * @author vitaly
     *
     */
    private static class StatemachineTransformationCommand extends AbstractTransactionalCommand {

        /**
         * Specialised exception class for no EClass found situation.
         * 
         * @author vitaly
         *
         */
        public class NoEClassException extends Exception {
            /**
             * 
             */
            private static final long serialVersionUID = -6910179434320404735L;

            /**
             * @param string
             */
            public NoEClassException(String string) {
                super(string);
            }
        }

        private Statemachine statemachine;

        /**
         * @param modelEditingDomain 
         * @param statemachine
         */
        public StatemachineTransformationCommand(TransactionalEditingDomain modelEditingDomain,
                Statemachine statemachine) {
            super(modelEditingDomain, "Transformation Command", null);
            setOptions(Collections.singletonMap(Transaction.OPTION_UNPROTECTED, Boolean.TRUE));
            this.statemachine = statemachine;
        }

        @Override
        public boolean canRedo() {
            return false;
        }

        @Override
        public boolean canUndo() {
            return false;
        }

        /* (non-Javadoc)
         * @see org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand#doExecuteWithResult(org.eclipse.core.runtime.IProgressMonitor, org.eclipse.core.runtime.IAdaptable)
         */
        @Override
        protected CommandResult doExecuteWithResult(IProgressMonitor monitor, IAdaptable info)
                throws ExecutionException {

            try {

                RodinCore.run(new IWorkspaceRunnable() {
                    public void run(final IProgressMonitor monitor) throws CoreException {
                        TransactionalEditingDomain editingDomain = getEditingDomain();

                        monitor.beginTask(
                                "Running Event-B Transformation for statemachine : " + statemachine.getName(), 10);
                        monitor.setTaskName(
                                "Running Event-B Transformation for statemachine : " + statemachine.getName());

                        // flush the command stack as this is unprotected and has no undo/redo
                        editingDomain.getCommandStack().flush();

                        ////
                        // find topmost container statemachine
                        Statemachine rootSM = statemachine;
                        for (; rootSM.eContainer() instanceof State && rootSM.eContainer()
                                .eContainer() instanceof Statemachine; rootSM = (Statemachine) rootSM.eContainer()
                                        .eContainer())
                            ;

                        EObject container = EcoreUtil.getRootContainer(statemachine);
                        final Machine machine = (Machine) container;

                        // URIs of input resources
                        final List<URI> inResourceURIs = new ArrayList<URI>(2);
                        inResourceURIs.add(machine.getURI());
                        if (TranslationKind.SINGLEVAR.equals(rootSM.getTranslation())
                                || TranslationKind.REFINEDVAR.equals(rootSM.getTranslation()))
                            inResourceURIs.add(getImplicitCtxURIForMachine(machine));

                        List<Resource> inResources = loadResources(inResourceURIs, editingDomain);
                        List<ModelExtent> input = new ArrayList<ModelExtent>(inResources.size() + 1);

                        // Refer to an existing transformation via URI
                        URI transformationURI = getScriptURI(rootSM.getTranslation());

                        // create executor for the given transformation
                        final TransformationExecutor executor = new TransformationExecutor(transformationURI);

                        // define transformation resource inputs
                        for (Resource inRes : inResources)
                            input.add(new BasicModelExtent(inRes.getContents()));

                        // define transformation statemachine extension input
                        //FIXME: assumption on first resource as a statemachine container presumably is weak or error prone
                        Resource mchResource = inResources.get(0);
                        input.add(new BasicModelExtent(Collections
                                .singletonList(mchResource.getEObject(rootSM.eResource().getURIFragment(rootSM)))));

                        // setup the execution environment details -> 
                        // configuration properties, logger, monitor object etc.
                        final ExecutionContextImpl context = new ExecutionContextImpl();
                        //context.setConfigProperty("keepModeling", true);
                        monitor.worked(4);
                        // run the transformation assigned to the executor with the given 
                        // input and output and execution context -> ChangeTheWorld(in, out)
                        // Remark: variable arguments count is supported
                        ExecutionDiagnostic result = executor.execute(context, input.toArray(new ModelExtent[] {}));
                        monitor.worked(4);
                        // check the result for success
                        if (result.getSeverity() == Diagnostic.OK) {
                            // let's persist using a resource 
                            for (Resource res : inResources) {
                                try {
                                    res.save(Collections.emptyMap());
                                } catch (IOException e) {
                                    //throw this as a CoreException
                                    throw new CoreException(new Status(Status.ERROR, TransformationPlugin.PLUGIN_ID,
                                            "IO exception while saving " + res.getURI(), e));
                                }
                            }
                        } else {
                            // turn the result diagnostic into status and send it to error log         
                            IStatus status = BasicDiagnostic.toIStatus(result);
                            TransformationPlugin.getDefault().getLog().log(status);
                            throw new CoreException(new Status(Status.ERROR, TransformationPlugin.PLUGIN_ID,
                                    "Error while executing transformation: " + status.getMessage(), null));

                        }
                        monitor.done();
                    }
                }, monitor);
                return CommandResult.newOKCommandResult();
                //         } catch (WrappedException e) {
                //            TransformationPlugin.getDefault().logError("Unable to load resource for transformation", e);
                //            return CommandResult.newErrorCommandResult(e);
            } catch (RodinDBException e) {
                TransformationPlugin.getDefault().logError("Rodin DB exception", e);
                return CommandResult.newErrorCommandResult(e);
            } finally {
                monitor.done();
            }
        }

        /**
         * Load resources from URIs.
         * 
         * @param inResourceURIs
         * @param editingDomain 
         * @return array of loaded resources
         */
        private List<Resource> loadResources(List<URI> inResourceURIs, TransactionalEditingDomain editingDomain) {
            List<Resource> resources = new ArrayList<Resource>(inResourceURIs.size());
            try {
                for (URI uri : inResourceURIs) {
                    Resource resource = null;
                    try {
                        resource = editingDomain.getResourceSet().getResource(uri, true);
                    } catch (Exception e) {
                        resource = editingDomain.getResourceSet().createResource(uri);
                        createResourceContent(resource);
                        resource.save(Collections.EMPTY_MAP);
                        resource.load(Collections.EMPTY_MAP);
                    }
                    resources.add(resource);
                }
            } catch (Exception e) {
                throw new WrappedException("Creating a resource failed: " + e.getMessage(), e);
            }
            return resources;
        }

        /**
         * Creates basic content in a resource i.e. a root container.
         * 
         * @param resource
         * @throws NoEClassException if required EClass to create a root element cannot be derived from resource URI
         */
        private void createResourceContent(Resource resource) throws NoEClassException {
            EClass clazz = getRootClass(resource);
            if (clazz == null)
                throw new NoEClassException("Cannot find root class of a resource " + resource.getURI());
            EObject element = EcoreUtil.create(clazz);
            if (clazz.getEStructuralFeature("name") != null)
                EMFCoreUtil.setName(element, resource.getURI().trimFileExtension().lastSegment());
            resource.getContents().add(element);
        }

        /**
         * Returns a root EClass for a resource.
         * 
         * @param resource
         * @return root EClass
         */
        private EClass getRootClass(Resource resource) {
            if (resource.getURI() != null) {
                if ("bum".equals(resource.getURI().fileExtension()))
                    return MachinePackage.eINSTANCE.getMachine();
                if ("buc".equals(resource.getURI().fileExtension()))
                    return ContextPackage.eINSTANCE.getContext();
            }
            return null;
        }

        /**
         * Returns save options for Resource save operation.
         * 
         * @return
         */
        //      private Map<?, ?> getSaveOptions() {
        //         HashMap<String, Object> saveOptions = new HashMap<String, Object>();
        //         saveOptions.put(XMLResource.OPTION_ENCODING, "UTF-8");
        //         return saveOptions;
        //      }

        /**
         * Returns load options for Resource load operation.
         * 
         * @return
         */
        //      private Map<?, ?> getLoadOptions() {
        //         HashMap<String, Object> loadOptions = new HashMap<String, Object>();
        //         loadOptions.put(XMLResource.OPTION_USE_PARSER_POOL, "org.eclipse.emf.ecore.xmi.XMLParserPool");
        //         return loadOptions;
        //      }

        /**
         * Returns transformation script URI depending on translation kind provided.
         * 
         * @param kind translation kind
         * @return script URI
         */
        private URI getScriptURI(TranslationKind kind) {
            String scriptPath = null;

            if (TranslationKind.SINGLEVAR.equals(kind))
                scriptPath = "/transforms/enum_statemachines2eventb.qvto";
            else if (TranslationKind.MULTIVAR.equals(kind))
                scriptPath = "/transforms/vars_statemachines2eventb.qvto";
            else if (TranslationKind.REFINEDVAR.equals(kind))
                TransformationPlugin.getDefault()
                        .logError("Statemachine translation kind 'Refined' is no longer supported");
            //scriptPath = "/transforms/ref_statemachines2eventb.qvto";
            else
                return null;

            URL url = Platform.getBundle(TransformationPlugin.PLUGIN_ID).getEntry(scriptPath);
            return URI.createURI(url.toString());
        }

        /**
         * Returns implicit context name for a machine.
         * 
         * @param machine
         * @return context name
         */
        private String getImplicitCtxNameForMachine(Machine machine) {
            return machine.getName() + "_implicitContext";
        }

        /**
         * Returns implicit context URI for machine.
         * 
         * @param machine
         * @return URI
         */
        private URI getImplicitCtxURIForMachine(Machine machine) {
            return machine.getURI().trimSegments(1).appendSegment(getImplicitCtxNameForMachine(machine))
                    .appendFileExtension("buc");
        }
    }
}