Split out BaseTypeFactories 71/86071/5
authorRobert Varga <robert.varga@pantheon.tech>
Fri, 29 Nov 2019 15:27:50 +0000 (16:27 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Sat, 30 Nov 2019 11:15:44 +0000 (12:15 +0100)
Instantiating BaseTypes with complex definition is a prime example
of where either a builder or a factory pattern is appropriate. Given
we are dealing with a few types with very few customizations, this
patch applies the factory pattern to the problem.

This makes all BaseType subclasses properly immutable and has the
benefit of squashing instances to singletons when they are not
otherwise customized.

Change-Id: I86d09324624c1e5b1a73a0be31269f2233fc15b3
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
library/impl/src/main/java/org/opendaylight/ovsdb/lib/schema/BaseType.java
library/impl/src/main/java/org/opendaylight/ovsdb/lib/schema/BaseTypeFactory.java [new file with mode: 0644]
library/impl/src/main/java/org/opendaylight/ovsdb/lib/schema/BooleanBaseType.java
library/impl/src/main/java/org/opendaylight/ovsdb/lib/schema/IntegerBaseType.java
library/impl/src/main/java/org/opendaylight/ovsdb/lib/schema/RealBaseType.java
library/impl/src/main/java/org/opendaylight/ovsdb/lib/schema/StringBaseType.java
library/impl/src/main/java/org/opendaylight/ovsdb/lib/schema/UuidBaseType.java

index 3c37aa2a726f7b112fa7e84162de9512125b4516..e5eb29604961700363c92c3b58a732250941599b 100644 (file)
@@ -33,12 +33,11 @@ public abstract class BaseType<E extends BaseType<E>> {
         }
         if (type.isObject()) {
             //json like  {"type" : "string", "enum": ["set", ["access", "native-tagged"]]}" for key or value
-            final JsonNode nestedType = type.get("type");
-            if (nestedType != null) {
-                final BaseType ret = builderFor(nestedType.asText());
-                if (ret != null) {
-                    ret.fillConstraints(type);
-                    return ret;
+            final JsonNode typeName = type.get("type");
+            if (typeName != null) {
+                final BaseTypeFactory<?> factory = factoryFor(typeName.asText());
+                if (factory != null) {
+                    return factory.create(type);
                 }
             }
         }
@@ -46,8 +45,6 @@ public abstract class BaseType<E extends BaseType<E>> {
         return null;
     }
 
-    abstract void fillConstraints(JsonNode type);
-
     public abstract Object toValue(JsonNode value);
 
     public abstract void validate(Object value);
@@ -71,23 +68,22 @@ public abstract class BaseType<E extends BaseType<E>> {
         }
     }
 
-    // Create a new instance for customization
-    private static BaseType builderFor(final String type) {
+    // Find a factory for custom instantiation
+    private static BaseTypeFactory<?> factoryFor(final String type) {
         switch (type) {
             case "boolean":
-                return new BooleanBaseType();
+                return BooleanBaseType.FACTORY;
             case "integer":
-                return new IntegerBaseType();
+                return IntegerBaseType.FACTORY;
             case "real":
-                return new RealBaseType();
+                return RealBaseType.FACTORY;
             case "string":
-                return new StringBaseType();
+                return StringBaseType.FACTORY;
             case "uuid":
-                return new UuidBaseType();
+                return UuidBaseType.FACTORY;
             default:
                 LOG.debug("Unknown base type {}", type);
                 return null;
         }
     }
-
 }
diff --git a/library/impl/src/main/java/org/opendaylight/ovsdb/lib/schema/BaseTypeFactory.java b/library/impl/src/main/java/org/opendaylight/ovsdb/lib/schema/BaseTypeFactory.java
new file mode 100644 (file)
index 0000000..299ec0c
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.ovsdb.lib.schema;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.List;
+
+abstract class BaseTypeFactory<T extends BaseType<T>> {
+
+    abstract T create(JsonNode typeDefinition);
+
+    abstract static class WithEnum<T extends BaseType<T>, N extends Number> extends BaseTypeFactory<T> {
+
+        final ImmutableSet<N> parseEnums(final JsonNode node) {
+            final List<N> tmp = new ArrayList<>();
+            for (JsonNode enm : node.get(1)) {
+                tmp.add(getEnumValue(enm));
+            }
+            return ImmutableSet.copyOf(tmp);
+        }
+
+        abstract N getEnumValue(JsonNode jsonEnum);
+    }
+}
index c9d893a240bf0275864d13271d6b2bf1434d5ea4..b3ed8743fc01099106703e57c981495cab9e6991 100644 (file)
@@ -9,13 +9,15 @@ package org.opendaylight.ovsdb.lib.schema;
 
 import com.fasterxml.jackson.databind.JsonNode;
 
-final class BooleanBaseType extends BaseType {
+final class BooleanBaseType extends BaseType<BooleanBaseType> {
     static final BooleanBaseType SINGLETON = new BooleanBaseType();
-
-    @Override
-    void fillConstraints(final JsonNode node) {
-        //no op
-    }
+    static final BaseTypeFactory<BooleanBaseType> FACTORY = new BaseTypeFactory<>() {
+        @Override
+        BooleanBaseType create(final JsonNode typeDefinition) {
+            // No constraints possible, just return the singleton
+            return SINGLETON;
+        }
+    };
 
     @Override
     public Object toValue(final JsonNode value) {
index de1d874ff4294fd92a68f14b25c169b3137e3a61..834b9442023865ccdcd3a7f447f069c783d9be47 100644 (file)
@@ -8,31 +8,21 @@
 package org.opendaylight.ovsdb.lib.schema;
 
 import com.fasterxml.jackson.databind.JsonNode;
-import java.util.HashSet;
-import java.util.Optional;
+import com.google.common.collect.ImmutableSet;
 import java.util.Set;
 
 final class IntegerBaseType extends BaseType<IntegerBaseType> {
-    static final IntegerBaseType SINGLETON = new IntegerBaseType();
+    static final IntegerBaseType SINGLETON = new IntegerBaseType(Long.MIN_VALUE, Long.MAX_VALUE, null);
+    static final BaseTypeFactory<IntegerBaseType> FACTORY = new Factory();
 
-    private long min = Long.MIN_VALUE;
-    private long max = Long.MAX_VALUE;
-    private Set<Integer> enums;
+    private final long min;
+    private final long max;
+    private final ImmutableSet<Integer> enums;
 
-    @Override
-    void fillConstraints(final JsonNode type) {
-        JsonNode typeMaxNode = type.get("maxInteger");
-        if (typeMaxNode != null) {
-            max = typeMaxNode.asLong();
-        }
-        JsonNode typeMinNode = type.get("minInteger");
-        if (typeMinNode != null) {
-            min = typeMinNode.asLong();
-        }
-        Optional<Set<Integer>> typeEnumsOpt = populateEnum(type);
-        if (typeEnumsOpt.isPresent()) {
-            enums = typeEnumsOpt.get();
-        }
+    IntegerBaseType(final long min, final long max, final ImmutableSet<Integer> enums) {
+        this.min = min;
+        this.max = max;
+        this.enums = enums;
     }
 
     @Override
@@ -45,19 +35,6 @@ final class IntegerBaseType extends BaseType<IntegerBaseType> {
 
     }
 
-    private static Optional<Set<Integer>> populateEnum(final JsonNode node) {
-        if (node.has("enum")) {
-            Set<Integer> nodesEnums = new HashSet<>();
-            JsonNode anEnum = node.get("enum").get(1);
-            for (JsonNode enm : anEnum) {
-                nodesEnums.add(enm.asInt());
-            }
-            return Optional.of(nodesEnums);
-        } else {
-            return Optional.empty();
-        }
-    }
-
     public long getMin() {
         return min;
     }
@@ -112,4 +89,26 @@ final class IntegerBaseType extends BaseType<IntegerBaseType> {
         }
         return true;
     }
-}
\ No newline at end of file
+
+    private static final class Factory extends BaseTypeFactory.WithEnum<IntegerBaseType, Integer> {
+        @Override
+        IntegerBaseType create(final JsonNode typeDefinition) {
+            final JsonNode typeMaxNode = typeDefinition.get("maxInteger");
+            final long max = typeMaxNode != null ? typeMaxNode.asLong() : Long.MAX_VALUE;
+
+            final JsonNode typeMinNode = typeDefinition.get("minInteger");
+            final long min = typeMinNode != null ? typeMinNode.asLong() : Long.MIN_VALUE;
+
+            final JsonNode typeEnumNode = typeDefinition.get("enum");
+            final ImmutableSet<Integer> enums = typeEnumNode != null ? parseEnums(typeEnumNode) : null;
+
+            return min == Long.MIN_VALUE && max == Long.MAX_VALUE && enums == null ? SINGLETON
+                    : new IntegerBaseType(min, max, enums);
+        }
+
+        @Override
+        Integer getEnumValue(final JsonNode jsonEnum) {
+            return jsonEnum.asInt();
+        }
+    }
+}
index 199a8fd54906134f21101190c632e0cd1af02298..569aaff3f11a3817dcbd7a07798103937803b766 100644 (file)
@@ -8,31 +8,21 @@
 package org.opendaylight.ovsdb.lib.schema;
 
 import com.fasterxml.jackson.databind.JsonNode;
-import java.util.HashSet;
-import java.util.Optional;
+import com.google.common.collect.ImmutableSet;
 import java.util.Set;
 
 final class RealBaseType extends BaseType<RealBaseType> {
-    static final RealBaseType SINGLETON = new RealBaseType();
+    static final RealBaseType SINGLETON = new RealBaseType(Double.MIN_VALUE, Double.MAX_VALUE, null);
+    static final BaseTypeFactory<RealBaseType> FACTORY = new Factory();
 
-    private double min = Double.MIN_VALUE;
-    private double max = Double.MAX_VALUE;
-    private Set<Double> enums;
+    private final double min;
+    private final double max;
+    private final ImmutableSet<Double> enums;
 
-    @Override
-    void fillConstraints(final JsonNode type) {
-        JsonNode typeMaxNode = type.get("maxReal");
-        if (typeMaxNode != null) {
-            max = typeMaxNode.asLong();
-        }
-        JsonNode typeMinNode = type.get("minReal");
-        if (typeMinNode != null) {
-            min = typeMinNode.asLong();
-        }
-        Optional<Set<Double>> typeEnumsOpt = populateEnum(type);
-        if (typeEnumsOpt.isPresent()) {
-            enums = typeEnumsOpt.get();
-        }
+    RealBaseType(final double min, final double max, final ImmutableSet<Double> enums) {
+        this.min = min;
+        this.max = max;
+        this.enums = enums;
     }
 
     @Override
@@ -45,19 +35,6 @@ final class RealBaseType extends BaseType<RealBaseType> {
 
     }
 
-    private static Optional<Set<Double>> populateEnum(final JsonNode node) {
-        if (node.has("enum")) {
-            Set<Double> nodesEnums = new HashSet<>();
-            JsonNode anEnum = node.get("enum").get(1);
-            for (JsonNode enm : anEnum) {
-                nodesEnums.add(enm.asDouble());
-            }
-            return Optional.of(nodesEnums);
-        } else {
-            return Optional.empty();
-        }
-    }
-
     public double getMin() {
         return min;
     }
@@ -115,4 +92,28 @@ final class RealBaseType extends BaseType<RealBaseType> {
         }
         return true;
     }
-}
\ No newline at end of file
+
+    private static final class Factory extends BaseTypeFactory.WithEnum<RealBaseType, Double> {
+        @Override
+        RealBaseType create(final JsonNode typeDefinition) {
+            // FIXME: is asLong() appropriate here?
+            final JsonNode typeMaxNode = typeDefinition.get("maxReal");
+            final double max = typeMaxNode != null ? typeMaxNode.asLong() : Double.MAX_VALUE;
+
+            final JsonNode typeMinNode = typeDefinition.get("minReal");
+            final double min = typeMinNode != null ? typeMinNode.asLong() : Double.MIN_VALUE;
+
+            final JsonNode typeEnumNode = typeDefinition.get("enum");
+            final ImmutableSet<Double> enums = typeEnumNode != null ? parseEnums(typeEnumNode) : null;
+
+            // TODO: improve accuracy here -- requires understanding the FIXME above
+            return typeMinNode == null && typeMaxNode == null && enums == null ? SINGLETON
+                    : new RealBaseType(min, max, enums);
+        }
+
+        @Override
+        Double getEnumValue(final JsonNode jsonEnum) {
+            return jsonEnum.asDouble();
+        }
+    }
+}
index 77680c189364a8f9c9ad28c40ba59ad1635d0f42..eeb32a7635d83c40c197014129984a50dbe257e4 100644 (file)
@@ -8,31 +8,24 @@
 package org.opendaylight.ovsdb.lib.schema;
 
 import com.fasterxml.jackson.databind.JsonNode;
-import java.util.HashSet;
-import java.util.Optional;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Set;
 
 final class StringBaseType extends BaseType<StringBaseType> {
-    static final StringBaseType SINGLETON = new StringBaseType();
+    // FIXME: negative minimum leng
+    static final StringBaseType SINGLETON = new StringBaseType(Integer.MIN_VALUE, Integer.MAX_VALUE, null);
+    static final BaseTypeFactory<StringBaseType> FACTORY = new Factory();
 
-    private int minLength = Integer.MIN_VALUE;
-    private int maxLength = Integer.MAX_VALUE;
-    private Set<String> enums;
+    private final int minLength;
+    private final int maxLength;
+    private final ImmutableSet<String> enums;
 
-    @Override
-    void fillConstraints(final JsonNode type) {
-        JsonNode typeMaxNode = type.get("maxLength");
-        if (typeMaxNode != null) {
-            maxLength = typeMaxNode.asInt();
-        }
-        JsonNode typeMinNode = type.get("minLength");
-        if (typeMinNode != null) {
-            minLength = typeMinNode.asInt();
-        }
-        Optional<Set<String>> typeEnumsOpt = populateEnum(type);
-        if (typeEnumsOpt.isPresent()) {
-            enums = typeEnumsOpt.get();
-        }
+    StringBaseType(final int min, final int max, final ImmutableSet<String> enums) {
+        this.minLength = min;
+        this.maxLength = max;
+        this.enums = enums;
     }
 
     @Override
@@ -45,24 +38,6 @@ final class StringBaseType extends BaseType<StringBaseType> {
 
     }
 
-    private static Optional<Set<String>> populateEnum(final JsonNode node) {
-        if (node.has("enum")) {
-            Set<String> nodesEnums = new HashSet<>();
-            JsonNode enumVal = node.get("enum");
-            if (enumVal.isArray()) {
-                JsonNode anEnum = enumVal.get(1);
-                for (JsonNode enm : anEnum) {
-                    nodesEnums.add(enm.asText());
-                }
-            } else if (enumVal.isTextual()) {
-                nodesEnums.add(enumVal.asText());
-            }
-            return Optional.of(nodesEnums);
-        } else {
-            return Optional.empty();
-        }
-    }
-
     public int getMinLength() {
         return minLength;
     }
@@ -117,4 +92,37 @@ final class StringBaseType extends BaseType<StringBaseType> {
         }
         return true;
     }
+
+    private static final class Factory extends BaseTypeFactory<StringBaseType> {
+        @Override
+        StringBaseType create(final JsonNode typeDefinition) {
+            final JsonNode typeMaxNode = typeDefinition.get("maxLength");
+            final int max = typeMaxNode != null ? typeMaxNode.asInt() : Integer.MAX_VALUE;
+
+            final JsonNode typeMinNode = typeDefinition.get("minLength");
+            final int min = typeMinNode != null ? typeMinNode.asInt() : Integer.MIN_VALUE;
+
+            final JsonNode typeEnumNode = typeDefinition.get("enum");
+            final ImmutableSet<String> enums = typeEnumNode != null ? parseEnums(typeEnumNode) : null;
+
+            return min == Integer.MIN_VALUE && max == Integer.MAX_VALUE && enums == null ? SINGLETON
+                    : new StringBaseType(min, max, enums);
+        }
+
+
+        private static ImmutableSet<String> parseEnums(final JsonNode enumVal) {
+            if (enumVal.isTextual()) {
+                return ImmutableSet.of(enumVal.asText());
+            }
+            if (enumVal.isArray()) {
+                final List<String> tmp = new ArrayList<>();
+                JsonNode anEnum = enumVal.get(1);
+                for (JsonNode enm : anEnum) {
+                    tmp.add(enm.asText());
+                }
+                return ImmutableSet.copyOf(tmp);
+            }
+            return ImmutableSet.of();
+        }
+    }
 }
index 5e88e30d652077b646cbe9d4e944b19575eb20d3..91fcbfd82e41c135940f0e5f2f7f2f6ab9c6ff01 100644 (file)
@@ -15,18 +15,28 @@ final class UuidBaseType extends BaseType<UuidBaseType> {
     // These enum types correspond to JSON values and need to be in lower-case currently
     public enum RefType { strong, weak }
 
-    static final UuidBaseType SINGLETON = new UuidBaseType();
+    static final UuidBaseType SINGLETON = new UuidBaseType(null, null);
+    static final BaseTypeFactory<UuidBaseType> FACTORY = new BaseTypeFactory<>() {
+        @Override
+        UuidBaseType create(final JsonNode typeDefinition) {
+            final JsonNode refTableNode = typeDefinition.get("refTable");
+            final String refTable = refTableNode != null ? refTableNode.asText() : null;
 
-    private String refTable;
-    private UuidBaseType.RefType refType;
+            final JsonNode refTypeJson = typeDefinition.get("refType");
+            final RefType refType = refTypeJson != null ? RefType.valueOf(refTypeJson.asText()) : RefType.strong;
 
-    @Override
-    void fillConstraints(final JsonNode node) {
-        JsonNode refTableNode = node.get("refTable");
-        refTable = refTableNode != null ? refTableNode.asText() : null;
+            // FIXME: this is weird from refTable/refType perspective -- if there is no table, we should not default
+            //        to strong reference and squash to singleton
+            return new UuidBaseType(refTable, refType);
+        }
+    };
+
+    private final String refTable;
+    private final RefType refType;
 
-        JsonNode refTypeJson = node.get("refType");
-        refType = refTypeJson != null ? RefType.valueOf(refTypeJson.asText()) : RefType.strong;
+    UuidBaseType(final String refTable, final RefType refType) {
+        this.refTable = refTable;
+        this.refType = refType;
     }
 
     @Override