Merge "Bug 1113 - ietf-restconf needs to specify the version of ietf-yangtypes that...
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / NodeUtils.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.yang.data.impl;
9
10 import java.util.AbstractMap.SimpleEntry;
11 import java.util.ArrayList;
12 import java.util.HashMap;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Stack;
16
17 import javax.xml.parsers.DocumentBuilder;
18 import javax.xml.parsers.DocumentBuilderFactory;
19 import javax.xml.parsers.ParserConfigurationException;
20 import javax.xml.xpath.XPath;
21 import javax.xml.xpath.XPathConstants;
22 import javax.xml.xpath.XPathExpression;
23 import javax.xml.xpath.XPathExpressionException;
24 import javax.xml.xpath.XPathFactory;
25
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
28 import org.opendaylight.yangtools.yang.data.api.ModifyAction;
29 import org.opendaylight.yangtools.yang.data.api.Node;
30 import org.opendaylight.yangtools.yang.data.api.NodeModification;
31 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
32 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
33 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38 import org.w3c.dom.Element;
39
40 import com.google.common.base.Joiner;
41 import com.google.common.collect.Lists;
42 import com.google.common.collect.Maps;
43
44 /**
45  * @author michal.rehak
46  *
47  */
48 public abstract class NodeUtils {
49
50     private static final Logger LOG = LoggerFactory.getLogger(NodeUtils.class);
51
52     /**
53      *
54      */
55     private static final String USER_KEY_NODE = "node";
56
57     /**
58      * @param node
59      * @return node path up till root node
60      */
61     public static String buildPath(final Node<?> node) {
62         List<String> breadCrumbs = new ArrayList<>();
63         Node<?> tmpNode = node;
64         while (tmpNode != null) {
65             breadCrumbs.add(0, tmpNode.getNodeType().getLocalName());
66             tmpNode = tmpNode.getParent();
67         }
68
69         return Joiner.on(".").join(breadCrumbs);
70     }
71
72     /**
73      * @param treeRootNode
74      * @return dom tree, containing same node structure, yang nodes are
75      *         associated to dom nodes as user data
76      */
77     public static org.w3c.dom.Document buildShadowDomTree(final CompositeNode treeRootNode) {
78         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
79         org.w3c.dom.Document doc = null;
80         try {
81             DocumentBuilder bob = dbf.newDocumentBuilder();
82             doc = bob.newDocument();
83         } catch (ParserConfigurationException e) {
84             LOG.error("documentBuilder problem", e);
85             return null;
86         }
87
88         Stack<SimpleEntry<org.w3c.dom.Node, Node<?>>> jobQueue = new Stack<>();
89         jobQueue.push(new SimpleEntry<org.w3c.dom.Node, Node<?>>(doc, treeRootNode));
90
91         while (!jobQueue.isEmpty()) {
92             SimpleEntry<org.w3c.dom.Node, Node<?>> job = jobQueue.pop();
93             org.w3c.dom.Node jointPlace = job.getKey();
94             Node<?> item = job.getValue();
95             QName nodeType = item.getNodeType();
96             Element itemEl = doc.createElementNS(nodeType.getNamespace().toString(), item.getNodeType().getLocalName());
97             itemEl.setUserData(USER_KEY_NODE, item, null);
98             if (item instanceof SimpleNode<?>) {
99                 Object value = ((SimpleNode<?>) item).getValue();
100                 if(value != null) {
101                     itemEl.setTextContent(String.valueOf(value));
102                 }
103             }
104             if (item instanceof NodeModification) {
105                 ModifyAction modificationAction = ((NodeModification) item).getModificationAction();
106                 if (modificationAction != null) {
107                     itemEl.setAttribute("modifyAction", modificationAction.toString());
108                 }
109             }
110
111             jointPlace.appendChild(itemEl);
112
113             if (item instanceof CompositeNode) {
114                 for (Node<?> child : ((CompositeNode) item).getValue()) {
115                     jobQueue.push(new SimpleEntry<org.w3c.dom.Node, Node<?>>(itemEl, child));
116                 }
117             }
118         }
119
120         return doc;
121     }
122
123     /**
124      * @param doc
125      * @param xpathEx
126      * @return user data value on found node
127      * @throws XPathExpressionException
128      */
129     @SuppressWarnings("unchecked")
130     public static <T> T findNodeByXpath(final org.w3c.dom.Document doc, final String xpathEx) throws XPathExpressionException {
131         T userNode = null;
132         XPathFactory xPathfactory = XPathFactory.newInstance();
133         XPath xpath = xPathfactory.newXPath();
134         XPathExpression expr = xpath.compile(xpathEx);
135
136         org.w3c.dom.Node result = (org.w3c.dom.Node) expr.evaluate(doc, XPathConstants.NODE);
137         if (result != null) {
138             userNode = (T) result.getUserData(USER_KEY_NODE);
139         }
140
141         return userNode;
142     }
143
144     /**
145      * build NodeMap, where key = qName; value = node
146      *
147      * @param value
148      * @return map of children, where key = qName and value is list of children
149      *         groupped by qName
150      */
151     public static Map<QName, List<Node<?>>> buildNodeMap(final List<Node<?>> value) {
152         Map<QName, List<Node<?>>> nodeMapTmp = Maps.newLinkedHashMap();
153         if (value == null) {
154             throw new IllegalStateException("nodeList should not be null or empty");
155         }
156         for (Node<?> node : value) {
157             List<Node<?>> qList = nodeMapTmp.get(node.getNodeType());
158             if (qList == null) {
159                 qList = new ArrayList<>();
160                 nodeMapTmp.put(node.getNodeType(), qList);
161             }
162             qList.add(node);
163         }
164         return nodeMapTmp;
165     }
166
167     /**
168      * @param context
169      * @return map of lists, where key = path; value = {@link DataSchemaNode}
170      */
171     public static Map<String, ListSchemaNode> buildMapOfListNodes(final SchemaContext context) {
172         Map<String, ListSchemaNode> mapOfLists = new HashMap<>();
173
174         Stack<DataSchemaNode> jobQueue = new Stack<>();
175         jobQueue.addAll(context.getDataDefinitions());
176
177         while (!jobQueue.isEmpty()) {
178             DataSchemaNode dataSchema = jobQueue.pop();
179             if (dataSchema instanceof ListSchemaNode) {
180                 mapOfLists.put(schemaPathToPath(dataSchema.getPath().getPath()), (ListSchemaNode) dataSchema);
181             }
182
183             if (dataSchema instanceof DataNodeContainer) {
184                 jobQueue.addAll(((DataNodeContainer) dataSchema).getChildNodes());
185             }
186         }
187
188         return mapOfLists;
189     }
190
191     /**
192      * @param qNamesPath
193      * @return path
194      */
195     private static String schemaPathToPath(final List<QName> qNamesPath) {
196         List<String> pathSeed = new ArrayList<>();
197         for (QName qNameItem : qNamesPath) {
198             pathSeed.add(qNameItem.getLocalName());
199         }
200         return Joiner.on(".").join(pathSeed);
201     }
202
203     /**
204      * add given node to it's parent's list of children
205      *
206      * @param newNode
207      */
208     public static void fixParentRelation(final Node<?> newNode) {
209         if (newNode.getParent() != null) {
210             List<Node<?>> siblings = newNode.getParent().getValue();
211             if (!siblings.contains(newNode)) {
212                 siblings.add(newNode);
213             }
214         }
215     }
216
217     /**
218      * crawl all children of given node and assign it as their parent
219      *
220      * @param parentNode
221      */
222     public static void fixChildrenRelation(final CompositeNode parentNode) {
223         if (parentNode.getValue() != null) {
224             for (Node<?> child : parentNode.getValue()) {
225                 if (child instanceof AbstractNodeTO<?>) {
226                     ((AbstractNodeTO<?>) child).setParent(parentNode);
227                 }
228             }
229         }
230     }
231
232     /**
233      * @param keys
234      * @param dataMap
235      * @return list of values of map, found by given keys
236      */
237     public static <T, K> List<K> collectMapValues(final List<T> keys, final Map<T, K> dataMap) {
238         List<K> valueSubList = new ArrayList<>();
239         for (T key : keys) {
240             valueSubList.add(dataMap.get(key));
241         }
242
243         return valueSubList;
244     }
245
246     /**
247      * @param nodes
248      * @return list of children in list of appropriate type
249      */
250     public static List<Node<?>> buildChildrenList(final Node<?>... nodes) {
251         return Lists.newArrayList(nodes);
252     }
253
254 }