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.parser.builder;
10 import static org.opendaylight.restconf.utils.parser.builder.ParserBuilderConstants.Serializer;
12 import com.google.common.base.Preconditions;
14 import java.util.Iterator;
15 import java.util.Map.Entry;
16 import org.opendaylight.restconf.utils.RestconfConstants;
17 import org.opendaylight.restconf.utils.parser.builder.ParserBuilderConstants;
18 import org.opendaylight.yangtools.yang.common.QName;
19 import org.opendaylight.yangtools.yang.common.QNameModule;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
21 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
24 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
25 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
26 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
29 * Serializer for {@link YangInstanceIdentifier} to {@link String} for restconf
32 public final class YangInstanceIdentifierSerializer {
34 private YangInstanceIdentifierSerializer() {
35 throw new UnsupportedOperationException("Util class.");
39 * Method to create String from {@link Iterable} of {@link PathArgument}
40 * which are parsing from data by {@link SchemaContext}
42 * @param schemaContext
43 * - for validate of parsing path arguments
46 * @return {@link String}
48 public static String create(final SchemaContext schemaContext, final YangInstanceIdentifier data) {
49 final DataSchemaContextNode<?> current = DataSchemaContextTree.from(schemaContext).getRoot();
50 final MainVarsWrapper variables = new MainVarsWrapper(current);
52 // for empty data return slash
53 final StringBuilder path = (data.getPathArguments().size() == 0) ?
54 new StringBuilder(String.valueOf(RestconfConstants.SLASH)) : new StringBuilder();
56 QNameModule parentModule = null;
57 for (int i = 0; i < data.getPathArguments().size(); i++) {
58 // get module of the parent
59 if (!variables.getCurrent().isMixin()) {
60 parentModule = variables.getCurrent().getDataSchemaNode().getQName().getModule();
63 final PathArgument arg = data.getPathArguments().get(i);
64 variables.setCurrent(variables.getCurrent().getChild(arg));
66 Preconditions.checkArgument(variables.getCurrent() != null,
67 "Invalid input %s: schema for argument %s (after %s) not found", data, arg, path);
69 if (variables.getCurrent().isMixin()) {
73 // append namespace before every node which is defined in other module than its parent
74 // condition is satisfied also for the first path argument
75 if (!arg.getNodeType().getModule().equals(parentModule)) {
76 path.append(RestconfConstants.SLASH
77 + prefixForNamespace(arg.getNodeType())
78 + ParserBuilderConstants.Deserializer.COLON);
80 path.append(RestconfConstants.SLASH);
83 if (arg instanceof NodeIdentifierWithPredicates) {
84 prepareNodeWithPredicates(path, arg);
85 } else if (arg instanceof NodeWithValue) {
86 prepareNodeWithValue(path, arg);
88 appendQName(path, arg.getNodeType());
92 return path.toString();
95 private static void prepareNodeWithValue(final StringBuilder path, final PathArgument arg) {
96 path.append(arg.getNodeType().getLocalName());
97 path.append(ParserBuilderConstants.Deserializer.EQUAL);
99 String value = ((NodeWithValue<String>) arg).getValue();
100 if (Serializer.PERCENT_ENCODE_CHARS.matchesAnyOf(value)) {
101 value = parsePercentEncodeChars(value);
106 private static void prepareNodeWithPredicates(final StringBuilder path, final PathArgument arg) {
107 path.append(arg.getNodeType().getLocalName());
109 final Iterator<Entry<QName, Object>> iterator = ((NodeIdentifierWithPredicates) arg).getKeyValues()
110 .entrySet().iterator();
112 if (iterator.hasNext()) {
113 path.append(ParserBuilderConstants.Deserializer.EQUAL);
116 while (iterator.hasNext()) {
117 String valueOf = String.valueOf(iterator.next().getValue());
118 if (Serializer.PERCENT_ENCODE_CHARS.matchesAnyOf(valueOf)) {
119 valueOf = parsePercentEncodeChars(valueOf);
121 path.append(valueOf);
122 if (iterator.hasNext()) {
123 path.append(ParserBuilderConstants.Deserializer.COMMA);
129 * Encode {@link Serializer#DISABLED_CHARS} chars to percent encoded chars
133 * @return encoded {@link String}
135 private static String parsePercentEncodeChars(final String valueOf) {
136 final StringBuilder sb = new StringBuilder();
138 while (start < valueOf.length()) {
139 if (Serializer.PERCENT_ENCODE_CHARS.matches(valueOf.charAt(start))) {
140 final String format = String.format("%x", (int) valueOf.charAt(start));
141 final String upperCase = format.toUpperCase();
142 sb.append(ParserBuilderConstants.Deserializer.PERCENT_ENCODING + upperCase);
144 sb.append(valueOf.charAt(start));
148 return sb.toString();
152 * Add {@link QName} to the serialized string
155 * - {@link StringBuilder}
157 * - {@link QName} node
158 * @return {@link StringBuilder}
160 private final static StringBuilder appendQName(final StringBuilder path, final QName qname) {
161 path.append(qname.getLocalName());
166 * Create prefix of namespace from {@link QName}
170 * @return {@link String}
172 private static String prefixForNamespace(final QName qname) {
173 final URI namespace = qname.getNamespace();
174 Preconditions.checkArgument(namespace != null, "Failed to map QName {}", qname);
175 final String prefix = namespace.toString();
176 return prefix.replace(ParserBuilderConstants.Deserializer.COLON, ParserBuilderConstants.Deserializer.HYPHEN);
179 private static final class MainVarsWrapper {
181 private DataSchemaContextNode<?> current;
183 public MainVarsWrapper(final DataSchemaContextNode<?> current) {
184 this.setCurrent(current);
187 public DataSchemaContextNode<?> getCurrent() {
191 public void setCurrent(final DataSchemaContextNode<?> current) {
192 this.current = current;