package org.opendaylight.mdsal.binding.java.api.generator
import static extension org.apache.commons.text.StringEscapeUtils.escapeJava
-import static org.opendaylight.mdsal.binding.model.util.BindingTypes.DATA_OBJECT
+import static org.opendaylight.mdsal.binding.model.ri.BindingTypes.DATA_OBJECT
import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.AUGMENTABLE_AUGMENTATION_NAME
import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.AUGMENTATION_FIELD
import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.DATA_CONTAINER_IMPLEMENTED_INTERFACE_NAME
import org.opendaylight.mdsal.binding.model.api.MethodSignature;
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.model.ri.TypeConstants
+import org.opendaylight.mdsal.binding.model.ri.Types
import org.opendaylight.mdsal.binding.spec.naming.BindingMapping
-import org.opendaylight.yangtools.concepts.Builder
/**
* Template for generating JAVA builder classes.
*/
class BuilderTemplate extends AbstractBuilderTemplate {
- /**
- * Constant used as suffix for builder name.
- */
- package static val BUILDER_STR = "Builder";
-
- static val BUILDER = JavaTypeName.create(Builder)
-
val BuilderImplTemplate implTemplate
/**
«wrapToDocumentation(formatDataForJavaDoc(targetType))»
«targetType.annotations.generateDeprecatedAnnotation»
«generatedAnnotation»
- public class «type.name» implements «BUILDER.importedName»<«targetType.importedName»> {
+ public class «type.name» {
«generateFields(false)»
«generateAugmentField()»
«ENDIF»
+ /**
+ * Construct an empty builder.
+ */
+ public «type.name»() {
+ // No-op
+ }
+
«generateConstructorsFromIfcs()»
+ «val targetTypeName = targetType.importedName»
+ /**
+ * Construct a builder initialized with state from specified {@link «targetTypeName»}.
+ *
+ * @param base «targetTypeName» from which the builder should be initialized
+ */
public «generateCopyConstructor(targetType, type.enclosedTypes.get(0))»
«generateMethodFieldsFrom()»
«generateSetters»
- @«OVERRIDE.importedName»
- public «targetType.name» build() {
+ /**
+ * A new {@link «targetTypeName»} instance.
+ *
+ * @return A new {@link «targetTypeName»} instance.
+ */
+ public «targetTypeName» build() {
return new «type.enclosedTypes.get(0).importedName»(this);
}
* Generate default constructor and constructor for every implemented interface from uses statements.
*/
def private generateConstructorsFromIfcs() '''
- public «type.name»() {
- }
-
«IF (!(targetType instanceof GeneratedTransferObject))»
«FOR impl : targetType.implements SEPARATOR "\n"»
«generateConstructorFromIfc(impl)»
def private Object generateConstructorFromIfc(Type impl) '''
«IF (impl instanceof GeneratedType)»
«IF impl.hasNonDefaultMethods»
- public «type.name»(«impl.importedName» arg) {
+ «val typeName = impl.importedName»
+ /**
+ * Construct a new builder initialized from specified {@link «typeName»}.
+ *
+ * @param arg «typeName» from which the builder should be initialized
+ */
+ public «type.name»(«typeName» arg) {
«printConstructorPropertySetter(impl)»
}
+
«ENDIF»
«FOR implTypeImplement : impl.implements»
«generateConstructorFromIfc(implTypeImplement)»
* Set fields from given grouping argument. Valid argument is instance of one of following types:
* <ul>
«FOR impl : type.getAllIfcs»
- * <li>«impl.importedName»</li>
+ * <li>{@link «impl.importedName»}</li>
«ENDFOR»
* </ul>
*
if (Types.strictTypeEquals(getter.returnType, ownGetterType)) {
return "this._" + propertyName + " = " + retrieveProperty
}
- if (Types.isListType(ownGetterType)) {
- val itemType = (ownGetterType as ParameterizedType).actualTypeArguments.get(0)
- return '''
- this._«propertyName» = «CODEHELPERS.importedName».checkListFieldCast(«itemType.importedName».class, "«propertyName»", «retrieveProperty»)'''
+ if (ownGetterType instanceof ParameterizedType) {
+ val itemType = ownGetterType.actualTypeArguments.get(0)
+ if (Types.isListType(ownGetterType)) {
+ val importedClass = importedClass(itemType)
+ if (importedClass !== null) {
+ return printPropertySetter(retrieveProperty, propertyName, "checkListFieldCastIdentity", importedClass)
+ }
+ return printPropertySetter(retrieveProperty, propertyName, "checkListFieldCast", itemType.importedName)
+ }
+ if (Types.isSetType(ownGetterType)) {
+ val importedClass = importedClass(itemType)
+ if (importedClass !== null) {
+ return printPropertySetter(retrieveProperty, propertyName, "checkSetFieldCastIdentity", importedClass)
+ }
+ return printPropertySetter(retrieveProperty, propertyName, "checkSetFieldCast", itemType.importedName)
+ }
+ if (Types.CLASS.equals(ownGetterType)) {
+ return printPropertySetter(retrieveProperty, propertyName, "checkFieldCastIdentity", itemType.identifier.importedName)
+ }
}
- return '''
- this._«propertyName» = «CODEHELPERS.importedName».checkFieldCast(«ownGetter.returnType.importedName».class, "«propertyName»", «retrieveProperty»)'''
+ return printPropertySetter(retrieveProperty, propertyName, "checkFieldCast", ownGetterType.importedName)
+ }
+
+ def private printPropertySetter(String retrieveProperty, String propertyName, String checkerName, String className) '''
+ this._«propertyName» = «CODEHELPERS.importedName».«checkerName»(«className».class, "«propertyName»", «retrieveProperty»)'''
+
+ private def importedClass(Type type) {
+ if (type instanceof ParameterizedType) {
+ if (Types.CLASS.equals(type.rawType)) {
+ return type.actualTypeArguments.get(0).identifier.importedName
+ }
+ }
+ return null
}
private def List<Type> getBaseIfcs(GeneratedType type) {
def private generateSetter(GeneratedProperty field) {
val returnType = field.returnType
if (returnType instanceof ParameterizedType) {
- if (Types.isListType(returnType)) {
+ if (Types.isListType(returnType) || Types.isSetType(returnType)) {
val arguments = returnType.actualTypeArguments
if (arguments.isEmpty) {
return generateListSetter(field, Types.objectType)
'''
private def createDescription(GeneratedType targetType) {
- val target = type.importedName
+ val target = targetType.importedName
return '''
Class that builds {@link «target»} instances. Overall design of the class is that of a
<a href="https://en.wikipedia.org/wiki/Fluent_interface">fluent interface</a>, where method chaining is used.
In general, this class is supposed to be used like this template:
<pre>
<code>
- «target» createTarget(int fooXyzzy, int barBaz) {
+ «target» create«target»(int fooXyzzy, int barBaz) {
return new «target»Builder()
.setFoo(new FooBuilder().setXyzzy(fooXyzzy).build())
.setBar(new BarBuilder().setBaz(barBaz).build())
invocation, which is terminated by {@link #build()}, which is then returned from the method</li>
<li>better understanding by humans, as the scope of mutable state (the builder) is kept to a minimum and is
very localized</li>
- <li>better optimization oportunities, as the object scope is minimized in terms of invocation (rather than
+ <li>better optimization opportunities, as the object scope is minimized in terms of invocation (rather than
method) stack, making <a href="https://en.wikipedia.org/wiki/Escape_analysis">escape analysis</a> a lot
easier. Given enough compiler (JIT/AOT) prowess, the cost of th builder object can be completely
eliminated</li>
</ul>
@see «target»
- @see «BUILDER.importedName»
'''
}
«ENDFOR»
'''
- override protected generateCopyAugmentation(Type implType) {
- val hashMapRef = JU_HASHMAP.importedName
- val augmentTypeRef = augmentType.importedName
- return '''
- «JU_MAP.importedName»<«CLASS.importedName»<? extends «augmentTypeRef»>, «augmentTypeRef»> aug = base.augmentations();
- if (!aug.isEmpty()) {
- this.«AUGMENTATION_FIELD» = new «hashMapRef»<>(aug);
- }
- '''
- }
+ override protected generateCopyAugmentation(Type implType) '''
+ final var aug = base.augmentations();
+ if (!aug.isEmpty()) {
+ this.«AUGMENTATION_FIELD» = new «JU_HASHMAP.importedName»<>(aug);
+ }
+ '''
}