/* * Copyright (c) 2006, 2010 Borland Software Corporation 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: * Dmitry Stadnik (Borland) - initial API and implementation * Artem Tikhomirov (independent) - [304421] allow code generation to run in background */ package org.eclipse.gmf.internal.codegen.popup.actions; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.IJobChangeEvent; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.jobs.JobChangeAdapter; import org.eclipse.emf.common.util.Diagnostic; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.plugin.EcorePlugin; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.gmf.codegen.gmfgen.GenDiagram; import org.eclipse.gmf.codegen.gmfgen.GenEditorGenerator; import org.eclipse.gmf.codegen.util.Generator; import org.eclipse.gmf.internal.bridge.transform.ValidationHelper; import org.eclipse.gmf.internal.codegen.CodeGenUIPlugin; import org.eclipse.gmf.internal.common.codegen.GeneratorBase; import org.eclipse.gmf.internal.common.migrate.ModelLoadHelper; import org.eclipse.gmf.internal.xpand.Activator; import org.eclipse.jface.action.Action; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.MessageDialogWithToggle; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.progress.IProgressConstants; /** * Operation that processes templates to generate diagram editor. * Clients should call run() method to invoke it. * Configured operation should normally run without interacting * with the user so it could be used as part of diagram editor * build process. * * @author dstadnik */ public class ExecuteTemplatesOperation { private static final String ASK_OK = "ask_ok"; //$NON-NLS-1$ private String name; private Shell shell; private URI genModelURI; private GenEditorGenerator myGenModel; public String getName() { if (name == null) { return "Execute Templates"; //$NON-NLS-1$ } return name; } public void setName(String name) { this.name = name; } public Shell getShell() { if (shell == null) { return Display.getDefault().getActiveShell(); } return shell; } public void setShell(Shell shell) { this.shell = shell; } public URI getGenModelURI() { return genModelURI; } public void setGenModelURI(URI uri) { genModelURI = uri; } // TODO Jobs public void run() { if (getGenModelURI() == null) { return; } Diagnostic loadStatus = loadGenModel(); if (!canProcessGMFGenModel(loadStatus)) { return; } assert getGenModel() != null; Diagnostic isGenModelValid = validateGenModel(); if (!ValidationHelper.isOK(isGenModelValid)) { final String msg = CodeGenUIPlugin.getBundleString("generatecode.badsrc"); //$NON-NLS-1$ if (DiagnosticsDialog.openProceedCancel(getShell(), getName(), msg, isGenModelValid) == IDialogConstants.CANCEL_ID) { return; } } if (!PlatformUI.getWorkbench().saveAllEditors(true)) { return; } final Job job = new Job(getName()) { @Override protected IStatus run(IProgressMonitor monitor) { try { IStatus s = ExecuteTemplatesOperation.this.run(monitor); if (!s.isOK()) { return s; } // usually, ok status has just "OK" text. Since we put this into a job, visible in the progress view, better label needed return new Status(IStatus.OK, s.getPlugin(), CodeGenUIPlugin.getBundleString("generatecode.ok")); //$NON-NLS-1$ } catch (InterruptedException ex) { return Status.CANCEL_STATUS; } } @Override public boolean belongsTo(Object family) { if (family instanceof Job) { return family.getClass().equals(this.getClass()); } return false; } }; job.setUser(true); job.setProperty(IProgressConstants.KEEPONE_PROPERTY, true); job.setProperty(IProgressConstants.ACTION_PROPERTY, new Action() { @Override public void run() { IStatus runStatus = job.getResult(); if (runStatus == null) { return; } if (runStatus.isOK()) { showOk(true); } else if (runStatus.matches(IStatus.ERROR)) { ErrorDialog.openError(getShell(), getName(), CodeGenUIPlugin.getBundleString("generatecode.err"), runStatus); //$NON-NLS-1$ } else if (runStatus.matches(IStatus.WARNING)) { ErrorDialog.openError(getShell(), getName(), CodeGenUIPlugin.getBundleString("generatecode.warn"), runStatus); //$NON-NLS-1$ } else if (runStatus.matches(IStatus.INFO)) { ErrorDialog.openError(getShell(), getName(), CodeGenUIPlugin.getBundleString("generatecode.info"), runStatus); //$NON-NLS-1$ } } }); job.addJobChangeListener(new JobChangeAdapter() { @Override public void done(IJobChangeEvent event) { unloadGenModel(); // IStatus runStatus = event.getResult(); if (runStatus.isOK() && Boolean.TRUE.equals(event.getJob().getProperty(IProgressConstants.PROPERTY_IN_DIALOG))) { showOk(false); } else if (runStatus.matches(IStatus.ERROR)) { CodeGenUIPlugin.getDefault().getLog().log(runStatus); } } }); job.schedule(); } protected void showOk(boolean force) { final boolean neverShowAgain = MessageDialogWithToggle.ALWAYS.equals(getPreferences().getString(ASK_OK)); if (force || !neverShowAgain) { final String okMsg = CodeGenUIPlugin.getBundleString("generatecode.ok"); //$NON-NLS-1$ final String neverMsg = CodeGenUIPlugin.getBundleString("generatecode.neveragain"); //$NON-NLS-1$ Runnable r = new Runnable() { public void run() { MessageDialogWithToggle dlg = MessageDialogWithToggle.openInformation(getShell(), getName(), okMsg, neverMsg, neverShowAgain, getPreferences(), ASK_OK); if (!dlg.getToggleState()) { // Unfortunately, MessageDialogWithToggle doesn't support clearing 'ask me again' option once set // @see #buttonPressed - preferences are modified only when toggleState == true // Hence, need to clear it manually getPreferences().setValue(ASK_OK, MessageDialogWithToggle.PROMPT); } } }; if (Display.getCurrent() != null) { r.run(); } else { Display.getDefault().asyncExec(r); } } } /** * Checks if loaded gmfgen model can be processed further. * <p> * Note: Must be called after {@link #loadGenModel()}. * * @param loadStatus * the result status of loading the gmfgen model * @param action * the action in execution * @return <code>true</code> if gmfgen model is available with OK status or in case of load problems, user decided to proceed, <code>false</code> otherwise. */ private boolean canProcessGMFGenModel(Diagnostic loadStatus) { if (!ValidationHelper.isOK(loadStatus)) { boolean disableProceed = myGenModel == null; // we cannot proceed further as there is no gmfgen, disable proceed button if (IDialogConstants.CANCEL_ID == DiagnosticsDialog.openProceedCancel(getShell(), getName(), null, loadStatus, disableProceed)) { return false; } } return true; } protected IStatus run(IProgressMonitor monitor) throws InterruptedException { GeneratorBase g = createGenerator(); g.run(monitor); return g.getRunStatus(); } protected GeneratorBase createGenerator() { return new Generator(getGenModel(), CodeGenUIPlugin.getDefault().getEmitters(getGenModel())); } protected final GenEditorGenerator getGenModel() { return myGenModel; } private Diagnostic loadGenModel() { ResourceSet srcResSet = new ResourceSetImpl(); srcResSet.getURIConverter().getURIMap().putAll(EcorePlugin.computePlatformURIMap()); Activator.fillWorkspaceMetaModelsMap(srcResSet.getPackageRegistry()); ModelLoadHelper loadHelper = new ModelLoadHelper(srcResSet, getGenModelURI()); Object root = loadHelper.getContentsRoot(); if (root instanceof GenDiagram) { myGenModel = ((GenDiagram) root).getEditorGen(); } else if (root instanceof GenEditorGenerator) { myGenModel = (GenEditorGenerator) root; } if (myGenModel != null && myGenModel.getDomainGenModel() != null) { myGenModel.getDomainGenModel().reconcile(); } return ValidationHelper.createResourceProblemMarkers(loadHelper.getDiagnostics()); } private void unloadGenModel() { if (myGenModel != null && myGenModel.eResource() != null) { myGenModel.eResource().unload(); } myGenModel = null; } private Diagnostic validateGenModel() { return ValidationHelper.validate(getGenModel(), true); } private static IPreferenceStore getPreferences() { return CodeGenUIPlugin.getDefault().getPreferenceStore(); } }