mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-16 08:48:55 +00:00
[generator] Added new version of grammar access fragment
Signed-off-by: Miro Spönemann <miro.spoenemann@itemis.de>
This commit is contained in:
parent
054ffcf424
commit
6f68d69dfc
13 changed files with 879 additions and 12 deletions
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.*
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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)]
|
||||
}
|
||||
|
||||
}
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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.
|
|
@ -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
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue