*/
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;
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();
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;
path.append('/');
}
- path.append(prefixForNamespace(arg.getNodeType(), schemaContext)).append(':');
+ path.append(prefixForNamespace(arg.getNodeType(), databind.modelContext())).append(':');
} else {
path.append('/');
}
// 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) {
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(',');
}
/**
* 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 {