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