BUG-1485: make AbstractRangeGenerator deal with GTOs 49/21349/6
authorRobert Varga <rovarga@cisco.com>
Thu, 28 May 2015 14:57:20 +0000 (16:57 +0200)
committerRobert Varga <rovarga@cisco.com>
Sun, 31 May 2015 17:25:09 +0000 (19:25 +0200)
For enforcement of ranges in builders, we will need to deal with types
other than ConcreteType. Also clean up internal interfaces, add some
javadocs and remove some trailing whitespace.

Also perform Number class conversion silently if it does not result in
loss of precision. Emit a warning if it does.

Change-Id: I8f3ba96c104fb28f6d2cfc4b05b5d24bb7f80aa1
Signed-off-by: Robert Varga <rovarga@cisco.com>
code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/AbstractBigRangeGenerator.java
code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/AbstractPrimitiveRangeGenerator.java
code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/AbstractRangeGenerator.java
code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/AbstractSubIntegerRangeGenerator.java
code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/BigDecimalRangeGenerator.java
code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/BigIntegerRangeGenerator.java
code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/IntegerRangeGenerator.java
code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/LongRangeGenerator.java

index dce97a271afd2db3c681d8948eb573ac004a8260..737aadf805725f7d0a4c46521dc18a6acd9d919e 100644 (file)
@@ -7,60 +7,68 @@
  */
 package org.opendaylight.yangtools.sal.java.api.generator;
 
+import com.google.common.collect.Range;
 import java.util.Collection;
 import javax.annotation.Nonnull;
 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
 
+/**
+ * Abstract base for generators which require instantiation of boundary values
+ * to check. These are implemented by generating an array constant within the
+ * class, which contains {@link Range} instances, which hold pre-instantiated
+ * boundary values.
+ *
+ * @param <T> type of the class
+ */
 abstract class AbstractBigRangeGenerator<T extends Number & Comparable<T>> extends AbstractRangeGenerator<T> {
+    private static final String RANGE = Range.class.getName();
+
     protected AbstractBigRangeGenerator(final Class<T> typeClass) {
         super(typeClass);
     }
 
-    private String itemType() {
-        final StringBuilder sb = new StringBuilder("com.google.common.collect.Range<");
-        sb.append(getTypeName()).append('>');
-
-        return sb.toString();
+    private StringBuilder itemType() {
+        return new StringBuilder(RANGE).append('<').append(getTypeName()).append('>');
     }
 
-    private String arrayType() {
-        return new StringBuilder(itemType()).append("[]").toString();
+    private StringBuilder arrayType() {
+        return new StringBuilder(itemType()).append("[]");
     }
 
     @Override
-    protected final String generateRangeCheckerImplementation(final String checkerName, @Nonnull final Collection<RangeConstraint> restrictions) {
+    protected final String generateRangeCheckerImplementation(final String checkerName, @Nonnull final Collection<RangeConstraint> constraints) {
+        final String fieldName = checkerName.toUpperCase() + "_RANGES";
         final StringBuilder sb = new StringBuilder();
 
         // Field to hold the Range objects in an array
-        sb.append("private static final ").append(arrayType()).append(' ').append(checkerName).append(";\n");
+        sb.append("private static final ").append(arrayType()).append(' ').append(fieldName).append(";\n");
 
         // Static initializer block for the array
         sb.append("static {\n");
         sb.append("    @SuppressWarnings(\"unchecked\")\n");
         sb.append("    final ").append(arrayType()).append(" a = (").append(arrayType())
-        .append(") java.lang.reflect.Array.newInstance(com.google.common.collect.Range.class, ").append(restrictions.size()).append(");\n");
+        .append(") java.lang.reflect.Array.newInstance(").append(RANGE).append(".class, ").append(constraints.size()).append(");\n");
 
         int i = 0;
-        for (RangeConstraint r : restrictions) {
+        for (RangeConstraint r : constraints) {
             final String min = format(getValue(r.getMin()));
             final String max = format(getValue(r.getMax()));
 
-            sb.append("    a[").append(i++).append("] = com.google.common.collect.Range.closed(").append(min).append(", ").append(max).append(");\n");
+            sb.append("    a[").append(i++).append("] = ").append(RANGE).append(".closed(").append(min).append(", ").append(max).append(");\n");
         }
 
-        sb.append("    ").append(checkerName).append(" = a;\n");
-        sb.append("}\n\n");
+        sb.append("    ").append(fieldName).append(" = a;\n");
+        sb.append("}\n");
 
         // Static enforcement method
         sb.append("private static void ").append(checkerName).append("(final ").append(getTypeName()).append(" value) {\n");
-        sb.append("    for (").append(itemType()).append(" r : ").append(checkerName).append(") {\n");
+        sb.append("    for (").append(itemType()).append(" r : ").append(fieldName).append(") {\n");
         sb.append("        if (r.contains(value)) {\n");
         sb.append("            return;\n");
         sb.append("        }\n");
         sb.append("    }\n");
-        sb.append("\n");
-        sb.append("    throw new IllegalArgumentException(String.format(\"Invalid value %s, expected: %s.\", value, java.util.Arrays.asList(").append(checkerName).append(")));\n");
-        sb.append("}\n\n");
+        sb.append("    throw new IllegalArgumentException(String.format(\"Invalid range: %s, expected: %s.\", value, java.util.Arrays.asList(").append(fieldName).append(")));\n");
+        sb.append("}\n");
 
         return sb.toString();
     }
index fffe2d1a6246be3181f5f4fb61d31f3bb46aebec..72091a3a9d36f36a630293879f193b64bc751eb4 100644 (file)
@@ -8,8 +8,10 @@
 package org.opendaylight.yangtools.sal.java.api.generator;
 
 import com.google.common.base.Preconditions;
+import com.google.common.collect.Range;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -33,10 +35,10 @@ abstract class AbstractPrimitiveRangeGenerator<T extends Number & Comparable<T>>
         return minValue.compareTo(minToEnforce) < 0;
     }
 
-    private final Collection<String> createConditionals(final Collection<RangeConstraint> restrictions) {
-        final Collection<String> ret = new ArrayList<>(restrictions.size());
+    private final Collection<String> createExpressions(final Collection<RangeConstraint> constraints) {
+        final Collection<String> ret = new ArrayList<>(constraints.size());
 
-        for (RangeConstraint r : restrictions) {
+        for (RangeConstraint r : constraints) {
             final T min = getValue(r.getMin());
             final boolean needMin = needsMinimumEnforcement(min);
 
@@ -51,11 +53,11 @@ abstract class AbstractPrimitiveRangeGenerator<T extends Number & Comparable<T>>
             final StringBuilder sb = new StringBuilder();
             if (needMin) {
                 sb.append("value >= ").append(format(min));
-                if (needMax) {
-                    sb.append(" && ");
-                }
             }
             if (needMax) {
+                if (needMin) {
+                    sb.append(" && ");
+                }
                 sb.append("value <= ").append(format(max));
             }
 
@@ -65,24 +67,35 @@ abstract class AbstractPrimitiveRangeGenerator<T extends Number & Comparable<T>>
         return ret;
     }
 
+    private String createRangeString(final Collection<RangeConstraint> constraints) {
+        final List<Range<T>> ranges = new ArrayList<>(constraints.size());
+
+        for (RangeConstraint c : constraints) {
+            ranges.add(Range.closed(getValue(c.getMin()), getValue(c.getMax())));
+        }
+
+        return ranges.toString();
+    }
+
     @Override
-    protected final String generateRangeCheckerImplementation(final String checkerName, final Collection<RangeConstraint> restrictions) {
+    protected final String generateRangeCheckerImplementation(final String checkerName, final Collection<RangeConstraint> constraints) {
         final StringBuilder sb = new StringBuilder();
-        final Collection<String> conditionals = createConditionals(restrictions);
+        final Collection<String> expressions = createExpressions(constraints);
 
         sb.append("private static void ").append(checkerName).append("(final ").append(getTypeName()).append(" value) {\n");
 
-        if (!conditionals.isEmpty()) {
-            for (String c : conditionals) {
-                sb.append("    if (").append(c).append(") {\n");
+        if (!expressions.isEmpty()) {
+            for (String exp : expressions) {
+                sb.append("    if (").append(exp).append(") {\n");
                 sb.append("        return;\n");
                 sb.append("    }\n");
             }
 
-            sb.append("    throw new IllegalArgumentException(String.format(\"Invalid value %s does not match any required ranges\", value));\n");
+            sb.append("    throw new IllegalArgumentException(String.format(\"Invalid range: %s, expected: ")
+              .append(createRangeString(constraints)).append(".\", value));\n");
         }
 
-        sb.append("}\n\n");
+        sb.append("}\n");
 
         return sb.toString();
     }
index 3f09cff106a4fbf03c6e188b3bf09bae851c50a5..31c651a059a847777d2ec8ca0b7a4f605da1aafe 100644 (file)
@@ -13,7 +13,10 @@ import com.google.common.collect.ImmutableMap.Builder;
 import java.util.Collection;
 import java.util.Map;
 import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
+import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType;
+import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty;
+import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject;
+import org.opendaylight.yangtools.sal.binding.model.api.Type;
 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -43,8 +46,34 @@ abstract class AbstractRangeGenerator<T extends Number & Comparable<T>> {
         this.type = Preconditions.checkNotNull(typeClass);
     }
 
-    static AbstractRangeGenerator<?> getInstance(final String canonicalName) {
-        return GENERATORS.get(canonicalName);
+    // We need to walk up the GTO tree to get the root and then return its 'value' property
+    private static Type javaTypeForGTO(final GeneratedTransferObject gto) {
+        GeneratedTransferObject rootGto = gto;
+        while (rootGto.getSuperType() != null) {
+            rootGto = rootGto.getSuperType();
+        }
+
+        LOG.debug("Root GTO of {} is {}", rootGto, gto);
+        for (GeneratedProperty s : rootGto.getProperties()) {
+            if ("value".equals(s.getName())) {
+                return s.getReturnType();
+            }
+        }
+
+        throw new IllegalArgumentException(String.format("Failed to resolve GTO {} root {} to a Java type, properties are {}", gto, rootGto));
+    }
+
+    static AbstractRangeGenerator<?> forType(@Nonnull final Type type) {
+        final Type javaType;
+        if (type instanceof GeneratedTransferObject) {
+            javaType = javaTypeForGTO((GeneratedTransferObject) type);
+            LOG.debug("Resolved GTO {} to concrete type {}", type, javaType);
+        } else {
+            javaType = type;
+        }
+
+        Preconditions.checkArgument(javaType instanceof ConcreteType, "Unsupported type %s", type);
+        return GENERATORS.get(javaType.getFullyQualifiedName());
     }
 
     /**
@@ -65,13 +94,28 @@ abstract class AbstractRangeGenerator<T extends Number & Comparable<T>> {
         return type.getName();
     }
 
+    /**
+     * Return the value in the native type from a particular Number instance.
+     *
+     * @param value Value as a Number
+     * @return Value in native format.
+     */
     protected final @Nonnull T getValue(final Number value) {
         if (type.isInstance(value)) {
             return type.cast(value);
         }
 
-        LOG.info("Number class conversion from {} to {} may lose precision of {}", value.getClass(), type, value);
-        return convert(value);
+        LOG.debug("Converting value {} from {} to {}", value, value.getClass(), type);
+        final T ret = convert(value);
+
+        // Check if the conversion lost any precision by performing conversion the other way around
+        final AbstractRangeGenerator<?> gen = GENERATORS.get(value.getClass().getName());
+        final Number check = gen.convert(ret);
+        if (!value.equals(check)) {
+            LOG.warn("Number class conversion from {} to {} truncated value {} to {}", value.getClass(), type, value, ret);
+        }
+
+        return ret;
     }
 
     // FIXME: Once BUG-3399 is fixed, we should never need this
@@ -81,33 +125,29 @@ abstract class AbstractRangeGenerator<T extends Number & Comparable<T>> {
     /**
      * Format a value into a Java-compilable expression which results in the appropriate
      * type.
-     * @param number Number value
+     * @param value Number value
      * @return Java language string representation
      */
-    protected abstract @Nonnull String format(final T number);
+    protected abstract @Nonnull String format(final T value);
 
     /**
      * Generate the checker method source code.
      * @param checkerName Name of the checker method.
-     * @param restrictions Restrictions which need to be applied.
+     * @param constraints Restrictions which need to be applied.
      * @return Method source code.
      */
-    protected abstract @Nonnull String generateRangeCheckerImplementation(@Nonnull final String checkerName, @Nonnull final Collection<RangeConstraint> restrictions);
+    protected abstract @Nonnull String generateRangeCheckerImplementation(@Nonnull final String checkerName, @Nonnull final Collection<RangeConstraint> constraints);
 
     private static String rangeCheckerName(final String member) {
-        final StringBuilder sb = new StringBuilder("check");
-        if (member != null) {
-            sb.append(member);
-        }
-        return sb.append("Range").toString();
+        return "check" + member + "Range";
     }
 
-    String generateRangeChecker(@Nullable final String member, @Nonnull final Collection<RangeConstraint> restrictions) {
-        Preconditions.checkArgument(!restrictions.isEmpty(), "Restrictions may not be empty");
-        return generateRangeCheckerImplementation(rangeCheckerName(member), restrictions);
+    String generateRangeChecker(@Nonnull final String member, @Nonnull final Collection<RangeConstraint> constraints) {
+        Preconditions.checkArgument(!constraints.isEmpty(), "Restrictions may not be empty");
+        return generateRangeCheckerImplementation(rangeCheckerName(member), constraints);
     }
 
-    String generateRangeCheckerCall(@Nullable final String member, @Nonnull final String valueReference) {
+    String generateRangeCheckerCall(@Nonnull final String member, @Nonnull final String valueReference) {
         return rangeCheckerName(member) + '(' + valueReference + ");\n";
     }
 }
index 020da1e46084cf5462305e01972846acb9e1cd40..8a23cc96e617ff849a6aa4a9437981a1a513b248 100644 (file)
@@ -18,8 +18,8 @@ abstract class AbstractSubIntegerRangeGenerator<T extends Number & Comparable<T>
     }
 
     @Override
-    protected final String format(final T number) {
+    protected final String format(final T value) {
         // Make sure the number constant is cast to the corresponding primitive type
-        return '(' + castType + ')' + number;
+        return '(' + castType + ')' + value;
     }
 }
index 30eef9f89091230ba583b2ae05cb73e06e448a81..e466ff705876b0ffa02dc13307b342172b343431 100644 (file)
@@ -16,19 +16,19 @@ final class BigDecimalRangeGenerator extends AbstractBigRangeGenerator<BigDecima
     }
 
     @Override
-    protected String format(final BigDecimal number) {
-        if (BigDecimal.ZERO.equals(number)) {
+    protected String format(final BigDecimal value) {
+        if (BigDecimal.ZERO.equals(value)) {
             return "java.math.BigDecimal.ZERO";
         }
-        if (BigDecimal.ONE.equals(number)) {
+        if (BigDecimal.ONE.equals(value)) {
             return "java.math.BigDecimal.ONE";
         }
-        if (BigDecimal.TEN.equals(number)) {
+        if (BigDecimal.TEN.equals(value)) {
             return "java.math.BigDecimal.TEN";
         }
 
         // FIXME: can we do something better?
-        return "new java.math.BigDecimal(\"" + number + "\")";
+        return "new java.math.BigDecimal(\"" + value + "\")";
     }
 
     @Override
index 7c2a4c95dbb6977848823ec0f70db6cb1a17225e..cac96713d7cc4d6061ad903aab5ef6a7022e1dff 100644 (file)
@@ -15,23 +15,23 @@ final class BigIntegerRangeGenerator extends AbstractBigRangeGenerator<BigIntege
     }
 
     @Override
-    protected String format(final BigInteger number) {
-        if (BigInteger.ZERO.equals(number)) {
+    protected String format(final BigInteger value) {
+        if (BigInteger.ZERO.equals(value)) {
             return "java.math.BigInteger.ZERO";
         }
-        if (BigInteger.ONE.equals(number)) {
+        if (BigInteger.ONE.equals(value)) {
             return "java.math.BigInteger.ONE";
         }
-        if (BigInteger.TEN.equals(number)) {
+        if (BigInteger.TEN.equals(value)) {
             return "java.math.BigInteger.TEN";
         }
 
         // Check for conversion to long
-        final long l = number.longValue();
-        if (number.equals(BigInteger.valueOf(l))) {
+        final long l = value.longValue();
+        if (value.equals(BigInteger.valueOf(l))) {
             return "java.math.BigInteger.valueOf(" + l + "L)";
         } else {
-            return "new java.math.BigInteger(\"" + number.toString() + "\")";
+            return "new java.math.BigInteger(\"" + value.toString() + "\")";
         }
     }
 
index fb23ec8e504c56dec39fd2c9fbd4b56627763ab1..ffb9990528134619693b75965e13e7d063418c51 100644 (file)
@@ -13,8 +13,8 @@ final class IntegerRangeGenerator extends AbstractPrimitiveRangeGenerator<Intege
     }
 
     @Override
-    protected final String format(final Integer number) {
-        return number.toString();
+    protected final String format(final Integer value) {
+        return value.toString();
     }
 
     @Override
index 7a26bee2d01ae4227230abc9626dd39ce6abe68f..0afba8404827e8d88d92f6e5b4b9343f0716b254 100644 (file)
@@ -14,8 +14,8 @@ final class LongRangeGenerator extends AbstractPrimitiveRangeGenerator<Long> {
     }
 
     @Override
-    protected String format(final Long number) {
-        return number.toString() + 'L';
+    protected String format(final Long value) {
+        return value.toString() + 'L';
     }
 
     @Override