BUG-1485: Create LengthGenerator 09/21509/8
authorRobert Varga <rovarga@cisco.com>
Sun, 31 May 2015 11:33:03 +0000 (13:33 +0200)
committerRobert Varga <rovarga@cisco.com>
Mon, 8 Jun 2015 16:32:43 +0000 (18:32 +0200)
LengthGenerator is akin to AbstractRangeGenerator, only simpler. Since
the type of the checked property can only be an integer, we can live
without subclasses. One weirdness is that it needs to support both
String and byte[] fields, but that is easily catered with two distinct
methods.

Change-Id: I6b714b4094c332c18ee210451b3bd1672ca74129
Signed-off-by: Robert Varga <rovarga@cisco.com>
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/LengthGenerator.java [new file with mode: 0644]
code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/TypeUtils.java [new file with mode: 0644]

index 31c651a059a847777d2ec8ca0b7a4f605da1aafe..aaf77677086c6f240a7c6c9b9adb3bf330887305 100644 (file)
@@ -14,8 +14,6 @@ import java.util.Collection;
 import java.util.Map;
 import javax.annotation.Nonnull;
 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;
@@ -46,33 +44,8 @@ abstract class AbstractRangeGenerator<T extends Number & Comparable<T>> {
         this.type = Preconditions.checkNotNull(typeClass);
     }
 
-    // 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);
+        final ConcreteType javaType = TypeUtils.getBaseYangType(type);
         return GENERATORS.get(javaType.getFullyQualifiedName());
     }
 
diff --git a/code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/LengthGenerator.java b/code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/LengthGenerator.java
new file mode 100644 (file)
index 0000000..74745fb
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.sal.java.api.generator;
+
+import com.google.common.collect.Range;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.sal.binding.model.api.Type;
+import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
+
+final class LengthGenerator {
+    private LengthGenerator() {
+        throw new UnsupportedOperationException();
+    }
+
+    private static String lengthCheckerName(final String member) {
+        return "check" + member + "Length";
+    }
+
+    private static final Collection<String> createExpressions(final Collection<LengthConstraint> constraints) {
+        final Collection<String> ret = new ArrayList<>(constraints.size());
+
+        for (LengthConstraint l : constraints) {
+            final StringBuilder sb = new StringBuilder("length >");
+
+            // We have to deal with restrictions being out of integer's range
+            if (l.getMin().longValue() <= Integer.MAX_VALUE) {
+                sb.append('=');
+            }
+            sb.append(' ').append(l.getMin().intValue());
+
+            final int max = l.getMax().intValue();
+            if (max < Integer.MAX_VALUE) {
+                sb.append(" && length <= ").append(max);
+            }
+
+            ret.add(sb.toString());
+        }
+
+        return ret;
+    }
+
+    private static String createLengthString(final Collection<LengthConstraint> constraints) {
+        final List<Range<BigInteger>> ranges = new ArrayList<>(constraints.size());
+
+        for (LengthConstraint c : constraints) {
+            ranges.add(Range.closed(new BigInteger(c.getMin().toString()), new BigInteger(c.getMax().toString())));
+        }
+
+        return ranges.toString();
+    }
+
+    private static String generateArrayLengthChecker(final String member, final Collection<LengthConstraint> constraints) {
+        final StringBuilder sb = new StringBuilder();
+        final Collection<String> expressions = createExpressions(constraints);
+
+        sb.append("private static void ").append(lengthCheckerName(member)).append("(final byte[] value) {\n");
+
+        if (!expressions.isEmpty()) {
+            sb.append("    final int length = value.length;\n");
+
+            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 length: %s, expected: ")
+              .append(createLengthString(constraints)).append(".\", java.util.Arrays.toString(value)));\n");
+        }
+
+        sb.append("}\n");
+
+        return sb.toString();
+    }
+
+    private static String generateStringLengthChecker(final String member, final Collection<LengthConstraint> constraints) {
+        final StringBuilder sb = new StringBuilder();
+        final Collection<String> expressions = createExpressions(constraints);
+
+        sb.append("private static void ").append(lengthCheckerName(member)).append("(final String value) {\n");
+
+        if (!expressions.isEmpty()) {
+            sb.append("    final int length = value.length();\n");
+
+            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 length: %s, expected: ")
+              .append(createLengthString(constraints)).append(".\", value));\n");
+        }
+
+        sb.append("}\n");
+
+        return sb.toString();
+    }
+
+    static String generateLengthChecker(final String member, final Type type, final Collection<LengthConstraint> constraints) {
+        if (TypeUtils.getBaseYangType(type).getName().contains("[")) {
+            return generateArrayLengthChecker(member, constraints);
+        } else {
+            return generateStringLengthChecker(member, constraints);
+        }
+    }
+
+    static String generateLengthCheckerCall(@Nullable final String member, @Nonnull final String valueReference) {
+        return lengthCheckerName(member) + '(' + valueReference + ");\n";
+    }
+}
diff --git a/code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/TypeUtils.java b/code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/TypeUtils.java
new file mode 100644 (file)
index 0000000..928c8e9
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.sal.java.api.generator;
+
+import com.google.common.base.Preconditions;
+import javax.annotation.Nonnull;
+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;
+
+/**
+ * Random utility methods for dealing with {@link Type} objects.
+ */
+final class TypeUtils {
+    private static final String VALUE_PROP = "value";
+
+    private TypeUtils() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Given a {@link Type} object lookup the base Java type which sits at the top
+     * of its type hierarchy.
+     *
+     * @param type Input Type object
+     * @return Resolved {@link ConcreteType} instance.
+     */
+    static ConcreteType getBaseYangType(@Nonnull final Type type) {
+        // Already the correct type
+        if (type instanceof ConcreteType) {
+            return (ConcreteType) type;
+        }
+
+        Preconditions.checkArgument(type instanceof GeneratedTransferObject, "Unsupported type %s", type);
+
+        // Need to walk up the GTO chain to the root
+        GeneratedTransferObject rootGto = (GeneratedTransferObject) type;
+        while (rootGto.getSuperType() != null) {
+            rootGto = rootGto.getSuperType();
+        }
+
+        // Look for the 'value' property and return its type
+        for (GeneratedProperty s : rootGto.getProperties()) {
+            if (VALUE_PROP.equals(s.getName())) {
+                return (ConcreteType) s.getReturnType();
+            }
+        }
+
+        // Should never happen
+        throw new IllegalArgumentException(String.format("Type %s root %s properties %s do not include \"%s\"",
+            type, rootGto, rootGto.getProperties(), VALUE_PROP));
+    }
+}