RFC8040 'rc:yang-data' support for mdsal binding generator
[mdsal.git] / binding / mdsal-binding-spec-util / src / main / java / org / opendaylight / mdsal / binding / spec / naming / BindingMapping.java
index dd53f3d2d26a4140aa303dd852c9192d856ba132..81cf8aab123d1dfe8ec191d3bbbd8d9cf3d151d2 100644 (file)
@@ -23,10 +23,15 @@ import java.util.Locale;
 import java.util.Optional;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import org.checkerframework.checker.regex.qual.Regex;
 import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.binding.Action;
 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.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.Rpc;
+import org.opendaylight.yangtools.yang.binding.RpcInput;
 import org.opendaylight.yangtools.yang.binding.ScalarTypeObject;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
@@ -59,9 +64,18 @@ public final class BindingMapping {
         "record");
 
     public static final @NonNull String DATA_ROOT_SUFFIX = "Data";
+    @Deprecated(since = "11.0.0", forRemoval = true)
     public static final @NonNull String RPC_SERVICE_SUFFIX = "Service";
+    @Deprecated(since = "10.0.3", forRemoval = true)
     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";
+    // ietf-restconf:yang-data, i.e. YangDataName
+    public static final @NonNull String NAME_STATIC_FIELD_NAME = "NAME";
+    // everything that can have a QName (e.g. identifier bound to a namespace)
     public static final @NonNull String QNAME_STATIC_FIELD_NAME = "QNAME";
+    // concrete extensible contracts, for example 'feature', 'identity' and similar
+    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";
 
@@ -73,6 +87,7 @@ public final class BindingMapping {
 
     public static final @NonNull String MODULE_INFO_CLASS_NAME = "$YangModuleInfoImpl";
     public static final @NonNull String MODULE_INFO_QNAMEOF_METHOD_NAME = "qnameOf";
+    public static final @NonNull String MODULE_INFO_YANGDATANAMEOF_METHOD_NAME = "yangDataNameOf";
     public static final @NonNull String MODEL_BINDING_PROVIDER_CLASS_NAME = "$YangModelBindingProvider";
 
     /**
@@ -86,9 +101,9 @@ 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 default {@link Object#hashCode()} implementation for instantiated DataObjects. Each such generated
@@ -108,6 +123,16 @@ public final class BindingMapping {
      */
     public static final @NonNull String BINDING_TO_STRING_NAME = "bindingToString";
 
+    /**
+     * Name of {@link Action#invoke(InstanceIdentifier, RpcInput)}.
+     */
+    public static final @NonNull String ACTION_INVOKE_NAME = "invoke";
+
+    /**
+     * Name of {@link Rpc#invoke(org.opendaylight.yangtools.yang.binding.RpcInput)}.
+     */
+    public static final @NonNull String RPC_INVOKE_NAME = "invoke";
+
     /**
      * Name of {@link ScalarTypeObject#getValue()}.
      */
@@ -132,6 +157,10 @@ public final class BindingMapping {
     public static final @NonNull String RPC_OUTPUT_SUFFIX = "Output";
 
     private static final Interner<String> PACKAGE_INTERNER = Interners.newWeakInterner();
+    @Regex
+    private static final String ROOT_PACKAGE_PATTERN_STRING =
+            "(org.opendaylight.yang.gen.v1.[a-z0-9_\\.]*\\.(?:rev[0-9][0-9][0-1][0-9][0-3][0-9]|norev))";
+    private static final Pattern ROOT_PACKAGE_PATTERN = Pattern.compile(ROOT_PACKAGE_PATTERN_STRING);
 
     private BindingMapping() {
         // Hidden on purpose
@@ -150,22 +179,10 @@ public final class BindingMapping {
         final char[] chars = namespace.toCharArray();
         for (int i = 0; i < chars.length; ++i) {
             switch (chars[i]) {
-                case '/':
-                case ':':
-                case '-':
-                case '@':
-                case '$':
-                case '#':
-                case '\'':
-                case '*':
-                case '+':
-                case ',':
-                case ';':
-                case '=':
-                    chars[i] = '.';
-                    break;
-                default:
+                case '/', ':', '-', '@', '$', '#', '\'', '*', '+', ',', ';', '=' -> chars[i] = '.';
+                default -> {
                     // no-op
+                }
             }
         }
 
@@ -352,6 +369,24 @@ public final class BindingMapping {
         return JAVA_RESERVED_WORDS.contains(methodName) ? methodName + "$" : methodName;
     }
 
+    /**
+     * Returns root package name for supplied package name.
+     *
+     * @param packageName Package for which find model root package.
+     * @return Package of model root.
+     * @throws NullPointerException if {@code packageName} is {@code null}
+     * @throws IllegalArgumentException if {@code packageName} does not start with {@link #PACKAGE_PREFIX} or it does
+     *                                  not match package name formatting rules
+     */
+    public static @NonNull String getModelRootPackageName(final String packageName) {
+        checkArgument(packageName.startsWith(PACKAGE_PREFIX), "Package name not starting with %s, is: %s",
+            PACKAGE_PREFIX, packageName);
+        final var match = ROOT_PACKAGE_PATTERN.matcher(packageName);
+        checkArgument(match.find(), "Package name '%s' does not match required pattern '%s'", packageName,
+                ROOT_PACKAGE_PATTERN_STRING);
+        return match.group(0);
+    }
+
     /**
      * 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
@@ -396,6 +431,19 @@ public final class BindingMapping {
         return javaToYang.inverse();
     }
 
+    /**
+     * Builds class name representing yang-data template name which is not yang identifier compliant.
+     *
+     * @param templateName template name
+     * @return Java class name
+     * @throws NullPointerException if {@code templateName} is {@code null}
+     * @throws IllegalArgumentException if (@code templateName} is empty
+     */
+    // TODO: take YangDataName once we have it readily available
+    public static String mapYangDataName(final String templateName) {
+        return mapEnumAssignedName(templateName);
+    }
+
     // 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