Map identities to proper objects
[mdsal.git] / binding / mdsal-binding-spec-util / src / main / java / org / opendaylight / mdsal / binding / spec / naming / BindingMapping.java
index a9b85d6be983e5aa83008656b39e6185af487b68..57dae3eac9dfb31eb391fca8f223177ee4f84857 100644 (file)
@@ -8,6 +8,7 @@
 package org.opendaylight.mdsal.binding.spec.naming;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.CharMatcher;
@@ -24,8 +25,9 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.yangtools.yang.binding.Augmentable;
-import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.binding.BindingContract;
 import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.ScalarTypeObject;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.common.Revision;
@@ -35,22 +37,34 @@ public final class BindingMapping {
 
     public static final @NonNull String VERSION = "0.6";
 
+    // Note: these are not just JLS keywords, but rather character sequences which are reserved in codegen contexts
     public static final ImmutableSet<String> JAVA_RESERVED_WORDS = ImmutableSet.of(
-        // https://docs.oracle.com/javase/specs/jls/se9/html/jls-3.html#jls-3.9
+        // https://docs.oracle.com/javase/specs/jls/se9/html/jls-3.html#jls-3.9 except module-info.java constructs
         "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue",
         "default", "do", "double", "else", "enum", "extends", "final", "finally", "float", "for", "goto", "if",
         "implements", "import", "instanceof", "int", "interface", "long", "native", "new", "package", "private",
         "protected", "public", "return", "short", "static", "strictfp", "super", "switch", "synchronized", "this",
         "throw", "throws", "transient", "try", "void", "volatile", "while", "_",
+        // "open", "module", "requires", "transitive", "exports, "opens", "to", "uses", "provides", "with",
+
         // https://docs.oracle.com/javase/specs/jls/se9/html/jls-3.html#jls-3.10.3
         "false", "true",
         // https://docs.oracle.com/javase/specs/jls/se9/html/jls-3.html#jls-3.10.7
-        "null");
+        "null",
+        // https://docs.oracle.com/javase/specs/jls/se10/html/jls-3.html#jls-3.9
+        "var",
+        // https://docs.oracle.com/javase/specs/jls/se14/html/jls-3.html#jls-3.9
+        "yield",
+        // https://docs.oracle.com/javase/specs/jls/se16/html/jls-3.html#jls-3.9
+        "record");
 
     public static final @NonNull String DATA_ROOT_SUFFIX = "Data";
     public static final @NonNull String RPC_SERVICE_SUFFIX = "Service";
     public static final @NonNull String NOTIFICATION_LISTENER_SUFFIX = "Listener";
+    public static final @NonNull String BUILDER_SUFFIX = "Builder";
+    public static final @NonNull String KEY_SUFFIX = "Key";
     public static final @NonNull String QNAME_STATIC_FIELD_NAME = "QNAME";
+    public static final @NonNull String VALUE_STATIC_FIELD_NAME = "VALUE";
     public static final @NonNull String PACKAGE_PREFIX = "org.opendaylight.yang.gen.v1";
     public static final @NonNull String AUGMENTATION_FIELD = "augmentation";
 
@@ -75,14 +89,32 @@ public final class BindingMapping {
     public static final @NonNull String IDENTIFIABLE_KEY_NAME = "key";
 
     /**
-     * Name of {@link DataContainer#implementedInterface()}.
+     * Name of {@link BindingContract#implementedInterface()}.
+     */
+    public static final @NonNull String BINDING_CONTRACT_IMPLEMENTED_INTERFACE_NAME = "implementedInterface";
+
+    /**
+     * Name of default {@link Object#hashCode()} implementation for instantiated DataObjects. Each such generated
+     * interface contains this static method.
+     */
+    public static final @NonNull String BINDING_HASHCODE_NAME = "bindingHashCode";
+
+    /**
+     * Name of default {@link Object#equals(Object)} implementation for instantiated DataObjects. Each such generated
+     * interface contains this static method.
      */
-    public static final @NonNull String DATA_CONTAINER_IMPLEMENTED_INTERFACE_NAME = "implementedInterface";
+    public static final @NonNull String BINDING_EQUALS_NAME = "bindingEquals";
 
     /**
-     * Prefix for getter methods working on top of boolean.
+     * Name of default {@link Object#toString()} implementation for instantiated DataObjects. Each such generated
+     * interface contains this static method.
      */
-    public static final @NonNull String BOOLEAN_GETTER_PREFIX = "is";
+    public static final @NonNull String BINDING_TO_STRING_NAME = "bindingToString";
+
+    /**
+     * Name of {@link ScalarTypeObject#getValue()}.
+     */
+    public static final @NonNull String SCALAR_TYPE_OBJECT_GET_VALUE_NAME = "getValue";
 
     /**
      * Prefix for normal getter methods.
@@ -94,13 +126,18 @@ public final class BindingMapping {
      */
     public static final @NonNull String NONNULL_PREFIX = "nonnull";
 
+    /**
+     * Prefix for require default wrapper methods. These methods always wrap a corresponding normal getter
+     * of leaf objects.
+     */
+    public static final @NonNull String REQUIRE_PREFIX = "require";
     public static final @NonNull String RPC_INPUT_SUFFIX = "Input";
     public static final @NonNull String RPC_OUTPUT_SUFFIX = "Output";
 
     private static final Interner<String> PACKAGE_INTERNER = Interners.newWeakInterner();
 
     private BindingMapping() {
-        throw new UnsupportedOperationException("Utility class should not be instantiated");
+        // Hidden on purpose
     }
 
     public static @NonNull String getRootPackageName(final QName module) {
@@ -192,20 +229,16 @@ public final class BindingMapping {
         return getMethodName(name.getLocalName());
     }
 
-    public static @NonNull String getGetterPrefix(final boolean isBoolean) {
-        return isBoolean ? BOOLEAN_GETTER_PREFIX : GETTER_PREFIX;
-    }
-
-    public static @NonNull String getGetterMethodName(final String localName, final boolean isBoolean) {
-        return getGetterPrefix(isBoolean) + toFirstUpper(getPropertyName(localName));
+    public static @NonNull String getGetterMethodName(final String localName) {
+        return GETTER_PREFIX + toFirstUpper(getPropertyName(localName));
     }
 
-    public static @NonNull String getGetterMethodName(final QName name, final boolean isBoolean) {
-        return getGetterPrefix(isBoolean) + getGetterSuffix(name);
+    public static @NonNull String getGetterMethodName(final QName name) {
+        return GETTER_PREFIX + getGetterSuffix(name);
     }
 
     public static boolean isGetterMethodName(final String methodName) {
-        return methodName.startsWith(GETTER_PREFIX) || methodName.startsWith(BOOLEAN_GETTER_PREFIX);
+        return methodName.startsWith(GETTER_PREFIX);
     }
 
     public static @NonNull String getGetterMethodForNonnull(final String methodName) {
@@ -221,6 +254,19 @@ public final class BindingMapping {
         return methodName.startsWith(NONNULL_PREFIX);
     }
 
+    public static @NonNull String getGetterMethodForRequire(final String methodName) {
+        checkArgument(isRequireMethodName(methodName));
+        return GETTER_PREFIX + methodName.substring(REQUIRE_PREFIX.length());
+    }
+
+    public static @NonNull String getRequireMethodName(final String localName) {
+        return REQUIRE_PREFIX + toFirstUpper(getPropertyName(localName));
+    }
+
+    public static boolean isRequireMethodName(final String methodName) {
+        return methodName.startsWith(REQUIRE_PREFIX);
+    }
+
     public static @NonNull String getGetterSuffix(final QName name) {
         final String candidate = toFirstUpper(toCamelCase(name.getLocalName()));
         return "Class".equals(candidate) ? "XmlClass" : candidate;
@@ -234,6 +280,13 @@ public final class BindingMapping {
         return potential;
     }
 
+    // FIXME: this is legacy union/leafref property handling. The resulting value is *not* normalized for use as a
+    //        property.
+    public static @NonNull String getUnionLeafrefMemberName(final String unionClassSimpleName,
+            final String referencedClassSimpleName) {
+        return requireNonNull(referencedClassSimpleName) + requireNonNull(unionClassSimpleName) + "Value";
+    }
+
     private static @NonNull String toCamelCase(final String rawString) {
         StringBuilder builder = new StringBuilder();
         for (String comp : CAMEL_SPLITTER.split(rawString)) {
@@ -256,7 +309,7 @@ public final class BindingMapping {
      * @param str the string that should get an upper case first character.
      * @return the {@link String} {@code str} with an upper case first character.
      */
-    private static @NonNull String toFirstUpper(final @NonNull String str) {
+    public static @NonNull String toFirstUpper(final @NonNull String str) {
         if (str.isEmpty()) {
             return str;
         }
@@ -290,6 +343,18 @@ public final class BindingMapping {
         return str.substring(0, 1).toLowerCase(Locale.ENGLISH) + str.substring(1);
     }
 
+    /**
+     * Returns the {@link String} {@code s} with a '$' character as suffix.
+     *
+     * @param qname RPC QName
+     * @return The RPC method name as determined by considering the localname against the JLS.
+     * @throws NullPointerException if {@code qname} is null
+     */
+    public static @NonNull String getRpcMethodName(final @NonNull QName qname) {
+        final String methodName = getMethodName(qname);
+        return JAVA_RESERVED_WORDS.contains(methodName) ? methodName + "$" : methodName;
+    }
+
     /**
      * Returns Java identifiers, conforming to JLS9 Section 3.8 to use for specified YANG assigned names
      * (RFC7950 Section 9.6.4). This method considers two distinct encodings: one the pre-Fluorine mapping, which is
@@ -334,7 +399,9 @@ public final class BindingMapping {
         return javaToYang.inverse();
     }
 
-    // See https://docs.oracle.com/javase/specs/jls/se9/html/jls-3.html#jls-3.8
+    // See https://docs.oracle.com/javase/specs/jls/se16/html/jls-3.html#jls-3.8
+    // TODO: we are being conservative here, but should differentiate TypeIdentifier and UnqualifiedMethodIdentifier,
+    //       which have different exclusions
     private static boolean isValidJavaIdentifier(final String str) {
         return !str.isEmpty() && !JAVA_RESERVED_WORDS.contains(str)
                 && Character.isJavaIdentifierStart(str.codePointAt(0))