mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-16 16:58:56 +00:00
[generator] Improved handling of type references
Signed-off-by: Miro Spönemann <miro.spoenemann@itemis.de>
This commit is contained in:
parent
491104576d
commit
b423503771
4 changed files with 186 additions and 116 deletions
|
@ -130,11 +130,11 @@ class XtextGeneratorTemplates {
|
|||
register(injector);
|
||||
return injector;
|
||||
}
|
||||
|
||||
|
||||
public «Injector» createInjector() {
|
||||
return «Guice».createInjector(new «runtimeModule»());
|
||||
}
|
||||
|
||||
|
||||
public void register(«Injector» injector) {
|
||||
«FOR reg : langConfig.runtimeGenSetup.registrations»
|
||||
«reg»
|
||||
|
@ -147,21 +147,19 @@ class XtextGeneratorTemplates {
|
|||
}
|
||||
|
||||
private def getBindMethodName(GuiceModuleAccess.Binding it) {
|
||||
(if (!value.provider && value.statements.isEmpty)
|
||||
'bind'
|
||||
else if (value.statements.isEmpty)
|
||||
'provide'
|
||||
else 'configure')
|
||||
+ key.type.simpleMethodName
|
||||
+ if (value.expression !== null && !value.provider) 'ToInstance' else ''
|
||||
'''«IF !value.provider && value.statements.isEmpty
|
||||
»bind«
|
||||
ELSEIF value.statements.isEmpty
|
||||
»provide«
|
||||
ELSE
|
||||
»configure«
|
||||
ENDIF
|
||||
»«key.name ?: key.type.simpleMethodName
|
||||
»«IF value.expression !== null && !value.provider»ToInstance«ENDIF»'''
|
||||
}
|
||||
|
||||
private def getSimpleMethodName(TypeReference type) {
|
||||
type.name.replaceAll('<', '\\.').replaceAll('>', '\\.').split('\\.').filter[matches('[A-Z].*')].join('$')
|
||||
}
|
||||
|
||||
private def endsWith(CharSequence sequence, char c) {
|
||||
sequence.length > 0 && sequence.charAt(sequence.length - 1) == c
|
||||
private def String getSimpleMethodName(TypeReference type) {
|
||||
type.simpleNames.join('$') + type.typeArguments.join('$', '$', '', [simpleMethodName])
|
||||
}
|
||||
|
||||
private def StringConcatenationClient createBindingMethod(GuiceModuleAccess.Binding it) '''
|
||||
|
@ -181,7 +179,7 @@ class XtextGeneratorTemplates {
|
|||
// contributed by «contributedBy»
|
||||
public void «bindMethodName»(«Binder» binder) {
|
||||
«FOR statement : value.statements»
|
||||
«statement»«IF !statement.endsWith(';')»;«ENDIF»
|
||||
«statement»
|
||||
«ENDFOR»
|
||||
}
|
||||
«ENDIF»
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.List
|
|||
import java.util.Set
|
||||
import org.eclipse.xtend.lib.annotations.Accessors
|
||||
import org.eclipse.xtend.lib.annotations.Data
|
||||
import org.eclipse.xtend2.lib.StringConcatenationClient
|
||||
import org.eclipse.xtext.util.internal.Log
|
||||
|
||||
@Log
|
||||
|
@ -19,19 +20,23 @@ class GuiceModuleAccess {
|
|||
|
||||
@Data
|
||||
static class BindKey {
|
||||
String name
|
||||
TypeReference type
|
||||
boolean singleton
|
||||
boolean eagerSingleton
|
||||
|
||||
override equals(Object other) {
|
||||
if (other instanceof BindKey)
|
||||
this.type == other.type
|
||||
this.name == other.name && this.type == other.type
|
||||
else
|
||||
false
|
||||
}
|
||||
|
||||
override hashCode() {
|
||||
type.hashCode
|
||||
var h = 0
|
||||
if (name !== null) h += name.hashCode
|
||||
if (type !== null) h += type.hashCode
|
||||
return h
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,7 +45,7 @@ class GuiceModuleAccess {
|
|||
Object expression
|
||||
TypeReference type
|
||||
boolean provider
|
||||
List<CharSequence> statements
|
||||
List<Object> statements
|
||||
}
|
||||
|
||||
@Data
|
||||
|
@ -134,15 +139,19 @@ class GuiceModuleAccess {
|
|||
}
|
||||
|
||||
private def key(TypeReference type) {
|
||||
return new BindKey(type, false, false)
|
||||
return new BindKey(null, type, false, false)
|
||||
}
|
||||
|
||||
private def key(String name) {
|
||||
return new BindKey(name, null, false, false)
|
||||
}
|
||||
|
||||
private def eagerSingleton(TypeReference type) {
|
||||
return new BindKey(type, true, true)
|
||||
return new BindKey(null, type, true, true)
|
||||
}
|
||||
|
||||
private def singleton(TypeReference type) {
|
||||
return new BindKey(type, true, false)
|
||||
return new BindKey(null, type, true, false)
|
||||
}
|
||||
|
||||
private def value(TypeReference type) {
|
||||
|
@ -161,82 +170,97 @@ class GuiceModuleAccess {
|
|||
return new BindValue(expr, null, true, Collections.emptyList)
|
||||
}
|
||||
|
||||
private def statements(String[] statements) {
|
||||
private def statements(Object[] statements) {
|
||||
return new BindValue(null, null, false, statements)
|
||||
}
|
||||
|
||||
def BindingFactory addTypeToInstance(TypeReference s1, Object s2) {
|
||||
add(key(s1), expr(s2))
|
||||
return this
|
||||
}
|
||||
|
||||
def BindingFactory addTypeToProviderInstance(TypeReference s1, Object s2) {
|
||||
add(key(s1), providerExpr(s2))
|
||||
return this
|
||||
}
|
||||
|
||||
def BindingFactory addConfiguredBinding(TypeReference key, String... statements) {
|
||||
add(key(key), statements(statements))
|
||||
def BindingFactory addTypeToInstance(TypeReference type, String expression) {
|
||||
add(key(type), expr(expression))
|
||||
return this
|
||||
}
|
||||
|
||||
def BindingFactory addTypeToType(TypeReference s1, TypeReference s2){
|
||||
add(key(s1), value(s2))
|
||||
def BindingFactory addTypeToInstance(TypeReference type, StringConcatenationClient expression) {
|
||||
add(key(type), expr(expression))
|
||||
return this
|
||||
}
|
||||
|
||||
def BindingFactory addTypeToTypeSingleton(TypeReference s1, TypeReference s2){
|
||||
add(singleton(s1), value(s2))
|
||||
def BindingFactory addTypeToProviderInstance(TypeReference type, String expression) {
|
||||
add(key(type), providerExpr(expression))
|
||||
return this
|
||||
}
|
||||
|
||||
def BindingFactory addTypeToTypeEagerSingleton(TypeReference s1, TypeReference s2){
|
||||
add(eagerSingleton(s1), value(s2))
|
||||
def BindingFactory addTypeToProviderInstance(TypeReference type, StringConcatenationClient expression) {
|
||||
add(key(type), providerExpr(expression))
|
||||
return this
|
||||
}
|
||||
|
||||
def BindingFactory addTypeToProvider(TypeReference s1, TypeReference s2){
|
||||
add(key(s1), provider(s2))
|
||||
def BindingFactory addConfiguredBinding(String name, String... statements) {
|
||||
add(key(name), statements(statements))
|
||||
return this
|
||||
}
|
||||
|
||||
def BindingFactory addTypeToProviderSingleton(TypeReference s1, TypeReference s2){
|
||||
add(singleton(s1), provider(s2))
|
||||
def BindingFactory addConfiguredBinding(String name, StringConcatenationClient... statements) {
|
||||
add(key(name), statements(statements))
|
||||
return this
|
||||
}
|
||||
|
||||
def BindingFactory addTypeToType(TypeReference keyType, TypeReference valueType){
|
||||
add(key(keyType), value(valueType))
|
||||
return this
|
||||
}
|
||||
|
||||
def BindingFactory addTypeToProviderEagerSingleton(TypeReference s1, TypeReference s2){
|
||||
add(eagerSingleton(s1), provider(s2))
|
||||
def BindingFactory addTypeToTypeSingleton(TypeReference keyType, TypeReference valueType){
|
||||
add(singleton(keyType), value(valueType))
|
||||
return this
|
||||
}
|
||||
|
||||
def BindingFactory addfinalTypeToType(TypeReference s1, TypeReference s2){
|
||||
add(key(s1), value(s2), true)
|
||||
def BindingFactory addTypeToTypeEagerSingleton(TypeReference keyType, TypeReference valueType){
|
||||
add(eagerSingleton(keyType), value(valueType))
|
||||
return this
|
||||
}
|
||||
|
||||
def BindingFactory addfinalTypeToTypeSingleton(TypeReference s1, TypeReference s2){
|
||||
add(singleton(s1), value(s2), true)
|
||||
def BindingFactory addTypeToProvider(TypeReference keyType, TypeReference valueType){
|
||||
add(key(keyType), provider(valueType))
|
||||
return this
|
||||
}
|
||||
|
||||
def BindingFactory addfinalTypeToTypeEagerSingleton(TypeReference s1, TypeReference s2){
|
||||
add(eagerSingleton(s1), value(s2), true)
|
||||
def BindingFactory addTypeToProviderSingleton(TypeReference keyType, TypeReference valueType){
|
||||
add(singleton(keyType), provider(valueType))
|
||||
return this
|
||||
}
|
||||
|
||||
def BindingFactory addfinalTypeToProvider(TypeReference s1, TypeReference s2){
|
||||
add(key(s1), provider(s2), true)
|
||||
def BindingFactory addTypeToProviderEagerSingleton(TypeReference keyType, TypeReference valueType){
|
||||
add(eagerSingleton(keyType), provider(valueType))
|
||||
return this
|
||||
}
|
||||
|
||||
def BindingFactory addfinalTypeToProviderSingleton(TypeReference s1, TypeReference s2){
|
||||
add(singleton(s1), provider(s2), true)
|
||||
def BindingFactory addfinalTypeToType(TypeReference keyType, TypeReference valueType){
|
||||
add(key(keyType), value(valueType), true)
|
||||
return this
|
||||
}
|
||||
|
||||
def BindingFactory addfinalTypeToProviderEagerSingleton(TypeReference s1, TypeReference s2){
|
||||
add(eagerSingleton(s1), provider(s2), true)
|
||||
def BindingFactory addfinalTypeToTypeSingleton(TypeReference keyType, TypeReference valueType){
|
||||
add(singleton(keyType), value(valueType), true)
|
||||
return this
|
||||
}
|
||||
|
||||
def BindingFactory addfinalTypeToTypeEagerSingleton(TypeReference keyType, TypeReference valueType){
|
||||
add(eagerSingleton(keyType), value(valueType), true)
|
||||
return this
|
||||
}
|
||||
|
||||
def BindingFactory addfinalTypeToProvider(TypeReference keyType, TypeReference valueType){
|
||||
add(key(keyType), provider(valueType), true)
|
||||
return this
|
||||
}
|
||||
|
||||
def BindingFactory addfinalTypeToProviderSingleton(TypeReference keyType, TypeReference valueType){
|
||||
add(singleton(keyType), provider(valueType), true)
|
||||
return this
|
||||
}
|
||||
|
||||
def BindingFactory addfinalTypeToProviderEagerSingleton(TypeReference keyType, TypeReference valueType){
|
||||
add(eagerSingleton(keyType), provider(valueType), true)
|
||||
return this
|
||||
}
|
||||
|
||||
|
|
|
@ -19,9 +19,9 @@ import org.eclipse.xtext.xtext.generator.CodeConfig
|
|||
|
||||
class JavaFileAccess extends TextFileAccess {
|
||||
|
||||
val Map<String, TypeReference> imports = newHashMap
|
||||
val Map<String, String> imports = newHashMap
|
||||
|
||||
val String packageName
|
||||
val TypeReference javaType
|
||||
|
||||
val CodeConfig codeConfig
|
||||
|
||||
|
@ -39,27 +39,42 @@ class JavaFileAccess extends TextFileAccess {
|
|||
}
|
||||
|
||||
new(TypeReference typeRef, CodeConfig codeConfig) {
|
||||
this.packageName = typeRef.package
|
||||
if (typeRef.name != packageName + '.' + typeRef.simpleName)
|
||||
if (typeRef.simpleNames.length > 1)
|
||||
throw new IllegalArgumentException('Nested type cannot be serialized: ' + typeRef)
|
||||
this.path = typeRef.path
|
||||
this.javaType = typeRef
|
||||
this.codeConfig = codeConfig
|
||||
setPath(typeRef.path)
|
||||
}
|
||||
|
||||
def String importType(TypeReference typeRef) {
|
||||
var name = typeRef.simpleName
|
||||
var packageName = typeRef.package
|
||||
val isJavaDefaultType = CodeGenUtil.isJavaDefaultType(name)
|
||||
if (isJavaDefaultType && packageName != 'java.lang') {
|
||||
name = typeRef.name
|
||||
} else if (!isJavaDefaultType && this.packageName != packageName) {
|
||||
val imported = imports.get(name)
|
||||
if (imported === null)
|
||||
imports.put(name, typeRef)
|
||||
else if (imported.name != typeRef.name)
|
||||
name = typeRef.name
|
||||
val simpleNames = typeRef.simpleNames
|
||||
var String usableName
|
||||
if (typeRef.packageName == 'java.lang' || typeRef.packageName == javaType.packageName) {
|
||||
usableName = simpleNames.join('.')
|
||||
} else {
|
||||
var found = false
|
||||
for (var i = simpleNames.length - 1; i >= 0 && !found; i--) {
|
||||
val simpleName = simpleNames.get(i)
|
||||
if (usableName === null)
|
||||
usableName = simpleName
|
||||
else
|
||||
usableName = simpleName + '.' + usableName
|
||||
if (!CodeGenUtil.isJavaDefaultType(simpleName)
|
||||
&& !(i == simpleNames.length - 1 && i > 0 && simpleName.length <= 8)) {
|
||||
val importable = typeRef.packageName + '.' + simpleNames.subList(0, i + 1).join('.')
|
||||
val imported = imports.get(usableName)
|
||||
if (imported === null) {
|
||||
imports.put(usableName, importable)
|
||||
found = true
|
||||
} else if (imported == importable) {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
usableName = typeRef.name
|
||||
}
|
||||
return name + typeRef.arguments.join('<', ', ', '>', [importType])
|
||||
return usableName + typeRef.typeArguments.join('<', ', ', '>', [importType])
|
||||
}
|
||||
|
||||
def void setJavaContent(StringConcatenationClient javaContent) {
|
||||
|
@ -71,11 +86,11 @@ class JavaFileAccess extends TextFileAccess {
|
|||
override generate() {
|
||||
val classAnnotations = annotations + codeConfig.classAnnotations.filter[appliesTo(this)]
|
||||
classAnnotations.forEach[importType(annotationImport)]
|
||||
val sortedImports = Lists.newArrayList(imports.values.map[name])
|
||||
val sortedImports = Lists.newArrayList(imports.values)
|
||||
Collections.sort(sortedImports)
|
||||
return '''
|
||||
«codeConfig.fileHeader»
|
||||
package «packageName»;
|
||||
package «javaType.packageName»;
|
||||
|
||||
«FOR importName : sortedImports»
|
||||
import «importName»;
|
||||
|
|
|
@ -11,6 +11,7 @@ import java.util.Collections
|
|||
import java.util.List
|
||||
import org.eclipse.xtend.lib.annotations.Accessors
|
||||
import org.eclipse.xtend.lib.annotations.EqualsHashCode
|
||||
import java.util.regex.Pattern
|
||||
|
||||
@Accessors
|
||||
@EqualsHashCode
|
||||
|
@ -24,68 +25,100 @@ class TypeReference {
|
|||
new TypeReference(clazz, arguments.map[new TypeReference(it)])
|
||||
}
|
||||
|
||||
val String name
|
||||
static val PACKAGE_MATCHER = Pattern.compile('[a-z][a-zA-Z0-9_]*(\\.[a-z][a-zA-Z0-9_]*)*')
|
||||
static val CLASS_MATCHER = Pattern.compile('[A-Z][a-zA-Z0-9_]*(\\.[A-Z][a-zA-Z0-9_]*)*')
|
||||
|
||||
val List<TypeReference> arguments
|
||||
val String packageName
|
||||
|
||||
new(String name) {
|
||||
this.name = name
|
||||
this.arguments = Collections.emptyList
|
||||
val List<String> simpleNames
|
||||
|
||||
val List<TypeReference> typeArguments
|
||||
|
||||
new(String qualifiedName) {
|
||||
this(qualifiedName, null as List<TypeReference>)
|
||||
}
|
||||
|
||||
new(String name, List<TypeReference> arguments) {
|
||||
this.name = name
|
||||
this.arguments = Collections.unmodifiableList(arguments)
|
||||
new(String qualifiedName, List<TypeReference> arguments) {
|
||||
this(getPackageName(qualifiedName), getClassName(qualifiedName), arguments)
|
||||
}
|
||||
|
||||
new(String packageName, String className) {
|
||||
this.name = packageName + '.' + className
|
||||
this.arguments = Collections.emptyList
|
||||
this(packageName, className, null)
|
||||
}
|
||||
|
||||
new(String packageName, String className, List<TypeReference> arguments) {
|
||||
if (packageName === null || !PACKAGE_MATCHER.matcher(packageName).matches)
|
||||
throw new IllegalArgumentException('Invalid package name: ' + packageName)
|
||||
if (className === null || !CLASS_MATCHER.matcher(className).matches)
|
||||
throw new IllegalArgumentException('Invalid class name: ' + className)
|
||||
this.packageName = packageName
|
||||
this.simpleNames = className.split('\\.')
|
||||
this.typeArguments = arguments ?: Collections.emptyList
|
||||
}
|
||||
|
||||
new(Class<?> clazz) {
|
||||
this.name = clazz.name.replace('$', '.')
|
||||
this.arguments = Collections.emptyList
|
||||
this(clazz, null)
|
||||
}
|
||||
|
||||
new(Class<?> clazz, List<TypeReference> arguments) {
|
||||
this.name = clazz.name.replace('$', '.')
|
||||
this.arguments = Collections.unmodifiableList(arguments)
|
||||
if (clazz.primitive)
|
||||
throw new IllegalArgumentException('Type is primitive: ' + clazz.name)
|
||||
if (clazz.anonymousClass)
|
||||
throw new IllegalArgumentException('Class is anonymous: ' + clazz.name)
|
||||
if (clazz.localClass)
|
||||
throw new IllegalArgumentException('Class is local: ' + clazz.name)
|
||||
this.packageName = clazz.package.name
|
||||
this.simpleNames = newArrayList
|
||||
this.typeArguments = arguments ?: Collections.emptyList
|
||||
var c = clazz
|
||||
do {
|
||||
simpleNames.add(0, c.simpleName)
|
||||
c = c.declaringClass
|
||||
} while (c !== null)
|
||||
}
|
||||
|
||||
override toString() {
|
||||
name + arguments.join('<', ', ', '>', [toString])
|
||||
}
|
||||
|
||||
def String getSimpleName() {
|
||||
val simpleNameIndex = name.lastIndexOf('.')
|
||||
return name.substring(simpleNameIndex + 1)
|
||||
}
|
||||
|
||||
def String getPackage() {
|
||||
var packageEnd = name.length
|
||||
for (var i = name.length - 1; i >= 0; i--) {
|
||||
if (name.charAt(i).matches('.')) {
|
||||
if (Character.isLowerCase(name.charAt(i + 1)))
|
||||
return name.substring(0, packageEnd)
|
||||
private static def getPackageName(String qualifiedName) {
|
||||
var packageEnd = qualifiedName.length
|
||||
for (var i = qualifiedName.length - 1; i >= 0; i--) {
|
||||
if (qualifiedName.charAt(i).matches('.')) {
|
||||
if (Character.isLowerCase(qualifiedName.charAt(i + 1)))
|
||||
return qualifiedName.substring(0, packageEnd)
|
||||
else
|
||||
packageEnd = i
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
def String getPath() {
|
||||
val packageName = getPackage
|
||||
var className = name.substring(packageName.length + 1)
|
||||
val outerClassEnd = className.indexOf('.')
|
||||
if (outerClassEnd >= 0)
|
||||
className = className.substring(0, outerClassEnd)
|
||||
return packageName.replace('.', '/') + '/' + className + '.java'
|
||||
private static def getClassName(String qualifiedName) {
|
||||
var classStart = qualifiedName.length
|
||||
for (var i = qualifiedName.length - 1; i >= 0; i--) {
|
||||
if (qualifiedName.charAt(i).matches('.')) {
|
||||
if (Character.isLowerCase(qualifiedName.charAt(i + 1)))
|
||||
return qualifiedName.substring(classStart)
|
||||
else
|
||||
classStart = i + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def matches(char c1, char c2) {
|
||||
private static def matches(char c1, char c2) {
|
||||
c1 == c2
|
||||
}
|
||||
|
||||
override toString() {
|
||||
name + typeArguments.join('<', ', ', '>', [toString])
|
||||
}
|
||||
|
||||
def String getName() {
|
||||
packageName + '.' + simpleNames.join('.')
|
||||
}
|
||||
|
||||
def String getSimpleName() {
|
||||
simpleNames.last
|
||||
}
|
||||
|
||||
def String getPath() {
|
||||
return packageName.replace('.', '/') + '/' + simpleNames.head + '.java'
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue