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