Use DatabindContext in instance identifier serialization
[netconf.git] / restconf / restconf-nb / src / main / java / org / opendaylight / restconf / nb / rfc8040 / utils / parser / YangInstanceIdentifierSerializer.java
index b1c690048eaf159d9bc4f3b0aeb9b4c4db86b872..d053e83ae7806189cf475b2f7616045e0adbed31 100644 (file)
@@ -7,13 +7,11 @@
  */
 package org.opendaylight.restconf.nb.rfc8040.utils.parser;
 
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.escape.Escaper;
-import com.google.common.escape.Escapers;
-import java.util.HexFormat;
 import java.util.Map.Entry;
 import java.util.Set;
+import org.opendaylight.restconf.api.ApiPath;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.nb.rfc8040.databind.DatabindContext;
 import org.opendaylight.yangtools.yang.common.ErrorTag;
 import org.opendaylight.yangtools.yang.common.ErrorType;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -24,51 +22,27 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithV
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.util.DataSchemaContext;
 import org.opendaylight.yangtools.yang.data.util.DataSchemaContext.PathMixin;
-import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
-import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 /**
  * Serializer for {@link YangInstanceIdentifier} to {@link String} for restconf.
  */
 public final class YangInstanceIdentifierSerializer {
-    // Escaper based on RFC8040-requirement to percent-encode reserved characters, as defined in
-    // https://tools.ietf.org/html/rfc3986#section-2.2
-    @VisibleForTesting
-    static final Escaper PERCENT_ESCAPER;
-
-    static {
-        final var hexFormat = HexFormat.of().withUpperCase();
-        final var builder = Escapers.builder();
-        for (char ch : new char[] {
-            // Reserved characters as per https://tools.ietf.org/html/rfc3986#section-2.2
-            ':', '/', '?', '#', '[', ']', '@',
-            '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=',
-            // FIXME: this space should not be here, but that was a day-0 bug and we have asserts on this
-            ' '
-        }) {
-            builder.addEscape(ch, "%" + hexFormat.toHighHexDigit(ch) + hexFormat.toLowHexDigit(ch));
-        }
-        PERCENT_ESCAPER = builder.build();
-    }
 
     private YangInstanceIdentifierSerializer() {
         // Hidden on purpose
     }
 
     /**
-     * Method to create String from {@link Iterable} of {@link PathArgument}
-     * which are parsing from data by {@link SchemaContext}.
+     * Method to create String from {@link Iterable} of {@link PathArgument} which are parsing from data with the help
+     * of an {@link EffectiveModelContext}.
      *
-     * @param schemaContext
-     *             for validate of parsing path arguments
-     * @param data
-     *             path to data
+     * @param databind for validate of parsing path arguments
+     * @param data path to data
      * @return {@link String}
      */
-    public static String create(final EffectiveModelContext schemaContext, final YangInstanceIdentifier data) {
-        final var current = DataSchemaContextTree.from(schemaContext).getRoot();
+    public static String create(final DatabindContext databind, final YangInstanceIdentifier data) {
+        final var current = databind.schemaTree().getRoot();
         final var variables = new MainVarsWrapper(current);
         final var path = new StringBuilder();
 
@@ -83,10 +57,12 @@ public final class YangInstanceIdentifierSerializer {
 
             final var childContext = currentContext instanceof DataSchemaContext.Composite composite
                 ? composite.childByArg(arg) : null;
+            if (childContext == null) {
+                throw new RestconfDocumentedException(
+                    "Invalid input '%s': schema for argument '%s' (after '%s') not found".formatted(data, arg, path),
+                    ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT);
+            }
 
-            RestconfDocumentedException.throwIfNull(childContext, ErrorType.APPLICATION,
-                    ErrorTag.UNKNOWN_ELEMENT, "Invalid input '%s': schema for argument '%s' (after '%s') not found",
-                    data, arg, path);
             variables.setCurrent(childContext);
             if (childContext instanceof PathMixin) {
                 continue;
@@ -100,7 +76,7 @@ public final class YangInstanceIdentifierSerializer {
                     path.append('/');
                 }
 
-                path.append(prefixForNamespace(arg.getNodeType(), schemaContext)).append(':');
+                path.append(prefixForNamespace(arg.getNodeType(), databind.modelContext())).append(':');
             } else {
                 path.append('/');
             }
@@ -121,7 +97,7 @@ public final class YangInstanceIdentifierSerializer {
 
         // FIXME: this is quite fishy
         final var str = String.valueOf(value);
-        path.append(PERCENT_ESCAPER.escape(str));
+        path.append(ApiPath.PERCENT_ESCAPER.escape(str));
     }
 
     private static void prepareNodeWithPredicates(final StringBuilder path, final Set<Entry<QName, Object>> entries) {
@@ -133,7 +109,7 @@ public final class YangInstanceIdentifierSerializer {
         while (iterator.hasNext()) {
             // FIXME: this is quite fishy
             final var str = String.valueOf(iterator.next().getValue());
-            path.append(PERCENT_ESCAPER.escape(str));
+            path.append(ApiPath.PERCENT_ESCAPER.escape(str));
             if (iterator.hasNext()) {
                 path.append(',');
             }
@@ -143,13 +119,11 @@ public final class YangInstanceIdentifierSerializer {
     /**
      * Create prefix of namespace from {@link QName}.
      *
-     * @param qname
-     *             {@link QName}
+     * @param qname {@link QName}
      * @return {@link String}
      */
-    private static String prefixForNamespace(final QName qname, final SchemaContext schemaContext) {
-        final Module module = schemaContext.findModule(qname.getModule()).orElse(null);
-        return module.getName();
+    private static String prefixForNamespace(final QName qname, final EffectiveModelContext schemaContext) {
+        return schemaContext.findModuleStatement(qname.getModule()).orElseThrow().argument().getLocalName();
     }
 
     private static final class MainVarsWrapper {