2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.restconf.nb.rfc8040.utils.parser;
10 import java.util.Map.Entry;
12 import org.opendaylight.restconf.api.ApiPath;
13 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
14 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindContext;
15 import org.opendaylight.yangtools.yang.common.ErrorTag;
16 import org.opendaylight.yangtools.yang.common.ErrorType;
17 import org.opendaylight.yangtools.yang.common.QName;
18 import org.opendaylight.yangtools.yang.common.QNameModule;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
21 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
23 import org.opendaylight.yangtools.yang.data.util.DataSchemaContext;
24 import org.opendaylight.yangtools.yang.data.util.DataSchemaContext.PathMixin;
25 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
28 * Serializer for {@link YangInstanceIdentifier} to {@link String} for restconf.
30 public final class YangInstanceIdentifierSerializer {
32 private YangInstanceIdentifierSerializer() {
37 * Method to create String from {@link Iterable} of {@link PathArgument} which are parsing from data with the help
38 * of an {@link EffectiveModelContext}.
40 * @param databind for validate of parsing path arguments
41 * @param data path to data
42 * @return {@link String}
44 public static String create(final DatabindContext databind, final YangInstanceIdentifier data) {
45 final var current = databind.schemaTree().getRoot();
46 final var variables = new MainVarsWrapper(current);
47 final var path = new StringBuilder();
49 QNameModule parentModule = null;
50 for (final PathArgument arg : data.getPathArguments()) {
51 // get module of the parent
52 final var currentContext = variables.getCurrent();
54 if (!(currentContext instanceof PathMixin)) {
55 parentModule = currentContext.dataSchemaNode().getQName().getModule();
58 final var childContext = currentContext instanceof DataSchemaContext.Composite composite
59 ? composite.childByArg(arg) : null;
60 if (childContext == null) {
61 throw new RestconfDocumentedException(
62 "Invalid input '%s': schema for argument '%s' (after '%s') not found".formatted(data, arg, path),
63 ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT);
66 variables.setCurrent(childContext);
67 if (childContext instanceof PathMixin) {
71 // append namespace before every node which is defined in other module than its parent
72 // condition is satisfied also for the first path argument
73 if (!arg.getNodeType().getModule().equals(parentModule)) {
74 // append slash if it is not the first path argument
75 if (path.length() > 0) {
79 path.append(prefixForNamespace(arg.getNodeType(), databind.modelContext())).append(':');
84 path.append(arg.getNodeType().getLocalName());
85 if (arg instanceof NodeIdentifierWithPredicates withPredicates) {
86 prepareNodeWithPredicates(path, withPredicates.entrySet());
87 } else if (arg instanceof NodeWithValue<?> withValue) {
88 prepareNodeWithValue(path, withValue.getValue());
92 return path.toString();
95 private static void prepareNodeWithValue(final StringBuilder path, final Object value) {
98 // FIXME: this is quite fishy
99 final var str = String.valueOf(value);
100 path.append(ApiPath.PERCENT_ESCAPER.escape(str));
103 private static void prepareNodeWithPredicates(final StringBuilder path, final Set<Entry<QName, Object>> entries) {
104 final var iterator = entries.iterator();
105 if (iterator.hasNext()) {
109 while (iterator.hasNext()) {
110 // FIXME: this is quite fishy
111 final var str = String.valueOf(iterator.next().getValue());
112 path.append(ApiPath.PERCENT_ESCAPER.escape(str));
113 if (iterator.hasNext()) {
120 * Create prefix of namespace from {@link QName}.
122 * @param qname {@link QName}
123 * @return {@link String}
125 private static String prefixForNamespace(final QName qname, final EffectiveModelContext schemaContext) {
126 return schemaContext.findModuleStatement(qname.getModule()).orElseThrow().argument().getLocalName();
129 private static final class MainVarsWrapper {
130 private DataSchemaContext current;
132 MainVarsWrapper(final DataSchemaContext current) {
136 public DataSchemaContext getCurrent() {
140 public void setCurrent(final DataSchemaContext current) {
141 this.current = current;