Bug 5527 - Re-implement ControllerContext.toInstanceIdentifier() method
[netconf.git] / restconf / sal-rest-connector / src / main / java / org / opendaylight / restconf / parser / builder / YangInstanceIdentifierSerializer.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.restconf.parser.builder;
9
10 import com.google.common.base.Preconditions;
11 import java.net.URI;
12 import java.util.Map;
13 import java.util.Map.Entry;
14 import java.util.Set;
15 import org.opendaylight.restconf.utils.parser.builder.ParserBuilderConstants;
16 import org.opendaylight.yangtools.yang.common.QName;
17 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
18 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
21 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
22 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
23 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
24
25 /**
26  * Serializer for {@link YangInstanceIdentifier} to {@link String} for restconf
27  *
28  */
29 public final class YangInstanceIdentifierSerializer {
30
31     private YangInstanceIdentifierSerializer() {
32         throw new UnsupportedOperationException("Util class.");
33     }
34
35     /**
36      * Method to create String from {@link Iterable} of {@link PathArgument}
37      * which are parsing from data by {@link SchemaContext}
38      *
39      * @param schemaContext
40      *            - for validate of parsing path arguments
41      * @param data
42      *            - path to data
43      * @return {@link String}
44      */
45     public static String create(final SchemaContext schemaContext, final YangInstanceIdentifier data) {
46         final DataSchemaContextNode<?> current = DataSchemaContextTree.from(schemaContext).getRoot();
47         final MainVarsWrappar variables = new YangInstanceIdentifierSerializer.MainVarsWrappar(current);
48
49         final StringBuilder path = prepareFirstArgForPath(variables, data);
50
51         for (int i = 1; i < data.getPathArguments().size(); i++) {
52             final PathArgument arg = data.getPathArguments().get(i);
53             variables.setCurrent(variables.getCurrent().getChild(arg));
54             Preconditions.checkArgument(current != null,
55                     "Invalid input %s: schema for argument %s (after %s) not found", data, arg, path);
56
57             if (variables.getCurrent().isMixin()) {
58                 continue;
59             }
60             path.append('/');
61
62             if (arg instanceof NodeIdentifierWithPredicates) {
63                 prepareNodeWithPredicates(path, arg);
64             } else if (arg instanceof NodeWithValue) {
65                 prepareNodeWithValue(path, arg);
66             }
67         }
68         return path.toString();
69     }
70
71     private static void prepareNodeWithValue(final StringBuilder path, final PathArgument arg) {
72         path.append(arg.getNodeType().getLocalName());
73         path.append("=");
74
75         String value = ((NodeWithValue<String>) arg).getValue();
76         if (ParserBuilderConstants.Serializer.PERCENT_ENCODE_CHARS.matchesAnyOf(value)) {
77             value = parsePercentEncodeChars(value);
78         }
79         path.append(value);
80     }
81
82     private static void prepareNodeWithPredicates(final StringBuilder path, final PathArgument arg) {
83         path.append(arg.getNodeType().getLocalName());
84         path.append("=");
85
86         final Set<Entry<QName, Object>> entrySet = ((NodeIdentifierWithPredicates) arg).getKeyValues().entrySet();
87         final int endOfSet = entrySet.size();
88         int s = 1;
89         for (final Map.Entry<QName, Object> entry : entrySet) {
90             String valueOf = String.valueOf(entry.getValue());
91             if (ParserBuilderConstants.Serializer.PERCENT_ENCODE_CHARS.matchesAnyOf(valueOf)) {
92                 valueOf = parsePercentEncodeChars(valueOf);
93             }
94             path.append(valueOf);
95             if (s != endOfSet) {
96                 path.append(",");
97                 s++;
98             }
99         }
100     }
101
102     private static StringBuilder prepareFirstArgForPath(final MainVarsWrappar variables,
103             final YangInstanceIdentifier data) {
104         final PathArgument firstArg = data.getPathArguments().get(0);
105         variables.setCurrent(variables.getCurrent().getChild(firstArg));
106
107         final StringBuilder path = new StringBuilder("/");
108         appendQName(path, firstArg.getNodeType());
109         return path;
110     }
111
112     /**
113      * Encode {@link YangInstanceIdentifierSerializer#DISABLED_CHARS}
114      * chars to percent encoded chars
115      *
116      * @param valueOf
117      *            - string to encode
118      * @return encoded {@link String}
119      */
120     private static String parsePercentEncodeChars(final String valueOf) {
121         final StringBuilder sb = new StringBuilder();
122         int start = 0;
123         while (start < valueOf.length()) {
124             if (ParserBuilderConstants.Serializer.PERCENT_ENCODE_CHARS.matches(valueOf.charAt(start))) {
125                 final String format = String.format("%x", (int) valueOf.charAt(start));
126                 final String upperCase = format.toUpperCase();
127                 sb.append("%" + upperCase);
128             } else {
129                 sb.append(valueOf.charAt(start));
130             }
131             start++;
132         }
133         return sb.toString();
134     }
135
136     /**
137      * Add {@link QName} to the serialized string
138      *
139      * @param path
140      *            - {@link StringBuilder}
141      * @param qname
142      *            - {@link QName} node
143      * @return {@link StringBuilder}
144      */
145     private final static StringBuilder appendQName(final StringBuilder path, final QName qname) {
146         final String prefix = prefixForNamespace(qname.getNamespace());
147         Preconditions.checkArgument(prefix != null, "Failed to map QName {}", qname);
148         path.append(prefix);
149         path.append(':');
150         path.append(qname.getLocalName());
151         return path;
152     }
153
154     /**
155      * Create prefix of namespace from {@link URI}
156      *
157      * @param namespace
158      *            - {@link URI}
159      * @return {@link String}
160      */
161     private static String prefixForNamespace(final URI namespace) {
162         final String prefix = namespace.toString();
163         return prefix.replace(':', '-');
164     }
165
166     private static class MainVarsWrappar {
167
168         private DataSchemaContextNode<?> current;
169
170         public MainVarsWrappar(final DataSchemaContextNode<?> current) {
171             this.setCurrent(current);
172         }
173
174         public DataSchemaContextNode<?> getCurrent() {
175             return this.current;
176         }
177
178         public void setCurrent(final DataSchemaContextNode<?> current) {
179             this.current = current;
180         }
181
182     }
183
184 }