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();
105 sb.append(nodeIdentifier);
108 List<QName> keyDefinition = node.getKeyDefinition();
109 boolean _hasElements = false;
110 for (final QName key : keyDefinition) {
116 Object _get = keyValues.get(key);
117 sb.append(toUriString(_get));
120 return sb.toString();
123 private static String toUriString(final Object object) {
124 if (object == null) {
127 String _string = object.toString();
128 return URLEncoder.encode(_string);
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) {
140 String name = moduleSchema.getName();
141 uriToModuleName.put(namespace, name);
144 return module + ':' + qname.getLocalName();
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,
156 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.asList(argument, node).toString());
160 private static boolean isListOrContainer(final DataSchemaNode node) {
161 if (node instanceof ListSchemaNode || node instanceof ContainerSchemaNode) {
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);
174 return filterLatestModule;
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;
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) {
196 moduleName = module.getName();
197 uriToModuleName.put(namespace, moduleName);
203 * Parse rpc services from input stream.
206 * stream containing rpc services definition in xml
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
214 public static Set<Class<? extends RpcService>> rpcServicesFromInputStream(final InputStream inputStream,
215 final BindingIndependentMappingService mappingService, final SchemaContext schemaContext) {
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();
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;
232 for (QName q : icNode.keySet()) {
233 if (q.getLocalName().equals("namespace")) {
236 if (q.getLocalName().equals("revision")) {
239 if (q.getLocalName().equals("name")) {
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()
252 if (rpcService.isPresent()) {
253 rpcServices.add(rpcService.get());
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);
270 * Parse DataObject from input stream.
273 * identifier of expected result object
275 * stream containing xml data to parse
276 * @param schemaContext
277 * parsed yang data context
278 * @param mappingService
279 * current mapping service
281 * yang data schema node representation of resulting data object
282 * @return DataObject instance parsed from input stream
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
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
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);