blob: f5f57302bdd449c5d887698201ca3b7f7e4f9c4e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011-2013 EclipseSource Muenchen GmbH 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:
* Clemens Elflein - initial API and implementation
******************************************************************************/
package org.eclipse.emfforms.internal.editor.genmodel.toolbaractions;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.text.MessageFormat;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.codegen.ecore.generator.Generator;
import org.eclipse.emf.codegen.ecore.genmodel.GenModel;
import org.eclipse.emf.codegen.ecore.genmodel.GenPackage;
import org.eclipse.emf.codegen.ecore.genmodel.generator.GenBaseGeneratorAdapter;
import org.eclipse.emf.codegen.ecore.genmodel.util.GenModelUtil;
import org.eclipse.emf.common.util.BasicMonitor;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecp.view.spi.model.reporting.StatusReport;
import org.eclipse.emfforms.internal.editor.genmodel.Activator;
import org.eclipse.emfforms.internal.editor.genmodel.util.PluginXmlUtil;
import org.eclipse.emfforms.spi.editor.IToolbarAction;
import org.eclipse.emfforms.spi.editor.helpers.ResourceUtil;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IMenuCreator;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.osgi.framework.FrameworkUtil;
/**
*
* The ToolbarAction allowing the User to generate Java code for the currently visible Genmodel.
*
* @author Clemens Elflein
*/
public class GenerateJavaCodeAction implements IToolbarAction {
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emfforms.spi.editor.IToolbarAction#getAction(java.lang.Object)
*/
@Override
public Action getAction(Object currentObject) {
return new CreateJavaCodeAction(currentObject);
}
@Override
public boolean canExecute(Object object) {
// We can't execute our Action on Objects other than ResourceSet
if (!(object instanceof ResourceSet)) {
return false;
}
// Check, if the ResourceSet contains a Genmodel. If so, we also can't execute our Action
final ResourceSet resourceSet = (ResourceSet) object;
for (final Resource r : resourceSet.getResources()) {
if (r.getContents().size() > 0 && r.getContents().get(0) instanceof GenModel) {
return true;
}
}
return false;
}
/**
* ToolbarAction to generate Java Code. It also provides the DropDown menu to create
* each type separately (Model, Edit, Editor, Tests).
*/
private static class CreateJavaCodeAction extends Action {
private final Object[] types;
private final Object currentObject;
public static final String FILE_NAME_PLUGIN_XML = "plugin.xml";
public static final String FILE_NAME_META_INF = "META-INF";
public static final String FILE_NAME_MANIFEST_MF = "MANIFEST.MF";
public static final String REQUIRE_BUNDLE_HEADER = "Require-Bundle";
public static final String EDITOR_BUNDLE_NAME = "org.eclipse.emfforms.editor";
public static final String EDITOR_BUNDLE_VERSION = "1.8.0";
public static final String EMFFORMS_EDITOR_CLASS_NAME = "org.eclipse.emfforms.spi.editor.GenericEditor";
public static final String EMFFORMS_EDITOR_NAME = "EMFForms Editor";
public static final String EMFFORMS_EDITOR_ID = "emfformseditor";
CreateJavaCodeAction(String text, Object[] types, Object currentObject) {
super(text);
this.types = types;
this.currentObject = currentObject;
}
CreateJavaCodeAction(Object currentObject) {
super("Generate All", SWT.DROP_DOWN);
this.currentObject = currentObject;
types = new Object[] {
GenBaseGeneratorAdapter.MODEL_PROJECT_TYPE,
GenBaseGeneratorAdapter.EDIT_PROJECT_TYPE,
GenBaseGeneratorAdapter.EDITOR_PROJECT_TYPE,
GenBaseGeneratorAdapter.TESTS_PROJECT_TYPE
};
setMenuCreator(new GenmodelDropdownCreator());
setImageDescriptor(ImageDescriptor.createFromURL(FrameworkUtil.getBundle(
this.getClass()).getResource("icons/page_white_cup.png")));
}
/**
* {@inheritDoc}
*
* @see org.eclipse.jface.action.Action#run()
*/
@Override
public void run() {
final IRunnableWithProgress generateCodeRunnable = new GenerateJavaCodeRunnable();
try {
new ProgressMonitorDialog(Display.getCurrent().getActiveShell()).run(true, false, generateCodeRunnable);
} catch (final InvocationTargetException ex) {
Activator.getDefault().getReportService().report(
new StatusReport(new Status(IStatus.ERROR, Activator.PLUGIN_ID, ex.getMessage(), ex)));
} catch (final InterruptedException ex) {
Activator.getDefault().getReportService().report(
new StatusReport(new Status(IStatus.ERROR, Activator.PLUGIN_ID, ex.getMessage(), ex)));
}
}
/**
* {@link IRunnableWithProgress} to execute code generation.
*/
public class GenerateJavaCodeRunnable implements IRunnableWithProgress {
@Override
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
monitor.beginTask("Generating Code", IProgressMonitor.UNKNOWN);
final ResourceSet resourceSet = (ResourceSet) currentObject;
final Resource genmodelResource = resourceSet.getResources().get(0);
final GenModel genmodel = (GenModel) genmodelResource.getContents().get(0);
// XXX Begin
int tries = 0;
boolean exception = true;
Exception target = null;
while (tries < 5 && exception) {
try {
tries += 1;
genmodel.reconcile();
exception = false;
} // BEGIN SUPRESS CATCH EXCEPTION
catch (final RuntimeException ex) {// END SUPRESS CATCH EXCEPTION
/* fail silently */
target = ex;
}
}
if (exception) {
throw new InvocationTargetException(target);
}
// XXX End
genmodel.setCanGenerate(true);
final Generator generator = GenModelUtil
.createGenerator(genmodel);
final Set<URI> knownUris = new HashSet<URI>();
for (final Object type : types) {
generate(monitor, genmodel, generator, knownUris, type);
}
}
private void generate(IProgressMonitor monitor, final GenModel genmodel, final Generator generator,
final Set<URI> knownUris, final Object type) {
generator.generate(genmodel, type,
new BasicMonitor.EclipseSubProgress(monitor, 1));
final Set<URI> uris = new HashSet<URI>(generator.getGeneratedOutputs());
uris.removeAll(knownUris);
knownUris.addAll(uris);
if (GenBaseGeneratorAdapter.EDITOR_PROJECT_TYPE.equals(type)) {
handleGeneratedEditorProject(uris, genmodel);
}
}
/**
* Handles the case that an editor project was generated. Takes a set of generated URIs to find the
* plugin.xml file to edit.
*
* @param uris a set of generated URIs
* @param genmodel
*/
private void handleGeneratedEditorProject(Set<URI> uris, GenModel genmodel) {
URI pluginXml = null;
URI manifestMf = null;
for (final URI uri : uris) {
if (isPluginXml(uri)) {
pluginXml = uri;
} else if (isManifestMf(uri)) {
manifestMf = uri;
}
}
if (pluginXml != null && manifestMf != null) {
injectPluginXml(pluginXml, genmodel);
injectManifestMf(manifestMf);
}
}
private boolean isPluginXml(URI uri) {
if (uri.segmentCount() != 2) {
return false;
}
final String lastSegment = uri.lastSegment();
return FILE_NAME_PLUGIN_XML.equals(lastSegment);
}
private boolean isManifestMf(URI uri) {
if (uri.segmentCount() != 3) {
return false;
}
final String secondSegment = uri.segment(1);
final String lastSegment = uri.lastSegment();
return FILE_NAME_META_INF.equals(secondSegment) && FILE_NAME_MANIFEST_MF.equals(lastSegment);
}
private void injectManifestMf(URI manifestMf) {
final IResource resource = ResourceUtil.getResourceFromURI(manifestMf);
final IFile file = (IFile) resource;
InputStream original;
try {
original = file.getContents();
final Manifest originalManifest = new Manifest(original);
final Attributes attributes = originalManifest.getMainAttributes();
String newValue = attributes.getValue(REQUIRE_BUNDLE_HEADER);
if (newValue == null) {
newValue = "";
} else {
newValue += ",";
}
newValue += EDITOR_BUNDLE_NAME;
if (EDITOR_BUNDLE_VERSION != null) {
newValue += ";bundle-version=\"" + EDITOR_BUNDLE_VERSION + "\"";
}
attributes.putValue(REQUIRE_BUNDLE_HEADER, newValue);
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
originalManifest.write(bos);
file.setContents(new ByteArrayInputStream(bos.toByteArray()), false, true,
new NullProgressMonitor());
} catch (final CoreException ex) {
Activator.getDefault().getReportService().report(
new StatusReport(new Status(IStatus.ERROR, Activator.PLUGIN_ID, ex.getMessage(), ex)));
} catch (final IOException ex) {
Activator.getDefault().getReportService().report(
new StatusReport(new Status(IStatus.ERROR, Activator.PLUGIN_ID, ex.getMessage(), ex)));
}
}
private void injectPluginXml(URI pluginXml, GenModel genmodel) {
final IResource resource = ResourceUtil.getResourceFromURI(pluginXml);
final GenPackage genPackage = genmodel.getGenPackages().get(0);
PluginXmlUtil.addEditorExtensionPoint(
(IFile) resource,
EMFFORMS_EDITOR_CLASS_NAME,
false, /* default */
getFileExtension(genmodel),
/**
* Code from org.eclipse.emf.codegen.ecore.templates.editor.PluginXML
* stringBuffer.append("icon=\"icons/full/obj16/");
* stringBuffer.append(genPackage.getPrefix());
* stringBuffer.append("ModelFile.gif\"");
*/
MessageFormat.format("icons/full/obj16/{0}ModelFile.gif", genPackage.getPrefix()),
/**
* Default id is the fully qualified generated editor class appended by ID.
* e.g.: org.eclipse.emf.ecp.makeithappen.model.task.presentation.TaskEditorID
*
* we will use the generated editor plugin id appended by the package prefix appended by
* emfformseditor.
* e.g.: org.eclipse.emf.ecp.makeithappen.model.editor.Task.emfformseditor
*/
MessageFormat.format("{0}.{1}.{2}", genmodel.getEditorPluginID(), genPackage.getPrefix(),
EMFFORMS_EDITOR_ID),
EMFFORMS_EDITOR_NAME);
}
private String getFileExtension(GenModel genmodel) {
return genmodel.getGenPackages().get(0).getFileExtensions();
}
}
/**
* IMenuCreator to create each type separately.
*/
private class GenmodelDropdownCreator implements IMenuCreator {
private Menu dropDown;
/**
* {@inheritDoc}
*
* @see org.eclipse.jface.action.IMenuCreator#dispose()
*/
@Override
public void dispose() {
if (dropDown != null) {
dropDown.dispose();
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.jface.action.IMenuCreator#getMenu(org.eclipse.swt.widgets.Control)
*/
@Override
public Menu getMenu(Control parent) {
dispose();
dropDown = new Menu(parent);
final Action generateModelAndEdit = new CreateJavaCodeAction("Generate Model + Edit",
new Object[] {
GenBaseGeneratorAdapter.MODEL_PROJECT_TYPE,
GenBaseGeneratorAdapter.EDIT_PROJECT_TYPE
}, currentObject);
final Action generateModel = new CreateJavaCodeAction("Generate Model",
new Object[] {
GenBaseGeneratorAdapter.MODEL_PROJECT_TYPE
}, currentObject);
final Action generateEdit = new CreateJavaCodeAction("Generate Edit",
new Object[] {
GenBaseGeneratorAdapter.EDIT_PROJECT_TYPE
}, currentObject);
final Action generateEditor = new CreateJavaCodeAction("Generate Editor",
new Object[] {
GenBaseGeneratorAdapter.EDITOR_PROJECT_TYPE
}, currentObject);
final Action generateTests = new CreateJavaCodeAction("Generate Tests",
new Object[] {
GenBaseGeneratorAdapter.TESTS_PROJECT_TYPE
}, currentObject);
new ActionContributionItem(generateModelAndEdit).fill(dropDown, 0);
new ActionContributionItem(generateModel).fill(dropDown, 1);
new ActionContributionItem(generateEdit).fill(dropDown, 2);
new ActionContributionItem(generateEditor).fill(dropDown, 3);
new ActionContributionItem(generateTests).fill(dropDown, 4);
return dropDown;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.jface.action.IMenuCreator#getMenu(org.eclipse.swt.widgets.Menu)
*/
@Override
public Menu getMenu(Menu parent) {
return null;
}
}
}
}