8c3d8e94880a88bac967df66ae02c183b6c47d48
[yangtools.git] / restconf / restconf-util / src / main / java / org / opendaylight / yangtools / restconf / utils / RestconfUtils.java
1 /*
2  * Copyright (c) 2013 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.yangtools.restconf.utils;
9
10 import com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import com.google.common.collect.BiMap;
13 import com.google.common.collect.HashBiMap;
14 import com.google.common.collect.Iterables;
15 import java.io.IOException;
16 import java.io.InputStream;
17 import java.net.URI;
18 import java.net.URLEncoder;
19 import java.util.AbstractMap.SimpleEntry;
20 import java.util.Arrays;
21 import java.util.Date;
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Map.Entry;
26 import java.util.Set;
27 import javax.xml.parsers.DocumentBuilder;
28 import javax.xml.parsers.DocumentBuilderFactory;
29 import javax.xml.parsers.ParserConfigurationException;
30 import org.opendaylight.yangtools.yang.binding.DataObject;
31 import org.opendaylight.yangtools.yang.binding.RpcService;
32 import org.opendaylight.yangtools.yang.common.QName;
33 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
35 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
36 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
37 import org.opendaylight.yangtools.yang.data.api.Node;
38 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
39 import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
40 import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
41 import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider;
42 import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
43 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
45 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
46 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
47 import org.opendaylight.yangtools.yang.model.api.Module;
48 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51 import org.w3c.dom.Document;
52 import org.w3c.dom.Element;
53 import org.xml.sax.SAXException;
54
55 public class RestconfUtils {
56
57     private static final Logger logger = LoggerFactory.getLogger(RestconfUtils.class);
58
59     private static final BiMap<URI, String> uriToModuleName = HashBiMap.<URI, String> create();
60
61     public static Entry<String, DataSchemaNode> toRestconfIdentifier(
62             final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<?> bindingIdentifier,
63             final BindingIndependentMappingService mappingService, final SchemaContext schemaContext) {
64         YangInstanceIdentifier domIdentifier = mappingService.toDataDom(bindingIdentifier);
65         return toRestconfIdentifier(domIdentifier, schemaContext);
66     }
67
68     public static Entry<String, DataSchemaNode> toRestconfIdentifier(final YangInstanceIdentifier xmlInstanceIdentifier,
69             final SchemaContext schemaContext) {
70
71         final Iterable<YangInstanceIdentifier.PathArgument> elements = xmlInstanceIdentifier.getPathArguments();
72         final StringBuilder ret = new StringBuilder();
73         final QName startQName = elements.iterator().next().getNodeType();
74         URI namespace = startQName.getNamespace();
75         Date revision = startQName.getRevision();
76         final Module initialModule = schemaContext.findModuleByNamespaceAndRevision(namespace, revision);
77         DataNodeContainer node = (initialModule);
78         DataSchemaNode schemaNode = null;
79         for (final YangInstanceIdentifier.PathArgument element : elements) {
80             final DataSchemaNode potentialNode = node.getDataChildByName(element.getNodeType());
81             if (!isListOrContainer(potentialNode)) {
82                 return null;
83             }
84             node = ((DataNodeContainer) potentialNode);
85             schemaNode = potentialNode;
86             ret.append(convertToRestconfIdentifier(element, node, schemaContext));
87         }
88         return new SimpleEntry<>(ret.toString(), schemaNode);
89     }
90
91     private static CharSequence convertContainerToRestconfIdentifier(final YangInstanceIdentifier.NodeIdentifier argument,
92             final SchemaContext schemaContext) {
93         return "/" + toRestconfIdentifier(argument.getNodeType(), schemaContext);
94     }
95
96     private static CharSequence convertListToRestconfIdentifier(
97             final YangInstanceIdentifier.NodeIdentifierWithPredicates argument, final ListSchemaNode node,
98             final SchemaContext schemaContext) {
99         QName _nodeType = argument.getNodeType();
100         final CharSequence nodeIdentifier = toRestconfIdentifier(_nodeType, schemaContext);
101         final Map<QName, Object> keyValues = argument.getKeyValues();
102
103         StringBuilder sb = new StringBuilder();
104         sb.append('/');
105         sb.append(nodeIdentifier);
106         sb.append('/');
107
108         List<QName> keyDefinition = node.getKeyDefinition();
109         boolean _hasElements = false;
110         for (final QName key : keyDefinition) {
111             if (_hasElements) {
112                 sb.append('/');
113             } else {
114                 _hasElements = true;
115             }
116             Object _get = keyValues.get(key);
117             sb.append(toUriString(_get));
118         }
119
120         return sb.toString();
121     }
122
123     private static String toUriString(final Object object) {
124         if (object == null) {
125             return "";
126         }
127         String _string = object.toString();
128         return URLEncoder.encode(_string);
129     }
130
131     public static CharSequence toRestconfIdentifier(final QName qname, final SchemaContext schemaContext) {
132         URI namespace = qname.getNamespace();
133         String module = uriToModuleName.get(namespace);
134         if (module == null) {
135             Date revision = qname.getRevision();
136             final Module moduleSchema = schemaContext.findModuleByNamespaceAndRevision(namespace, revision);
137             if (moduleSchema == null) {
138                 return null;
139             }
140             String name = moduleSchema.getName();
141             uriToModuleName.put(namespace, name);
142             module = name;
143         }
144         return module + ':' + qname.getLocalName();
145     }
146
147     private static CharSequence convertToRestconfIdentifier(final YangInstanceIdentifier.PathArgument argument,
148             final DataNodeContainer node, final SchemaContext schemaContext) {
149         if (argument instanceof YangInstanceIdentifier.NodeIdentifier) {
150             return convertContainerToRestconfIdentifier((NodeIdentifier) argument, schemaContext);
151         } else if (argument instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates
152                 && node instanceof ListSchemaNode) {
153             return convertListToRestconfIdentifier((NodeIdentifierWithPredicates) argument, (ListSchemaNode) node,
154                     schemaContext);
155         } else {
156             throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.asList(argument, node).toString());
157         }
158     }
159
160     private static boolean isListOrContainer(final DataSchemaNode node) {
161         if (node instanceof ListSchemaNode || node instanceof ContainerSchemaNode) {
162             return true;
163         }
164         return false;
165     }
166
167     public static Module findModuleByNamespace(final URI namespace, final SchemaContext schemaContext) {
168         Preconditions.checkArgument(namespace != null);
169         final Set<Module> moduleSchemas = schemaContext.findModuleByNamespace(namespace);
170         Module filterLatestModule = null;
171         if (moduleSchemas != null) {
172             filterLatestModule = filterLatestModule(moduleSchemas);
173         }
174         return filterLatestModule;
175     }
176
177     private static Module filterLatestModule(final Iterable<Module> modules) {
178         Module latestModule = Iterables.getFirst(modules, null);
179         for (final Module module : modules) {
180             Date revision = module.getRevision();
181             Date latestModuleRevision = latestModule.getRevision();
182             if (revision.after(latestModuleRevision)) {
183                 latestModule = module;
184             }
185         }
186         return latestModule;
187     }
188
189     public String findModuleNameByNamespace(final URI namespace, final SchemaContext schemaContext) {
190         String moduleName = uriToModuleName.get(namespace);
191         if (moduleName == null) {
192             final Module module = findModuleByNamespace(namespace, schemaContext);
193             if (module == null) {
194                 return null;
195             }
196             moduleName = module.getName();
197             uriToModuleName.put(namespace, moduleName);
198         }
199         return moduleName;
200     }
201
202     /**
203      * Parse rpc services from input stream.
204      *
205      * @param inputStream
206      *            stream containing rpc services definition in xml
207      *            representation
208      * @param mappingService
209      *            current mapping service
210      * @param schemaContext
211      *            parsed yang data context
212      * @return Set of classes representing rpc services parsed from input stream
213      */
214     public static Set<Class<? extends RpcService>> rpcServicesFromInputStream(final InputStream inputStream,
215             final BindingIndependentMappingService mappingService, final SchemaContext schemaContext) {
216         try {
217             DocumentBuilderFactory documentBuilder = DocumentBuilderFactory.newInstance();
218             documentBuilder.setNamespaceAware(true);
219             DocumentBuilder builder = documentBuilder.newDocumentBuilder();
220             Document doc = builder.parse(inputStream);
221             Element rootElement = doc.getDocumentElement();
222
223             List<Node<?>> domNodes = XmlDocumentUtils.toDomNodes(rootElement,
224                     Optional.of(schemaContext.getChildNodes()));
225             Set<Class<? extends RpcService>> rpcServices = new HashSet<Class<? extends RpcService>>();
226             for (Node<?> node : domNodes) {
227                 if (node instanceof ImmutableCompositeNode) {
228                     ImmutableCompositeNode icNode = (ImmutableCompositeNode) node;
229                     QName namespace = null;
230                     QName revision = null;
231                     QName name = null;
232                     for (QName q : icNode.keySet()) {
233                         if (q.getLocalName().equals("namespace")) {
234                             namespace = q;
235                         }
236                         if (q.getLocalName().equals("revision")) {
237                             revision = q;
238                         }
239                         if (q.getLocalName().equals("name")) {
240                             name = q;
241                         }
242
243                     }
244
245                     // FIXME: Method getRpcServiceClassFor has been modified and
246                     // fixed to follow API contract. This call MUST be updated
247                     // to follow contract i.e. pass correct parameters:
248                     // "NAMESPACE" and "REVISION"
249                     Optional<Class<? extends RpcService>> rpcService = mappingService.getRpcServiceClassFor(
250                             icNode.get(name).get(0).getValue().toString(), icNode.get(revision).get(0).getValue()
251                                     .toString());
252                     if (rpcService.isPresent()) {
253                         rpcServices.add(rpcService.get());
254                     }
255                 }
256             }
257
258             return rpcServices;
259         } catch (ParserConfigurationException e) {
260             logger.trace("Parse configuration exception {}", e);
261         } catch (SAXException e) {
262             logger.trace("SAX exception {}", e);
263         } catch (IOException e) {
264             logger.trace("IOException {}", e);
265         }
266         return null;
267     }
268
269     /**
270      * Parse DataObject from input stream.
271      *
272      * @param path
273      *            identifier of expected result object
274      * @param inputStream
275      *            stream containing xml data to parse
276      * @param schemaContext
277      *            parsed yang data context
278      * @param mappingService
279      *            current mapping service
280      * @param dataSchema
281      *            yang data schema node representation of resulting data object
282      * @return DataObject instance parsed from input stream
283      */
284     public static DataObject dataObjectFromInputStream(
285             final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<?> path, final InputStream inputStream,
286             final SchemaContext schemaContext, final BindingIndependentMappingService mappingService,
287             final DataSchemaNode dataSchema) {
288         // Parse stream into w3c Document
289         try {
290             DocumentBuilderFactory documentBuilder = DocumentBuilderFactory.newInstance();
291             documentBuilder.setNamespaceAware(true);
292             DocumentBuilder builder = documentBuilder.newDocumentBuilder();
293             Document doc = builder.parse(inputStream);
294             Element rootElement = doc.getDocumentElement();
295             Node<?> domNode = XmlDocumentUtils.toDomNode(rootElement, Optional.of(dataSchema),
296                     Optional.<XmlCodecProvider> absent());
297             DataObject dataObject = mappingService.dataObjectFromDataDom(path, (CompositeNode) domNode); // getDataFromResponse
298             return dataObject;
299         } catch (DeserializationException e) {
300             logger.trace("Deserialization exception {}", e);
301         } catch (ParserConfigurationException e) {
302             logger.trace("Parse configuration exception {}", e);
303         } catch (SAXException e) {
304             logger.trace("SAX exception {}", e);
305         } catch (IOException e) {
306             logger.trace("IOException {}", e);
307         }
308         return null;
309     }
310
311 }