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=2390ffbdca8273a7539635f3ee35ea9e89197aaf;hb=b76fcb3bb19ecffdfbb47d7840a51e335bead392;hp=d2be1ba763ddd6244458c0a13194efec61b92048;hpb=0f351bbc28ddf2cddfe30c8d018646d81953fa17;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 d2be1ba763..2390ffbdca 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 @@ -8,6 +8,7 @@ package org.opendaylight.mdsal.binding.java.api.generator 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 @@ -18,7 +19,7 @@ import java.util.HashSet import java.util.List import java.util.Map import java.util.Set -import java.util.regex.Pattern +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 @@ -27,8 +28,9 @@ import org.opendaylight.mdsal.binding.model.api.ParameterizedType import org.opendaylight.mdsal.binding.model.api.Type import org.opendaylight.mdsal.binding.model.util.TypeConstants import org.opendaylight.mdsal.binding.model.util.Types +import org.opendaylight.mdsal.binding.spec.naming.BindingMapping import org.opendaylight.yangtools.concepts.Builder -import org.opendaylight.yangtools.yang.binding.CodeHelpers +import org.opendaylight.yangtools.yang.binding.AugmentationHolder import org.opendaylight.yangtools.yang.binding.DataObject /** @@ -40,6 +42,8 @@ class BuilderTemplate extends AbstractBuilderTemplate { */ public static val BUILDER = "Builder"; + static val AUGMENTATION_FIELD_UPPER = AUGMENTATION_FIELD.toFirstUpper + /** * Constructs new instance of this class. * @throws IllegalArgumentException if genType equals null @@ -60,26 +64,33 @@ class BuilderTemplate extends AbstractBuilderTemplate { * @return string with JAVA source code */ override body() ''' - «wrapToDocumentation(formatDataForJavaDoc(type))» + «wrapToDocumentation(formatDataForJavaDoc(targetType))» + «targetType.annotations.generateDeprecatedAnnotation» public class «type.name» implements «Builder.importedName»<«targetType.importedName»> { «generateFields(false)» «constantsDeclarations()» - «generateAugmentField(false)» + «IF augmentType !== null» + «generateAugmentField()» + «ENDIF» «generateConstructorsFromIfcs()» - «generateCopyConstructor(false, targetType, type.enclosedTypes.get(0))» + public «generateCopyConstructor(targetType, type.enclosedTypes.get(0))» «generateMethodFieldsFrom()» «generateGetters(false)» + «IF augmentType !== null» + + «generateAugmentation()» + «ENDIF» «generateSetters» - @«Override.importedName» + @«OVERRIDE.importedName» public «targetType.name» build() { return new «type.enclosedTypes.get(0).importedName»(this); } @@ -88,6 +99,14 @@ class BuilderTemplate extends AbstractBuilderTemplate { } ''' + 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. */ @@ -106,7 +125,7 @@ class BuilderTemplate extends AbstractBuilderTemplate { */ def private Object generateConstructorFromIfc(Type impl) ''' «IF (impl instanceof GeneratedType)» - «IF !(impl.methodDefinitions.empty)» + «IF impl.hasNonDefaultMethods» public «type.name»(«impl.fullyQualifiedName» arg) { «printConstructorPropertySetter(impl)» } @@ -120,8 +139,10 @@ class BuilderTemplate extends AbstractBuilderTemplate { 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)» @@ -142,7 +163,7 @@ class BuilderTemplate extends AbstractBuilderTemplate { «FOR impl : targetType.getAllIfcs» «generateIfCheck(impl, done)» «ENDFOR» - «CodeHelpers.importedName».validValue(isValidArg, arg, "«targetType.getAllIfcs.toListOfNames»"); + «CODEHELPERS.importedName».validValue(isValidArg, arg, "«targetType.getAllIfcs.toListOfNames»"); } «ENDIF» «ENDIF» @@ -168,7 +189,7 @@ class BuilderTemplate extends AbstractBuilderTemplate { 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 } } @@ -176,7 +197,7 @@ class BuilderTemplate extends AbstractBuilderTemplate { } 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)» @@ -188,8 +209,10 @@ class BuilderTemplate extends AbstractBuilderTemplate { 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» ''' @@ -197,7 +220,7 @@ class BuilderTemplate extends AbstractBuilderTemplate { 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) } } @@ -209,7 +232,7 @@ class BuilderTemplate extends AbstractBuilderTemplate { 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) @@ -231,11 +254,13 @@ class BuilderTemplate extends AbstractBuilderTemplate { «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» - private static final «Pattern.importedName» «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «Pattern.importedName».compile("«cValue.keySet.get(0).escapeJava»"); - private static final String «Constants.MEMBER_REGEX_LIST»«fieldSuffix» = "«cValue.values.get(0).escapeJava»"; + «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 «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «CodeHelpers.importedName».compilePatterns(«ImmutableList.importedName».of( + 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» }; @@ -246,6 +271,16 @@ class BuilderTemplate extends AbstractBuilderTemplate { «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» @@ -254,38 +289,51 @@ class BuilderTemplate extends AbstractBuilderTemplate { public «type.getName» set«field.getName.toFirstUpper»(final «field.returnType.importedName» values) { «IF restrictions !== null» if (values != null) { - for («actualType.getFullyQualifiedName» value : values) { + for («actualType.importedName» value : values) { «checkArgument(field, restrictions, actualType, "value")» } } «ENDIF» - this.«field.fieldName.toString» = values; + this.«field.fieldName» = values; return this; } ''' - def private generateSetter(GeneratedProperty field, Type actualType) ''' + 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; + «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» ''' - private def Type getActualType(ParameterizedType ptype) { - return ptype.getActualTypeArguments.get(0) - } - /** * Template method which generates setter methods * @@ -299,17 +347,15 @@ class BuilderTemplate extends AbstractBuilderTemplate { } «ENDIF» «FOR property : properties» - «IF property.returnType instanceof ParameterizedType && Types.isListType(property.returnType)» - «generateListSetter(property, getActualType(property.returnType as ParameterizedType))» - «ELSE» - «generateSetter(property, property.returnType)» - «ENDIF» + «generateSetter(property)» «ENDFOR» «IF augmentType !== null» - public «type.name» add«AUGMENTATION_FIELD.toFirstUpper»(«Class.importedName» augmentationType, «augmentType.importedName» augmentationValue) { + «val augmentTypeRef = augmentType.importedName» + «val jlClassRef = CLASS.importedName» + public «type.name» add«AUGMENTATION_FIELD_UPPER»(«jlClassRef» augmentationType, «augmentTypeRef» augmentationValue) { if (augmentationValue == null) { - return remove«AUGMENTATION_FIELD.toFirstUpper»(augmentationType); + return remove«AUGMENTATION_FIELD_UPPER»(augmentationType); } if (!(this.«AUGMENTATION_FIELD» instanceof «HashMap.importedName»)) { @@ -320,7 +366,7 @@ class BuilderTemplate extends AbstractBuilderTemplate { return this; } - public «type.name» remove«AUGMENTATION_FIELD.toFirstUpper»(«Class.importedName» augmentationType) { + public «type.name» remove«AUGMENTATION_FIELD_UPPER»(«jlClassRef» augmentationType) { if (this.«AUGMENTATION_FIELD» instanceof «HashMap.importedName») { this.«AUGMENTATION_FIELD».remove(augmentationType); } @@ -329,11 +375,45 @@ class BuilderTemplate extends AbstractBuilderTemplate { «ENDIF» ''' - private def createDescription(GeneratedType type) { + private def createDescription(GeneratedType targetType) { + val target = type.importedName return ''' - Class that builds {@link «type.importedName»} instances. - - @see «type.importedName» + 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» ''' } @@ -346,5 +426,42 @@ class BuilderTemplate extends AbstractBuilderTemplate { «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") + «JU_MAP.importedName»<«CLASS.importedName», «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]) + } +}