/*******************************************************************************
* Copyright (c) 2006-2009
* Software Technology Group, Dresden University of Technology
*
* 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:
* Software Technology Group - TU Dresden, Germany
* - initial API and implementation
******************************************************************************/
package org.reuseware.comogen.fragment.dslbuilder.ui.eclipse.actions;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Iterator;
import org.eclipse.core.resources.ICommand;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.epsilon.commons.parse.problem.ParseProblem;
import org.eclipse.epsilon.egl.EglModule;
import org.eclipse.epsilon.emc.emf.InMemoryEmfModel;
import org.eclipse.epsilon.eol.exceptions.EolRuntimeException;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.IActionDelegate;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PlatformUI;
// TODO Figure out correct usage of progress monitors
public class GenerateDSLComposerAction implements IObjectActionDelegate {
private ISelection selection;
/**
* Constructor for Action1.
*/
public GenerateDSLComposerAction() {
super();
}
/**
* Calls {@link #process(IFile)} for all selected <i>ecore</i> files .
*/
public void run(IAction action) {
if (selection instanceof IStructuredSelection) {
for (Iterator<?> i = ((IStructuredSelection) selection).iterator(); i
.hasNext();) {
Object o = i.next();
if (o instanceof IFile) {
IFile file = (IFile) o;
if (file.getFileExtension().startsWith("ecore"))
process(file);
}
}
}
}
private void process(final IFile file) {
try {
IRunnableWithProgress runnable = new IRunnableWithProgress() {
public void run(IProgressMonitor monitor)
throws InvocationTargetException, InterruptedException {
try {
SubMonitor progress = SubMonitor.convert(monitor, 100);
ResourceSet rs = new ResourceSetImpl();
Resource ecoreResource = rs.getResource(org.eclipse.emf.common.util.URI
.createPlatformResourceURI(file.getFullPath()
.toString(), true), true);
final EPackage epMetamodel = (EPackage) ecoreResource
.getContents().get(0);
String sPackageName = file.getProject().getName() + ".composer";
String projectName = sPackageName;
// create project
IProject project = createProject(projectName, progress);
// Generate the actual transformation...
generateTransformation (epMetamodel, project, progress.newChild (20));
// Setup plug-in configuration files
configurePlugin (project, epMetamodel, projectName, progress);
// Refresh project
project.refreshLocal(IProject.DEPTH_INFINITE, progress
.newChild(10));
}
catch (IOException ioe) {
ioe.printStackTrace();
}
catch (CoreException e) {
throw new InvocationTargetException(e);
}
}
};
PlatformUI.getWorkbench().getProgressService().busyCursorWhile(
runnable);
} catch (InvocationTargetException e) {
e.getTargetException().printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
protected void configurePlugin(IProject project, EPackage epMetamodel, String projectName, SubMonitor progress) throws IOException, CoreException {
IFolder metaFolder = project.getFolder("/META-INF");
IFile manifestMFFile = project
.getFile("/META-INF/MANIFEST.MF");
IFile pluginXMLFile = project
.getFile("/plugin.xml");
IFolder javaPackage = project.getFolder("src-gen");
for(String packagePart : projectName.split("\\.")) {
javaPackage = javaPackage.getFolder(packagePart);
if (!javaPackage.exists()) {
javaPackage.create(true, true, progress);
}
}
IFile epsilonCallFile = javaPackage.getFile("/EpsilonBasedUpdateOperation.java");
if (!metaFolder.exists())
metaFolder.create(true, true, progress.newChild(5));
// Create MANIFEST.MF
if (!manifestMFFile.exists()) {
manifestMFFile.create(new ByteArrayInputStream(generateManifestMF(epMetamodel, projectName).getBytes()), true, progress.newChild(5));
}
// Create plugin.xml
if (!pluginXMLFile.exists()) {
pluginXMLFile.create(new ByteArrayInputStream(generatePluginXml(epMetamodel, projectName).getBytes()), true, progress.newChild(5));
}
// Create EpsilonBasedUpdateOperation.java
if(!javaPackage.exists()) {
javaPackage.create(true, true, null);
}
if (!epsilonCallFile.exists()) {
epsilonCallFile.create(new ByteArrayInputStream(generateEpsilonCall(epMetamodel, projectName).getBytes()), true, progress.newChild(5));
}
//Create call
// Copy over prepared build.properties file
IFile targetBuildFile = project.getFile("/build.properties");
InputStream isSourceBuildFile = FileLocator.openStream (Platform.getBundle("org.reuseware.comogen.ui.eclipse.dslbuilder"),
new Path("/files/build.properties"), false);
if (!targetBuildFile.exists()) {
targetBuildFile.create(isSourceBuildFile, true, progress.newChild(5));
}
}
protected IProject createProject(String projectName, SubMonitor progress) throws CoreException {
IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
if (!project.exists()) {
project.create(progress.newChild(10));
}
project.open(progress.newChild(10));
IProjectDescription description = project.getDescription();
description.setNatureIds (new String[] {JavaCore.NATURE_ID, "org.eclipse.pde.PluginNature" });
ICommand command1 = description.newCommand();
command1.setBuilderName("org.eclipse.jdt.core.javabuilder");
ICommand command2 = description.newCommand();
command2.setBuilderName("org.eclipse.pde.ManifestBuilder");
ICommand command3 = description.newCommand();
command3.setBuilderName("org.eclipse.pde.SchemaBuilder");
description.setBuildSpec(new ICommand[] { command1, command2, command3 });
project.setDescription(description, null);
IFolder srcFolder = project.getFolder("/src-gen");
if (!srcFolder.exists()) {
srcFolder.create(true, true, progress.newChild(5));
}
IFolder outFolder = project.getFolder("/bin");
if (!outFolder.exists()) {
outFolder.create(true, true, progress.newChild(5));
}
IJavaProject jp = JavaCore.create(project);
jp.setRawClasspath(
new IClasspathEntry[] {
JavaCore.newSourceEntry(srcFolder.getFullPath()),
JavaRuntime.getDefaultJREContainerEntry(),
JavaCore.newContainerEntry(new Path("org.eclipse.pde.core.requiredPlugins")) },
outFolder.getFullPath(), progress.newChild(5));
return project;
}
/**
* Generates the transformation to use to translate instances of an abstract DSL into Reuseware composition programs.
*
* @param epMetamodel the metamodel for the abstract DSL
* @param project the target project currently being generated
* @param progress a progress monitor to reflect progress being made
* @throws CoreException thrown when file creation doesn't work
*/
protected void generateTransformation (EPackage epMetamodel, IProject project, SubMonitor progress) throws CoreException {
// Code copied and adapted from Epsilon examples
EglModule module = new EglModule();
try {
module.parse(getFile("transformations/ECore2Etl4Reuseware.egl"));
if (module.getParseProblems().size() > 0) {
System.err.println("Parse errors occured...");
for (ParseProblem problem : module.getParseProblems()) {
System.err.println(problem.toString());
}
throw new RuntimeException ("Parse Problems.");
}
//Code below copied and adjusted from Builder.java
InMemoryEmfModel source = new InMemoryEmfModel ("ECore", epMetamodel.eResource(), EcorePackage.eINSTANCE);
module.getContext().getModelRepository().addModel(source);
module.execute();
// Write generation result to file
String sGenerated = module.getContext().getOutputBuffer().toString();
IFolder transformationsFolder = project.getFolder("/transformations");
IFile builderETLFile = project
.getFile("/transformations/builder.etl");
if (!transformationsFolder.exists())
transformationsFolder.create(true, true, progress.newChild(5));
if (builderETLFile.exists()) {
builderETLFile.delete(true, null);
}
builderETLFile.create(new ByteArrayInputStream(sGenerated.getBytes()),
true, progress.newChild(5));
} catch (IOException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (EolRuntimeException e) {
e.printStackTrace();
}
finally {
module.getContext().getModelRepository().dispose();
}
}
protected URI getFile(String fileName) throws URISyntaxException {
URI uri = FileLocator.find(Platform.getBundle("org.reuseware.comogen.ui.eclipse.dslbuilder"),
new Path(fileName), null).toURI();
return uri;
}
private static String generateEpsilonCall(EPackage epMetamodel, String packageName) {
StringBuffer s = new StringBuffer();
s.append("package " + packageName + ";\n");
s.append("\n");
s.append("import org.eclipse.core.resources.ResourcesPlugin;\n");
s.append("import org.eclipse.emf.common.util.URI;\n");
s.append("import org.eclipse.emf.ecore.EObject;\n");
s.append("import org.eclipse.emf.ecore.util.EcoreUtil;\n");
s.append("import org.eclipse.epsilon.commons.util.StringProperties;\n");
s.append("import org.eclipse.epsilon.emc.emf.EmfModel;\n");
s.append("import org.eclipse.epsilon.eol.execute.context.Variable;\n");
s.append("import org.eclipse.epsilon.etl.EtlModule;\n");
s.append("import org.reuseware.coconut.compositionprogram.CompositionprogramFactory;\n");
s.append("import org.reuseware.coconut.compositionprogram.CompositionprogramPackage;\n");
s.append("import org.reuseware.coconut.compositionprogram.DerivedCompositionProgram;\n");
s.append("import org.reuseware.coconut.compositionprogram.util.IDerivedCompositionProgramUpdateOperation;\n");
s.append("import org.reuseware.coconut.fragment.Fragment;\n");
s.append("import org.reuseware.coconut.repository.ID;\n");
s.append("import org.reuseware.coconut.repository.resource.ReuseResources;\n");
s.append("import org.reuseware.coconut.repository.util.IDUtil;\n");
s.append("\n");
s.append("public class EpsilonBasedUpdateOperation implements IDerivedCompositionProgramUpdateOperation {\n");
s.append("\n");
s.append(" public static final String fileExtension = \"" + epMetamodel.getName() + "\";\n");
s.append(" public static final String nsURI = \"" + epMetamodel.getNsURI() + "\";\n");
s.append(" \n");
s.append(" public boolean canUpdate(Fragment fragment) {\n");
s.append(" return fragment != null && fragment.getID() != null && fragment.getID().segment(-1).endsWith(\".\" + fileExtension);\n");
s.append(" }\n");
s.append(" \n");
s.append(" public DerivedCompositionProgram update(Fragment fragment) {\n");
s.append(" if (fragment == null || fragment.getID() == null) {\n");
s.append(" return null;\n");
s.append(" }\n");
s.append(" \n");
s.append(" DerivedCompositionProgram derivedCompositionProgram = null;\n");
s.append(" ID cpID = fragment.getID().appendExtension(\"fc\");\n");
s.append(" \n");
s.append(" URI physicalFragmentURI = ReuseResources.INSTANCE.getResourceSet(\n");
s.append(" ).getURIConverter().normalize(IDUtil.idToURI(fragment.getID()));\n");
s.append(" URI physicalCpURI = physicalFragmentURI.appendFileExtension(\"temp_fc\");\n");
s.append(" \n");
s.append(" if (!IDUtil.equals(physicalFragmentURI, fragment.getID())) {\n");
s.append(" EtlModule module = new EtlModule();\n");
s.append(" try {\n");
s.append(" module.parse(this.getClass().getClassLoader().getResource(\"transformations/builder.etl\").toURI());\n");
s.append(" EmfModel source = new EmfModel();\n");
s.append(" StringProperties properties = new StringProperties();\n");
s.append(" properties.put(EmfModel.PROPERTY_NAME, \"Source\");\n");
s.append(" properties.put(EmfModel.PROPERTY_IS_METAMODEL_FILE_BASED, \"false\");\n");
s.append(" properties.put(EmfModel.PROPERTY_METAMODEL_URI, nsURI);\n");
s.append(" properties.put(EmfModel.PROPERTY_MODEL_FILE, physicalFragmentURI.toPlatformString(true));\n");
s.append(" properties.put(EmfModel.PROPERTY_READONLOAD, \"true\");\n");
s.append(" properties.put(EmfModel.PROPERTY_STOREONDISPOSAL,\"false\");\n");
s.append(" source.load(properties, ResourcesPlugin.getWorkspace().getRoot().getRawLocation().toPortableString());\n");
s.append(" module.getContext().getModelRepository().addModel(source);\n");
s.append(" \n");
s.append(" EmfModel fc = new EmfModel();\n");
s.append(" properties = new StringProperties();\n");
s.append(" properties.put(EmfModel.PROPERTY_NAME, \"Fc\");\n");
s.append(" properties.put(EmfModel.PROPERTY_IS_METAMODEL_FILE_BASED, \"false\");\n");
s.append(" properties.put(EmfModel.PROPERTY_METAMODEL_URI, CompositionprogramPackage.eNS_URI);\n");
s.append(" properties.put(EmfModel.PROPERTY_MODEL_FILE, physicalCpURI.toPlatformString(true));\n");
s.append(" properties.put(EmfModel.PROPERTY_READONLOAD, \"false\");\n");
s.append(" properties.put(EmfModel.PROPERTY_STOREONDISPOSAL,\"false\");\n");
s.append(" fc.load(properties, ResourcesPlugin.getWorkspace().getRoot().getRawLocation().toPortableString());\n");
s.append(" \n");
s.append(" module.getContext().getModelRepository().addModel(fc);\n");
s.append(" \n");
s.append(" ID targetUFI = fragment.getID(); //Reuseware will change the file extensions\n");
s.append(" module.getContext().getFrameStack().put(Variable.createReadOnlyVariable(\"targetUFI\", targetUFI.getSegments()));\n");
s.append(" \n");
s.append(" module.execute();\n");
s.append(" \n");
s.append(" for(EObject result : fc.allContents()) {\n");
s.append(" if(result instanceof DerivedCompositionProgram) {\n");
s.append(" derivedCompositionProgram = (DerivedCompositionProgram) EcoreUtil.copy(result);\n");
s.append(" }\n");
s.append(" }\n");
s.append(" }\n");
s.append(" catch (Exception e) {\n");
s.append(" e.printStackTrace();\n");
s.append(" }\n");
s.append(" finally {\n");
s.append(" module.getContext().getModelRepository().dispose();\n");
s.append(" } }\n");
s.append("\n");
s.append(" if (derivedCompositionProgram == null) {\n");
s.append(" derivedCompositionProgram = \n");
s.append(" CompositionprogramFactory.eINSTANCE.createDerivedCompositionProgram();\n");
s.append(" }\n");
s.append(" derivedCompositionProgram.setID(cpID);\n");
s.append(" \n");
s.append(" return derivedCompositionProgram;\n");
s.append(" }\n");
s.append("}\n");
return s.toString();
}
/**
* Generate the MANIFEST.MF file for the plugin
*
* @param epMetamodel
* the metamodel.
* @param packageName
* Name of the Java package.
* @return Generated code.
*/
private static String generateManifestMF(EPackage epMetamodel,
String packageName) {
StringBuffer s = new StringBuffer();
s.append("Manifest-Version: 1.0\n");
s.append("Bundle-ManifestVersion: 2\n");
s.append("Bundle-Name: Reuseware DSL Builder Plugin: "
+ epMetamodel.getName() + "\n");
s.append("Bundle-SymbolicName: " + packageName + ";singleton:=true\n");
s.append("Bundle-Version: 1.0.0\n");
s.append("Bundle-Vendor: Software Engineering Group - TU Dresden Germany\n");
s.append("Bundle-RequiredExecutionEnvironment: J2SE-1.5\n");
s.append("Require-Bundle: org.eclipse.core.runtime,\n");
s.append(" org.eclipse.core.resources,\n");
s.append(" org.eclipse.epsilon.etl.engine,\n");
s.append(" org.eclipse.epsilon.emc.emf,\n");
s.append(" org.eclipse.epsilon.eol.engine,\n");
s.append(" org.eclipse.epsilon.erl.engine,\n");
s.append(" org.reuseware.coconut.compositionprogram\n\n");
return s.toString();
}
/**
* Generate the XML file describing the plugin.
*
* @param epMetamodel
* the metamodel.
* @param packageName
* Name of the Java package.
* @return Generated code.
*/
private static String generatePluginXml(EPackage epMetamodel, String packageName) {
StringBuffer s = new StringBuffer();
s.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
s.append("<?eclipse version=\"3.2\"?>\n");
s.append("<plugin>\n");
// Register the generated transformation
s.append("<extension point=\"org.reuseware.coconut.compositionprogram.derivedCompositionProgramUpdateOperation\">\n");
s.append("<derivedCompositionProgramUpdateOperation\n");
s.append("\tclass=\"" + packageName + ".EpsilonBasedUpdateOperation\">\n");
s.append("\t</derivedCompositionProgramUpdateOperation>\n</extension>\n");
s.append("\n<extension point=\"org.reuseware.coconut.resource.fragmentStores\">\n");
s.append("<fragmentfiles\n");
s.append("\textension=\"" + epMetamodel.getName() + "\"\n");
s.append("\tdiagramExtension=\"" + epMetamodel.getName() + "_diagram\">\n");
s.append("</fragmentfiles>\n</extension>\n");
s.append("</plugin>\n");
return s.toString();
}
/**
* @see IActionDelegate#selectionChanged(IAction, ISelection)
*/
public void selectionChanged(IAction action, ISelection selection) {
this.selection = selection;
}
public void setActivePart(IAction action, IWorkbenchPart targetPart) {
// this.part = targetPart;
}
}