Skip to content

Instantly share code, notes, and snippets.

@oehme
Last active October 5, 2016 00:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oehme/5428675 to your computer and use it in GitHub Desktop.
Save oehme/5428675 to your computer and use it in GitHub Desktop.
class ImmutableProcessor extends AbstractClassProcessor {
override doRegisterGlobals(ClassDeclaration cls, RegisterGlobalsContext context) {
context.registerClass(cls.builderClassName)
}
override doTransform(MutableClassDeclaration cls, extension TransformationContext context) {
if(cls.extendedClass != object) cls.addError("Inheritance does not play well with immutability")
cls.final = true
val builder = cls.builderClassName.findClass => [
final = true
addMethod("build") [
returnType = cls.newTypeReference
body = [
'''
return new «cls.simpleName»(«cls.dataFields.join(",")[simpleName]»);
''']
]
cls.dataFields.forEach [ field |
addMethod(field.simpleName) [
addParameter(field.simpleName, field.type)
returnType = cls.builderClassName.newTypeReference
body = [
'''
this.«field.simpleName» = «field.simpleName»;
return this;
''']
]
addField(field.simpleName) [
type = field.type
]
]
]
cls.addMethod("build") [
static = true
returnType = cls.newTypeReference
addParameter("init", typeof(Procedures$Procedure1).newTypeReference(builder.newTypeReference))
body = [
'''
«cls.builderClassName» builder = builder();
init.apply(builder);
return builder.build();
''']
]
cls.addMethod("builder") [
returnType = cls.builderClassName.newTypeReference
static = true
body = [
'''
return new «cls.builderClassName»();
''']
]
cls.addConstructor [
cls.dataFields.forEach [ field |
addParameter(field.simpleName, field.type)
]
body = [
'''
«FOR p : cls.dataFields»
this.«p.simpleName» = «p.simpleName»;
«ENDFOR»
''']
]
cls.dataFields.forEach [ field |
cls.addMethod("get" + field.simpleName.toFirstUpper) [
returnType = field.type
body = [
'''
return «field.simpleName»;
''']
]
]
cls.addMethod("equals") [
returnType = primitiveBoolean
addParameter("o", object)
body = [
'''
if (o instanceof «cls.simpleName») {
«cls.simpleName» other = («cls.simpleName») o;
return «cls.dataFields.join("\n&& ")['''«objects».equal(«simpleName», other.«simpleName»)''']»;
}
return false;
''']
]
cls.addMethod("hashCode") [
returnType = primitiveInt
body = ['''return «objects».hashCode(«cls.dataFields.join(",")[simpleName]»);''']
]
cls.addMethod("toString") [
returnType = string
body = ['''return new org.eclipse.xtext.xbase.lib.util.ToStringHelper().toString(this);''']
]
}
def dataFields(MutableClassDeclaration cls) {
cls.declaredFields.filter[static == false]
}
def builderClassName(ClassDeclaration cls) {
cls.qualifiedName + "Builder"
}
def objects() {
"com.google.common.base.Objects"
}
}
@vladdu
Copy link

vladdu commented May 3, 2013

Hi! Thanks for sharing this!

I tried it, but I get "unused field" warnings on all fields, do you know what the problem can be?

regards,
Vlad

@vladdu
Copy link

vladdu commented May 3, 2013

Also, a suggestion for improvement: accesors with boolean type should be calles is_, not get_.

cls.dataFields.forEach [ field |
  val fieldType = field.type
  val prefix = if(fieldType == primitiveBoolean || fieldType.type == typeof(Boolean)) "is" else "get"
  cls.addMethod(prefix + field.simpleName.toFirstUpper) [

@djozis
Copy link

djozis commented Mar 25, 2016

Is there anywhere this is built/resolvable as a maven style dependency?

@vorburger
Copy link

see @buildable in project https://github.com/oehme/xtend-contrib (which is available as a maven style dependency)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment