[generator] Improved handling of type references

Signed-off-by: Miro Spönemann <miro.spoenemann@itemis.de>
This commit is contained in:
Miro Spönemann 2015-07-09 10:02:34 +02:00
parent 491104576d
commit b423503771
4 changed files with 186 additions and 116 deletions

View file

@ -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»

View file

@ -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
}

View file

@ -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»;

View file

@ -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'
}
}