From e3bd95a4f62f42cafc56efbf5954d7db600da9a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Sp=C3=B6nemann?= Date: Thu, 9 Jul 2015 14:17:00 +0200 Subject: [PATCH] [generator] Added new Xbase and Xtype fragments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Miro Spönemann --- .../AbstractGeneratorFragment2.xtend | 24 + .../xtext/xtext/generator/GenModelUtil.xtend | 170 ++++++ .../xtext/generator/LanguageConfig2.xtend | 23 +- .../xtext/generator/XtextGenerator.xtend | 9 +- .../generator/model/FileSystemAccess.xtend | 12 + .../generator/model/JavaFileAccess.xtend | 14 +- .../xtext/generator/model/TypeReference.xtend | 2 +- .../generator/model/XtendFileAccess.xtend | 30 ++ .../xbase/XbaseGeneratorFragment2.xtend | 483 ++++++++++++++++++ .../xbase/XtypeGeneratorFragment2.xtend | 30 ++ .../src/org/eclipse/xtext/GrammarUtil.java | 14 + 11 files changed, 797 insertions(+), 14 deletions(-) create mode 100644 plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/AbstractGeneratorFragment2.xtend create mode 100644 plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/GenModelUtil.xtend create mode 100644 plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/XtendFileAccess.xtend create mode 100644 plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/xbase/XbaseGeneratorFragment2.xtend create mode 100644 plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/xbase/XtypeGeneratorFragment2.xtend diff --git a/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/AbstractGeneratorFragment2.xtend b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/AbstractGeneratorFragment2.xtend new file mode 100644 index 000000000..3871a1ff5 --- /dev/null +++ b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/AbstractGeneratorFragment2.xtend @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2014 itemis AG (http://www.itemis.eu) 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 + *******************************************************************************/ +package org.eclipse.xtext.xtext.generator + +import org.eclipse.xtext.xtext.generator.IGeneratorFragment2 +import org.eclipse.emf.mwe.core.issues.Issues +import com.google.inject.Injector + +abstract class AbstractGeneratorFragment2 implements IGeneratorFragment2 { + + override checkConfiguration(XtextGenerator generator, Issues issues) { + // Override this method to check the configured properties of this fragment + } + + override initialize(Injector injector) { + injector.injectMembers(this) + } + +} \ No newline at end of file diff --git a/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/GenModelUtil.xtend b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/GenModelUtil.xtend new file mode 100644 index 000000000..80b1ede5a --- /dev/null +++ b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/GenModelUtil.xtend @@ -0,0 +1,170 @@ +/** + * Copyright (c) 2011 itemis AG (http://www.itemis.eu) 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 + */ +package org.eclipse.xtext.xtext.generator + +import org.eclipse.emf.codegen.ecore.genmodel.GenClass +import org.eclipse.emf.codegen.ecore.genmodel.GenClassifier +import org.eclipse.emf.codegen.ecore.genmodel.GenDataType +import org.eclipse.emf.codegen.ecore.genmodel.GenEnum +import org.eclipse.emf.codegen.ecore.genmodel.GenFeature +import org.eclipse.emf.codegen.ecore.genmodel.GenModel +import org.eclipse.emf.codegen.ecore.genmodel.GenPackage +import org.eclipse.emf.ecore.EClass +import org.eclipse.emf.ecore.EClassifier +import org.eclipse.emf.ecore.EDataType +import org.eclipse.emf.ecore.EEnum +import org.eclipse.emf.ecore.EObject +import org.eclipse.emf.ecore.EPackage +import org.eclipse.emf.ecore.EStructuralFeature +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 + +class GenModelUtil { + + def static GenClass getGenClass(EClass cls, ResourceSet resourceSet) { + return getGenClassifier(cls, resourceSet) as GenClass + } + + def static GenClassifier getGenClassifier(EClassifier cls, ResourceSet resourceSet) { + val genPackage = getGenPackage(cls.EPackage, resourceSet) + for (genCls : genPackage.genClassifiers) { + if (cls.name == genCls.ecoreClassifier.name) { + return genCls + } + } + throw new RuntimeException('''No GenClassifier named '«cls.name»' found in GenModel «genPackage.eResource.URI»''') + } + + def static GenDataType getGenDataType(EDataType dt, ResourceSet resourceSet) { + return getGenClassifier(dt, resourceSet) as GenDataType + } + + def static GenEnum getGenEnum(EEnum en, ResourceSet resourceSet) { + return getGenClassifier(en, resourceSet) as GenEnum + } + + def static GenFeature getGenFeature(EStructuralFeature feature, ResourceSet resourceSet) { + val genCls = getGenClassifier(feature.EContainingClass, resourceSet) as GenClass + for (genFeat : genCls.genFeatures) { + if (feature.name == genFeat.ecoreFeature.name) { + return genFeat + } + } + throw new RuntimeException('''No GenFeature named '«feature.name»' found in GenClass '«genCls»' from GenModel«genCls.eResource.URI»''') + } + + def static String getGenIntLiteral(EClass clazz, EStructuralFeature feature, ResourceSet resourceSet) { + val genFeature = getGenFeature(feature, resourceSet) + val genClass = getGenClass(clazz, resourceSet) + return genClass.genPackage.packageInterfaceName + '.' + genClass.getFeatureID(genFeature) + } + + def static String getGenIntLiteral(EClassifier classifier, ResourceSet resourceSet) { + val genClassifier = getGenClassifier(classifier, resourceSet) + return genClassifier.genPackage.packageInterfaceName + '.' + genClassifier.classifierID + } + + def static GenPackage getGenPackage(EPackage pkg, ResourceSet resourceSet) { + val nsURI = pkg.nsURI + var String location + if (pkg.eResource?.URI !== null) + location = pkg.eResource.URI.toString + val genModelResource = getGenModelResource(location, nsURI, resourceSet) + if (genModelResource !== null) { + for (model : genModelResource.contents) { + if (model instanceof GenModel) { + val genPkg = model.findGenPackage(pkg) + if (genPkg !== null) { + genPkg.getEcorePackage.getEClassifiers() + return genPkg + } + } + } + throw new RuntimeException('''No GenPackage for NsURI «nsURI» found in «genModelResource.URI»''') + } + throw new RuntimeException('''No GenPackage for NsURI «nsURI».''') + } + + def static Resource getGenModelResource(String locationInfo, String nsURI, ResourceSet resourceSet) { + val genModelURI = EcorePlugin.getEPackageNsURIToGenModelLocationMap(false).get(nsURI) + if (genModelURI === null) { + if (EcorePackage.eNS_URI.equals(nsURI)) // If we really want to use the registered ecore ... + return null // look into the resource set to find a genpackage for the given URI + for (res : resourceSet.resources) { + // We only look into the first level, as genmodels are usually among the top level elements. + // This is just to avoid traversing all eobjects in the resource set. + for (obj : res.contents) { + if (obj instanceof GenModel) { + for (genPackage : obj.genPackages) { + if (genPackage.NSURI.equals(nsURI)) { + return genPackage.eResource + } + } + } + } + } + val buf = new StringBuilder + var loc = locationInfo + if (loc !== null && loc.length > 0) + loc = ' from ' + loc + else + loc = '' + buf.append("Could not find a GenModel for EPackage '").append(nsURI).append("'").append(loc).append("\n") + // TODO replace with references to new fragments + buf.append('''If the missing GenModel has been generated via EMFGeneratorFragment.class.getSimpleName() or org.eclipse.xtext.generator.ecore.EcoreGeneratorFragment.class.getSimpleName()''') + buf.append(" make sure to run it first in the workflow.\n") + buf.append('''If you have a *.genmodel-file, make sure to register it via StandaloneSetup.registerGenModelFile(String)''') + throw new RuntimeException(buf.toString) + } + if (resourceSet === null) + throw new RuntimeException('''There is no ResourceSet for EPackage '«nsURI»'. Please make sure the EPackage has been loaded from a .ecore file and not from the generated Java file.''') + val genModelResource = resourceSet.getResource(genModelURI, true) + if (genModelResource === null) + throw new RuntimeException('''Error loading GenModel «genModelURI»''') + for (EObject content : genModelResource.contents) { + if (content instanceof GenModel) + content.reconcile() + } + return genModelResource + } + + def static String getGenTypeLiteral(EClassifier classifier, ResourceSet resourceSet) { + // inspired by org.eclipse.emf.codegen.ecore.genmodel.impl.GenClassifierImpl.getQualifiedClassifierAccessor() + val genClassifier = getGenClassifier(classifier, resourceSet) + var String pkg = genClassifier.genPackage.packageInterfaceName + if (genClassifier.genPackage.isLiteralsInterface) + return pkg + '.Literals.' + genClassifier.classifierID + else + return pkg + '.eINSTANCE.get' + genClassifier.classifierAccessorName + '()' + } + + def static String getGenTypeLiteral(EPackage pkg, ResourceSet resourceSet) { + return getGenPackage(pkg, resourceSet).packageInterfaceName + '.eINSTANCE' + } + + def static String getGenTypeLiteral(EStructuralFeature feature, ResourceSet resourceSet) { + // inspired by org.eclipse.emf.codegen.ecore.genmodel.impl.GenFeatureImpl.getQualifiedFeatureAccessor() + val genFeature = getGenFeature(feature, resourceSet) + val pkg = genFeature.genPackage.packageInterfaceName + if (genFeature.genPackage.isLiteralsInterface) + return pkg + '.Literals.' + genFeature.genClass.getFeatureID(genFeature) + else + return pkg + '.eINSTANCE.get' + genFeature.featureAccessorName + '()' + } + + def static String getJavaTypeName(EClassifier classifier, ResourceSet resourceSet) { + val genClassifier = getGenClassifier(classifier, resourceSet) + if (genClassifier instanceof GenClass) + return genClassifier.qualifiedInterfaceName + else + return (genClassifier as GenDataType).qualifiedInstanceClassName + } + +} diff --git a/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/LanguageConfig2.xtend b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/LanguageConfig2.xtend index 7a6f87424..4e05166b1 100644 --- a/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/LanguageConfig2.xtend +++ b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/LanguageConfig2.xtend @@ -39,7 +39,7 @@ import org.eclipse.xtext.resource.impl.ResourceDescriptionsData import org.eclipse.xtext.util.internal.Log import org.eclipse.xtext.xtext.generator.model.GuiceModuleAccess import org.eclipse.xtext.xtext.generator.model.StandaloneSetupAccess -import org.eclipse.xtext.xtext.generator.model.TypeReference +import org.eclipse.xtext.xtext.generator.xbase.XbaseGeneratorFragment2 import static extension org.eclipse.xtext.xtext.generator.model.TypeReference.* @@ -83,7 +83,7 @@ class LanguageConfig2 extends CompositeGeneratorFragment2 { } def void setFileExtensions(String fileExtensions) { - this.fileExtensions = fileExtensions.trim.split("\\s*,\\s*").toList + this.fileExtensions = fileExtensions.trim.split('\\s*,\\s*').toList } def List getFileExtensions() { @@ -254,11 +254,11 @@ class LanguageConfig2 extends CompositeGeneratorFragment2 { } override generate(LanguageConfig2 language) { - addImplicitContributions() + addImplicitContributions(language) super.generate(language) } - protected def void addImplicitContributions() { + protected def void addImplicitContributions(LanguageConfig2 language) { if (projectConfig.runtimeManifest !== null) { projectConfig.runtimeManifest.requiredBundles.addAll(#[ 'org.eclipse.xtext', 'org.eclipse.xtext.util' @@ -270,10 +270,19 @@ class LanguageConfig2 extends CompositeGeneratorFragment2 { 'org.eclipse.xtext.ui', 'org.eclipse.xtext.ui.shared', 'org.eclipse.ui.editors', 'org.eclipse.ui' ]) } + val StringConcatenationClient expression = '''«'org.eclipse.xtext.ui.shared.Access'.typeRef».getJavaProjectsState()''' - new GuiceModuleAccess.BindingFactory() - .addTypeToProviderInstance(new TypeReference(IAllContainersState), expression) - .contributeTo(eclipsePluginGenModule) + val bindingFactory = new GuiceModuleAccess.BindingFactory() + .addTypeToProviderInstance(IAllContainersState.typeRef, expression) + if (XbaseGeneratorFragment2.inheritsXbase(language.grammar)) { + bindingFactory.addTypeToType('org.eclipse.xtext.ui.editor.XtextEditor'.typeRef, + 'org.eclipse.xtext.xbase.ui.editor.XbaseEditor'.typeRef) + .addTypeToType('org.eclipse.xtext.ui.editor.model.XtextDocumentProvider'.typeRef, + 'org.eclipse.xtext.xbase.ui.editor.XbaseDocumentProvider'.typeRef) + .addTypeToType('org.eclipse.xtext.ui.generator.trace.OpenGeneratedFileHandler'.typeRef, + 'org.eclipse.xtext.xbase.ui.generator.trace.XbaseOpenGeneratedFileHandler'.typeRef) + } + bindingFactory.contributeTo(eclipsePluginGenModule) } } \ No newline at end of file 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 ed0ef7442..49ce86956 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 @@ -29,6 +29,7 @@ import org.eclipse.xtext.Grammar import org.eclipse.xtext.XtextStandaloneSetup import org.eclipse.xtext.util.MergeableManifest import org.eclipse.xtext.util.internal.Log +import org.eclipse.xtext.xtext.generator.model.FileSystemAccess import org.eclipse.xtext.xtext.generator.model.ManifestAccess import org.eclipse.xtext.xtext.generator.model.TypeReference @@ -53,6 +54,8 @@ class XtextGenerator extends AbstractWorkflowComponent2 implements IGuiceAwareGe @Inject XtextGeneratorTemplates templates + @Inject extension FileSystemAccess.Extensions + new() { new XtextStandaloneSetup().createInjectorAndDoEMFRegistration() } @@ -123,17 +126,17 @@ class XtextGenerator extends AbstractWorkflowComponent2 implements IGuiceAwareGe protected def generateRuntimeSetup(LanguageConfig2 language) { templates.createRuntimeGenSetup(language).writeTo(projectConfig.runtimeSrcGen) - if (!projectConfig.runtimeSrc.isFile(language.naming.runtimeSetup.path)) + if (!projectConfig.runtimeSrc.containsJavaFile(language.naming.runtimeSetup)) templates.createRuntimeSetup(language).writeTo(projectConfig.runtimeSrc) } protected def generateModules(LanguageConfig2 language) { templates.createRuntimeGenModule(language).writeTo(projectConfig.runtimeSrcGen) - if (!projectConfig.runtimeSrc.isFile(language.naming.runtimeModule.path)) + if (!projectConfig.runtimeSrc.containsJavaFile(language.naming.runtimeModule)) templates.createRuntimeModule(language).writeTo(projectConfig.runtimeSrc) if (projectConfig.eclipsePluginSrcGen !== null) templates.createEclipsePluginGenModule(language).writeTo(projectConfig.eclipsePluginSrcGen) - if (projectConfig.eclipsePluginSrc !== null && !projectConfig.eclipsePluginSrc.isFile(language.naming.eclipsePluginModule.path)) + if (projectConfig.eclipsePluginSrc !== null && !projectConfig.eclipsePluginSrc.containsJavaFile(language.naming.eclipsePluginModule)) templates.createEclipsePluginModule(language).writeTo(projectConfig.eclipsePluginSrc) } diff --git a/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/FileSystemAccess.xtend b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/FileSystemAccess.xtend index fa3145cd5..00d19078f 100644 --- a/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/FileSystemAccess.xtend +++ b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/FileSystemAccess.xtend @@ -22,6 +22,18 @@ import org.eclipse.xtext.xtext.generator.IGuiceAwareGeneratorComponent class FileSystemAccess implements IFileSystemAccess2, IGuiceAwareGeneratorComponent { + static class Extensions { + + def boolean containsJavaFile(IFileSystemAccess2 fsa, TypeReference typeRef) { + fsa.isFile(typeRef.path + '.java') + } + + def boolean containsXtendFile(IFileSystemAccess2 fsa, TypeReference typeRef) { + fsa.isFile(typeRef.path + '.xtend') + } + + } + @Inject IEncodingProvider encodingProvider val URI baseUri diff --git a/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/JavaFileAccess.xtend b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/JavaFileAccess.xtend index 18a4a3a54..82940c2cf 100644 --- a/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/JavaFileAccess.xtend +++ b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/JavaFileAccess.xtend @@ -43,7 +43,11 @@ class JavaFileAccess extends TextFileAccess { throw new IllegalArgumentException('Nested type cannot be serialized: ' + typeRef) this.javaType = typeRef this.codeConfig = codeConfig - setPath(typeRef.path) + setPath(typeRef.path + '.' + fileExtension) + } + + protected def String getFileExtension() { + 'java' } def String importType(TypeReference typeRef) { @@ -83,6 +87,10 @@ class JavaFileAccess extends TextFileAccess { setContent(javaStringConcat) } + protected def boolean appendSemicolons() { + true + } + override generate() { val classAnnotations = annotations + codeConfig.classAnnotations.filter[appliesTo(this)] classAnnotations.forEach[importType(annotationImport)] @@ -90,10 +98,10 @@ class JavaFileAccess extends TextFileAccess { Collections.sort(sortedImports) return ''' «codeConfig.fileHeader» - package «javaType.packageName»; + package «javaType.packageName»«IF appendSemicolons»;«ENDIF» «FOR importName : sortedImports» - import «importName»; + import «importName»«IF appendSemicolons»;«ENDIF» «ENDFOR» «typeComment» diff --git a/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/TypeReference.xtend b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/TypeReference.xtend index 619550739..a9d57c20b 100644 --- a/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/TypeReference.xtend +++ b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/TypeReference.xtend @@ -118,7 +118,7 @@ class TypeReference { } def String getPath() { - return packageName.replace('.', '/') + '/' + simpleNames.head + '.java' + return packageName.replace('.', '/') + '/' + simpleNames.head } } \ No newline at end of file diff --git a/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/XtendFileAccess.xtend b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/XtendFileAccess.xtend new file mode 100644 index 000000000..230ce1d31 --- /dev/null +++ b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/model/XtendFileAccess.xtend @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2015 itemis AG (http://www.itemis.eu) 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 + *******************************************************************************/ +package org.eclipse.xtext.xtext.generator.model + +import org.eclipse.xtext.xtext.generator.CodeConfig + +class XtendFileAccess extends JavaFileAccess { + + new(String qualifiedName, CodeConfig codeConfig) { + super(qualifiedName, codeConfig) + } + + new(TypeReference typeRef, CodeConfig codeConfig) { + super(typeRef, codeConfig) + } + + override protected getFileExtension() { + 'xtend' + } + + override protected appendSemicolons() { + false + } + +} \ No newline at end of file diff --git a/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/xbase/XbaseGeneratorFragment2.xtend b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/xbase/XbaseGeneratorFragment2.xtend new file mode 100644 index 000000000..a22ad1336 --- /dev/null +++ b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/xbase/XbaseGeneratorFragment2.xtend @@ -0,0 +1,483 @@ +/******************************************************************************* + * Copyright (c) 2015 itemis AG (http://www.itemis.eu) 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 + *******************************************************************************/ +package org.eclipse.xtext.xtext.generator.xbase + +import com.google.inject.Inject +import com.google.inject.name.Names +import java.util.Set +import org.eclipse.xtend.lib.annotations.Accessors +import org.eclipse.xtend2.lib.StringConcatenationClient +import org.eclipse.xtext.AbstractRule +import org.eclipse.xtext.Grammar +import org.eclipse.xtext.GrammarUtil +import org.eclipse.xtext.naming.IQualifiedNameProvider +import org.eclipse.xtext.parser.IEncodingProvider +import org.eclipse.xtext.resource.ILocationInFileProvider +import org.eclipse.xtext.scoping.IGlobalScopeProvider +import org.eclipse.xtext.scoping.IScopeProvider +import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider +import org.eclipse.xtext.validation.IResourceValidator +import org.eclipse.xtext.xtext.UsedRulesFinder +import org.eclipse.xtext.xtext.generator.AbstractGeneratorFragment2 +import org.eclipse.xtext.xtext.generator.CodeConfig +import org.eclipse.xtext.xtext.generator.IXtextProjectConfig +import org.eclipse.xtext.xtext.generator.LanguageConfig2 +import org.eclipse.xtext.xtext.generator.model.FileSystemAccess +import org.eclipse.xtext.xtext.generator.model.GuiceModuleAccess +import org.eclipse.xtext.xtext.generator.model.TypeReference +import org.eclipse.xtext.xtext.generator.model.XtendFileAccess + +import static extension org.eclipse.xtext.xtext.generator.GenModelUtil.* +import static extension org.eclipse.xtext.xtext.generator.model.TypeReference.* + +@Accessors(PUBLIC_SETTER) +class XbaseGeneratorFragment2 extends AbstractGeneratorFragment2 { + + static def boolean inheritsXbase(Grammar grammar) { + GrammarUtil.inherits(grammar, 'org.eclipse.xtext.xbase.Xbase') + } + + static def boolean inheritsXbaseWithAnnotations(Grammar grammar) { + GrammarUtil.inherits(grammar, 'org.eclipse.xtext.xbase.annotations.XbaseWithAnnotations') + } + + static def boolean usesXImportSection(Grammar grammar) { + val Set usedRules = newHashSet + new UsedRulesFinder(usedRules).compute(grammar) + return usedRules.exists[name == 'XImportSection' && GrammarUtil.getGrammar(it).name == 'org.eclipse.xtext.xbase.Xtype'] + } + + boolean generateXtendInferrer = true + boolean useInferredJvmModel = true + boolean jdtTypeHierarchy = true + boolean jdtCallHierarchy = true + boolean skipExportedPackage = false + + @Inject IXtextProjectConfig projectConfig + + @Inject CodeConfig codeConfig + + @Inject IEncodingProvider encodingProvider + + @Inject extension FileSystemAccess.Extensions + + def TypeReference getJvmModelInferrer(LanguageConfig2 langConfig) { + new TypeReference(langConfig.naming.runtimeBasePackage + '.jvmmodel.' + GrammarUtil.getName(langConfig.grammar) + 'JvmModelInferrer') + } + + def TypeReference getImportScopeProvider(LanguageConfig2 langConfig) { + if (langConfig.grammar.usesXImportSection) + 'org.eclipse.xtext.xbase.scoping.XImportSectionNamespaceScopeProvider'.typeRef + else + 'org.eclipse.xtext.xbase.scoping.XbaseImportedNamespaceScopeProvider'.typeRef + } + + override generate(LanguageConfig2 language) { + if (!language.grammar.inheritsXbase) + return; + + addRuntimeGuiceBindings(language) + addEclipsePluginGuiceBindings(language) + if (projectConfig.eclipsePluginPluginXml !== null) + addEclipsePluginExtensions(language) + if (!projectConfig.runtimeSrc.containsXtendFile(language.jvmModelInferrer)) + doGenerateXtendInferrer(language) + + if (projectConfig.runtimeManifest !== null) { + projectConfig.runtimeManifest.requiredBundles.addAll(#[ + 'org.eclipse.xtext.xbase', 'org.eclipse.xtext.xbase.lib' + ]) + if ((generateXtendInferrer || useInferredJvmModel) && !skipExportedPackage) { + projectConfig.runtimeManifest.exportedPackages += language.jvmModelInferrer.packageName + } + } + if (projectConfig.eclipsePluginManifest !== null) { + projectConfig.eclipsePluginManifest.requiredBundles.addAll(#[ + 'org.eclipse.xtext.xbase.ui', 'org.eclipse.jdt.debug.ui' + ]) + } + } + + protected def addRuntimeGuiceBindings(LanguageConfig2 language) { + val bindingFactory = new GuiceModuleAccess.BindingFactory() + // overrides binding from org.eclipse.xtext.generator.exporting.QualifiedNamesFragment + .addTypeToType(IQualifiedNameProvider.typeRef, + 'org.eclipse.xtext.xbase.scoping.XbaseQualifiedNameProvider'.typeRef) + if (useInferredJvmModel) { + bindingFactory + .addTypeToType(ILocationInFileProvider.typeRef, + 'org.eclipse.xtext.xbase.jvmmodel.JvmLocationInFileProvider'.typeRef) + .addTypeToType(IGlobalScopeProvider.typeRef, + 'org.eclipse.xtext.common.types.xtext.TypesAwareDefaultGlobalScopeProvider'.typeRef) + .addTypeToType('org.eclipse.xtext.xbase.validation.FeatureNameValidator'.typeRef, + 'org.eclipse.xtext.xbase.validation.LogicalContainerAwareFeatureNameValidator'.typeRef) + .addTypeToType('org.eclipse.xtext.xbase.typesystem.internal.DefaultBatchTypeResolver'.typeRef, + 'org.eclipse.xtext.xbase.typesystem.internal.LogicalContainerAwareBatchTypeResolver'.typeRef) + .addTypeToType('org.eclipse.xtext.xbase.typesystem.internal.DefaultReentrantTypeResolver'.typeRef, + 'org.eclipse.xtext.xbase.typesystem.internal.LogicalContainerAwareReentrantTypeResolver'.typeRef) + .addTypeToType(IResourceValidator.typeRef, + 'org.eclipse.xtext.xbase.annotations.validation.DerivedStateAwareResourceValidator'.typeRef) + if (generateXtendInferrer) { + bindingFactory + .addTypeToType('org.eclipse.xtext.xbase.jvmmodel.IJvmModelInferrer'.typeRef, language.jvmModelInferrer) + } + } else { + bindingFactory + .addTypeToType(ILocationInFileProvider.typeRef, + 'org.eclipse.xtext.xbase.resource.XbaseLocationInFileProvider'.typeRef) + + } + if (language.grammar.usesXImportSection) { + val StringConcatenationClient statement = ''' + binder.bind(«IScopeProvider».class).annotatedWith(«Names».named(«AbstractDeclarativeScopeProvider».NAMED_DELEGATE)).to(«language.importScopeProvider».class); + ''' + bindingFactory + .addConfiguredBinding(IScopeProvider.simpleName + 'Delegate', statement); + } + bindingFactory.contributeTo(language.runtimeGenModule) + + if (language.grammar.inheritsXbaseWithAnnotations) + language.runtimeGenModule.superClass = 'org.eclipse.xtext.xbase.annotations.DefaultXbaseWithAnnotationsRuntimeModule'.typeRef + else + language.runtimeGenModule.superClass = 'org.eclipse.xtext.xbase.DefaultXbaseRuntimeModule'.typeRef + } + + protected def addEclipsePluginGuiceBindings(LanguageConfig2 language) { + val bindingFactory = new GuiceModuleAccess.BindingFactory() + if (useInferredJvmModel) { + val StringConcatenationClient statement = ''' + if («'org.eclipse.ui.PlatformUI'.typeRef».isWorkbenchRunning()) { + binder.bind(«'org.eclipse.xtext.ui.editor.IURIEditorOpener'.typeRef».class).annotatedWith(«'org.eclipse.xtext.ui.LanguageSpecific'.typeRef».class).to(«'org.eclipse.xtext.xbase.ui.jvmmodel.navigation.DerivedMemberAwareEditorOpener'.typeRef».class); + binder.bind(«'org.eclipse.xtext.common.types.ui.navigation.IDerivedMemberAwareEditorOpener'.typeRef».class).to(«'org.eclipse.xtext.xbase.ui.jvmmodel.navigation.DerivedMemberAwareEditorOpener'.typeRef».class); + } + ''' + // Rename refactoring + bindingFactory + .addTypeToType('org.eclipse.xtext.ui.editor.findrefs.FindReferencesHandler'.typeRef, + 'org.eclipse.xtext.xbase.ui.jvmmodel.findrefs.JvmModelFindReferenceHandler'.typeRef) + .addTypeToType('org.eclipse.xtext.ui.editor.findrefs.ReferenceQueryExecutor'.typeRef, + 'org.eclipse.xtext.xbase.ui.jvmmodel.findrefs.JvmModelReferenceQueryExecutor'.typeRef) + + // overrides binding from org.eclipse.xtext.generator.exporting.QualifiedNamesFragment + .addTypeToType('org.eclipse.xtext.ui.refactoring.IDependentElementsCalculator'.typeRef, + 'org.eclipse.xtext.xbase.ui.jvmmodel.refactoring.JvmModelDependentElementsCalculator'.typeRef) + // overrides binding from RefactorElementNameFragment + .addTypeToType('org.eclipse.xtext.ui.refactoring.IRenameRefactoringProvider'.typeRef, + 'org.eclipse.xtext.xbase.ui.jvmmodel.refactoring.jdt.CombinedJvmJdtRenameRefactoringProvider'.typeRef) + // overrides binding from RefactorElementNameFragment + .addTypeToType('org.eclipse.xtext.ui.refactoring.IReferenceUpdater'.typeRef, + 'org.eclipse.xtext.xbase.ui.refactoring.XbaseReferenceUpdater'.typeRef) + // overrides binding from RefactorElementNameFragment + .addfinalTypeToType('org.eclipse.xtext.ui.refactoring.ui.IRenameContextFactory'.typeRef, + 'org.eclipse.xtext.xbase.ui.jvmmodel.refactoring.jdt.CombinedJvmJdtRenameContextFactory'.typeRef) + // overrides binding from RefactorElementNameFragment + .addTypeToType('org.eclipse.xtext.ui.refactoring.IRenameStrategy'.typeRef, + 'org.eclipse.xtext.xbase.ui.jvmmodel.refactoring.DefaultJvmModelRenameStrategy'.typeRef) + + .addTypeToType('org.eclipse.xtext.common.types.ui.refactoring.participant.JdtRenameParticipant.ContextFactory'.typeRef, + 'org.eclipse.xtext.xbase.ui.jvmmodel.refactoring.JvmModelJdtRenameParticipantContext.ContextFactory'.typeRef) + .addTypeToType('org.eclipse.xtext.ui.editor.outline.impl.OutlineNodeElementOpener'.typeRef, + 'org.eclipse.xtext.xbase.ui.jvmmodel.outline.JvmOutlineNodeElementOpener'.typeRef) + .addTypeToType('org.eclipse.xtext.ui.editor.GlobalURIEditorOpener'.typeRef, + 'org.eclipse.xtext.common.types.ui.navigation.GlobalDerivedMemberAwareURIEditorOpener'.typeRef) + .addTypeToType('org.eclipse.xtext.ui.editor.occurrences.IOccurrenceComputer'.typeRef, + 'org.eclipse.xtext.xbase.ui.jvmmodel.occurrence.JvmModelOccurrenceComputer'.typeRef) + .addTypeToType('org.eclipse.xtext.common.types.ui.query.IJavaSearchParticipation'.typeRef, + 'org.eclipse.xtext.common.types.ui.query.IJavaSearchParticipation.No'.typeRef) + // DerivedMemberAwareEditorOpener + .addConfiguredBinding('LanguageSpecificURIEditorOpener', statement) + } else { + bindingFactory + .addTypeToType('org.eclipse.xtext.ui.refactoring.IRenameStrategy'.typeRef, + 'org.eclipse.xtext.xbase.ui.refactoring.XbaseRenameStrategy'.typeRef) + } + if (language.grammar.usesXImportSection) { + bindingFactory + .addTypeToType('org.eclipse.xtext.xbase.imports.IUnresolvedTypeResolver'.typeRef, + 'org.eclipse.xtext.xbase.ui.imports.InteractiveUnresolvedTypeResolver'.typeRef) + .addTypeToType('org.eclipse.xtext.common.types.xtext.ui.ITypesProposalProvider'.typeRef, + 'org.eclipse.xtext.xbase.ui.contentassist.ImportingTypesProposalProvider'.typeRef) + .addTypeToType(' org.eclipse.xtext.ui.editor.templates.XtextTemplateContextType'.typeRef, + 'org.eclipse.xtext.xbase.ui.templates.XbaseTemplateContextType'.typeRef) + } else { + bindingFactory + .addTypeToType('org.eclipse.xtext.xbase.ui.quickfix.JavaTypeQuickfixes'.typeRef, + 'org.eclipse.xtext.xbase.ui.quickfix.JavaTypeQuickfixesNoImportSection'.typeRef) + } + bindingFactory.contributeTo(language.eclipsePluginGenModule) + + if (language.grammar.inheritsXbaseWithAnnotations) + language.eclipsePluginGenModule.superClass = 'org.eclipse.xtext.xbase.annotations.ui.DefaultXbaseWithAnnotationsUiModule'.typeRef + else + language.eclipsePluginGenModule.superClass = 'org.eclipse.xtext.xbase.ui.DefaultXbaseUiModule'.typeRef + } + + protected def doGenerateXtendInferrer(LanguageConfig2 language) { + val xtendFile = new XtendFileAccess(language.jvmModelInferrer, codeConfig) + xtendFile.encodingProvider = encodingProvider + + xtendFile.typeComment = ''' + /** + *

Infers a JVM model from the source model.

+ * + *

The JVM model should contain all elements that would appear in the Java code + * which is generated from the source model. Other models link against the JVM model rather than the source model.

+ */ + ''' + val firstRuleType = language.grammar.rules.head.type.classifier.getJavaTypeName(language.grammar.eResource.resourceSet).typeRef + xtendFile.javaContent = ''' + class «language.jvmModelInferrer.simpleName» extends «'org.eclipse.xtext.xbase.jvmmodel.AbstractModelInferrer'.typeRef» { + + /** + * convenience API to build and initialize JVM types and their members. + */ + @«Inject» extension «'org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder'.typeRef» + + /** + * The dispatch method {@code infer} is called for each instance of the + * given element's type that is contained in a resource. + * + * @param element + * the model to create one or more + * {@link org.eclipse.xtext.common.types.JvmDeclaredType declared + * types} from. + * @param acceptor + * each created + * {@link org.eclipse.xtext.common.types.JvmDeclaredType type} + * without a container should be passed to the acceptor in order + * get attached to the current resource. The acceptor's + * {@link IJvmDeclaredTypeAcceptor#accept(org.eclipse.xtext.common.types.JvmDeclaredType) + * accept(..)} method takes the constructed empty type for the + * pre-indexing phase. This one is further initialized in the + * indexing phase using the closure you pass to the returned + * {@link org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor.IPostIndexingInitializing#initializeLater(org.eclipse.xtext.xbase.lib.Procedures.Procedure1) + * initializeLater(..)}. + * @param isPreIndexingPhase + * whether the method is called in a pre-indexing phase, i.e. + * when the global index is not yet fully updated. You must not + * rely on linking using the index if isPreIndexingPhase is + * true. + */ + def dispatch void infer(«firstRuleType» element, «'org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor'.typeRef» acceptor, boolean isPreIndexingPhase) { + // Here you explain how your model is mapped to Java elements, by writing the actual translation code. + + // An implementation for the initial hello world example could look like this: + // acceptor.accept(element.toClass("my.company.greeting.MyGreetings")) [ + // for (greeting : element.greetings) { + // members += greeting.toMethod("hello" + greeting.name, typeRef(String)) [ + // body = «"'''"» + // return "Hello «'«'»greeting.name«'»'»"; + // «"'''"» + // ] + // } + // ] + } + } + ''' + xtendFile.writeTo(projectConfig.runtimeSrc) + } + + protected def addEclipsePluginExtensions(LanguageConfig2 language) { + val name = language.grammar.name + if (jdtTypeHierarchy) { + projectConfig.eclipsePluginPluginXml.entries += ''' + + + + + + + + + + + + + + + «IF language.grammar.usesXImportSection» + + + + + + + «ENDIF» + + + «IF language.grammar.usesXImportSection» + + + + + + + + + «ENDIF» + + + + + + + + + + + + + + + + ''' + } + if (jdtCallHierarchy) { + projectConfig.eclipsePluginPluginXml.entries += ''' + + + + + + + + + + + + + + + + + + + ''' + } + projectConfig.eclipsePluginPluginXml.entries += ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ''' + } + +} diff --git a/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/xbase/XtypeGeneratorFragment2.xtend b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/xbase/XtypeGeneratorFragment2.xtend new file mode 100644 index 000000000..dbe10a12d --- /dev/null +++ b/plugins/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/xbase/XtypeGeneratorFragment2.xtend @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2015 itemis AG (http://www.itemis.eu) 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 + *******************************************************************************/ +package org.eclipse.xtext.xtext.generator.xbase + +import com.google.inject.Inject +import org.eclipse.xtext.Grammar +import org.eclipse.xtext.GrammarUtil +import org.eclipse.xtext.xtext.generator.AbstractGeneratorFragment2 +import org.eclipse.xtext.xtext.generator.IXtextProjectConfig +import org.eclipse.xtext.xtext.generator.LanguageConfig2 + +class XtypeGeneratorFragment2 extends AbstractGeneratorFragment2 { + + static def boolean inheritsXtype(Grammar grammar) { + GrammarUtil.inherits(grammar, 'org.eclipse.xtext.xbase.Xtype') + } + + @Inject IXtextProjectConfig projectConfig + + override generate(LanguageConfig2 language) { + if (language.grammar.inheritsXtype && projectConfig.eclipsePluginManifest !== null) + projectConfig.eclipsePluginManifest.requiredBundles += 'org.eclipse.xtext.xbase.ui' + } + +} diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.java index 38a2028fc..1a62074e4 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.java @@ -270,6 +270,20 @@ public class GrammarUtil { collectAllUsedGrammars(grammars, grammar); return grammars; } + + /** + * @since 2.9 + */ + public static boolean inherits(Grammar grammar, String languageID) { + if (grammar.getName().equals(languageID)) + return true; + for (Grammar grammar2 : grammar.getUsedGrammars()) { + if (inherits(grammar2, languageID)) { + return true; + } + } + return false; + } private static void collectAllUsedGrammars(List grammars, Grammar grammar) { grammars.addAll(grammar.getUsedGrammars());