Map identities to proper objects
[mdsal.git] / binding / mdsal-binding-spec-util / src / main / java / org / opendaylight / mdsal / binding / spec / naming / BindingMapping.java
index 48c08379f0ab44401dbee9b0e56cb4dc43c042c6..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,7 +25,7 @@ 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;
@@ -36,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";
 
@@ -76,19 +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 DATA_CONTAINER_IMPLEMENTED_INTERFACE_NAME = "implementedInterface";
+    public static final @NonNull String BINDING_CONTRACT_IMPLEMENTED_INTERFACE_NAME = "implementedInterface";
 
     /**
-     * Name of {@link ScalarTypeObject#getValue()}.
+     * Name of default {@link Object#hashCode()} implementation for instantiated DataObjects. Each such generated
+     * interface contains this static method.
      */
-    public static final @NonNull String SCALAR_TYPE_OBJECT_GET_VALUE_NAME = "getValue";
+    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 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.
@@ -100,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) {
@@ -198,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) {
@@ -227,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;
@@ -240,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)) {
@@ -262,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;
         }
@@ -352,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))