»
«val String fieldSuffix = c.getName.substring(TypeConstants.PATTERN_CONSTANT_NAME.length)»
«IF cValue.size == 1»
«val firstEntry = cValue.entrySet.iterator.next»
private static final «Pattern.importedName» «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «Pattern.importedName».compile("«firstEntry.key.escapeJava»");
private static final String «Constants.MEMBER_REGEX_LIST»«fieldSuffix» = "«firstEntry.value.escapeJava»";
«ELSE»
private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «CodeHelpers.importedName».compilePatterns(«ImmutableList.importedName».of(
«FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»));
private static final String[] «Constants.MEMBER_REGEX_LIST»«fieldSuffix» = { «
FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
«ENDIF»
«ELSE»
«emitConstant(c)»
«ENDIF»
«ENDFOR»
'''
def private generateSetter(GeneratedProperty field) {
val returnType = field.returnType
if (returnType instanceof ParameterizedType) {
if (Types.isListType(returnType)) {
return generateListSetter(field, returnType.actualTypeArguments.get(0))
}
}
return generateSimpleSetter(field, returnType)
}
def private generateListSetter(GeneratedProperty field, Type actualType) '''
«val restrictions = restrictionsForSetter(actualType)»
«IF restrictions !== null»
«generateCheckers(field, restrictions, actualType)»
«ENDIF»
public «type.getName» set«field.getName.toFirstUpper»(final «field.returnType.importedName» values) {
«IF restrictions !== null»
if (values != null) {
for («actualType.importedName» value : values) {
«checkArgument(field, restrictions, actualType, "value")»
}
}
«ENDIF»
this.«field.fieldName.toString» = values;
return this;
}
'''
def private generateSimpleSetter(GeneratedProperty field, Type actualType) '''
«val restrictions = restrictionsForSetter(actualType)»
«IF restrictions !== null»
«generateCheckers(field, restrictions, actualType)»
«ENDIF»
public «type.getName» set«field.getName.toFirstUpper»(final «field.returnType.importedName» value) {
«IF restrictions !== null»
if (value != null) {
«checkArgument(field, restrictions, actualType, "value")»
}
«ENDIF»
this.«field.fieldName.toString» = value;
return this;
}
'''
/**
* Template method which generates setter methods
*
* @return string with the setter methods
*/
def private generateSetters() '''
«IF keyType !== null»
public «type.getName» withKey(final «keyType.importedName» key) {
this.key = key;
return this;
}
«ENDIF»
«FOR property : properties»
«generateSetter(property)»
«ENDFOR»
«IF augmentType !== null»
«val augmentTypeRef = augmentType.importedName»
public «type.name» add«AUGMENTATION_FIELD_UPPER»(«Class.importedName» extends «augmentTypeRef»> augmentationType, «augmentTypeRef» augmentationValue) {
if (augmentationValue == null) {
return remove«AUGMENTATION_FIELD_UPPER»(augmentationType);
}
if (!(this.«AUGMENTATION_FIELD» instanceof «HashMap.importedName»)) {
this.«AUGMENTATION_FIELD» = new «HashMap.importedName»<>();
}
this.«AUGMENTATION_FIELD».put(augmentationType, augmentationValue);
return this;
}
public «type.name» remove«AUGMENTATION_FIELD_UPPER»(«Class.importedName» extends «augmentTypeRef»> augmentationType) {
if (this.«AUGMENTATION_FIELD» instanceof «HashMap.importedName») {
this.«AUGMENTATION_FIELD».remove(augmentationType);
}
return this;
}
«ENDIF»
'''
private def createDescription(GeneratedType targetType) {
val target = type.importedName
return '''
Class that builds {@link «target»} instances. Overall design of the class is that of a
fluent interface, where method chaining is used.
In general, this class is supposed to be used like this template:
«target» createTarget(int fooXyzzy, int barBaz) {
return new «target»Builder()
.setFoo(new FooBuilder().setXyzzy(fooXyzzy).build())
.setBar(new BarBuilder().setBaz(barBaz).build())
.build();
}
This pattern is supported by the immutable nature of «target», as instances can be freely passed around without
worrying about synchronization issues.
As a side note: method chaining results in:
- very efficient Java bytecode, as the method invocation result, in this case the Builder reference, is
on the stack, so further method invocations just need to fill method arguments for the next method
invocation, which is terminated by {@link #build()}, which is then returned from the method
- better understanding by humans, as the scope of mutable state (the builder) is kept to a minimum and is
very localized
- better optimization oportunities, as the object scope is minimized in terms of invocation (rather than
method) stack, making escape analysis a lot
easier. Given enough compiler (JIT/AOT) prowess, the cost of th builder object can be completely
eliminated
@see «target»
@see «Builder.importedName»
'''
}
override protected String formatDataForJavaDoc(GeneratedType type) {
val typeDescription = createDescription(type)
return '''
«IF !typeDescription.nullOrEmpty»
«typeDescription»
«ENDIF»
'''.toString
}
private def generateAugmentation() '''
@«SUPPRESS_WARNINGS.importedName»({ "unchecked", "checkstyle:methodTypeParameterName"})
public E$$ «AUGMENTABLE_AUGMENTATION_NAME»(«Class.importedName» augmentationType) {
return (E$$) «AUGMENTATION_FIELD».get(«CodeHelpers.importedName».nonNullValue(augmentationType, "augmentationType"));
}
'''
override protected generateCopyKeys(List keyProps) '''
this.key = base.«BindingMapping.IDENTIFIABLE_KEY_NAME»();
«FOR field : keyProps»
this.«field.fieldName» = base.«field.getterMethodName»();
«ENDFOR»
'''
override protected generateCopyAugmentation(Type implType) {
val augmentationHolderRef = AugmentationHolder.importedName
val typeRef = targetType.importedName
val hashMapRef = HashMap.importedName
val augmentTypeRef = augmentType.importedName
return '''
if (base instanceof «augmentationHolderRef») {
@SuppressWarnings("unchecked")
«Map.importedName»<«Class.importedName» extends «augmentTypeRef»>, «augmentTypeRef»> aug =((«augmentationHolderRef»<«typeRef»>) base).augmentations();
if (!aug.isEmpty()) {
this.«AUGMENTATION_FIELD» = new «hashMapRef»<>(aug);
}
}
'''
}
private static def hasNonDefaultMethods(GeneratedType type) {
!type.methodDefinitions.isEmpty && type.methodDefinitions.exists([def | !def.isDefault])
}
private static def nonDefaultMethods(GeneratedType type) {
type.methodDefinitions.filter([def | !def.isDefault])
}
}