Split Restconf implementations (draft02 and RFC) - Application
[netconf.git] / restconf / restconf-nb-bierman02 / 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.Iterator;
13 import java.util.Map.Entry;
14 import org.opendaylight.restconf.utils.RestconfConstants;
15 import org.opendaylight.restconf.utils.parser.builder.ParserBuilderConstants;
16 import org.opendaylight.restconf.utils.parser.builder.ParserBuilderConstants.Serializer;
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.DataSchemaContextNode;
24 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
25 import org.opendaylight.yangtools.yang.model.api.Module;
26 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
27
28 /**
29  * Serializer for {@link YangInstanceIdentifier} to {@link String} for restconf.
30  *
31  * @deprecated move to splitted module restconf-nb-rfc8040
32  */
33 @Deprecated
34 public final class YangInstanceIdentifierSerializer {
35
36     private YangInstanceIdentifierSerializer() {
37         throw new UnsupportedOperationException("Util class.");
38     }
39
40     /**
41      * Method to create String from {@link Iterable} of {@link PathArgument}
42      * which are parsing from data by {@link SchemaContext}.
43      *
44      * @param schemaContext
45      *             for validate of parsing path arguments
46      * @param data
47      *             path to data
48      * @return {@link String}
49      */
50     public static String create(final SchemaContext schemaContext, final YangInstanceIdentifier data) {
51         final DataSchemaContextNode<?> current = DataSchemaContextTree.from(schemaContext).getRoot();
52         final MainVarsWrapper variables = new MainVarsWrapper(current);
53         final StringBuilder path = new StringBuilder();
54
55         QNameModule parentModule = null;
56         for (int i = 0; i < data.getPathArguments().size(); i++) {
57             // get module of the parent
58             if (!variables.getCurrent().isMixin()) {
59                 parentModule = variables.getCurrent().getDataSchemaNode().getQName().getModule();
60             }
61
62             final PathArgument arg = data.getPathArguments().get(i);
63             variables.setCurrent(variables.getCurrent().getChild(arg));
64
65             Preconditions.checkArgument(variables.getCurrent() != null,
66                     "Invalid input %s: schema for argument %s (after %s) not found", data, arg, path);
67
68             if (variables.getCurrent().isMixin()) {
69                 continue;
70             }
71
72             // append namespace before every node which is defined in other module than its parent
73             // condition is satisfied also for the first path argument
74             if (!arg.getNodeType().getModule().equals(parentModule)) {
75                 // append slash if it is not the first path argument
76                 if (path.length() > 0) {
77                     path.append(RestconfConstants.SLASH);
78                 }
79
80                 path.append(prefixForNamespace(arg.getNodeType(), schemaContext));
81                 path.append(ParserBuilderConstants.Deserializer.COLON);
82             } else {
83                 path.append(RestconfConstants.SLASH);
84             }
85
86             if (arg instanceof NodeIdentifierWithPredicates) {
87                 prepareNodeWithPredicates(path, arg);
88             } else if (arg instanceof NodeWithValue) {
89                 prepareNodeWithValue(path, arg);
90             } else {
91                 appendQName(path, arg.getNodeType());
92             }
93         }
94
95         return path.toString();
96     }
97
98     private static void prepareNodeWithValue(final StringBuilder path, final PathArgument arg) {
99         path.append(arg.getNodeType().getLocalName());
100         path.append(ParserBuilderConstants.Deserializer.EQUAL);
101
102         String value = String.valueOf(((NodeWithValue<String>) arg).getValue());
103         if (Serializer.PERCENT_ENCODE_CHARS.matchesAnyOf(value)) {
104             value = parsePercentEncodeChars(value);
105         }
106         path.append(value);
107     }
108
109     private static void prepareNodeWithPredicates(final StringBuilder path, final PathArgument arg) {
110         path.append(arg.getNodeType().getLocalName());
111
112         final Iterator<Entry<QName, Object>> iterator = ((NodeIdentifierWithPredicates) arg).getKeyValues()
113                 .entrySet().iterator();
114
115         if (iterator.hasNext()) {
116             path.append(ParserBuilderConstants.Deserializer.EQUAL);
117         }
118
119         while (iterator.hasNext()) {
120             String valueOf = String.valueOf(iterator.next().getValue());
121             if (Serializer.PERCENT_ENCODE_CHARS.matchesAnyOf(valueOf)) {
122                 valueOf = parsePercentEncodeChars(valueOf);
123             }
124             path.append(valueOf);
125             if (iterator.hasNext()) {
126                 path.append(ParserBuilderConstants.Deserializer.COMMA);
127             }
128         }
129     }
130
131     /**
132      * Encode {@link Serializer#DISABLED_CHARS} chars to percent encoded chars.
133      *
134      * @param valueOf
135      *             string to encode
136      * @return encoded {@link String}
137      */
138     private static String parsePercentEncodeChars(final String valueOf) {
139         final StringBuilder sb = new StringBuilder();
140         int start = 0;
141         while (start < valueOf.length()) {
142             if (Serializer.PERCENT_ENCODE_CHARS.matches(valueOf.charAt(start))) {
143                 final String format = String.format("%x", (int) valueOf.charAt(start));
144                 final String upperCase = format.toUpperCase();
145                 sb.append(ParserBuilderConstants.Deserializer.PERCENT_ENCODING + upperCase);
146             } else {
147                 sb.append(valueOf.charAt(start));
148             }
149             start++;
150         }
151         return sb.toString();
152     }
153
154     /**
155      * Add {@link QName} to the serialized string.
156      *
157      * @param path
158      *             {@link StringBuilder}
159      * @param qname
160      *             {@link QName} node
161      * @return {@link StringBuilder}
162      */
163     private static StringBuilder appendQName(final StringBuilder path, final QName qname) {
164         path.append(qname.getLocalName());
165         return path;
166     }
167
168     /**
169      * Create prefix of namespace from {@link QName}.
170      *
171      * @param qname
172      *             {@link QName}
173      * @return {@link String}
174      */
175     private static String prefixForNamespace(final QName qname, final SchemaContext schemaContext) {
176         final URI namespace = qname.getNamespace();
177         Preconditions.checkArgument(namespace != null, "Failed to map QName {}", qname);
178         final Module module = schemaContext.findModuleByNamespaceAndRevision(namespace, qname.getRevision());
179         return module.getName();
180     }
181
182     private static final class MainVarsWrapper {
183
184         private DataSchemaContextNode<?> current;
185
186         MainVarsWrapper(final DataSchemaContextNode<?> current) {
187             this.setCurrent(current);
188         }
189
190         public DataSchemaContextNode<?> getCurrent() {
191             return this.current;
192         }
193
194         public void setCurrent(final DataSchemaContextNode<?> current) {
195             this.current = current;
196         }
197
198     }
199 }