X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=binding%2Fmdsal-binding-java-api-generator%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fmdsal%2Fbinding%2Fjava%2Fapi%2Fgenerator%2FBuilderTemplate.xtend;h=14b77a06fdef05b464a54ae4310ac8b26cbc51bc;hb=c7da6941c5e7f46e189cc85a8b4eb66ba8551f70;hp=02d2f45132fe6c88a2459bb970ede660fb688078;hpb=636ac0055012e2ee436d5526a95640c6df35e46a;p=mdsal.git diff --git a/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderTemplate.xtend b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderTemplate.xtend index 02d2f45132..14b77a06fd 100644 --- a/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderTemplate.xtend +++ b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderTemplate.xtend @@ -7,213 +7,53 @@ */ package org.opendaylight.mdsal.binding.java.api.generator -import com.google.common.base.MoreObjects -import com.google.common.collect.ImmutableMap -import com.google.common.collect.ImmutableSortedSet +import static extension org.apache.commons.text.StringEscapeUtils.escapeJava +import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.AUGMENTABLE_AUGMENTATION_NAME +import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.AUGMENTATION_FIELD + +import com.google.common.collect.ImmutableList import java.util.ArrayList -import java.util.Arrays import java.util.Collection -import java.util.Collections import java.util.HashMap import java.util.HashSet -import java.util.LinkedHashSet import java.util.List import java.util.Map -import java.util.Objects import java.util.Set -import org.opendaylight.mdsal.binding.model.api.ConcreteType +import org.opendaylight.mdsal.binding.model.api.AnnotationType import org.opendaylight.mdsal.binding.model.api.GeneratedProperty import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject import org.opendaylight.mdsal.binding.model.api.GeneratedType -import org.opendaylight.mdsal.binding.model.api.MethodSignature +import org.opendaylight.mdsal.binding.model.api.JavaTypeName +import org.opendaylight.mdsal.binding.model.api.ParameterizedType import org.opendaylight.mdsal.binding.model.api.Type -import org.opendaylight.mdsal.binding.model.util.ReferencedTypeImpl +import org.opendaylight.mdsal.binding.model.util.TypeConstants import org.opendaylight.mdsal.binding.model.util.Types -import org.opendaylight.mdsal.binding.model.util.generated.type.builder.CodegenGeneratedTOBuilder +import org.opendaylight.mdsal.binding.spec.naming.BindingMapping import org.opendaylight.yangtools.concepts.Builder -import org.opendaylight.yangtools.yang.binding.Augmentable import org.opendaylight.yangtools.yang.binding.AugmentationHolder -import org.opendaylight.yangtools.yang.binding.CodeHelpers import org.opendaylight.yangtools.yang.binding.DataObject -import org.opendaylight.yangtools.yang.binding.Identifiable /** * Template for generating JAVA builder classes. */ - -class BuilderTemplate extends BaseTemplate { - - /** - * Constant with the name of the concrete method. - */ - val static GET_AUGMENTATION_METHOD_NAME = "getAugmentation" - - /** - * Constant with the suffix for builder classes. - */ - val static BUILDER = 'Builder' - - /** - * Constant with the name of the BuilderFor interface - */ - val static BUILDERFOR = Builder.simpleName; - +class BuilderTemplate extends AbstractBuilderTemplate { /** - * Constant with suffix for the classes which are generated from the builder classes. + * Constant used as suffix for builder name. */ - val static IMPL = 'Impl' + public static val BUILDER = "Builder"; - /** - * Generated property is set if among methods is found one with the name GET_AUGMENTATION_METHOD_NAME - */ - var GeneratedProperty augmentField - - /** - * Set of class attributes (fields) which are derived from the getter methods names - */ - val Set properties - - private static val METHOD_COMPARATOR = new AlphabeticallyTypeMemberComparator(); + static val AUGMENTATION_FIELD_UPPER = AUGMENTATION_FIELD.toFirstUpper /** * Constructs new instance of this class. * @throws IllegalArgumentException if genType equals null */ - new(GeneratedType genType) { - super(genType) - this.properties = propertiesFromMethods(createMethods) - addImport(Builder.simpleName, Builder.package.name) - } - - /** - * Returns set of method signature instances which contains all the methods of the genType - * and all the methods of the implemented interfaces. - * - * @returns set of method signature instances - */ - def private Set createMethods() { - val Set methods = new LinkedHashSet(); - methods.addAll(type.methodDefinitions) - collectImplementedMethods(methods, type.implements) - val Set sortedMethods = ImmutableSortedSet.orderedBy(METHOD_COMPARATOR).addAll(methods).build() - - return sortedMethods - } - - /** - * Adds to the methods set all the methods of the implementedIfcs - * and recursively their implemented interfaces. - * - * @param methods set of method signatures - * @param implementedIfcs list of implemented interfaces - */ - def private void collectImplementedMethods(Set methods, List implementedIfcs) { - if (implementedIfcs === null || implementedIfcs.empty) { - return - } - for (implementedIfc : implementedIfcs) { - if ((implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))) { - val ifc = implementedIfc as GeneratedType - methods.addAll(ifc.methodDefinitions) - collectImplementedMethods(methods, ifc.implements) - } else if (implementedIfc.fullyQualifiedName == Augmentable.name) { - for (m : Augmentable.methods) { - if (m.name == GET_AUGMENTATION_METHOD_NAME) { - val fullyQualifiedName = m.returnType.name - val pkg = fullyQualifiedName.package - val name = fullyQualifiedName.name - val tmpGenTO = new CodegenGeneratedTOBuilder(pkg, name) - val refType = new ReferencedTypeImpl(pkg, name) - val generic = new ReferencedTypeImpl(type.packageName, type.name) - val parametrizedReturnType = Types.parameterizedTypeFor(refType, generic) - tmpGenTO.addMethod(m.name).setReturnType(parametrizedReturnType) - augmentField = tmpGenTO.toInstance.methodDefinitions.first.propertyFromGetter - } - } - } - } - } - - /** - * Returns the first element of the list elements. - * - * @param list of elements - */ - def private first(List elements) { - elements.get(0) - } - - /** - * Returns the name of the package from fullyQualifiedName. - * - * @param fullyQualifiedName string with fully qualified type name (package + type) - * @return string with the package name - */ - def private String getPackage(String fullyQualifiedName) { - val lastDotIndex = fullyQualifiedName.lastIndexOf(Constants.DOT) - return if (lastDotIndex == -1) "" else fullyQualifiedName.substring(0, lastDotIndex) - } - - /** - * Returns the name of tye type from fullyQualifiedName - * - * @param fullyQualifiedName string with fully qualified type name (package + type) - * @return string with the name of the type - */ - def private String getName(String fullyQualifiedName) { - val lastDotIndex = fullyQualifiedName.lastIndexOf(Constants.DOT) - return if (lastDotIndex == -1) fullyQualifiedName else fullyQualifiedName.substring(lastDotIndex + 1) + new(GeneratedType genType, GeneratedType targetType, Set properties, Type augmentType, + Type keyType) { + super(genType, targetType, properties, augmentType, keyType) } - /** - * Creates set of generated property instances from getter methods. - * - * @param set of method signature instances which should be transformed to list of properties - * @return set of generated property instances which represents the getter methods - */ - def private propertiesFromMethods(Collection methods) { - if (methods === null || methods.isEmpty()) { - return Collections.emptySet - } - val Set result = new LinkedHashSet - for (m : methods) { - val createdField = m.propertyFromGetter - if (createdField !== null) { - result.add(createdField) - } - } - return result - } - - /** - * Creates generated property instance from the getter method name and return type. - * - * @param method method signature from which is the method name and return type obtained - * @return generated property instance for the getter method - * @throws IllegalArgumentException
    - *
  • if the method equals null
  • - *
  • if the name of the method equals null
  • - *
  • if the name of the method is empty
  • - *
  • if the return type of the method equals null
  • - *
- */ - def private GeneratedProperty propertyFromGetter(MethodSignature method) { - if (method === null || method.name === null || method.name.empty || method.returnType === null) { - throw new IllegalArgumentException("Method, method name, method return type reference cannot be NULL or empty!") - } - var prefix = "get"; - if (Types.BOOLEAN.equals(method.returnType)) { - prefix = "is"; - } - if (method.name.startsWith(prefix)) { - val fieldName = method.getName().substring(prefix.length()).toFirstLower - val tmpGenTO = new CodegenGeneratedTOBuilder("foo", "foo") - tmpGenTO.addProperty(fieldName).setReturnType(method.returnType) - return tmpGenTO.toInstance.properties.first - } - } - - override isLocalInnerClass(String importedTypePackageName) { + override isLocalInnerClass(JavaTypeName name) { // Builders do not have inner types return false; } @@ -224,59 +64,57 @@ class BuilderTemplate extends BaseTemplate { * @return string with JAVA source code */ override body() ''' - «wrapToDocumentation(formatDataForJavaDoc(type))» - public class «type.name»«BUILDER» implements «BUILDERFOR»<«type.importedName»> { + «wrapToDocumentation(formatDataForJavaDoc(targetType))» + «targetType.annotations.generateDeprecatedAnnotation» + public class «type.name» implements «Builder.importedName»<«targetType.importedName»> { «generateFields(false)» - «generateAugmentField(false)» - - «generateConstructorsFromIfcs(type)» - - «generateCopyConstructor(false)» + «constantsDeclarations()» - «generateMethodFieldsFrom(type)» - - «generateGetters(false)» - - «generateSetters» - - @Override - public «type.name» build() { - return new «type.name»«IMPL»(this); - } - - private static final class «type.name»«IMPL» implements «type.name» { - - «implementedInterfaceGetter» + «IF augmentType !== null» + «generateAugmentField()» + «ENDIF» - «generateFields(true)» + «generateConstructorsFromIfcs()» - «generateAugmentField(true)» + public «generateCopyConstructor(targetType, type.enclosedTypes.get(0))» - «generateCopyConstructor(true)» + «generateMethodFieldsFrom()» - «generateGetters(true)» + «generateGetters(false)» + «IF augmentType !== null» - «generateHashCode()» + «generateAugmentation()» + «ENDIF» - «generateEquals()» + «generateSetters» - «generateToString(properties)» + @«OVERRIDE.importedName» + public «targetType.name» build() { + return new «type.enclosedTypes.get(0).importedName»(this); } + «new BuilderImplTemplate(this, type.enclosedTypes.get(0)).body» } ''' + override generateDeprecatedAnnotation(AnnotationType ann) { + val forRemoval = ann.getParameter("forRemoval") + if (forRemoval !== null) { + return "@" + DEPRECATED.importedName + "(forRemoval = " + forRemoval.value + ")" + } + return "@" + SUPPRESS_WARNINGS.importedName + "(\"deprecation\")" + } + /** * Generate default constructor and constructor for every implemented interface from uses statements. */ - def private generateConstructorsFromIfcs(Type type) ''' - public «type.name»«BUILDER»() { + def private generateConstructorsFromIfcs() ''' + public «type.name»() { } - «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))» - «val ifc = type as GeneratedType» - «FOR impl : ifc.implements» + «IF (!(targetType instanceof GeneratedTransferObject))» + «FOR impl : targetType.implements» «generateConstructorFromIfc(impl)» «ENDFOR» «ENDIF» @@ -287,8 +125,8 @@ class BuilderTemplate extends BaseTemplate { */ def private Object generateConstructorFromIfc(Type impl) ''' «IF (impl instanceof GeneratedType)» - «IF !(impl.methodDefinitions.empty)» - public «type.name»«BUILDER»(«impl.fullyQualifiedName» arg) { + «IF impl.hasNonDefaultMethods» + public «type.name»(«impl.fullyQualifiedName» arg) { «printConstructorPropertySetter(impl)» } «ENDIF» @@ -301,8 +139,10 @@ class BuilderTemplate extends BaseTemplate { def private Object printConstructorPropertySetter(Type implementedIfc) ''' «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))» «val ifc = implementedIfc as GeneratedType» - «FOR getter : ifc.methodDefinitions» - this._«getter.propertyNameFromGetter» = arg.«getter.name»(); + «FOR getter : ifc.nonDefaultMethods» + «IF BindingMapping.isGetterMethodName(getter.name)» + this._«getter.propertyNameFromGetter» = arg.«getter.name»(); + «ENDIF» «ENDFOR» «FOR impl : ifc.implements» «printConstructorPropertySetter(impl)» @@ -313,18 +153,17 @@ class BuilderTemplate extends BaseTemplate { /** * Generate 'fieldsFrom' method to set builder properties based on type of given argument. */ - def private generateMethodFieldsFrom(Type type) ''' - «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))» - «val ifc = type as GeneratedType» - «IF ifc.hasImplementsFromUses» - «val List done = ifc.getBaseIfcs» - «generateMethodFieldsFromComment(ifc)» + def private generateMethodFieldsFrom() ''' + «IF (!(targetType instanceof GeneratedTransferObject))» + «IF targetType.hasImplementsFromUses» + «val List done = targetType.getBaseIfcs» + «generateMethodFieldsFromComment(targetType)» public void fieldsFrom(«DataObject.importedName» arg) { boolean isValidArg = false; - «FOR impl : ifc.getAllIfcs» + «FOR impl : targetType.getAllIfcs» «generateIfCheck(impl, done)» «ENDFOR» - «CodeHelpers.importedName».validValue(isValidArg, arg, "«ifc.getAllIfcs.toListOfNames»"); + «CODEHELPERS.importedName».validValue(isValidArg, arg, "«targetType.getAllIfcs.toListOfNames»"); } «ENDIF» «ENDIF» @@ -350,7 +189,7 @@ class BuilderTemplate extends BaseTemplate { def boolean hasImplementsFromUses(GeneratedType type) { var i = 0 for (impl : type.getAllIfcs) { - if ((impl instanceof GeneratedType) && !((impl as GeneratedType).methodDefinitions.empty)) { + if (impl instanceof GeneratedType && (impl as GeneratedType).hasNonDefaultMethods) { i = i + 1 } } @@ -358,7 +197,7 @@ class BuilderTemplate extends BaseTemplate { } def private generateIfCheck(Type impl, List done) ''' - «IF (impl instanceof GeneratedType) && !((impl as GeneratedType).methodDefinitions.empty)» + «IF (impl instanceof GeneratedType && (impl as GeneratedType).hasNonDefaultMethods)» «val implType = impl as GeneratedType» if (arg instanceof «implType.fullyQualifiedName») { «printPropertySetter(implType)» @@ -370,8 +209,10 @@ class BuilderTemplate extends BaseTemplate { def private printPropertySetter(Type implementedIfc) ''' «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))» «val ifc = implementedIfc as GeneratedType» - «FOR getter : ifc.methodDefinitions» - this._«getter.propertyNameFromGetter» = ((«implementedIfc.fullyQualifiedName»)arg).«getter.name»(); + «FOR getter : ifc.nonDefaultMethods» + «IF BindingMapping.isGetterMethodName(getter.name)» + this._«getter.propertyNameFromGetter» = ((«implementedIfc.fullyQualifiedName»)arg).«getter.name»(); + «ENDIF» «ENDFOR» «ENDIF» ''' @@ -379,7 +220,7 @@ class BuilderTemplate extends BaseTemplate { private def List getBaseIfcs(GeneratedType type) { val List baseIfcs = new ArrayList(); for (ifc : type.implements) { - if (ifc instanceof GeneratedType && !(ifc as GeneratedType).methodDefinitions.empty) { + if (ifc instanceof GeneratedType && (ifc as GeneratedType).hasNonDefaultMethods) { baseIfcs.add(ifc) } } @@ -391,7 +232,7 @@ class BuilderTemplate extends BaseTemplate { if (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject)) { val ifc = type as GeneratedType for (impl : ifc.implements) { - if (impl instanceof GeneratedType && !(impl as GeneratedType).methodDefinitions.empty) { + if (impl instanceof GeneratedType && (impl as GeneratedType).hasNonDefaultMethods) { baseIfcs.add(impl) } baseIfcs.addAll(impl.getAllIfcs) @@ -408,23 +249,88 @@ class BuilderTemplate extends BaseTemplate { return names } - /** - * Template method which generates class attributes. - * - * @param boolean value which specify whether field is|isn't final - * @return string with class attributes and their types - */ - def private generateFields(boolean _final) ''' - «IF properties !== null» - «FOR f : properties» - private«IF _final» final«ENDIF» «f.returnType.importedName» «f.fieldName»; - «ENDFOR» + def private constantsDeclarations() ''' + «FOR c : type.getConstantDefinitions» + «IF c.getName.startsWith(TypeConstants.PATTERN_CONSTANT_NAME)» + «val cValue = c.value as Map» + «val String fieldSuffix = c.getName.substring(TypeConstants.PATTERN_CONSTANT_NAME.length)» + «val jurPatternRef = JUR_PATTERN.importedName» + «IF cValue.size == 1» + «val firstEntry = cValue.entrySet.iterator.next» + private static final «jurPatternRef» «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «jurPatternRef».compile("«firstEntry.key.escapeJava»"); + private static final String «Constants.MEMBER_REGEX_LIST»«fieldSuffix» = "«firstEntry.value.escapeJava»"; + «ELSE» + private static final «jurPatternRef»[] «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.getFullyQualifiedName» value : values) { + «checkArgument(field, restrictions, actualType, "value")» + } + } + «ENDIF» + this.«field.fieldName» = values; + return this; + } + ''' - def private generateAugmentField(boolean isPrivate) ''' - «IF augmentField !== null» - «IF isPrivate»private «ENDIF»«Map.importedName»<«Class.importedName», «augmentField.returnType.importedName»> «augmentField.name» = «Collections.importedName».emptyMap(); + def private generateSimpleSetter(GeneratedProperty field, Type actualType) ''' + «val restrictions = restrictionsForSetter(actualType)» + «IF restrictions !== null» + + «generateCheckers(field, restrictions, actualType)» + «ENDIF» + + «val setterName = "set" + field.getName.toFirstUpper» + public «type.getName» «setterName»(final «field.returnType.importedName» value) { + «IF restrictions !== null» + if (value != null) { + «checkArgument(field, restrictions, actualType, "value")» + } + «ENDIF» + this.«field.fieldName» = value; + return this; + } + «val uintType = UINT_TYPES.get(field.returnType)» + «IF uintType !== null» + + /** + * Utility migration setter. + * + * @param value field value in legacy type + * @return this builder + * @deprecated Use {#link «setterName»(«field.returnType.importedJavadocName»)} instead. + */ + @Deprecated(forRemoval = true) + public «type.getName» «setterName»(final «uintType.importedName» value) { + return «setterName»(«CODEHELPERS.importedName».compatUint(value)); + } «ENDIF» ''' @@ -434,308 +340,128 @@ class BuilderTemplate extends BaseTemplate { * @return string with the setter methods */ def private generateSetters() ''' - «FOR field : properties SEPARATOR '\n'» - «/* FIXME: generate checkers as simple blocks and embed them directly in setters */» - «val restrictions = field.returnType.restrictions» - «IF !(field.returnType instanceof GeneratedType) && restrictions !== null» - «IF restrictions.rangeConstraint.present» - «val rangeGenerator = AbstractRangeGenerator.forType(field.returnType)» - «rangeGenerator.generateRangeChecker(field.name.toFirstUpper, restrictions.rangeConstraint.get, this)» - - «ENDIF» - «IF restrictions.lengthConstraint.present» - «LengthGenerator.generateLengthChecker(field.fieldName.toString, field.returnType, restrictions.lengthConstraint.get, this)» - - «ENDIF» - «ENDIF» - public «type.name»«BUILDER» set«field.name.toFirstUpper»(final «field.returnType.importedName» value) { - «IF !(field.returnType instanceof GeneratedType) && restrictions !== null» - «IF restrictions !== null && (restrictions.rangeConstraint.present || restrictions.lengthConstraint.present)» - if (value != null) { - «IF restrictions.rangeConstraint.present» - «val rangeGenerator = AbstractRangeGenerator.forType(field.returnType)» - «IF field.returnType instanceof ConcreteType» - «rangeGenerator.generateRangeCheckerCall(field.name.toFirstUpper, "value")» - «ELSE» - «rangeGenerator.generateRangeCheckerCall(field.name.toFirstUpper, "value.getValue()")» - «ENDIF» - «ENDIF» - «IF restrictions.lengthConstraint.present» - «IF field.returnType instanceof ConcreteType» - «LengthGenerator.generateLengthCheckerCall(field.fieldName.toString, "value")» - «ELSE» - «LengthGenerator.generateLengthCheckerCall(field.fieldName.toString, "value.getValue()")» - «ENDIF» - «ENDIF» - } - «ENDIF» - «ENDIF» - this.«field.fieldName» = value; + «IF keyType !== null» + public «type.getName» withKey(final «keyType.importedName» key) { + this.key = key; return this; } + «ENDIF» + «FOR property : properties» + «generateSetter(property)» «ENDFOR» - «IF augmentField !== null» - public «type.name»«BUILDER» add«augmentField.name.toFirstUpper»(«Class.importedName» augmentationType, «augmentField.returnType.importedName» augmentationValue) { + «IF augmentType !== null» + «val augmentTypeRef = augmentType.importedName» + «val jlClassRef = CLASS.importedName» + public «type.name» add«AUGMENTATION_FIELD_UPPER»(«jlClassRef» augmentationType, «augmentTypeRef» augmentationValue) { if (augmentationValue == null) { - return remove«augmentField.name.toFirstUpper»(augmentationType); + return remove«AUGMENTATION_FIELD_UPPER»(augmentationType); } - if (!(this.«augmentField.name» instanceof «HashMap.importedName»)) { - this.«augmentField.name» = new «HashMap.importedName»<>(); + if (!(this.«AUGMENTATION_FIELD» instanceof «HashMap.importedName»)) { + this.«AUGMENTATION_FIELD» = new «HashMap.importedName»<>(); } - this.«augmentField.name».put(augmentationType, augmentationValue); + this.«AUGMENTATION_FIELD».put(augmentationType, augmentationValue); return this; } - public «type.name»«BUILDER» remove«augmentField.name.toFirstUpper»(«Class.importedName» augmentationType) { - if (this.«augmentField.name» instanceof «HashMap.importedName») { - this.«augmentField.name».remove(augmentationType); + public «type.name» remove«AUGMENTATION_FIELD_UPPER»(«jlClassRef» augmentationType) { + if (this.«AUGMENTATION_FIELD» instanceof «HashMap.importedName») { + this.«AUGMENTATION_FIELD».remove(augmentationType); } return this; } «ENDIF» ''' - def private CharSequence generateCopyConstructor(boolean impl) ''' - «IF impl»private«ELSE»public«ENDIF» «type.name»«IF impl»«IMPL»«ELSE»«BUILDER»«ENDIF»(«type.name»«IF impl»«BUILDER»«ENDIF» base) { - «val allProps = new ArrayList(properties)» - «val isList = implementsIfc(type, Types.parameterizedTypeFor(Types.typeForClass(Identifiable), type))» - «val keyType = type.getKey» - «IF isList && keyType !== null» - «val keyProps = new ArrayList((keyType as GeneratedTransferObject).properties)» - «Collections.sort(keyProps, - [ p1, p2 | - return p1.name.compareTo(p2.name) - ]) - » - «FOR field : keyProps» - «removeProperty(allProps, field.name)» - «ENDFOR» - «removeProperty(allProps, "key")» - if (base.getKey() == null) { - this._key = new «keyType.importedName»( - «FOR keyProp : keyProps SEPARATOR ", "» - base.«keyProp.getterMethodName»() - «ENDFOR» - ); - «FOR field : keyProps» - this.«field.fieldName» = base.«field.getterMethodName»(); - «ENDFOR» - } else { - this._key = base.getKey(); - «FOR field : keyProps» - this.«field.fieldName» = _key.«field.getterMethodName»(); - «ENDFOR» - } - «ENDIF» - «FOR field : allProps» - this.«field.fieldName» = base.«field.getterMethodName»(); - «ENDFOR» - «IF augmentField !== null» - «IF impl» - this.«augmentField.name» = «ImmutableMap.importedName».copyOf(base.«augmentField.name»); - «ELSE» - if (base instanceof «type.name»«IMPL») { - «type.name»«IMPL» impl = («type.name»«IMPL») base; - if (!impl.«augmentField.name».isEmpty()) { - this.«augmentField.name» = new «HashMap.importedName»<>(impl.«augmentField.name»); - } - } else if (base instanceof «AugmentationHolder.importedName») { - @SuppressWarnings("unchecked") - «AugmentationHolder.importedName»<«type.importedName»> casted =(«AugmentationHolder.importedName»<«type.importedName»>) base; - if (!casted.augmentations().isEmpty()) { - this.«augmentField.name» = new «HashMap.importedName»<>(casted.augmentations()); - } - } - «ENDIF» - «ENDIF» - } - ''' - - private def boolean implementsIfc(GeneratedType type, Type impl) { - for (Type ifc : type.implements) { - if (ifc.equals(impl)) { - return true; + 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();
             }
-        }
-        return false;
+          
+        
+ +

+ 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» + ''' } - private def Type getKey(GeneratedType type) { - for (m : type.methodDefinitions) { - if ("getKey".equals(m.name)) { - return m.returnType; - } - } - return null; - } + override protected String formatDataForJavaDoc(GeneratedType type) { + val typeDescription = createDescription(type) - private def void removeProperty(Collection props, String name) { - var GeneratedProperty toRemove = null - for (p : props) { - if (p.name.equals(name)) { - toRemove = p; - } - } - if (toRemove !== null) { - props.remove(toRemove); - } + return ''' + «IF !typeDescription.nullOrEmpty» + «typeDescription» + «ENDIF» + '''.toString } - /** - * Template method which generate getter methods for IMPL class. - * - * @return string with getter methods - */ - def private generateGetters(boolean addOverride) ''' - «IF !properties.empty» - «FOR field : properties SEPARATOR '\n'» - «IF addOverride»@Override«ENDIF» - «field.getterMethod» - «ENDFOR» - «ENDIF» - «IF augmentField !== null» - - @SuppressWarnings("unchecked") - «IF addOverride»@Override«ENDIF» - public E get«augmentField.name.toFirstUpper»(«Class.importedName» augmentationType) { - return (E) «augmentField.name».get(«CodeHelpers.importedName».nonNullValue(augmentationType, "augmentationType")); - } - «ENDIF» + 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")); + } ''' - /** - * Template method which generates the method hashCode(). - * - * @return string with the hashCode() method definition in JAVA format - */ - def protected generateHashCode() ''' - «IF !properties.empty || augmentField !== null» - private int hash = 0; - private volatile boolean hashValid = false; - - @Override - public int hashCode() { - if (hashValid) { - return hash; - } - - final int prime = 31; - int result = 1; - «FOR property : properties» - «IF property.returnType.name.contains("[")» - result = prime * result + «Arrays.importedName».hashCode(«property.fieldName»); - «ELSE» - result = prime * result + «Objects.importedName».hashCode(«property.fieldName»); - «ENDIF» - «ENDFOR» - «IF augmentField !== null» - result = prime * result + «Objects.importedName».hashCode(«augmentField.name»); - «ENDIF» - - hash = result; - hashValid = true; - return result; - } - «ENDIF» + override protected generateCopyKeys(List keyProps) ''' + this.key = base.«BindingMapping.IDENTIFIABLE_KEY_NAME»(); + «FOR field : keyProps» + this.«field.fieldName» = base.«field.getterMethodName»(); + «ENDFOR» ''' - /** - * Template method which generates the method equals(). - * - * @return string with the equals() method definition in JAVA format - */ - def protected generateEquals() ''' - «IF !properties.empty || augmentField !== null» - @Override - public boolean equals(«Object.importedName» obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof «DataObject.importedName»)) { - return false; - } - if (!«type.importedName».class.equals(((«DataObject.importedName»)obj).getImplementedInterface())) { - return false; + 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") + «JU_MAP.importedName»<«CLASS.importedName», «augmentTypeRef»> aug =((«augmentationHolderRef»<«typeRef»>) base).augmentations(); + if (!aug.isEmpty()) { + this.«AUGMENTATION_FIELD» = new «hashMapRef»<>(aug); } - «type.importedName» other = («type.importedName»)obj; - «FOR property : properties» - «val fieldName = property.fieldName» - «IF property.returnType.name.contains("[")» - if (!«Arrays.importedName».equals(«fieldName», other.«property.getterMethodName»())) { - «ELSE» - if (!«Objects.importedName».equals(«fieldName», other.«property.getterMethodName»())) { - «ENDIF» - return false; - } - «ENDFOR» - «IF augmentField !== null» - if (getClass() == obj.getClass()) { - // Simple case: we are comparing against self - «type.name»«IMPL» otherImpl = («type.name»«IMPL») obj; - «val fieldName = augmentField.name» - if (!«Objects.importedName».equals(«fieldName», otherImpl.«fieldName»)) { - return false; - } - } else { - // Hard case: compare our augments with presence there... - for («Map.importedName».Entry<«Class.importedName», «augmentField.returnType.importedName»> e : «augmentField.name».entrySet()) { - if (!e.getValue().equals(other.getAugmentation(e.getKey()))) { - return false; - } - } - // .. and give the other one the chance to do the same - if (!obj.equals(this)) { - return false; - } - } - «ENDIF» - return true; - } - «ENDIF» - ''' - - def override generateToString(Collection properties) ''' - «IF properties !== null» - @Override - public «String.importedName» toString() { - final «MoreObjects.importedName».ToStringHelper helper = «MoreObjects.importedName».toStringHelper("«type.name»"); - «FOR property : properties» - «CodeHelpers.importedName».appendValue(helper, "«property.fieldName»", «property.fieldName»); - «ENDFOR» - «IF augmentField !== null» - «CodeHelpers.importedName».appendValue(helper, "«augmentField.name»", «augmentField.name».values()); - «ENDIF» - return helper.toString(); } - «ENDIF» - ''' - - def implementedInterfaceGetter() ''' - @Override - public «Class.importedName»<«type.importedName»> getImplementedInterface() { - return «type.importedName».class; + ''' } - ''' - private def createDescription(GeneratedType type) { - return ''' - Class that builds {@link «type.importedName»} instances. - - @see «type.importedName» - ''' + private static def hasNonDefaultMethods(GeneratedType type) { + !type.methodDefinitions.isEmpty && type.methodDefinitions.exists([def | !def.isDefault]) } - override def protected String formatDataForJavaDoc(GeneratedType type) { - val typeDescription = createDescription(type) - - return ''' - «IF !typeDescription.nullOrEmpty» - «typeDescription» - «ENDIF» - '''.toString + private static def nonDefaultMethods(GeneratedType type) { + type.methodDefinitions.filter([def | !def.isDefault]) } } -