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 5da6190a74534a4fae1fd258ab1ff53189d13ac1..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.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,11 +64,17 @@ 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";
@@ -76,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";
 
     /**
@@ -111,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()}.
      */
@@ -135,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
@@ -343,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
@@ -387,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