diff --git a/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/XtextGenerator.xtend b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/XtextGenerator.xtend index d4c1aaee9..a0e464352 100644 --- a/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/XtextGenerator.xtend +++ b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/XtextGenerator.xtend @@ -11,6 +11,11 @@ import com.google.inject.Guice import com.google.inject.Inject import com.google.inject.Injector import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream import java.util.List import org.eclipse.emf.mwe.core.WorkflowContext import org.eclipse.emf.mwe.core.issues.Issues @@ -18,9 +23,12 @@ import org.eclipse.emf.mwe.core.lib.AbstractWorkflowComponent2 import org.eclipse.emf.mwe.core.monitor.ProgressMonitor import org.eclipse.xtend.lib.annotations.Accessors import org.eclipse.xtext.XtextStandaloneSetup +import org.eclipse.xtext.util.MergeableManifest import org.eclipse.xtext.xtext.generator.model.CodeConfig import org.eclipse.xtext.xtext.generator.model.IXtextProjectConfig +import org.eclipse.xtext.xtext.generator.model.ManifestAccess import org.eclipse.xtext.xtext.generator.model.PluginXmlAccess +import org.eclipse.xtext.xtext.generator.model.TypeReference /** * The Xtext language infrastructure generator. Can be configured with {@link IGeneratorFragment} @@ -40,6 +48,8 @@ class XtextGenerator extends AbstractWorkflowComponent2 implements IGuiceAwareGe Injector injector + @Inject extension XtextGeneratorNaming + @Inject IXtextProjectConfig projectConfig @Inject XtextGeneratorTemplates templates @@ -87,6 +97,7 @@ class XtextGenerator extends AbstractWorkflowComponent2 implements IGuiceAwareGe } generatePluginXmls() generateManifests() + generateActivator() } protected def generateRuntimeSetup(LanguageConfig2 language) { @@ -109,16 +120,67 @@ class XtextGenerator extends AbstractWorkflowComponent2 implements IGuiceAwareGe } protected def generateManifests() { - projectConfig.runtimeManifest?.generate() - projectConfig.runtimeTestManifest?.generate() - projectConfig.genericIdeManifest?.generate() - projectConfig.genericIdeTestManifest?.generate() - projectConfig.eclipsePluginManifest?.generate() - projectConfig.eclipsePluginTestManifest?.generate() - projectConfig.ideaPluginManifest?.generate() - projectConfig.ideaPluginTestManifest?.generate() - projectConfig.webManifest?.generate() - projectConfig.webTestManifest?.generate() + val firstGrammar = languageConfigs.head?.grammar + generateManifest(projectConfig.runtimeManifest, null) + generateManifest(projectConfig.runtimeTestManifest, null) + generateManifest(projectConfig.genericIdeManifest, null) + generateManifest(projectConfig.genericIdeTestManifest, null) + generateManifest(projectConfig.eclipsePluginManifest, firstGrammar.eclipsePluginActivator) + generateManifest(projectConfig.eclipsePluginTestManifest, null) + generateManifest(projectConfig.ideaPluginManifest, null) + generateManifest(projectConfig.ideaPluginTestManifest, null) + generateManifest(projectConfig.webManifest, null) + generateManifest(projectConfig.webTestManifest, null) + } + + protected def generateManifest(ManifestAccess manifest, TypeReference activator) { + if (manifest !== null) { + if (manifest.bundleName === null) { + val segments = manifest.path.split('/') + if (segments.length >= 3 && segments.get(segments.length - 2) == 'META-INF') + manifest.bundleName = segments.get(segments.length - 3) + } + val file = new File(manifest.path) + if (file.exists) { + if (manifest.merge) { + mergeManifest(manifest, file, activator) + } else if (manifest.path.endsWith('.MF')) { + manifest.path = manifest.path + '_gen' + templates.createManifest(manifest, activator).writeToFile() + } + } else { + templates.createManifest(manifest, activator).writeToFile() + } + } + } + + protected def generateActivator() { + if (projectConfig.eclipsePluginSrcGen !== null) + templates.createEclipsePluginActivator(languageConfigs).writeTo(projectConfig.eclipsePluginSrcGen) + } + + protected def mergeManifest(ManifestAccess manifest, File file, TypeReference activator) throws IOException { + var InputStream in + var OutputStream out + try { + in = new FileInputStream(file) + val merge = new MergeableManifest(in, manifest.bundleName) + merge.addExportedPackages(manifest.exportedPackages) + merge.addRequiredBundles(manifest.requiredBundles) + merge.addImportedPackages(manifest.importedPackages) + if (activator !== null && !merge.mainAttributes.containsKey(MergeableManifest.BUNDLE_ACTIVATOR)) { + merge.mainAttributes.put(MergeableManifest.BUNDLE_ACTIVATOR, activator.name) + } + if (merge.isModified) { + out = new FileOutputStream(file) + merge.write(out) + } + } finally { + if (in !== null) + in.close() + if (out != null) + out.close() + } } protected def generatePluginXmls() { diff --git a/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/XtextGeneratorTemplates.xtend b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/XtextGeneratorTemplates.xtend index 17a48ad17..f35b4ee7e 100644 --- a/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/XtextGeneratorTemplates.xtend +++ b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/XtextGeneratorTemplates.xtend @@ -7,13 +7,16 @@ *******************************************************************************/ package org.eclipse.xtext.xtext.generator +import com.google.common.collect.Maps import com.google.inject.Binder import com.google.inject.Guice import com.google.inject.Inject import com.google.inject.Injector +import com.google.inject.Module import com.google.inject.Provider import com.google.inject.Singleton import com.google.inject.name.Names +import java.util.List import org.eclipse.xtext.Constants import org.eclipse.xtext.ISetup import org.eclipse.xtext.ISetupExtension @@ -21,11 +24,14 @@ import org.eclipse.xtext.XtextPackage import org.eclipse.xtext.parser.IEncodingProvider import org.eclipse.xtext.resource.impl.BinaryGrammarResourceFactoryImpl import org.eclipse.xtext.service.SingletonBinding +import org.eclipse.xtext.util.Modules2 import org.eclipse.xtext.xtext.generator.model.CodeConfig import org.eclipse.xtext.xtext.generator.model.GuiceModuleAccess import org.eclipse.xtext.xtext.generator.model.JavaFileAccess +import org.eclipse.xtext.xtext.generator.model.ManifestAccess import org.eclipse.xtext.xtext.generator.model.PluginXmlAccess import org.eclipse.xtext.xtext.generator.model.TextFileAccess +import org.eclipse.xtext.xtext.generator.model.TypeReference @Singleton class XtextGeneratorTemplates { @@ -93,6 +99,7 @@ class XtextGeneratorTemplates { «FOR usedGrammar : g.usedGrammars» «usedGrammar.runtimeSetup».doSetup(); «ENDFOR» + «IF g.usedGrammars.isEmpty» // register default ePackages if (!«'org.eclipse.emf.ecore.resource.Resource'».Factory.Registry.INSTANCE.getExtensionToFactoryMap().containsKey("ecore")) @@ -270,6 +277,36 @@ class XtextGeneratorTemplates { return javaFile } + def TextFileAccess createManifest(ManifestAccess manifest, TypeReference activator) { + val file = new TextFileAccess + file.encodingProvider = encodingProvider + file.path = manifest.path + file.content = ''' + Manifest-Version: 1.0 + Bundle-ManifestVersion: 2 + Bundle-Name: «manifest.bundleName» + Bundle-SymbolicName: «manifest.symbolicName ?: manifest.bundleName»; singleton:=true + «IF !manifest.version.nullOrEmpty» + Bundle-Version: «manifest.version» + «ENDIF» + Bundle-RequiredExecutionEnvironment: JavaSE-1.6 + Bundle-ActivationPolicy: lazy + «IF !manifest.exportedPackages.empty» + Export-Package: «FOR pack : manifest.exportedPackages.sort SEPARATOR ',\n '»«pack»«ENDFOR» + «ENDIF» + «IF !manifest.requiredBundles.empty» + Require-Bundle: «FOR bundle : manifest.requiredBundles.sort SEPARATOR ',\n '»«bundle»«ENDFOR» + «ENDIF» + «IF !manifest.importedPackages.empty» + Import-Package: «FOR pack : manifest.importedPackages.sort SEPARATOR ',\n '»«pack»«ENDFOR» + «ENDIF» + «IF activator !== null» + Bundle-Activator: «activator» + «ENDIF» + ''' + return file + } + def JavaFileAccess createEclipsePluginExecutableExtensionFactory(LanguageConfig2 langConfig) { val g = langConfig.grammar val javaFile = new JavaFileAccess(g.eclipsePluginExecutableExtensionFactory, codeConfig) @@ -300,4 +337,98 @@ class XtextGeneratorTemplates { return javaFile } + def JavaFileAccess createEclipsePluginActivator(List langConfigs) { + val gs = langConfigs.map[grammar].toList + val activator = gs.head.eclipsePluginActivator + val javaFile = new JavaFileAccess(activator, codeConfig) + javaFile.encodingProvider = encodingProvider + + javaFile.typeComment = ''' + /** + * This class was generated. Customizations should only happen in a newly + * introduced subclass. + */ + ''' + javaFile.javaContent = ''' + public class «activator.simpleName» extends «'org.eclipse.ui.plugin.AbstractUIPlugin'» { + + «FOR grammar : gs» + public static final String «grammar.name.toUpperCase.replaceAll('\\.', '_')» = "«grammar.name»"; + «ENDFOR» + + private static final Logger logger = Logger.getLogger(«activator.simpleName».class); + + private static «activator.simpleName» INSTANCE; + + private «'java.util.Map'» injectors = «'java.util.Collections'».synchronizedMap(«Maps». newHashMapWithExpectedSize(1)); + + @Override + public void start(«'org.osgi.framework.BundleContext'» context) throws Exception { + super.start(context); + INSTANCE = this; + } + + @Override + public void stop(«'org.osgi.framework.BundleContext'» context) throws Exception { + injectors.clear(); + INSTANCE = null; + super.stop(context); + } + + public static «activator.simpleName» getInstance() { + return INSTANCE; + } + + public «Injector» getInjector(String language) { + synchronized (injectors) { + «Injector» injector = injectors.get(language); + if (injector == null) { + injectors.put(language, injector = createInjector(language)); + } + return injector; + } + } + + protected «Injector» createInjector(String language) { + try { + «Module» runtimeModule = getRuntimeModule(language); + «Module» sharedStateModule = getSharedStateModule(); + «Module» uiModule = getUiModule(language); + «Module» mergedModule = «Modules2».mixin(runtimeModule, sharedStateModule, uiModule); + return «Guice».createInjector(mergedModule); + } catch (Exception e) { + logger.error("Failed to create injector for " + language); + logger.error(e.getMessage(), e); + throw new RuntimeException("Failed to create injector for " + language, e); + } + } + + protected Module getRuntimeModule(String grammar) { + «FOR grammar : gs» + if («grammar.name.toUpperCase.replaceAll('\\.', '_')».equals(grammar)) { + return new «grammar.runtimeModule»(); + } + «ENDFOR» + throw new IllegalArgumentException(grammar); + } + + protected «Module» getUiModule(String grammar) { + «FOR grammar : gs» + if («grammar.name.toUpperCase.replaceAll('\\.', '_')».equals(grammar)) { + return new «grammar.eclipsePluginModule»(this); + } + «ENDFOR» + throw new IllegalArgumentException(grammar); + } + + protected «Module» getSharedStateModule() { + return new «'org.eclipse.xtext.ui.shared.SharedStateModule'»(); + } + + } + ''' + javaFile.markedAsGenerated = true + return javaFile + } + } \ No newline at end of file diff --git a/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/ManifestAccess.xtend b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/ManifestAccess.xtend index ca1b018b1..af0738993 100644 --- a/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/ManifestAccess.xtend +++ b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/ManifestAccess.xtend @@ -7,17 +7,26 @@ *******************************************************************************/ package org.eclipse.xtext.xtext.generator.model +import java.util.Set import org.eclipse.xtend.lib.annotations.Accessors @Accessors class ManifestAccess { - String path + String path = 'META-INF/MANIFEST.MF' - boolean merge + String bundleName - def void generate() { - - } + String symbolicName + + String version = '0.0.1' + + boolean merge = true + + val Set exportedPackages = newHashSet + + val Set requiredBundles = newHashSet + + val Set importedPackages = newHashSet } diff --git a/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/PluginXmlAccess.xtend b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/PluginXmlAccess.xtend index 3db8ff746..945135113 100644 --- a/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/PluginXmlAccess.xtend +++ b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/PluginXmlAccess.xtend @@ -13,7 +13,7 @@ import org.eclipse.xtend.lib.annotations.Accessors @Accessors class PluginXmlAccess { - String path + String path = 'plugin.xml' val List entries = newArrayList diff --git a/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/TextFileAccess.xtend b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/TextFileAccess.xtend index f9306c22a..2f799952e 100644 --- a/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/TextFileAccess.xtend +++ b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/TextFileAccess.xtend @@ -9,6 +9,7 @@ package org.eclipse.xtext.xtext.generator.model import com.google.common.io.Files import java.io.File +import java.io.IOException import java.nio.charset.Charset import org.eclipse.emf.common.util.URI import org.eclipse.xtend.lib.annotations.Accessors @@ -34,7 +35,7 @@ class TextFileAccess { fileSystemAccess.generateFile(path, generate()) } - def writeToFile() { + def writeToFile() throws IOException { var Charset charset if (encodingProvider !== null) { val uri = URI.createFileURI(path)