Add generalized CanonicalValue YANG concept 89/70989/6
authorRobert Varga <robert.varga@pantheon.tech>
Mon, 16 Apr 2018 15:54:17 +0000 (17:54 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Tue, 17 Apr 2018 00:26:49 +0000 (02:26 +0200)
The parsing and validation concepts in DerivedString are really
useful in expressing that a particular value has been validated
to conform to some abstract contract.

This facility will allow us to side-step validation when moving
data between classes, especially when we can prove requried
validation has already been done.

This patch introduces CanonicalValue and support interfaces, so
it can be used more widely.

JIRA: YANGTOOLS-418
Change-Id: Ib3f8723facd60c6737351d863ad0f9cef6c4a5da
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/AbstractCanonicalValueImplementationValidator.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/AbstractCanonicalValueSupport.java [moved from yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/AbstractDerivedStringSupport.java with 60% similarity]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/AbstractCanonicalValueValidator.java [moved from yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/AbstractDerivedStringValidator.java with 59% similarity]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/CachingDerivedString.java
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/CanonicalValue.java [new file with mode: 0644]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/CanonicalValueSupport.java [moved from yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/DerivedStringSupport.java with 61% similarity]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/CanonicalValueValidator.java [moved from yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/DerivedStringValidator.java with 61% similarity]
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/DerivedString.java
yang/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/DerivedStringTest.java

diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/AbstractCanonicalValueImplementationValidator.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/AbstractCanonicalValueImplementationValidator.java
new file mode 100644 (file)
index 0000000..9b3b2b4
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, 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.yangtools.yang.common;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@NonNullByDefault
+abstract class AbstractCanonicalValueImplementationValidator extends ClassValue<Boolean> {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractCanonicalValueImplementationValidator.class);
+
+    @Override
+    protected final Boolean computeValue(final @Nullable Class<?> type) {
+        // Every DerivedString representation class must:
+        checkArgument(CanonicalValue.class.isAssignableFrom(type), "%s is not a DerivedString", type);
+
+        // be non-final and public
+        final int modifiers = type.getModifiers();
+        checkArgument(Modifier.isPublic(modifiers), "%s must be public", type);
+        checkArgument(!Modifier.isFinal(modifiers), "%s must not be final", type);
+
+        // have at least one public or protected constructor (for subclasses)
+        checkArgument(Arrays.stream(type.getDeclaredConstructors()).mapToInt(Constructor::getModifiers)
+            .anyMatch(mod -> Modifier.isProtected(mod) || Modifier.isPublic(mod)),
+            "%s must declare at least one protected or public constructor", type);
+
+        try {
+            // have a non-final non-abstract validator() method
+            final int validator;
+            try {
+                validator = type.getMethod("validator").getModifiers();
+            } catch (NoSuchMethodException e) {
+                throw new IllegalArgumentException(type + " must have a non-abstract non-final validator() method",
+                    e);
+            }
+            checkArgument(!Modifier.isFinal(validator), "%s must not have final validator()", type);
+
+            // have final toCanonicalString(), support(), hashCode() and equals(Object), compare(T) methods
+            checkFinalMethod(type, "toCanonicalString");
+            checkFinalMethod(type, "support");
+            checkFinalMethod(type, "hashCode");
+            checkFinalMethod(type, "equals", Object.class);
+        } catch (SecurityException e) {
+            LOG.warn("Cannot completely validate {}", type, e);
+            return Boolean.FALSE;
+        }
+
+        return Boolean.TRUE;
+    }
+
+    abstract void checkCompareTo(Class<?> type);
+
+    static void checkFinalMethod(final Class<?> type, final String name) {
+        try {
+            checkFinalMethod(type.getMethod(name).getModifiers(), type, name, "");
+        } catch (NoSuchMethodException e) {
+            throw new IllegalArgumentException(type + " must have a final " + name + "() method", e);
+        }
+    }
+
+    static void checkFinalMethod(final Class<?> type, final String name, final Class<?> arg) {
+        final String argName = arg.getSimpleName();
+        try {
+            checkFinalMethod(type.getMethod(name, arg).getModifiers(), type, name, argName);
+        } catch (NoSuchMethodException e) {
+            throw new IllegalArgumentException(type + " must have a final " + name + "(" + argName + ") method", e);
+        }
+    }
+
+    private static void checkFinalMethod(final int modifiers, final Class<?> type, final String name,
+            final String args) {
+        checkArgument(Modifier.isFinal(modifiers), "%s must have a final %s(%s) method", type, name, args);
+    }
+}
\ No newline at end of file
similarity index 60%
rename from yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/AbstractDerivedStringSupport.java
rename to yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/AbstractCanonicalValueSupport.java
index 56e4bac1ea1db6eec198e469e003c2643a05624c..40d45a8e26d79228124bdb67383740bb69d747da 100644 (file)
@@ -13,25 +13,27 @@ import static java.util.Objects.requireNonNull;
 import com.google.common.annotations.Beta;
 import java.lang.reflect.Modifier;
 import javax.annotation.concurrent.ThreadSafe;
+import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 
 /**
- * Base implementation of {@link DerivedStringSupport}. This class should be used as superclass to all implementations
- * of {@link DerivedStringSupport}, as doing so provides a simpler base and enforces some aspects of the subclass.
+ * Base implementation of {@link CanonicalValueSupport}. This class should be used as superclass to all implementations
+ * of {@link CanonicalValueSupport}, as doing so provides a simpler base and enforces some aspects of the subclass.
  *
- * @param <T> derived string type
+ * @param <T> canonical value type
  * @author Robert Varga
  */
 @Beta
 @NonNullByDefault
 @ThreadSafe
-public abstract class AbstractDerivedStringSupport<T extends DerivedString<T>> implements DerivedStringSupport<T> {
-    private static final ClassValue<Boolean> VALIDATED_INSTANCES = new ClassValue<Boolean>() {
+public abstract class AbstractCanonicalValueSupport<T extends CanonicalValue<T>> implements CanonicalValueSupport<T> {
+    private static final ClassValue<Boolean> SUPPORTS = new ClassValue<Boolean>() {
         @Override
         protected Boolean computeValue(final @Nullable Class<?> type) {
             // Every DerivedStringSupport representation class must:
-            checkArgument(DerivedStringSupport.class.isAssignableFrom(type), "%s is not a DerivedStringSupport", type);
+            checkArgument(CanonicalValueSupport.class.isAssignableFrom(type), "%s is not a CanonicalValueSupport",
+                type);
 
             // be final
             final int modifiers = type.getModifiers();
@@ -40,12 +42,19 @@ public abstract class AbstractDerivedStringSupport<T extends DerivedString<T>> i
             return Boolean.TRUE;
         }
     };
+    private static final ClassValue<Boolean> VALUES = new AbstractCanonicalValueImplementationValidator() {
+        @Override
+        void checkCompareTo(@NonNull final Class<?> type) {
+            checkFinalMethod(type, "compareTo", type);
+        }
+    };
 
     private final Class<T> representationClass;
 
-    protected AbstractDerivedStringSupport(final Class<T> representationClass) {
-        this.representationClass = DerivedString.validateRepresentationClass(representationClass);
-        VALIDATED_INSTANCES.get(getClass());
+    protected AbstractCanonicalValueSupport(final Class<T> representationClass) {
+        VALUES.get(representationClass);
+        this.representationClass = representationClass;
+        SUPPORTS.get(getClass());
     }
 
     @Override
similarity index 59%
rename from yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/AbstractDerivedStringValidator.java
rename to yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/AbstractCanonicalValueValidator.java
index fcac4cb68ef57e09515806dee68e161b6f178a38..c1ef9519428ea614eef2b4d28679ddaf4829ed5e 100644 (file)
@@ -12,49 +12,56 @@ import static java.util.Objects.requireNonNull;
 import com.google.common.annotations.Beta;
 import javax.annotation.concurrent.ThreadSafe;
 import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
 
 /**
  * Abstract base class for implementing validators.
  *
- * @param <R> string representation class
- * @param <T> validated string representation class
+ * @param <T> string representation class
+ * @param <V> validated string representation class
  * @author Robert Varga
  */
 @Beta
 @NonNullByDefault
 @ThreadSafe
-public abstract class AbstractDerivedStringValidator<R extends DerivedString<R>, T extends R>
-        implements DerivedStringValidator<R, T> {
-    private final DerivedStringSupport<R> representationSupport;
-    private final Class<T> validatedClass;
+public abstract class AbstractCanonicalValueValidator<T extends DerivedString<T>, V extends T>
+        implements CanonicalValueValidator<T, V> {
+    private static final ClassValue<Boolean> IMPLEMENTATIONS = new AbstractCanonicalValueImplementationValidator() {
+        @Override
+        void checkCompareTo(final Class<?> type) {
+            // Intentional no-op, as we'd need a type capture of the representation
+        }
+    };
 
-    protected AbstractDerivedStringValidator(final DerivedStringSupport<R> representationSupport,
-            final Class<T> validatedClass) {
+    private final CanonicalValueSupport<T> representationSupport;
+    private final Class<V> validatedClass;
+
+    protected AbstractCanonicalValueValidator(final CanonicalValueSupport<T> representationSupport,
+            final Class<V> validatedClass) {
         this.representationSupport = requireNonNull(representationSupport);
-        this.validatedClass = DerivedString.validateValidationClass(validatedClass);
+        IMPLEMENTATIONS.get(validatedClass);
+        this.validatedClass = validatedClass;
     }
 
     @Override
-    public final Class<R> getRepresentationClass() {
+    public final Class<T> getRepresentationClass() {
         return representationSupport.getRepresentationClass();
     }
 
     @Override
-    public final Class<T> getValidatedRepresentationClass() {
+    public final Class<V> getValidatedRepresentationClass() {
         return validatedClass;
     }
 
     @Override
-    public final T validateRepresentation(final R value) {
-        @Nullable T valid;
-        return (valid = castIfValid(value)) != null ? valid : validate(value);
+    public final V validateRepresentation(final T value) {
+        return validatedClass.isAssignableFrom(value.validator().getValidatedRepresentationClass())
+                ? validatedClass.cast(value) : validate(value);
     }
 
     @Override
-    public final T validateRepresentation(final R value, final String canonicalString) {
-        @Nullable T valid;
-        return (valid = castIfValid(value)) != null ? valid : validate(value, requireNonNull(canonicalString));
+    public final V validateRepresentation(final T value, final String canonicalString) {
+        return validatedClass.isAssignableFrom(value.validator().getValidatedRepresentationClass())
+                ? validatedClass.cast(value) : validate(value, requireNonNull(canonicalString));
     }
 
     /**
@@ -66,7 +73,7 @@ public abstract class AbstractDerivedStringValidator<R extends DerivedString<R>,
      * @throws NullPointerException if {@code value} is null
      * @throws IllegalArgumentException if the value does not meet validation criteria.
      */
-    protected T validate(final R value) {
+    protected V validate(final T value) {
         return validate(value, value.toCanonicalString());
     }
 
@@ -80,10 +87,5 @@ public abstract class AbstractDerivedStringValidator<R extends DerivedString<R>,
      * @throws NullPointerException if {@code value} or {@code canonicalString} is null.
      * @throws IllegalArgumentException if the value does not meet validation criteria.
      */
-    protected abstract T validate(R value, String canonicalString);
-
-    private @Nullable T castIfValid(final R value) {
-        return validatedClass.isAssignableFrom(value.validator().getValidatedRepresentationClass())
-                ? validatedClass.cast(value) : null;
-    }
+    protected abstract V validate(T value, String canonicalString);
 }
index 3a15c2a66e281eb7e0e763f47eb2bbeb46b6c8e4..5f16674aab36ca378388bb065b13a2bba32a4f4f 100644 (file)
@@ -8,6 +8,7 @@
 package org.opendaylight.yangtools.yang.common;
 import static java.util.Objects.requireNonNull;
 
+import com.google.common.annotations.Beta;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 
@@ -17,6 +18,7 @@ import org.eclipse.jdt.annotation.Nullable;
  * @param <T> derived string type
  * @author Robert Varga
  */
+@Beta
 @NonNullByDefault
 public abstract class CachingDerivedString<T extends CachingDerivedString<T>> extends DerivedString<T> {
     private static final long serialVersionUID = 1L;
@@ -43,5 +45,4 @@ public abstract class CachingDerivedString<T extends CachingDerivedString<T>> ex
      * @return Canonical string
      */
     protected abstract String computeCanonicalString();
-
 }
diff --git a/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/CanonicalValue.java b/yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/CanonicalValue.java
new file mode 100644 (file)
index 0000000..3e8b470
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, 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.yangtools.yang.common;
+
+import com.google.common.annotations.Beta;
+import java.io.Serializable;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+/**
+ * A typed value in its internal Java representation. Implementations of this interface are required to:
+ * <ul>
+ * <li>be immutable</li>
+ * <li>be {@link Serializable}</li>
+ * <li>accurately define total ordering of values</li>
+ * </ul>
+ *
+ * <p>
+ * Aside from providing the ability to hold a canonical value, this interface and its implementations support carrying
+ * additional information about how the value has been validated -- allowing efficient interchange of already-validated
+ * values between users. {@link #validator()} provides the link to a {@link CanonicalValueValidator} which has declared
+ * the value conform to it. Users can query the validator to establish whether the value needs to be further validated
+ * to conform to their requirement.
+ *
+ * @param <T> Canonical value type
+ * @author Robert Varga
+ */
+@Beta
+@NonNullByDefault
+public interface CanonicalValue<T extends CanonicalValue<T>> extends Comparable<T>, Immutable, Serializable {
+    /**
+     * Return the canonical string representation of this value.
+     *
+     * @return Canonical string
+     */
+    String toCanonicalString();
+
+    /**
+     * Return the {@link CanonicalValue} associated with this type. It can be used to create new instances of this
+     * representation.
+     *
+     * @return A {@link CanonicalValue} instance.
+     */
+    CanonicalValueSupport<T> support();
+
+    /**
+     * Return a {@link CanonicalValueValidator} associated with this value's validated type.
+     *
+     * @return A {@link CanonicalValueValidator} instance.
+     */
+    default CanonicalValueValidator<T, ? extends T> validator() {
+        return support();
+    }
+}
similarity index 61%
rename from yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/DerivedStringSupport.java
rename to yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/CanonicalValueSupport.java
index 56afaf63bfffa7c673bed841552627bc2e0e6d91..df98e07b343486b21f1ae5bfd58682611b768fab 100644 (file)
@@ -14,34 +14,34 @@ import javax.annotation.concurrent.ThreadSafe;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 
 /**
- * Support for a {@link DerivedString} subclasses. An implementation of this interface must be registered
- * in the system and be available from each DerivedString object.
+ * Support for a {@link CanonicalValue} subclasses. An implementation of this interface must be registered
+ * in the system and be available from each CanonicalValue object.
  *
  * <p>
- * Note: never implement this interface directly, subclass {@link AbstractDerivedStringSupport} instead.
+ * Note: never implement this interface directly, subclass {@link AbstractCanonicalValueSupport} instead.
  *
  * <p>
- * This interface allows a {@link DerivedString} to be instantiated from a String. The implementation is expected
+ * This interface allows a {@link CanonicalValue} to be instantiated from a String. The implementation is expected
  * to perform all checks implied by the corresponding YANG data model.
  *
- * @param <R> derived string representation
+ * @param <T> canonical value type
  * @author Robert Varga
  */
 @Beta
 @NonNullByDefault
 @ThreadSafe
-public interface DerivedStringSupport<R extends DerivedString<R>> extends DerivedStringValidator<R, R> {
+public interface CanonicalValueSupport<T extends CanonicalValue<T>> extends CanonicalValueValidator<T, T> {
     /**
      * Create a instance for a string representation. Implementations of this method are required to perform checks
      * equivalent to the YANG data model restrictions attached to the corresponding YANG type. Non-canonical format
      * strings must be accepted and result in objects equal to objects obtained from the corresponding canonical format.
      *
      * @param str String representation
-     * @return A {@link DerivedString} instance.
-     * @throws NullPointerException if str is null
-     * @throws IllegalArgumentException if str does not contain a valid representation
+     * @return A {@link CanonicalValue} instance.
+     * @throws NullPointerException if {@code str} is null
+     * @throws IllegalArgumentException if {@code str} does not contain a valid representation
      */
-    R fromString(String str);
+    T fromString(String str);
 
     /**
      * Create a instance for the canonical string representation. Implementations of this method may perform
@@ -49,11 +49,11 @@ public interface DerivedStringSupport<R extends DerivedString<R>> extends Derive
      * is detected.
      *
      * @param str String representation
-     * @return A {@link DerivedString} instance.
-     * @throws NullPointerException if str is null
-     * @throws IllegalArgumentException if str does not contain canonical representation
+     * @return A {@link CanonicalValue} instance.
+     * @throws NullPointerException if {@code str} is null
+     * @throws IllegalArgumentException if {@code str} does not contain canonical representation
      */
-    default R fromCanonicalString(final String str) {
+    default T fromCanonicalString(final String str) {
         return fromString(requireNonNull(str));
     }
 
@@ -63,7 +63,7 @@ public interface DerivedStringSupport<R extends DerivedString<R>> extends Derive
      * @return This instance cast to specified type
      */
     @SuppressWarnings("unchecked")
-    default <X extends DerivedString<X>> DerivedStringSupport<X> unsafe() {
-        return (DerivedStringSupport<X>) this;
+    default <X extends CanonicalValue<X>> CanonicalValueSupport<X> unsafe() {
+        return (CanonicalValueSupport<X>) this;
     }
 }
similarity index 61%
rename from yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/DerivedStringValidator.java
rename to yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/CanonicalValueValidator.java
index e5853287473f5217bfe1e408fd74a2a6d2d6ac3f..7aaccdf0687d907ef2b90d940165c38824dff206 100644 (file)
@@ -11,39 +11,39 @@ import com.google.common.annotations.Beta;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 
 /**
- * YANG string representation validator. Implementations of this interface can perform further validation of
- * representation state such that it conforms to a YANG string type derived from a type with a {@link DerivedString}
- * representation class.
+ * {@link CanonicalValue} validator interface. Implementations of this interface can perform further validation of
+ * representation state such that it conforms to a YANG type derived from a type with a {@link CanonicalValue}
+ * representation.
  *
  * <p>
- * Note: this interface should not be directly implemented. Use {@link AbstractDerivedStringValidator} instead.
+ * Note: this interface should not be directly implemented. Use {@link AbstractCanonicalValueValidator} instead.
  *
- * @param <R> string representation class
- * @param <T> validated string representation class
+ * @param <T> canonical value type
+ * @param <V> validated canonical value type
  * @author Robert Varga
  */
 @Beta
 @NonNullByDefault
-public interface DerivedStringValidator<R extends DerivedString<R>, T extends R> {
+public interface CanonicalValueValidator<T extends CanonicalValue<T>, V extends T> {
     /**
-     * Returns the instantiated representation class. The representation class is a {@link DerivedString} which
-     * understands the semantics of modeled data and has some internal representation of it. All {@link DerivedString}s
+     * Returns the instantiated representation class. The representation class is a {@link CanonicalValue} which
+     * understands the semantics of modeled data and has some internal representation of it. All {@link CanonicalValue}s
      * which share the same representation class are considered equal if their internal state would result in the
      * same canonical string representation as defined by the YANG data model.
      *
-     * @return Representation {@link DerivedString} class.
+     * @return Representation {@link CanonicalValue} class.
      */
-    Class<R> getRepresentationClass();
+    Class<T> getRepresentationClass();
 
     /**
      * Return the class which captures the fact it was validated by this validator.
      *
      * @return Validated capture of the representation class.
      */
-    Class<T> getValidatedRepresentationClass();
+    Class<V> getValidatedRepresentationClass();
 
     /**
-     * Validate a {@link DerivedString} representation. Implementations should override this method if they can
+     * Validate a {@link CanonicalValue} representation. Implementations should override this method if they can
      * provide a validation algorithm which does not rely on canonical strings but works on representation state only.
      *
      * @param value Representation value
@@ -51,14 +51,14 @@ public interface DerivedStringValidator<R extends DerivedString<R>, T extends R>
      * @throws NullPointerException if {@code value} is null
      * @throws IllegalArgumentException if the value does not meet validation criteria.
      */
-    default T validateRepresentation(final R value) {
+    default V validateRepresentation(final T value) {
         return validateRepresentation(value, value.toCanonicalString());
     }
 
     /**
-     * Validate a {@link DerivedString} representation. Implementations can chose whether they operate on representation
-     * state or canonical string -- both are considered equivalent. Users should call this method if they have
-     * a representation readily available.
+     * Validate a {@link CanonicalValue} representation. Implementations can chose whether they operate on
+     * representation state or canonical string -- both are considered equivalent. Users should call this method if they
+     * have a representation readily available.
      *
      * @param value Representation value
      * @param canonicalString Canonical string matching the representation value
@@ -66,5 +66,5 @@ public interface DerivedStringValidator<R extends DerivedString<R>, T extends R>
      * @throws NullPointerException if {@code value} or {@code canonicalString} is null.
      * @throws IllegalArgumentException if the value does not meet validation criteria.
      */
-    T validateRepresentation(R value, String canonicalString);
+    V validateRepresentation(T value, String canonicalString);
 }
index edfcc6046d60f8fa2ddb7365253e675d84f14325..a4154b1b580271bffb3d05e49c2a53d65bd39c7e 100644 (file)
@@ -7,20 +7,10 @@
  */
 package org.opendaylight.yangtools.yang.common;
 
-import static com.google.common.base.Preconditions.checkArgument;
-
 import com.google.common.annotations.Beta;
-import java.io.Serializable;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Modifier;
-import java.util.Arrays;
 import javax.annotation.concurrent.ThreadSafe;
-import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.yangtools.concepts.Immutable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * Abstract base class for objects which are string-equivalent to canonical string representation specified
@@ -28,12 +18,6 @@ import org.slf4j.LoggerFactory;
  * {@link #equals(Object)} contracts based on implementation particulars.
  *
  * <p>
- * Since YANG validation works on top of strings, which in itself is expensive and this class provides storage which
- * is potentially not based on strings, its design combines 'representation' and 'validated to match constraints'
- * aspects of a YANG type derived from string. To achieve that it cooperates with {@link DerivedStringValidator} and
- * {@link DerivedStringSupport}.
- *
- * <p>
  * Given the following YANG snippet:
  * <pre>
  *     typedef foo {
@@ -61,11 +45,11 @@ import org.slf4j.LoggerFactory;
  *         with a getInstance() method.</li>
  *     <li>{@code public class BarDerivedString extends FooDerivedString}, which overrides {@link #validator()} to
  *         indicate its contents have been validated to conform to bar -- it does that by returning the singleton
- *         instance of {@code BarDerivedStringValidator}.
+ *         instance of {@code BarDerivedStringValidator}.</li>
  *     <li>{@code public final class BarDerivedStringValidator extends DerivedStringValidator<FooDerivedString,
  *         BarDerivedString}. This method needs to notably implement
- *         {@link DerivedStringValidator#validateRepresentation(DerivedString)} to hand out BarDerivedString instances.
- *         This class needs to be a singleton with a getInstance() method, too.</li>
+ *         {@link CanonicalValueValidator#validateRepresentation(CanonicalValue)} to hand out BarDerivedString
+ *         instances. This class needs to be a singleton with a getInstance() method, too.</li>
  * </ul>
  * Since {@code baz} is not defining any new restrictions, all instances of FooDerivedString are valid for it and we
  * do not have to define any additional support.
@@ -74,118 +58,15 @@ import org.slf4j.LoggerFactory;
  * It is important for {@link DerivedString} subclasses not to be final because any YANG type can be further extended
  * and adding a final class in that hierarchy would prevent a proper class from being defined.
  *
- * @param <R> derived string representation
+ * @param <T> derived string representation
  * @author Robert Varga
  */
 @Beta
 @NonNullByDefault
 @ThreadSafe
-public abstract class DerivedString<R extends DerivedString<R>> implements Comparable<R>, Immutable, Serializable {
-    private abstract static class AbstractValidator extends ClassValue<Boolean> {
-        private static final Logger LOG = LoggerFactory.getLogger(AbstractValidator.class);
-
-        @Override
-        protected final Boolean computeValue(final @Nullable Class<?> type) {
-            // Every DerivedString representation class must:
-            checkArgument(DerivedString.class.isAssignableFrom(type), "%s is not a DerivedString", type);
-
-            // be non-final and public
-            final int modifiers = type.getModifiers();
-            checkArgument(Modifier.isPublic(modifiers), "%s must be public", type);
-            checkArgument(!Modifier.isFinal(modifiers), "%s must not be final", type);
-
-            // have at least one public or protected constructor (for subclasses)
-            checkArgument(Arrays.stream(type.getDeclaredConstructors()).mapToInt(Constructor::getModifiers)
-                .anyMatch(mod -> Modifier.isProtected(mod) || Modifier.isPublic(mod)),
-                "%s must declare at least one protected or public constructor", type);
-
-            try {
-                // have a non-final non-abstract validator() method
-                final int validator;
-                try {
-                    validator = type.getMethod("validator").getModifiers();
-                } catch (NoSuchMethodException e) {
-                    throw new IllegalArgumentException(type + " must have a non-abstract non-final validator() method",
-                        e);
-                }
-                checkArgument(!Modifier.isFinal(validator), "%s must not have final validator()", type);
-
-                // have final toCanonicalString(), support(), hashCode() and equals(Object), compare(T) methods
-                checkFinalMethod(type, "toCanonicalString");
-                checkFinalMethod(type, "support");
-                checkFinalMethod(type, "hashCode");
-                checkFinalMethod(type, "equals", Object.class);
-            } catch (SecurityException e) {
-                LOG.warn("Cannot completely validate {}", type, e);
-                return Boolean.FALSE;
-            }
-
-            return Boolean.TRUE;
-        }
-
-        abstract void checkCompareTo(Class<?> type);
-
-        private static void checkFinalMethod(final Class<?> type, final String name) {
-            try {
-                checkFinalMethod(type.getMethod(name).getModifiers(), type, name, "");
-            } catch (NoSuchMethodException e) {
-                throw new IllegalArgumentException(type + " must have a final " + name + "() method", e);
-            }
-        }
-
-        static void checkFinalMethod(final Class<?> type, final String name, final Class<?> arg) {
-            final String argName = arg.getSimpleName();
-            try {
-                checkFinalMethod(type.getMethod(name, arg).getModifiers(), type, name, argName);
-            } catch (NoSuchMethodException e) {
-                throw new IllegalArgumentException(type + " must have a final " + name + "(" + argName + ") method", e);
-            }
-        }
-
-        private static void checkFinalMethod(final int modifiers, final Class<?> type, final String name,
-                final String args) {
-            checkArgument(Modifier.isFinal(modifiers), "%s must have a final %s(%s) method", type, name, args);
-        }
-    }
-
-    private static final ClassValue<Boolean> VALIDATED_REPRESENTATIONS = new AbstractValidator() {
-        @Override
-        void checkCompareTo(@NonNull final Class<?> type) {
-            checkFinalMethod(type, "compareTo", type);
-        }
-    };
-    private static final ClassValue<Boolean> VALIDATED_VALIDATIONS = new AbstractValidator() {
-        @Override
-        void checkCompareTo(@NonNull final Class<?> type) {
-            // Intentional no-op, as we'd need a type capture of the representation
-        }
-    };
+public abstract class DerivedString<T extends DerivedString<T>> implements CanonicalValue<T> {
     private static final long serialVersionUID = 1L;
 
-    /**
-     * Return the canonical string representation of this object's value.
-     *
-     * @return Canonical string
-     */
-    public abstract String toCanonicalString();
-
-    /**
-     * Return the {@link DerivedStringSupport} associated with this type. It can be used to create new instances of this
-     * representation.
-     *
-     * @return A {@link DerivedStringSupport} instance.
-     */
-    public abstract DerivedStringSupport<R> support();
-
-    /**
-     * Return a {@link DerivedStringValidator} associated with this value's validated type.
-     *
-     * @return A {@link DerivedStringValidator} instance.
-     */
-    public DerivedStringValidator<R, ? extends R> validator() {
-        return support();
-    }
-
     @Override
     public abstract int hashCode();
 
@@ -196,16 +77,4 @@ public abstract class DerivedString<R extends DerivedString<R>> implements Compa
     public final String toString() {
         return toCanonicalString();
     }
-
-    static <T extends DerivedString<?>> Class<T> validateRepresentationClass(final Class<T> representationClass) {
-        // Validation is reflective, cache its result
-        VALIDATED_REPRESENTATIONS.get(representationClass);
-        return representationClass;
-    }
-
-    static <T extends DerivedString<?>> Class<T> validateValidationClass(final Class<T> representationClass) {
-        // Validation is reflective, cache its result
-        VALIDATED_VALIDATIONS.get(representationClass);
-        return representationClass;
-    }
 }
index df270828ee7211fc34ad33ff2d521708d41bbdbe..97d8bb00c90d6c92ca0fa6f1f0956a682f6a0ea4 100644 (file)
@@ -30,7 +30,7 @@ public class DerivedStringTest {
         }
 
         @Override
-        public final DerivedStringSupport<EagerDerivedString> support() {
+        public final CanonicalValueSupport<EagerDerivedString> support() {
             return EAGER_SUPPORT;
         }
 
@@ -74,7 +74,7 @@ public class DerivedStringTest {
         }
 
         @Override
-        public final DerivedStringSupport<LazyDerivedString> support() {
+        public final CanonicalValueSupport<LazyDerivedString> support() {
             return LAZY_SUPPORT;
         }
 
@@ -101,7 +101,7 @@ public class DerivedStringTest {
         }
     }
 
-    public static final class EagerDerivedStringSupport extends AbstractDerivedStringSupport<EagerDerivedString> {
+    public static final class EagerDerivedStringSupport extends AbstractCanonicalValueSupport<EagerDerivedString> {
         EagerDerivedStringSupport() {
             super(EagerDerivedString.class);
         }
@@ -112,7 +112,7 @@ public class DerivedStringTest {
         }
     }
 
-    public static final class LazyDerivedStringSupport extends AbstractDerivedStringSupport<LazyDerivedString> {
+    public static final class LazyDerivedStringSupport extends AbstractCanonicalValueSupport<LazyDerivedString> {
         LazyDerivedStringSupport() {
             super(LazyDerivedString.class);
         }
@@ -123,8 +123,8 @@ public class DerivedStringTest {
         }
     }
 
-    private static final DerivedStringSupport<EagerDerivedString> EAGER_SUPPORT = new EagerDerivedStringSupport();
-    private static final DerivedStringSupport<LazyDerivedString> LAZY_SUPPORT = new LazyDerivedStringSupport();
+    private static final CanonicalValueSupport<EagerDerivedString> EAGER_SUPPORT = new EagerDerivedStringSupport();
+    private static final CanonicalValueSupport<LazyDerivedString> LAZY_SUPPORT = new LazyDerivedStringSupport();
 
     @Test
     public void testEager() {