blob: c1e87716fead1f3ddd08645b98d777d6112148a6 [file] [log] [blame]
Andrea Campanella423962b2016-02-26 13:09:22 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Andrea Campanella423962b2016-02-26 13:09:22 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.drivers.utilities;
18
19import com.google.common.collect.ArrayListMultimap;
20import com.google.common.collect.ImmutableList;
21import com.google.common.collect.Multimap;
22import org.apache.commons.configuration.ConfigurationException;
23import org.apache.commons.configuration.HierarchicalConfiguration;
24import org.apache.commons.configuration.XMLConfiguration;
25import org.apache.commons.configuration.tree.ConfigurationNode;
26import org.slf4j.Logger;
27import org.slf4j.LoggerFactory;
28
29import java.io.InputStream;
30import java.io.StringWriter;
31import java.util.ArrayList;
32import java.util.Collections;
33import java.util.Comparator;
34import java.util.HashMap;
35import java.util.Iterator;
36import java.util.List;
37import java.util.Map;
38
39import static org.onlab.util.Tools.nullIsNotFound;
40
41/**
42 * Util CLass for Yang models.
43 * Clean abstraction to read, obtain and populate
44 * XML from Yang models translated into XML skeletons.
45 */
46public class YangXmlUtils {
47
48 public final Logger log = LoggerFactory
49 .getLogger(getClass());
50
51 private static YangXmlUtils instance = null;
52
53 //no instantiation, single instance.
54 protected YangXmlUtils() {
55
56 }
57
58 /**
59 * Retrieves a valid XML configuration for a specific XML path for a single
60 * instance of the Map specified key-value pairs.
61 *
62 * @param file path of the file to be used.
63 * @param values map of key and values to set under the generic path.
64 * @return Hierarchical configuration containing XML with values.
65 */
66 public XMLConfiguration getXmlConfiguration(String file, Map<String, String> values) {
67 InputStream stream = getCfgInputStream(file);
68 XMLConfiguration cfg = loadXml(stream);
69 XMLConfiguration complete = new XMLConfiguration();
70 List<String> paths = new ArrayList<>();
71 Map<String, String> valuesWithKey = new HashMap<>();
Sho SHIMIZUa09e1bb2016-08-01 14:25:25 -070072 values.keySet().forEach(path -> {
Andrea Campanella423962b2016-02-26 13:09:22 -080073 List<String> allPaths = findPaths(cfg, path);
74 String key = nullIsNotFound(allPaths.isEmpty() ? null : allPaths.get(0),
75 "Yang model does not contain desired path");
76 paths.add(key);
77 valuesWithKey.put(key, values.get(path));
78 });
79 Collections.sort(paths, new StringLengthComparator());
80 paths.forEach(key -> complete.setProperty(key, valuesWithKey.get(key)));
81 addProperties(cfg, complete);
82 return complete;
83 }
84
85
86 /**
87 * Retrieves a valid XML configuration for a specific XML path for multiple
88 * instance of YangElements objects.
89 *
90 * @param file path of the file to be used.
91 * @param elements List of YangElements that are to be set.
92 * @return Hierachical configuration containing XML with values.
93 */
94 public XMLConfiguration getXmlConfiguration(String file, List<YangElement> elements) {
95 InputStream stream = getCfgInputStream(file);
96 HierarchicalConfiguration cfg = loadXml(stream);
97 XMLConfiguration complete = new XMLConfiguration();
98 Multimap<String, YangElement> commonElements = ArrayListMultimap.create();
99
100 //saves the elements in a Multimap based on the computed key.
101 elements.forEach(element -> {
102 String completeKey = nullIsNotFound(findPath(cfg, element.getBaseKey()),
103 "Yang model does not contain desired path");
104 commonElements.put(completeKey, element);
105 });
106
107 //iterates over the elements and constructs the configuration
108 commonElements.keySet().forEach(key -> {
109 // if there is more than one element for a given path
110 if (commonElements.get(key).size() > 1) {
111 //creates a list of nodes that have to be added for that specific path
112 ArrayList<ConfigurationNode> nodes = new ArrayList<>();
113 //creates the nodes
114 commonElements.get(key).forEach(element -> nodes.add(getInnerNode(element).getRootNode()));
115 //computes the parent path
116 String parentPath = key.substring(0, key.lastIndexOf("."));
117 //adds the nodes to the complete configuration
118 complete.addNodes(parentPath, nodes);
119 } else {
120 //since there is only a single element we can assume it's the first one.
121 Map<String, String> keysAndValues = commonElements.get(key).stream().
122 findFirst().get().getKeysAndValues();
123 keysAndValues.forEach((k, v) -> complete.setProperty(key + "." + k, v));
124 }
125 });
126 addProperties(cfg, complete);
127 return complete;
128 }
129
130 //Adds all the properties of the original configuration to the new one.
131 private void addProperties(HierarchicalConfiguration cfg, HierarchicalConfiguration complete) {
132 cfg.getKeys().forEachRemaining(key -> {
133 String property = (String) cfg.getProperty(key);
134 if (!property.equals("")) {
135 complete.setProperty(key, property);
136 }
137 });
138 }
139
140 protected InputStream getCfgInputStream(String file) {
141 return getClass().getResourceAsStream(file);
142 }
143
144 /**
145 * Reads a valid XML configuration and returns a Map containing XML field name.
146 * and value contained for every subpath.
147 *
148 * @param cfg the Configuration to read.
149 * @param path path of the information to be read.
150 * @return list of elements containing baskey and map of key value pairs.
151 */
152 public List<YangElement> readXmlConfiguration(HierarchicalConfiguration cfg, String path) {
153 List<YangElement> elements = new ArrayList<>();
154
155 String key = nullIsNotFound(findPath(cfg, path), "Configuration does not contain desired path");
156
157 getElements(cfg.configurationsAt(key), elements, key, cfg, path, key);
158 return ImmutableList.copyOf(elements);
159 }
160
161 private void getElements(List<HierarchicalConfiguration> configurations,
162 List<YangElement> elements, String basekey,
163 HierarchicalConfiguration originalCfg, String path,
164 String originalKey) {
165 //consider each sub configuration
Sho SHIMIZUa09e1bb2016-08-01 14:25:25 -0700166 configurations.forEach(config -> {
Andrea Campanella423962b2016-02-26 13:09:22 -0800167
168 YangElement element = new YangElement(path, new HashMap<>());
169 //for each of the keys of the sub configuration
170 config.getKeys().forEachRemaining(key -> {
171 //considers only one step ahead
172 //if one step ahead has other steps calls self to analize them
173 //else adds to yang element.
174 if (key.split("\\.").length > 1) {
175 getElements(originalCfg.configurationsAt(basekey + "." + key.split("\\.")[0]),
176 elements, basekey + "." + key.split("\\.")[0], originalCfg, path,
177 originalKey);
178 } else {
179 String replaced = basekey.replace(originalKey, "");
180 String partialKey = replaced.isEmpty() ? key : replaced.substring(1) + "." + key;
181 partialKey = partialKey.isEmpty() ? originalKey : partialKey;
182 //Adds values to the element with a subkey starting from the requeste path onwards
183 element.getKeysAndValues().put(partialKey, config.getProperty(key).toString());
184 }
185 });
186 //if the element doesnt already exist
187 if (!elements.contains(element) && !element.getKeysAndValues().isEmpty()) {
188 elements.add(element);
189 }
190 });
191 }
192
193 /**
194 * Single Instance of Yang utilities retriever.
195 *
196 * @return instance of YangXmlUtils
197 */
198 public static YangXmlUtils getInstance() {
199 if (instance == null) {
200 instance = new YangXmlUtils();
201 }
202 return instance;
203 }
204
205 /**
206 * Return the string representation of the XMLConfig without header
207 * and configuration element.
208 *
209 * @param cfg the XML to convert
210 * @return the cfg string.
211 */
212 public String getString(XMLConfiguration cfg) {
213 StringWriter stringWriter = new StringWriter();
214 try {
215 cfg.save(stringWriter);
216 } catch (ConfigurationException e) {
217 log.error("Cannot convert configuration", e.getMessage());
218 }
219 String xml = stringWriter.toString();
220 xml = xml.substring(xml.indexOf("\n"));
221 xml = xml.substring(xml.indexOf(">") + 1);
222 return xml;
223 }
224
225 /**
226 * Method to read an input stream into a XMLConfiguration.
227 * @param xmlStream inputstream containing XML description
228 * @return the XMLConfiguration object
229 */
230 public XMLConfiguration loadXml(InputStream xmlStream) {
231 XMLConfiguration cfg = new XMLConfiguration();
232 try {
233 cfg.load(xmlStream);
234 return cfg;
235 } catch (ConfigurationException e) {
236 throw new IllegalArgumentException("Cannot load xml from Stream", e);
237 }
238 }
239
240 //Finds all paths for a corresponding element
241 private List<String> findPaths(HierarchicalConfiguration cfg, String path) {
242 List<String> paths = new ArrayList<>();
243 cfg.getKeys().forEachRemaining(key -> {
244 if (key.equals(path)) {
245 paths.add(key);
246 }
247 if (key.contains("." + path)) {
248 paths.add(key);
249 }
250 });
251 return paths;
252 }
253
254 //Finds the first parent path corresponding to an element.
255 private String findPath(HierarchicalConfiguration cfg, String element) {
256 Iterator<String> it = cfg.getKeys();
257 while (it.hasNext()) {
258 String key = it.next();
259 String[] arr = key.split("\\.");
260 for (int i = 0; i < arr.length; i++) {
261 if (element.equals(arr[i])) {
262 String completeKey = "";
263 for (int j = 0; j <= i; j++) {
264 completeKey = completeKey + "." + arr[j];
265 }
266 return completeKey.substring(1);
267 }
268 }
269 }
270 return null;
271 }
272
273 //creates a node based on a single Yang element.
274 private HierarchicalConfiguration getInnerNode(YangElement element) {
275 HierarchicalConfiguration node = new HierarchicalConfiguration();
276 node.setRoot(new HierarchicalConfiguration.Node(element.getBaseKey()));
277 element.getKeysAndValues().forEach(node::setProperty);
278 return node;
279 }
280
281 //String lenght comparator
282 private class StringLengthComparator implements Comparator<String> {
283
284 public int compare(String o1, String o2) {
285 if (o2 == null && o1 == null) {
286 return 0;
287 }
288
289 if (o1 == null) {
290 return o2.length();
291 }
292
293 if (o2 == null) {
294 return o1.length();
295 }
296
297 if (o1.length() != o2.length()) {
298 return o1.length() - o2.length(); //overflow impossible since lengths are non-negative
299 }
300 return o1.compareTo(o2);
301 }
302 }
303
304}