/*******************************************************************************
* Copyright (c) 2006-2015
* Software Technology Group, Dresden University of Technology
* DevBoost GmbH, Dresden, Amtsgericht Dresden, HRB 34001
*
* 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;
* DevBoost GmbH - Dresden, Germany
* - initial API and implementation
******************************************************************************/
package de.devboost.buildboost.buildext.emf;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.codegen.ecore.generator.Generator;
import org.eclipse.emf.codegen.ecore.generator.GeneratorAdapterFactory;
import org.eclipse.emf.codegen.ecore.generator.GeneratorAdapterFactory.Descriptor.Registry;
import org.eclipse.emf.codegen.ecore.genmodel.GenModel;
import org.eclipse.emf.codegen.ecore.genmodel.GenModelPackage;
import org.eclipse.emf.codegen.ecore.genmodel.generator.GenBaseGeneratorAdapter;
import org.eclipse.emf.codegen.ecore.genmodel.generator.GenModelGeneratorAdapterFactory;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.util.BasicMonitor;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.plugin.EcorePlugin;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.impl.EcoreResourceFactoryImpl;
import de.devboost.buildboost.artifacts.InvalidMetadataException;
import de.devboost.buildboost.artifacts.Plugin;
import de.devboost.buildboost.genext.emf.stages.GenerateEMFCodeStage;
import de.devboost.buildboost.genext.emf.steps.GenerateGenModelCodeStep;
/**
* The {@link HeadlessCodeGenerator} is executed by build scripts that are
* generated by the {@link GenerateEMFCodeStage} or the
* {@link GenerateGenModelCodeStep}. It uses the EMF code generators to obtain
* Java code from a given generator model. To allow access to referenced
* generator models a list of all available plug-ins can also be passed to this
* class.
*/
public class HeadlessCodeGenerator {
public static void main(String[] args) throws Exception {
// TODO use properties file to pass arguments instead
String pathToGenModel = args[0];
String projectName = args[1];
String projectPath = args[2];
List<String> pluginPaths = new ArrayList<String>();
for (int i = 3; i < args.length; i++) {
pluginPaths.add(args[i]);
}
HeadlessCodeGenerator generator = new HeadlessCodeGenerator();
generator.run(pathToGenModel, projectName, projectPath, pluginPaths);
}
private void run(String pathToGenModel, String projectName,
String projectPath, List<String> pluginPaths) throws Exception {
ResourceSet rs = new ResourceSetImpl();
registerFactoriesAndPackages(rs);
registerURIMappings(rs, pluginPaths);
EcorePlugin.getPlatformResourceMap().put(
projectName,
URI.createFileURI(projectPath + File.separator)
);
GenModel genModel = loadGenModel(pathToGenModel, rs);
if (genModel == null) {
return;
}
registerCodeGenAdapter();
generateCode(genModel, projectPath);
}
private void generateCode(GenModel genModel, String projectPath) {
// Create the generator and set the model-level input object.
Generator generator = new Generator();
generator.setInput(genModel);
genModel.setFacadeHelperClass(getClass().getName());
// Generate model code.
// EMF 2.8: This logs an exception to the console which is not a problem in our case.
// The logging was introduced in 2.8: https://bugs.eclipse.org/bugs/show_bug.cgi?id=359551
generateCode(genModel, generator,
GenBaseGeneratorAdapter.MODEL_PROJECT_TYPE);
// Generate edit code (if required).
if (mustGenerateEditCode(genModel, projectPath)) {
generateCode(genModel, generator,
GenBaseGeneratorAdapter.EDIT_PROJECT_TYPE);
}
}
private void generateCode(GenModel genModel, Generator generator,
String projectType) {
Diagnostic result = doGenerate(genModel, generator, projectType);
printDiagnostic(result);
}
private Diagnostic doGenerate(GenModel genModel, Generator generator,
String projectType) {
BasicMonitor.Printing systemOutMonitor = new BasicMonitor.Printing(System.out);
return generator.generate(genModel, projectType, systemOutMonitor);
}
private boolean mustGenerateEditCode(GenModel genModel, String projectPath) {
File workDir = new File(projectPath).getParentFile();
String editDirectory = genModel.getEditDirectory();
if (!editDirectory.endsWith("src-gen")) {
return false;
}
if (editDirectory.startsWith("/")) {
editDirectory = editDirectory.substring(1);
}
String editProjectName = editDirectory.substring(0, editDirectory.indexOf("/"));
File editProjectDir = new File(workDir, editProjectName);
if (!editProjectDir.exists()) {
return false;
}
EcorePlugin.getPlatformResourceMap().put(
editProjectName,
URI.createFileURI(editProjectDir.getAbsolutePath() + File.separator)
);
return editProjectDir.exists();
}
private void registerCodeGenAdapter() {
Registry adapterRegistry = GeneratorAdapterFactory.Descriptor.Registry.INSTANCE;
adapterRegistry.addDescriptor(GenModelPackage.eNS_URI, GenModelGeneratorAdapterFactory.DESCRIPTOR);
}
private GenModel loadGenModel(String pathToGenModel, ResourceSet rs) {
URI uri = URI.createFileURI(pathToGenModel);
Resource resource = rs.getResource(uri, true);
// TODO add checks
List<EObject> contents = resource.getContents();
GenModel genModel = (GenModel) contents.get(0);
// reconcile the GenModel: Since the IDE does also do this in the
// background on opening a GenModel, it can happen, that the model
// is not up-to-date w.r.t. the underlying Ecore model.
genModel.reconcile();
genModel.setCanGenerate(true);
// do not generate manifest, this may override an existing manifest
genModel.setBundleManifest(false);
EcoreUtil.resolveAll(rs);
boolean foundProxy = false;
TreeIterator<Notifier> allContents = rs.getAllContents();
while (allContents.hasNext()) {
Notifier notifier = (Notifier) allContents.next();
if (notifier instanceof EObject) {
EObject eObject = (EObject) notifier;
if (eObject.eIsProxy()) {
foundProxy = true;
System.out.println("ERROR: Found unresolved proxy in generator model " + eObject);
}
}
}
if (foundProxy) {
return null;
}
return genModel;
}
private void registerURIMappings(ResourceSet rs, List<String> pluginPaths)
throws Exception {
URIConverter uriConverter = rs.getURIConverter();
Map<URI, URI> uriMap = uriConverter.getURIMap();
for (String pluginPath : pluginPaths) {
registerURIMapping(uriMap, pluginPath);
}
}
private void registerURIMapping(Map<URI, URI> uriMap, String pluginPath)
throws IOException {
File pluginFile = new File(pluginPath);
if (pluginFile.isDirectory() && !pluginPath.endsWith("/")) {
pluginPath = pluginPath + "/";
}
Plugin plugin;
try {
plugin = new Plugin(pluginFile);
} catch (InvalidMetadataException e) {
return;
}
String identifier = plugin.getIdentifier();
URI from = URI.createPlatformPluginURI(identifier + "/", true);
URI to = URI.createFileURI(pluginPath);
boolean isJAR = "jar".equals(to.fileExtension());
if (isJAR) {
to = URI.createURI("archive:" + to.toString() + "!/");
} else if (!"".equals(to.lastSegment())) {
to = to.appendSegment("");
}
System.out.println("Mapping URI " + from + " to " + to);
uriMap.put(from, to);
}
private void registerFactoriesAndPackages(ResourceSet rs) {
// TODO we must search the target platform for registered resource
// factories, generator models and EPackages. Currently we do solely
// register the resource factories and EPackages for the Ecore and the
// GenModel meta models.
org.eclipse.emf.ecore.resource.Resource.Factory.Registry resourceFactoryRegistry = rs.getResourceFactoryRegistry();
Map<String, Object> extensionToFactoryMap = resourceFactoryRegistry.getExtensionToFactoryMap();
extensionToFactoryMap.put("ecore", new EcoreResourceFactoryImpl());
extensionToFactoryMap.put("genmodel", new EcoreResourceFactoryImpl());
GenModelPackage.eINSTANCE.getGenModel();
EcorePackage.eINSTANCE.getEPackage();
}
private void printDiagnostic(Diagnostic diagnostic) {
// TODO print only warnings and errors?
System.out.println("Diagnostic: " + diagnostic.getMessage());
List<Diagnostic> children = diagnostic.getChildren();
for (Diagnostic child : children) {
printDiagnostic(child);
}
}
}