[generator] Added new version of grammar access fragment

Signed-off-by: Miro Spönemann <miro.spoenemann@itemis.de>
This commit is contained in:
Miro Spönemann 2015-07-17 11:33:56 +02:00
parent 054ffcf424
commit 6f68d69dfc
13 changed files with 879 additions and 12 deletions

View file

@ -28,6 +28,7 @@ Export-Package: org.eclipse.xtext.xtext.generator,
org.eclipse.xtext.xtext.generator.exporting,
org.eclipse.xtext.xtext.generator.formatting,
org.eclipse.xtext.xtext.generator.generator,
org.eclipse.xtext.xtext.generator.grammarAccess,
org.eclipse.xtext.xtext.generator.model,
org.eclipse.xtext.xtext.generator.util,
org.eclipse.xtext.xtext.generator.xbase

View file

@ -18,7 +18,7 @@ import java.util.jar.Manifest
import org.eclipse.emf.common.EMFPlugin
import org.eclipse.xtend.lib.annotations.Accessors
import org.eclipse.xtext.util.Strings
import org.eclipse.xtext.xtext.generator.model.IClassAnnotation
import org.eclipse.xtext.xtext.generator.model.annotations.IClassAnnotation
/**
* Configuration object for generated code.

View file

@ -35,9 +35,9 @@ 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.SuppressWarningsAnnotation
import org.eclipse.xtext.xtext.generator.model.TextFileAccess
import org.eclipse.xtext.xtext.generator.model.TypeReference
import org.eclipse.xtext.xtext.generator.model.annotations.SuppressWarningsAnnotation
import static extension org.eclipse.xtext.xtext.generator.XtextGeneratorNaming.*
import static extension org.eclipse.xtext.xtext.generator.model.TypeReference.*

View file

@ -97,10 +97,11 @@ class GeneratorFragment2 extends AbstractGeneratorFragment2 {
override generate(LanguageConfig2 language) {
if (language.grammar.isGenerateStub) {
projectConfig.runtimeManifest.requiredBundles += 'org.eclipse.xtext.xbase.lib'
new GuiceModuleAccess.BindingFactory()
.addTypeToType(IGenerator.typeRef, language.grammar.generatorStub)
.contributeTo(language.runtimeGenModule)
if (projectConfig.runtimeManifest !== null)
projectConfig.runtimeManifest.requiredBundles += 'org.eclipse.xtext.xbase.lib'
if (!projectConfig.runtimeSrc.containsXtendFile(language.grammar.generatorStub))
doGenerateStubFile(language)
}
@ -115,10 +116,9 @@ class GeneratorFragment2 extends AbstractGeneratorFragment2 {
if (language.grammar.isGenerateMwe && !projectConfig.runtimeSrc.isFile(language.grammar.generatorStub.path + 'MWE.mwe2'))
doGenerateMweFile(language)
if (projectConfig.eclipsePluginManifest !== null) {
projectConfig.eclipsePluginManifest.requiredBundles += 'org.eclipse.xtext.builder'
}
contributeEclipsePluginGuiceBindings(language)
if (projectConfig.eclipsePluginManifest !== null)
projectConfig.eclipsePluginManifest.requiredBundles += 'org.eclipse.xtext.builder'
if (projectConfig.eclipsePluginPluginXml !== null)
contributeEclipsePluginExtensions(language)
}

View file

@ -0,0 +1,92 @@
/*******************************************************************************
* 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.grammarAccess
import java.util.Set
import org.eclipse.emf.common.util.URI
import org.eclipse.emf.ecore.EClassifier
import org.eclipse.emf.ecore.EObject
import org.eclipse.emf.ecore.EPackage
import org.eclipse.emf.ecore.xmi.XMLResource
import org.eclipse.emf.ecore.xmi.impl.EcoreResourceFactoryImpl
import org.eclipse.emf.ecore.xmi.impl.URIHandlerImpl
import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl
import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor
import org.eclipse.xtext.util.Wrapper
class FragmentFakingEcoreResource extends XMIResourceImpl {
val Wrapper<Boolean> isSaving
new(URI uri, Wrapper<Boolean> isSaving) {
super(uri)
this.isSaving = isSaving
encoding = "UTF-8"
getDefaultSaveOptions.put(XMLResource.OPTION_USE_ENCODED_ATTRIBUTE_STYLE, true)
getDefaultSaveOptions.put(XMLResource.OPTION_LINE_WIDTH, 80)
getDefaultSaveOptions.put(XMLResource.OPTION_URI_HANDLER, new URIHandlerImpl.PlatformSchemeAware)
}
override protected boolean useIDs() {
eObjectToIDMap !== null || idToEObjectMap !== null
}
override String getURIFragment(EObject eObject) {
if (isSaving.get) {
if (eObject instanceof EClassifier) {
val result = eObject.URIFragment
if (result !== null)
return result
}
}
return super.getURIFragment(eObject)
}
def String getURIFragment(EClassifier classifier) {
// We need to handle empty subpackages in a special way
if (classifier.EPackage.ESuperPackage !== null) {
val result = new StringBuilder(60)
calculateURIFragment(classifier.EPackage, result, newHashSet)
result.append(classifier.name)
return result.toString
}
return null
}
def private void calculateURIFragment(EPackage ePackage, StringBuilder result, Set<EPackage> visited) {
if (!visited.add(ePackage)) {
throw new IllegalStateException
}
if (ePackage.ESuperPackage !== null) {
if (ePackage.eResource === ePackage.ESuperPackage.eResource) {
calculateURIFragment(ePackage.ESuperPackage, result, visited)
if (!ePackage.EClassifiers.empty) {
if (result.length !== 0) {
result.append(ePackage.name).append('/')
return
}
}
}
}
result.append('//')
}
@FinalFieldsConstructor
static class FactoryImpl extends EcoreResourceFactoryImpl {
public static final String ECORE_SUFFIX = "ecore";
val Wrapper<Boolean> isSaving
override createResource(URI uri) {
return new FragmentFakingEcoreResource(uri, isSaving)
}
}
}

View file

@ -0,0 +1,346 @@
/*******************************************************************************
* 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.grammarAccess
import com.google.common.collect.Maps
import com.google.inject.Binder
import com.google.inject.Guice
import com.google.inject.Inject
import com.ibm.icu.text.Transliterator
import java.util.ArrayList
import java.util.List
import java.util.Map
import org.eclipse.emf.ecore.EObject
import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor
import org.eclipse.xtend2.lib.StringConcatenationClient
import org.eclipse.xtext.AbstractElement
import org.eclipse.xtext.AbstractRule
import org.eclipse.xtext.Action
import org.eclipse.xtext.Assignment
import org.eclipse.xtext.CrossReference
import org.eclipse.xtext.EnumLiteralDeclaration
import org.eclipse.xtext.Grammar
import org.eclipse.xtext.GrammarUtil
import org.eclipse.xtext.Keyword
import org.eclipse.xtext.RuleCall
import org.eclipse.xtext.TypeRef
import org.eclipse.xtext.XtextRuntimeModule
import org.eclipse.xtext.formatting.ILineSeparatorInformation
import org.eclipse.xtext.resource.SaveOptions
import org.eclipse.xtext.serializer.ISerializer
import org.eclipse.xtext.xtext.generator.CodeConfig
import org.eclipse.xtext.xtext.generator.model.TypeReference
import static extension java.lang.Character.*
import static extension org.eclipse.xtext.GrammarUtil.*
import static extension org.eclipse.xtext.xtext.generator.XtextGeneratorNaming.*
/**
* This API can be used by other templates to generate code
* that has hard references to grammar rules/elements
* @author Moritz Eysholdt
*/
class GrammarAccessExtensions {
val Map<String, ISerializer> xtextSerializerByLineDelimiter = Maps.newHashMapWithExpectedSize(2)
val transliterator = Transliterator.getInstance('Any-Name')
@Inject CodeConfig codeConfig
/**
* Returns a reference to the GrammarAccess implementation for a grammar.
*/
def TypeReference getGrammarAccess(Grammar grammar) {
new TypeReference(grammar.naming.runtimeBasePackage + '.services.' + GrammarUtil.getName(grammar) + 'GrammarAccess')
}
/**
* Returns the invocation of the element accessor method as fully qualified Java statement.
* Example: return FowlerDslTestLanguageGrammarAccess.INSTANCE.prStatemachine().ele1AssignmentStates()
*/
def StringConcatenationClient gaFullAccessor(AbstractElement ele) {
'''«ele.grammar.grammarAccess».INSTANCE.«ele.gaRuleElementAccessor»'''
}
/**
* Converts an arbitary string to a valid Java identifier.
* The string is split up along the the characters that are not valid as java
* identifier. The first character of each segments is made upper case which
* leads to a camel-case style.
* @param text the string
* @param uppercaseFirst whether the first character of the returned identifier should be uppercase or lowercase
*/
def String toJavaIdentifier(String text, boolean uppercaseFirst) {
try {
return toJavaIdentifierSegment(text, true, uppercaseFirst)
} catch (Throwable t) {
t.printStackTrace()
return '%_FAILURE_(' + text + ')%'
}
}
private def String toJavaIdentifier(List<String> text, boolean uppercaseFirst) {
val i = text.iterator
val b = new StringBuilder(toJavaIdentifierSegment(i.next(), true, uppercaseFirst))
while (i.hasNext) {
b.append(toJavaIdentifierSegment(i.next, false, true))
}
return b.toString
}
private def String toJavaIdentifierSegment(String text, boolean isFirst, boolean uppercaseFirst) {
val r = toJavaIdentifierSegmentInt(text, isFirst, uppercaseFirst)
if (r.length > 0) {
return r
}
val builder = new StringBuilder
for (c : text.toCharArray) {
val n = getUnicodeName(c)
if (n != null)
builder.append(n + ' ')
}
return toJavaIdentifierSegmentInt(builder.toString.toLowerCase.trim, isFirst, true)
}
/**
* Returns the Unicode string name for a character.
*/
def String getUnicodeName(char character) {
val transliterated = transliterator.transliterate(String.valueOf(character))
return transliterated.substring('\\N{'.length, transliterated.length - '}'.length)
}
private def String toJavaIdentifierSegmentInt(String text, boolean isFirst, boolean uppercaseFirst) {
var start = isFirst
var up = true
val builder = new StringBuilder
for (c : text.toCharArray) {
val valid = if (start) c.isJavaIdentifierStart else c.isJavaIdentifierPart
if (valid) {
if (start)
builder.append(if (uppercaseFirst) c.toUpperCase else c.toLowerCase)
else
builder.append(if (up) c.toUpperCase else c)
up = false
start = false
} else
up = true
}
return builder.toString
}
/**
* Creates an identifier for a Rule which is a valid Java identifier and unique within
* the Rule's grammar.
*/
def String gaRuleIdentifier(AbstractRule rule) {
rule.name.toJavaIdentifier(true)
}
/**
* Creates an identifier for an AbstractElement which is a valid Java identifier and
* which is unique whithin the element's rule. The identifier tries to be as
* human-readable as possible.
*/
def String gaElementIdentifier(AbstractElement element) {
try {
if (element === null)
return 'null'
val result = new ArrayList<String>
result.addAll(element.elementDescription)
result.add(element.elementTypeDescription)
result.add(element.elementPath)
return toJavaIdentifier(result, true)
} catch (Throwable t) {
t.printStackTrace()
return 'failure'
}
}
private def List<String> getElementDescription(AbstractElement element) {
val result = new ArrayList<String>
var container = element
while (container !== null) {
result.addAll(0, container.singleElementDescription)
val eContainer = container.eContainer
container = if (eContainer instanceof AbstractElement) eContainer else null
}
return result
}
private def List<String> getSingleElementDescription(AbstractElement ele) {
val r = new ArrayList<String>(2)
switch ele {
Keyword:
r += ele.value
Assignment:
r += ele.feature
RuleCall:
r += ele.rule.name
Action: {
if (ele.type?.classifier !== null)
r += ele.type.classifier.name
if (!ele.feature.nullOrEmpty)
r += ele.feature
}
CrossReference:
if (ele.type?.classifier !== null)
r += ele.type.classifier.name
EnumLiteralDeclaration:
r += ele.enumLiteral.name
}
return r
}
private def String getElementTypeDescription(AbstractElement ele) {
if (ele instanceof RuleCall)
ele.rule.eClass.name + 'Call'
else
ele.eClass.name
}
private def String getElementPath(AbstractElement ele) {
var EObject obj = ele;
val result = new StringBuilder
while (!(obj.eContainer instanceof AbstractRule) && obj.eContainer !== null) {
val eContainer = obj.eContainer
result.insert(0, eContainer.eContents.indexOf(obj))
result.insert(0, '_')
obj = eContainer
}
return result.toString
}
/**
* Returns the method name for accessing a rule via a GrammarAccess implementation.
*/
def String gaRuleAccessMethodName(AbstractRule rule) {
'get' + rule.gaRuleIdentifier + 'Rule'
}
/**
* Returns the method name for accessing a rule's content via a ParserRuleAccess implementation.
*/
def String gaRuleElementsMethodName(AbstractRule rule) {
'get' + rule.gaRuleIdentifier + 'Access'
}
/**
* Returns the method name for accessing an element via a GrammarAccess implementation.
*/
def String gaElementAccessMethodName(AbstractElement element) {
'get' + element.gaElementIdentifier
}
/**
* Returns the simple class name of a rule's facade. A GrammarAccess implementation has
* a facade for each parser rule, which contains the methods for accessing the rule's elements.
*/
def String gaRuleAccessorClassName(AbstractRule rule) {
rule.gaRuleIdentifier + 'Elements'
}
/**
* Returns the invocation of the rule accessor method as Java statement.
*/
def String gaRuleAccessor(AbstractRule rule) {
rule.gaRuleAccessMethodName + '()'
}
/**
* Returns the invocation of the rule's content accessor method as Java statement.
*/
def String gaElementsAccessor(AbstractRule rule) {
rule.gaRuleElementsMethodName + '()'
}
/**
* Returns the invocation of the element accessor method as Java statement.
* The called method is implemented by the rule's facade.
* Example: ele1AssignmentStates()
*/
def String gaElementAccessor(AbstractElement ele){
ele.gaElementAccessMethodName + '()'
}
/**
* Returns the invocation of the element accessor method for a GrammarAccess
* as Java statement.
* Example: prStatemachine().ele1AssignmentStates()
*/
def String gaRuleElementAccessor(AbstractElement ele) {
ele.containingRule.gaElementsAccessor + '.' + ele.gaElementAccessor
}
/**
* Returns the invocation of an element or rule accessor, including the .getType() call.
* Example1: getFooRule().getType()
* Example2: getBarRule().getFooAction().getType()
*/
def String gaTypeAccessor(TypeRef ele) {
switch cnt: ele.eContainer {
AbstractElement: cnt.gaRuleElementAccessor + '.getType()'
AbstractRule: cnt.gaRuleAccessor + '.getType()'
default: '<error: unknown type ' + ele.eContainer.eClass.name + '>'
};
}
/**
* Returns the invocation of an element or rule accessor.
* Example1: getFooRule()
* Example2: getBarRule().getFooAction()
*/
def String gaAccessor(EObject ele) {
switch ele {
AbstractElement: ele.gaRuleElementAccessor
AbstractRule: ele.gaRuleAccessor
default: '<error: unknown type ' + ele.eClass.name + '>'
}
}
def String grammarFragmentToString(EObject ele, String prefix) {
var String s
try {
val options = SaveOptions.newBuilder.format.options
s = serializer.serialize(ele, options)
} catch (Exception e) {
s = e.toString
}
s = prefix + s.trim.replaceAll('(\\r?\\n)', '$1' + prefix).replaceAll('/\\*', '/ *').replaceAll('\\*/', '* /')
return s
}
private def ISerializer getSerializer() {
val delimiter = codeConfig.lineDelimiter
var result = xtextSerializerByLineDelimiter.get(delimiter)
if (result != null) {
return result
}
val injector = Guice.createInjector(new LineSeparatorModule[delimiter])
result = injector.getInstance(ISerializer)
xtextSerializerByLineDelimiter.put(delimiter, result)
return result;
}
@FinalFieldsConstructor
protected static class LineSeparatorModule extends XtextRuntimeModule {
val ILineSeparatorInformation lineSeparatorInformation;
override configure(Binder binder) {
// avoid duplicate registration of the validator
val compound = getBindings()
compound.configure(binder)
}
def ILineSeparatorInformation bindILineSeparatorInformation() {
return lineSeparatorInformation;
}
}
}

View file

@ -0,0 +1,381 @@
/*******************************************************************************
* 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.grammarAccess
import com.google.inject.Inject
import java.io.IOException
import java.util.HashSet
import java.util.List
import java.util.Map
import java.util.Set
import org.eclipse.emf.common.util.URI
import org.eclipse.emf.ecore.EPackage
import org.eclipse.emf.ecore.resource.ContentHandler
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.emf.ecore.resource.ResourceSet
import org.eclipse.emf.ecore.resource.impl.BinaryResourceImpl
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl
import org.eclipse.emf.ecore.xmi.XMLResource
import org.eclipse.xtend.lib.annotations.Accessors
import org.eclipse.xtend2.lib.StringConcatenationClient
import org.eclipse.xtext.AbstractElement
import org.eclipse.xtext.AbstractRule
import org.eclipse.xtext.EcoreUtil2
import org.eclipse.xtext.EnumRule
import org.eclipse.xtext.Grammar
import org.eclipse.xtext.GrammarUtil
import org.eclipse.xtext.IGrammarAccess
import org.eclipse.xtext.ParserRule
import org.eclipse.xtext.TerminalRule
import org.eclipse.xtext.service.AbstractElementFinder.AbstractEnumRuleElementFinder
import org.eclipse.xtext.service.AbstractElementFinder.AbstractGrammarElementFinder
import org.eclipse.xtext.service.AbstractElementFinder.AbstractParserRuleElementFinder
import org.eclipse.xtext.service.GrammarProvider
import org.eclipse.xtext.util.Wrapper
import org.eclipse.xtext.util.internal.Log
import org.eclipse.xtext.xtext.generator.AbstractGeneratorFragment2
import org.eclipse.xtext.xtext.generator.IXtextProjectConfig
import org.eclipse.xtext.xtext.generator.LanguageConfig2
import org.eclipse.xtext.xtext.generator.model.FileAccessFactory
import org.eclipse.xtext.xtext.generator.model.GuiceModuleAccess
import org.eclipse.xtext.xtext.generator.model.annotations.SingletonClassAnnotation
import static extension org.eclipse.xtext.GrammarUtil.*
import static extension org.eclipse.xtext.xtext.generator.model.TypeReference.*
@Log
class GrammarAccessFragment2 extends AbstractGeneratorFragment2 {
@Accessors
String xmlVersion
@Inject IXtextProjectConfig projectConfig
@Inject FileAccessFactory fileAccessFactory
@Inject extension GrammarAccessExtensions
override generate(LanguageConfig2 language) {
val bindingFactory = new GuiceModuleAccess.BindingFactory()
if (language.grammar.name != 'org.eclipse.xtext.common.Terminals') {
bindingFactory.addTypeToInstance(ClassLoader.typeRef, 'getClass().getClassLoader()')
}
bindingFactory.addTypeToType(IGrammarAccess.typeRef, language.grammar.grammarAccess)
.contributeTo(language.runtimeGenModule)
if (projectConfig.runtimeManifest !== null) {
projectConfig.runtimeManifest.exportedPackages.addAll(#[
language.naming.runtimeBasePackage, language.naming.runtimeBasePackage + ".services"
])
}
doGenerateGrammarAccess(language)
writeGrammar(language)
}
protected def void writeGrammar(LanguageConfig2 language) {
val isSaving = Wrapper.wrap(false)
val ResourceSet cloneInto = new ResourceSetImpl
// Substitute the resource factory for ecore-files
cloneInto.resourceFactoryRegistry.extensionToFactoryMap.put(
FragmentFakingEcoreResource.FactoryImpl.ECORE_SUFFIX, new FragmentFakingEcoreResource.FactoryImpl(isSaving))
val resourceSet = EcoreUtil2.clone(cloneInto, language.grammar.eResource.resourceSet)
// Get the copy of the grammar and use this one
val copy = resourceSet.getResource(language.grammar.eResource.URI, true).contents.head as Grammar
// Save grammar model
val path =
if (xmlVersion === null) {
copy.classpathRelativePathToBinGrammar
} else {
LOG.warn("The property 'xmlVersion' has been specified for this " + GrammarAccessFragment2.simpleName
+ ". Therefore, the grammar is persisted as XMI and not as binary. This can be a performance drawback.")
copy.classpathRelativePathToXmi
}
val uri = projectConfig.runtimeSrcGen.getURI(path)
val resource = resourceSet.createResource(uri, ContentHandler.UNSPECIFIED_CONTENT_TYPE)
addAllGrammarsToResource(resource, copy, new HashSet<Grammar>())
isSaving.set(true)
val Map<String, Object> saveOptions = newHashMap
if (resource instanceof XMLResource) {
resource.XMLVersion = xmlVersion ?: '1.0'
} else if (resource instanceof BinaryResourceImpl){
saveOptions.put(BinaryResourceImpl.OPTION_VERSION, BinaryResourceImpl.BinaryIO.Version.VERSION_1_1)
saveOptions.put(BinaryResourceImpl.OPTION_STYLE_DATA_CONVERTER, true)
}
try {
resource.save(saveOptions)
} catch (IOException e) {
LOG.error(e.getMessage(), e)
} finally {
isSaving.set(false)
}
}
protected def void addAllGrammarsToResource(Resource resource, Grammar grammar, Set<Grammar> visitedGrammars) {
if (!visitedGrammars.add(grammar)) {
return
}
resource.contents.add(grammar)
// Replace resource URIs with namespace URIs
for (metamodelDecl : grammar.metamodelDeclarations) {
val pack = metamodelDecl.EPackage
val packResource = pack.eResource
if (packResource.URI.toString != pack.nsURI) {
val packResourceSet = packResource.resourceSet
if (packResourceSet !== null) {
var topMost = pack
// We need to be aware of empty subpackages
while (topMost.ESuperPackage !== null && topMost.ESuperPackage.eResource === topMost.eResource) {
topMost = topMost.ESuperPackage
}
if (packResource.contents.contains(topMost) && packResource.contents.size == 1) {
if (!topMost.EClassifiers.empty)
packResource.setURI(URI.createURI(topMost.nsURI))
else
moveSubpackagesToNewResource(topMost, resource.resourceSet)
}
if (!topMost.eResource.URI.toString.equals(topMost.nsURI))
movePackageToNewResource(topMost, resource.resourceSet)
}
}
}
for (usedGrammar : grammar.usedGrammars) {
addAllGrammarsToResource(resource, usedGrammar, visitedGrammars)
}
}
protected def void moveSubpackagesToNewResource(EPackage pack, ResourceSet set) {
for (var i = pack.ESubpackages.size - 1; i >= 0; i--) {
val sub = pack.ESubpackages.get(i)
if (sub.eResource === pack.eResource) {
if (sub.EClassifiers.empty) {
moveSubpackagesToNewResource(sub, set)
} else {
movePackageToNewResource(sub, set)
pack.ESubpackages.remove(i)
}
}
}
}
protected def void movePackageToNewResource(EPackage pack, ResourceSet set) {
val resource = set.createResource(
URI.createURI('___temp___.' + FragmentFakingEcoreResource.FactoryImpl.ECORE_SUFFIX),
ContentHandler.UNSPECIFIED_CONTENT_TYPE)
resource.setURI(URI.createURI(pack.nsURI))
resource.contents.add(pack)
}
protected def doGenerateGrammarAccess(LanguageConfig2 language) {
val javaFile = fileAccessFactory.with(language).createJavaFile(language.grammar.grammarAccess)
javaFile.annotations += new SingletonClassAnnotation
javaFile.javaContent = '''
public class «language.grammar.grammarAccess.simpleName» extends «AbstractGrammarElementFinder» {
«FOR r : language.grammar.rules.filter(ParserRule)»
«parserRuleClasses(r)»
«ENDFOR»
«FOR r : language.grammar.rules.filter(EnumRule)»
«parserRuleClasses(r)»
«ENDFOR»
«FOR r : language.grammar.rules»
«cache(r)»
«ENDFOR»
private final «Grammar» grammar;
«FOR g : language.grammar.usedGrammars»
private final «g.grammarAccess» «g.gaGrammarAccessLocalVarName»;
«ENDFOR»
@«Inject»
public «language.grammar.grammarAccess.simpleName»(«GrammarProvider» grammarProvider«FOR g : language.grammar.usedGrammars»,
«g.grammarAccess» «g.gaGrammarAccessLocalVarName»«ENDFOR») {
this.grammar = internalFindGrammar(grammarProvider);
«FOR g : language.grammar.usedGrammars»
this.«g.gaGrammarAccessLocalVarName» = «g.gaGrammarAccessLocalVarName»;
«ENDFOR»
«FOR r : language.grammar.rules»
«initializer(r)»
«ENDFOR»
}
protected «Grammar» internalFindGrammar(«GrammarProvider» grammarProvider) {
«Grammar» grammar = grammarProvider.getGrammar(this);
while (grammar != null) {
if ("«language.grammar.name»".equals(grammar.getName())) {
return grammar;
}
«List»<«Grammar»> grammars = grammar.getUsedGrammars();
if (!grammars.isEmpty()) {
grammar = grammars.iterator().next();
} else {
return null;
}
}
return grammar;
}
@Override
public «Grammar» getGrammar() {
return grammar;
}
«FOR g : language.grammar.usedGrammars»
public «g.grammarAccess» get«g.grammarAccess.simpleName»() {
return «g.gaGrammarAccessLocalVarName»;
}
«ENDFOR»
«FOR r : language.grammar.allRules»
«getter(r, language.grammar)»
«ENDFOR»
}
'''
javaFile.writeTo(projectConfig.runtimeSrcGen)
}
protected def StringConcatenationClient parserRuleClasses(ParserRule it) '''
public class «gaRuleAccessorClassName» extends «AbstractParserRuleElementFinder» {
private final «ParserRule» rule = («ParserRule») «GrammarUtil».findRuleForName(getGrammar(), "«name»");
«FOR e : containedAbstractElements»
private final «e.eClass» «e.gaElementAccessorLocalVarName» = «e.loadElementStatement»;
«ENDFOR»
«grammarFragmentToString('//')»
@Override public «ParserRule» getRule() { return rule; }
«FOR e : containedAbstractElements»
«e.grammarFragmentToString('//')»
public «e.eClass» «e.gaElementAccessMethodName»() { return «e.gaElementAccessorLocalVarName»; }
«ENDFOR»
}
'''
protected def StringConcatenationClient parserRuleClasses(EnumRule it) '''
public class «gaRuleAccessorClassName» extends «AbstractEnumRuleElementFinder» {
private final «EnumRule» rule = («EnumRule») «GrammarUtil».findRuleForName(getGrammar(), "«name»");
«FOR e : containedAbstractElements»
private final «e.eClass» «e.gaElementAccessorLocalVarName» = «e.loadElementStatement»;
«ENDFOR»
«grammarFragmentToString('//')»
public EnumRule getRule() { return rule; }
«FOR e : containedAbstractElements»
«e.grammarFragmentToString('//')»
public «e.eClass» «e.gaElementAccessMethodName»() { return «e.gaElementAccessorLocalVarName»; }
«ENDFOR»
}
'''
protected def dispatch StringConcatenationClient cache(ParserRule it) '''
private final «gaRuleAccessorClassName» «gaRuleAccessorLocalVarName»;
'''
protected def dispatch StringConcatenationClient cache(EnumRule it) '''
private final «gaRuleAccessorClassName» «gaRuleAccessorLocalVarName»;
'''
protected def dispatch StringConcatenationClient cache(TerminalRule it) '''
private final «TerminalRule» «gaRuleAccessorLocalVarName»;
'''
protected def dispatch StringConcatenationClient initializer(ParserRule it) '''
this.«gaRuleAccessorLocalVarName» = new «gaRuleAccessorClassName»();
'''
protected def dispatch StringConcatenationClient initializer(EnumRule it) '''
this.«gaRuleAccessorLocalVarName» = new «gaRuleAccessorClassName»();
'''
protected def dispatch StringConcatenationClient initializer(TerminalRule it) '''
this.«gaRuleAccessorLocalVarName» = («TerminalRule») «GrammarUtil».findRuleForName(getGrammar(), "«name»");
'''
protected def dispatch StringConcatenationClient getter(ParserRule it, Grammar original) '''
«grammarFragmentToString('//')»
«IF grammar === original»
public «gaRuleAccessorClassName» «gaElementsAccessor» {
return «gaRuleAccessorLocalVarName»;
}
«ELSE»
public «grammar.grammarAccess».«gaRuleAccessorClassName» «gaElementsAccessor» {
return «usedGrammar(original).gaGrammarAccessLocalVarName».«gaElementsAccessor»;
}
«ENDIF»
public ParserRule «gaRuleAccessor» {
return «gaElementsAccessor».getRule();
}
'''
protected def dispatch StringConcatenationClient getter(EnumRule it, Grammar original) '''
«grammarFragmentToString('//')»
«IF grammar === original»
public «gaRuleAccessorClassName» «gaElementsAccessor» {
return «gaRuleAccessorLocalVarName»;
}
«ELSE»
public «grammar.grammarAccess».«gaRuleAccessorClassName» «gaElementsAccessor» {
return «usedGrammar(original).gaGrammarAccessLocalVarName».«gaElementsAccessor»;
}
«ENDIF»
public «EnumRule» «gaRuleAccessor» {
return «gaElementsAccessor».getRule();
}
'''
protected def dispatch StringConcatenationClient getter(TerminalRule it, Grammar original) '''
«grammarFragmentToString('//')»
public «TerminalRule» «gaRuleAccessor» {
«IF grammar === original»
return «gaRuleAccessorLocalVarName»;
«ELSE»
return «usedGrammar(original).gaGrammarAccessLocalVarName».«gaRuleAccessor»;
«ENDIF»
}
'''
protected def String gaGrammarAccessLocalVarName(Grammar g) {
'ga' + GrammarUtil.getName(g)
}
protected def String gaElementAccessorLocalVarName(AbstractElement ele) {
'c' + ele.gaElementIdentifier
}
protected def dispatch String gaRuleAccessorLocalVarName(ParserRule rule) {
'p' + rule.gaRuleIdentifier
}
protected def dispatch String gaRuleAccessorLocalVarName(TerminalRule rule) {
't' + rule.gaRuleIdentifier
}
protected def StringConcatenationClient loadElementStatement(AbstractElement ele) {
'''(«ele.eClass»)«ele.loadElementParentStatement».eContents().get(«ele.eContainer.eContents.indexOf(ele)»)'''
}
protected def String loadElementParentStatement(AbstractElement ele) {
if (ele.eContainer instanceof AbstractElement)
(ele.eContainer as AbstractElement).gaElementAccessorLocalVarName
else
"rule"
}
protected def Grammar usedGrammar(AbstractRule rule, Grammar parent) {
parent.usedGrammars.findFirst[allRules.contains(rule)]
}
}

View file

@ -19,6 +19,7 @@ import org.eclipse.xtend2.lib.StringConcatenation
import org.eclipse.xtend2.lib.StringConcatenationClient
import org.eclipse.xtext.parser.IEncodingProvider
import org.eclipse.xtext.xtext.generator.CodeConfig
import org.eclipse.xtext.xtext.generator.model.annotations.IClassAnnotation
class JavaFileAccess extends TextFileAccess {

View file

@ -89,7 +89,7 @@ class TypeReference {
}
new(EClass clazz, ResourceSet resourceSet, List<TypeReference> arguments) {
this(GenModelUtil2.getGenClass(clazz, resourceSet).qualifiedInterfaceName, arguments)
this(getQualifiedName(clazz, resourceSet), arguments)
}
private static def getPackageName(String qualifiedName) {
@ -116,6 +116,13 @@ class TypeReference {
}
}
private static def getQualifiedName(EClass clazz, ResourceSet resourceSet) {
if (clazz.EPackage.nsURI == 'http://www.eclipse.org/2008/Xtext')
'org.eclipse.xtext.' + clazz.name
else
GenModelUtil2.getGenClass(clazz, resourceSet).qualifiedInterfaceName
}
private static def matches(char c1, char c2) {
c1 == c2
}

View file

@ -5,13 +5,15 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.xtext.xtext.generator.model
package org.eclipse.xtext.xtext.generator.model.annotations
import java.text.SimpleDateFormat
import java.util.Date
import org.eclipse.xtend.lib.annotations.Accessors
import org.eclipse.xtext.util.Strings
import org.eclipse.xtext.xtext.generator.XtextGenerator
import org.eclipse.xtext.xtext.generator.model.JavaFileAccess
import org.eclipse.xtext.xtext.generator.model.TypeReference
/**
* A class annotation configuration for the <code>@Generated</code> annotation.

View file

@ -5,7 +5,11 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.xtext.xtext.generator.model
package org.eclipse.xtext.xtext.generator.model.annotations
import org.eclipse.xtext.xtext.generator.XtextGenerator
import org.eclipse.xtext.xtext.generator.model.JavaFileAccess
import org.eclipse.xtext.xtext.generator.model.TypeReference
/**
* Class annotations can be added to the {@link XtextGenerator} workflow component in order

View file

@ -0,0 +1,31 @@
/*******************************************************************************
* 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.annotations
import com.google.inject.Singleton
import org.eclipse.xtext.xtext.generator.model.JavaFileAccess
import org.eclipse.xtext.xtext.generator.model.TypeReference
/**
* A class annotation configuration for the <code>@Singleton</code> annotation.
*/
class SingletonClassAnnotation implements IClassAnnotation {
override generate() {
'@Singleton'
}
override appliesTo(JavaFileAccess javaFile) {
true
}
override getAnnotationImport() {
return new TypeReference(Singleton)
}
}

View file

@ -5,9 +5,11 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.xtext.xtext.generator.model
package org.eclipse.xtext.xtext.generator.model.annotations
import org.eclipse.xtend.lib.annotations.Accessors
import org.eclipse.xtext.xtext.generator.model.JavaFileAccess
import org.eclipse.xtext.xtext.generator.model.TypeReference
/**
* A class annotation configuration for the <code>@SuppressWarnings</code> annotation.
@ -34,7 +36,7 @@ class SuppressWarningsAnnotation implements IClassAnnotation {
}
override getAnnotationImport() {
return new TypeReference('java.lang.SuppressWarnings')
return new TypeReference(SuppressWarnings)
}
}
}