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;
11 import com.google.common.base.Preconditions;
13 import java.util.Iterator;
14 import java.util.Map.Entry;
15 import org.opendaylight.restconf.utils.RestconfConstants;
16 import org.opendaylight.restconf.utils.parser.builder.ParserBuilderConstants;
17 import org.opendaylight.restconf.utils.parser.builder.ParserBuilderConstants.Serializer;
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.Module;
27 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
30 * Serializer for {@link YangInstanceIdentifier} to {@link String} for restconf
33 public final class YangInstanceIdentifierSerializer {
35 private YangInstanceIdentifierSerializer() {
36 throw new UnsupportedOperationException("Util class.");
40 * Method to create String from {@link Iterable} of {@link PathArgument}
41 * which are parsing from data by {@link SchemaContext}
43 * @param schemaContext
44 * - for validate of parsing path arguments
47 * @return {@link String}
49 public static String create(final SchemaContext schemaContext, final YangInstanceIdentifier data) {
50 final DataSchemaContextNode<?> current = DataSchemaContextTree.from(schemaContext).getRoot();
51 final MainVarsWrapper variables = new MainVarsWrapper(current);
53 // for empty data return slash
54 final StringBuilder path = (data.getPathArguments().size() == 0) ?
55 new StringBuilder(String.valueOf(RestconfConstants.SLASH)) : new StringBuilder();
57 QNameModule parentModule = null;
58 for (int i = 0; i < data.getPathArguments().size(); i++) {
59 // get module of the parent
60 if (!variables.getCurrent().isMixin()) {
61 parentModule = variables.getCurrent().getDataSchemaNode().getQName().getModule();
64 final PathArgument arg = data.getPathArguments().get(i);
65 variables.setCurrent(variables.getCurrent().getChild(arg));
67 Preconditions.checkArgument(variables.getCurrent() != null,
68 "Invalid input %s: schema for argument %s (after %s) not found", data, arg, path);
70 if (variables.getCurrent().isMixin()) {
74 // append namespace before every node which is defined in other module than its parent
75 // condition is satisfied also for the first path argument
76 if (!arg.getNodeType().getModule().equals(parentModule)) {
77 path.append(RestconfConstants.SLASH
78 + prefixForNamespace(arg.getNodeType(), schemaContext)
79 + ParserBuilderConstants.Deserializer.COLON);
81 path.append(RestconfConstants.SLASH);
84 if (arg instanceof NodeIdentifierWithPredicates) {
85 prepareNodeWithPredicates(path, arg);
86 } else if (arg instanceof NodeWithValue) {
87 prepareNodeWithValue(path, arg);
89 appendQName(path, arg.getNodeType());
93 return path.toString();
96 private static void prepareNodeWithValue(final StringBuilder path, final PathArgument arg) {
97 path.append(arg.getNodeType().getLocalName());
98 path.append(ParserBuilderConstants.Deserializer.EQUAL);
100 String value = ((NodeWithValue<String>) arg).getValue();
101 if (Serializer.PERCENT_ENCODE_CHARS.matchesAnyOf(value)) {
102 value = parsePercentEncodeChars(value);
107 private static void prepareNodeWithPredicates(final StringBuilder path, final PathArgument arg) {
108 path.append(arg.getNodeType().getLocalName());
110 final Iterator<Entry<QName, Object>> iterator = ((NodeIdentifierWithPredicates) arg).getKeyValues()
111 .entrySet().iterator();
113 if (iterator.hasNext()) {
114 path.append(ParserBuilderConstants.Deserializer.EQUAL);
117 while (iterator.hasNext()) {
118 String valueOf = String.valueOf(iterator.next().getValue());
119 if (Serializer.PERCENT_ENCODE_CHARS.matchesAnyOf(valueOf)) {
120 valueOf = parsePercentEncodeChars(valueOf);
122 path.append(valueOf);
123 if (iterator.hasNext()) {
124 path.append(ParserBuilderConstants.Deserializer.COMMA);
130 * Encode {@link Serializer#DISABLED_CHARS} chars to percent encoded chars
134 * @return encoded {@link String}
136 private static String parsePercentEncodeChars(final String valueOf) {
137 final StringBuilder sb = new StringBuilder();
139 while (start < valueOf.length()) {
140 if (Serializer.PERCENT_ENCODE_CHARS.matches(valueOf.charAt(start))) {
141 final String format = String.format("%x", (int) valueOf.charAt(start));
142 final String upperCase = format.toUpperCase();
143 sb.append(ParserBuilderConstants.Deserializer.PERCENT_ENCODING + upperCase);
145 sb.append(valueOf.charAt(start));
149 return sb.toString();
153 * Add {@link QName} to the serialized string
156 * - {@link StringBuilder}
158 * - {@link QName} node
159 * @return {@link StringBuilder}
161 private final static StringBuilder appendQName(final StringBuilder path, final QName qname) {
162 path.append(qname.getLocalName());
167 * Create prefix of namespace from {@link QName}
171 * @return {@link String}
173 private static String prefixForNamespace(final QName qname, final SchemaContext schemaContext) {
174 final URI namespace = qname.getNamespace();
175 Preconditions.checkArgument(namespace != null, "Failed to map QName {}", qname);
176 final Module module = schemaContext.findModuleByNamespaceAndRevision(namespace, qname.getRevision());
177 return module.getName();
180 private static final class MainVarsWrapper {
182 private DataSchemaContextNode<?> current;
184 public MainVarsWrapper(final DataSchemaContextNode<?> current) {
185 this.setCurrent(current);
188 public DataSchemaContextNode<?> getCurrent() {
192 public void setCurrent(final DataSchemaContextNode<?> current) {
193 this.current = current;