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