2 * Copyright (c) 2013 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.yangtools.restconf.utils;
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;
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;
25 import java.util.Map.Entry;
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;
55 public class RestconfUtils {
57 private static final Logger logger = LoggerFactory.getLogger(RestconfUtils.class);
59 private static final BiMap<URI, String> uriToModuleName = HashBiMap.<URI, String> create();
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);
68 public static Entry<String, DataSchemaNode> toRestconfIdentifier(final YangInstanceIdentifier xmlInstanceIdentifier,
69 final SchemaContext schemaContext) {
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)) {
84 node = ((DataNodeContainer) potentialNode);
85 schemaNode = potentialNode;
86 ret.append(convertToRestconfIdentifier(element, node, schemaContext));
88 return new SimpleEntry<>(ret.toString(), schemaNode);
91 private static CharSequence convertContainerToRestconfIdentifier(final YangInstanceIdentifier.NodeIdentifier argument,
92 final SchemaContext schemaContext) {
93 return "/" + toRestconfIdentifier(argument.getNodeType(), schemaContext);
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();
103 StringBuilder sb = new StringBuilder('/');
104 sb.append(nodeIdentifier);
107 List<QName> keyDefinition = node.getKeyDefinition();
108 boolean _hasElements = false;
109 for (final QName key : keyDefinition) {
115 Object _get = keyValues.get(key);
116 sb.append(toUriString(_get));
119 return sb.toString();
122 private static String toUriString(final Object object) {
123 if (object == null) {
126 String _string = object.toString();
127 return URLEncoder.encode(_string);
130 public static CharSequence toRestconfIdentifier(final QName qname, final SchemaContext schemaContext) {
131 URI namespace = qname.getNamespace();
132 String module = uriToModuleName.get(namespace);
133 if (module == null) {
134 Date revision = qname.getRevision();
135 final Module moduleSchema = schemaContext.findModuleByNamespaceAndRevision(namespace, revision);
136 if (moduleSchema == null) {
139 String name = moduleSchema.getName();
140 uriToModuleName.put(namespace, name);
143 return module + ':' + qname.getLocalName();
146 private static CharSequence convertToRestconfIdentifier(final YangInstanceIdentifier.PathArgument argument,
147 final DataNodeContainer node, final SchemaContext schemaContext) {
148 if (argument instanceof YangInstanceIdentifier.NodeIdentifier) {
149 return convertContainerToRestconfIdentifier((NodeIdentifier) argument, schemaContext);
150 } else if (argument instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates
151 && node instanceof ListSchemaNode) {
152 return convertListToRestconfIdentifier((NodeIdentifierWithPredicates) argument, (ListSchemaNode) node,
155 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.asList(argument, node).toString());
159 private static boolean isListOrContainer(final DataSchemaNode node) {
160 if (node instanceof ListSchemaNode || node instanceof ContainerSchemaNode) {
166 public static Module findModuleByNamespace(final URI namespace, final SchemaContext schemaContext) {
167 Preconditions.checkArgument(namespace != null);
168 final Set<Module> moduleSchemas = schemaContext.findModuleByNamespace(namespace);
169 Module filterLatestModule = null;
170 if (moduleSchemas != null) {
171 filterLatestModule = filterLatestModule(moduleSchemas);
173 return filterLatestModule;
176 private static Module filterLatestModule(final Iterable<Module> modules) {
177 Module latestModule = Iterables.getFirst(modules, null);
178 for (final Module module : modules) {
179 Date revision = module.getRevision();
180 Date latestModuleRevision = latestModule.getRevision();
181 if (revision.after(latestModuleRevision)) {
182 latestModule = module;
188 public String findModuleNameByNamespace(final URI namespace, final SchemaContext schemaContext) {
189 String moduleName = uriToModuleName.get(namespace);
190 if (moduleName == null) {
191 final Module module = findModuleByNamespace(namespace, schemaContext);
192 if (module == null) {
195 moduleName = module.getName();
196 uriToModuleName.put(namespace, moduleName);
202 * Parse rpc services from input stream.
205 * stream containing rpc services definition in xml
207 * @param mappingService
208 * current mapping service
209 * @param schemaContext
210 * parsed yang data context
211 * @return Set of classes representing rpc services parsed from input stream
213 public static Set<Class<? extends RpcService>> rpcServicesFromInputStream(final InputStream inputStream,
214 final BindingIndependentMappingService mappingService, final SchemaContext schemaContext) {
216 DocumentBuilderFactory documentBuilder = DocumentBuilderFactory.newInstance();
217 documentBuilder.setNamespaceAware(true);
218 DocumentBuilder builder = documentBuilder.newDocumentBuilder();
219 Document doc = builder.parse(inputStream);
220 Element rootElement = doc.getDocumentElement();
222 List<Node<?>> domNodes = XmlDocumentUtils.toDomNodes(rootElement,
223 Optional.of(schemaContext.getChildNodes()));
224 Set<Class<? extends RpcService>> rpcServices = new HashSet<Class<? extends RpcService>>();
225 for (Node<?> node : domNodes) {
226 if (node instanceof ImmutableCompositeNode) {
227 ImmutableCompositeNode icNode = (ImmutableCompositeNode) node;
228 QName namespace = null;
229 QName revision = null;
231 for (QName q : icNode.keySet()) {
232 if (q.getLocalName().equals("namespace")) {
235 if (q.getLocalName().equals("revision")) {
238 if (q.getLocalName().equals("name")) {
244 // FIXME: Method getRpcServiceClassFor has been modified and
245 // fixed to follow API contract. This call MUST be updated
246 // to follow contract i.e. pass correct parameters:
247 // "NAMESPACE" and "REVISION"
248 Optional<Class<? extends RpcService>> rpcService = mappingService.getRpcServiceClassFor(
249 icNode.get(name).get(0).getValue().toString(), icNode.get(revision).get(0).getValue()
251 if (rpcService.isPresent()) {
252 rpcServices.add(rpcService.get());
258 } catch (ParserConfigurationException e) {
259 logger.trace("Parse configuration exception {}", e);
260 } catch (SAXException e) {
261 logger.trace("SAX exception {}", e);
262 } catch (IOException e) {
263 logger.trace("IOException {}", e);
269 * Parse DataObject from input stream.
272 * identifier of expected result object
274 * stream containing xml data to parse
275 * @param schemaContext
276 * parsed yang data context
277 * @param mappingService
278 * current mapping service
280 * yang data schema node representation of resulting data object
281 * @return DataObject instance parsed from input stream
283 public static DataObject dataObjectFromInputStream(
284 final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<?> path, final InputStream inputStream,
285 final SchemaContext schemaContext, final BindingIndependentMappingService mappingService,
286 final DataSchemaNode dataSchema) {
287 // Parse stream into w3c Document
289 DocumentBuilderFactory documentBuilder = DocumentBuilderFactory.newInstance();
290 documentBuilder.setNamespaceAware(true);
291 DocumentBuilder builder = documentBuilder.newDocumentBuilder();
292 Document doc = builder.parse(inputStream);
293 Element rootElement = doc.getDocumentElement();
294 Node<?> domNode = XmlDocumentUtils.toDomNode(rootElement, Optional.of(dataSchema),
295 Optional.<XmlCodecProvider> absent());
296 DataObject dataObject = mappingService.dataObjectFromDataDom(path, (CompositeNode) domNode); // getDataFromResponse
298 } catch (DeserializationException e) {
299 logger.trace("Deserialization exception {}", e);
300 } catch (ParserConfigurationException e) {
301 logger.trace("Parse configuration exception {}", e);
302 } catch (SAXException e) {
303 logger.trace("SAX exception {}", e);
304 } catch (IOException e) {
305 logger.trace("IOException {}", e);