BUG-1485: switch BuilderTemplate to new range enforcement
[yangtools.git] / code-generator / binding-java-api-generator / src / main / java / org / opendaylight / yangtools / sal / java / api / generator / BuilderTemplate.xtend
index 710f1022104491baf8853ad89d4a2806be45a2eb..038a2a8c18a1a7252e3d52dc3050dbea48842e5c 100644 (file)
@@ -33,6 +33,10 @@ import org.opendaylight.yangtools.yang.binding.DataObject
 import org.opendaylight.yangtools.yang.binding.Identifiable
 import org.opendaylight.yangtools.concepts.Builder
 import org.opendaylight.yangtools.yang.binding.AugmentationHolder
+import org.opendaylight.yangtools.sal.binding.model.api.Restrictions
+import java.math.BigDecimal
+import java.math.BigInteger
+import com.google.common.collect.ImmutableList
 
 /**
  * Template for generating JAVA builder classes.
@@ -429,7 +433,7 @@ class BuilderTemplate extends BaseTemplate {
 
     def private generateAugmentField(boolean isPrivate) '''
         «IF augmentField != null»
-            «IF isPrivate»private «ENDIF»«Map.importedName»<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> «augmentField.name» = new «HashMap.importedName»<>();
+            «IF isPrivate»private «ENDIF»«Map.importedName»<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> «augmentField.name» = «Collections.importedName».emptyMap();
         «ENDIF»
     '''
 
@@ -441,14 +445,32 @@ class BuilderTemplate extends BaseTemplate {
     def private generateSetters() '''
         «FOR field : properties SEPARATOR '\n'»
             «val length = field.fieldName + "_length"»
-            «val range = field.fieldName + "_range"»
+            «val restrictions = field.returnType.restrictions»
+            «IF restrictions != null»
+                «IF !restrictions.rangeConstraints.nullOrEmpty»
+                    «val rangeGenerator = AbstractRangeGenerator.forType(field.returnType)»
+                    «rangeGenerator.generateRangeChecker(field.name.toFirstUpper, restrictions.rangeConstraints)»
+
+                «ENDIF»
+            «ENDIF»
             public «type.name»«BUILDER» set«field.name.toFirstUpper»(«field.returnType.importedName» value) {
-                «generateRestrictions(field, "value", length, range)»
+                «IF restrictions != null && !restrictions.rangeConstraints.nullOrEmpty»
+                if (value != null) {
+                    «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»
+                «generateRestrictions(field, "value", length)»
                 this.«field.fieldName» = value;
                 return this;
             }
             «generateLengthMethod(length, field.returnType, type.name+BUILDER, length)»
-            «generateRangeMethod(range, field.returnType.restrictions, field.returnType, type.name+BUILDER, range)»
+            «val range = field.fieldName + "_range"»
+            «generateRangeMethod(range, restrictions, field.returnType, type.name+BUILDER, range)»
         «ENDFOR»
         «IF augmentField != null»
 
@@ -456,36 +478,40 @@ class BuilderTemplate extends BaseTemplate {
                 if (augmentation == null) {
                     return remove«augmentField.name.toFirstUpper»(augmentationType);
                 }
+
+                if (!(this.«augmentField.name» instanceof «HashMap.importedName»)) {
+                    this.«augmentField.name» = new «HashMap.importedName»<>();
+                }
+
                 this.«augmentField.name».put(augmentationType, augmentation);
                 return this;
             }
 
             public «type.name»«BUILDER» remove«augmentField.name.toFirstUpper»(«Class.importedName»<? extends «augmentField.returnType.importedName»> augmentationType) {
-                this.«augmentField.name».remove(augmentationType);
+                if (this.«augmentField.name» instanceof «HashMap.importedName») {
+                    this.«augmentField.name».remove(augmentationType);
+                }
                 return this;
             }
         «ENDIF»
     '''
 
-    def generateRestrictions(GeneratedProperty field, String paramName, String lengthGetter, String rangeGetter) '''
+    def private generateRestrictions(GeneratedProperty field, String paramName, String lengthGetter) '''
         «val Type type = field.returnType»
         «IF type instanceof ConcreteType»
-            «createRestrictions(type, paramName, type.name.contains("["), lengthGetter, rangeGetter
+            «createRestrictions(type, paramName, type.name.contains("["), lengthGetter)»
         «ELSEIF type instanceof GeneratedTransferObject»
-            «createRestrictions(type, paramName, isArrayType(type as GeneratedTransferObject), lengthGetter, rangeGetter
+            «createRestrictions(type, paramName, isArrayType(type as GeneratedTransferObject), lengthGetter)»
         «ENDIF»
     '''
 
-    def private createRestrictions(Type type, String paramName, boolean isArray, String lengthGetter, String rangeGetter) '''
+    def private createRestrictions(Type type, String paramName, boolean isArray, String lengthGetter) '''
         «val restrictions = type.getRestrictions»
         «IF restrictions !== null»
             «val boolean isNestedType = !(type instanceof ConcreteType)»
             «IF !restrictions.lengthConstraints.empty»
                 «generateLengthRestriction(type, paramName, lengthGetter, isNestedType, isArray)»
             «ENDIF»
-            «IF !restrictions.rangeConstraints.empty»
-                «generateRangeRestriction(type, paramName, rangeGetter, isNestedType)»
-            «ENDIF»
         «ENDIF»
     '''
 
@@ -506,19 +532,69 @@ class BuilderTemplate extends BaseTemplate {
         }
     '''
 
-    def private generateRangeRestriction(Type type, String paramName, String getterName, boolean isNestedType) '''
-        if («paramName» != null) {
-            «printRangeConstraint(type, paramName, isNestedType)»
-            boolean isValidRange = false;
-            for («Range.importedName»<«type.importedNumber»> r : «getterName»()) {
-                if (r.contains(_constraint)) {
-                    isValidRange = true;
+    def private generateLengthMethod(String methodName, Type type, String className, String varName) '''
+        «val Restrictions restrictions = type.restrictions»
+        «IF restrictions != null && !(restrictions.lengthConstraints.empty)»
+            «val numberClass = restrictions.lengthConstraints.iterator.next.min.class»
+            /**
+             * @deprecated This method is slated for removal in a future release. See BUG-1485 for details.
+             */
+            @Deprecated
+            public static «List.importedName»<«Range.importedName»<«numberClass.importedNumber»>> «methodName»() {
+                «IF numberClass.equals(typeof(BigDecimal))»
+                    «lengthBody(restrictions, numberClass, className, varName)»
+                «ELSE»
+                    «lengthBody(restrictions, typeof(BigInteger), className, varName)»
+                «ENDIF»
+            }
+        «ENDIF»
+    '''
+
+    def private lengthBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
+        if («varName» == null) {
+            synchronized («className».class) {
+                if («varName» == null) {
+                    «ImmutableList.importedName».Builder<«Range.importedName»<«numberClass.importedName»>> builder = «ImmutableList.importedName».builder();
+                    «FOR r : restrictions.lengthConstraints»
+                        builder.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», «numericValue(numberClass, r.max)»));
+                    «ENDFOR»
+                    «varName» = builder.build();
                 }
             }
-            if (!isValidRange) {
-                throw new IllegalArgumentException(String.format("Invalid range: %s, expected: %s.", «paramName», «getterName»));
+        }
+        return «varName»;
+    '''
+
+    def private generateRangeMethod(String methodName, Restrictions restrictions, Type returnType, String className, String varName) '''
+        «IF restrictions != null && !(restrictions.rangeConstraints.empty)»
+            «val number = returnType.importedNumber»
+            /**
+             * @deprecated This method is slated for removal in a future release. See BUG-1485 for details.
+             */
+            @Deprecated
+            public static «List.importedName»<«Range.importedName»<«number»>> «methodName»() {
+                «IF returnType.fullyQualifiedName.equals(BigDecimal.canonicalName)»
+                    «rangeBody(restrictions, BigDecimal, className, varName)»
+                «ELSE»
+                    «rangeBody(restrictions, BigInteger, className, varName)»
+                «ENDIF»
+            }
+        «ENDIF»
+    '''
+
+    def private rangeBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
+        if («varName» == null) {
+            synchronized («className».class) {
+                if («varName» == null) {
+                    «ImmutableList.importedName».Builder<«Range.importedName»<«numberClass.importedName»>> builder = «ImmutableList.importedName».builder();
+                    «FOR r : restrictions.rangeConstraints»
+                        builder.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», «numericValue(numberClass, r.max)»));
+                    «ENDFOR»
+                    «varName» = builder.build();
+                }
             }
         }
+        return «varName»;
     '''
 
     def private CharSequence generateCopyConstructor(boolean impl) '''
@@ -562,9 +638,9 @@ class BuilderTemplate extends BaseTemplate {
                     case 0:
                         this.«augmentField.name» = «Collections.importedName».emptyMap();
                         break;
-                        case 1:
-                            final «Map.importedName».Entry<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> e = base.«augmentField.name».entrySet().iterator().next();
-                            this.«augmentField.name» = «Collections.importedName».<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»>singletonMap(e.getKey(), e.getValue());
+                    case 1:
+                        final «Map.importedName».Entry<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> e = base.«augmentField.name».entrySet().iterator().next();
+                        this.«augmentField.name» = «Collections.importedName».<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»>singletonMap(e.getKey(), e.getValue());
                         break;
                     default :
                         this.«augmentField.name» = new «HashMap.importedName»<>(base.«augmentField.name»);
@@ -572,11 +648,15 @@ class BuilderTemplate extends BaseTemplate {
                 «ELSE»
                     if (base instanceof «type.name»«IMPL») {
                         «type.name»«IMPL» impl = («type.name»«IMPL») base;
-                        this.«augmentField.name» = new «HashMap.importedName»<>(impl.«augmentField.name»);
+                        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;
-                        this.«augmentField.name» = new «HashMap.importedName»<>(casted.augmentations());
+                        if (!casted.augmentations().isEmpty()) {
+                            this.«augmentField.name» = new «HashMap.importedName»<>(casted.augmentations());
+                        }
                     }
                 «ENDIF»
             «ENDIF»