d311b07fcedbe1bd46309b3a9686fc9453511db3
[policy/drools-applications.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * Copyright (C) 2019-2020 AT&T Intellectual Property. All rights reserved.
4  * ================================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  * ============LICENSE_END=========================================================
17  */
18
19 package org.onap.policy.controlloop.utils;
20
21 import java.util.ArrayList;
22 import java.util.Iterator;
23 import java.util.LinkedHashMap;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Properties;
27 import java.util.regex.Matcher;
28 import java.util.regex.Pattern;
29 import org.onap.policy.controlloop.ControlLoopException;
30 import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
31 import org.onap.policy.controlloop.processor.ControlLoopProcessor;
32 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 /**
37  * Control Loop Utils.
38  */
39 public class ControlLoopUtils {
40
41     public static final Logger logger = LoggerFactory.getLogger(ControlLoopUtils.class);
42     private static final Pattern NAME_PAT = Pattern.compile("(.*)\\[(\\d{1,3})\\]");
43
44     private ControlLoopUtils() {
45         super();
46     }
47
48     /**
49      * Get a Control Loop Parameters object from a Tosca Policy.
50      */
51     public static ControlLoopParams toControlLoopParams(ToscaPolicy policy) {
52
53         /* No exceptions are thrown to keep the DRL simpler */
54
55         try {
56             return new ControlLoopProcessor(policy).getControlLoopParams();
57         } catch (ControlLoopException | RuntimeException e) {
58             logger.error("Invalid Policy because of {}: {}", e.getMessage(), policy, e);
59             return null;
60         }
61     }
62
63     // TODO move the following to policy-common/utils
64
65     /**
66      * Converts a set of properties to a Map. Supports json-path style property names with
67      * "." separating components, where components may have an optional subscript.
68      *
69      * @param properties properties to be converted
70      * @param prefix properties whose names begin with this prefix are included. The
71      *        prefix is stripped from the name before adding the value to the map
72      * @return a hierarchical map representing the properties
73      */
74     public static Map<String, Object> toObject(Properties properties, String prefix) {
75         String dottedPrefix = prefix + (prefix.isEmpty() || prefix.endsWith(".") ? "" : ".");
76         int pfxlen = dottedPrefix.length();
77
78         Map<String, Object> map = new LinkedHashMap<>();
79
80         for (String name : properties.stringPropertyNames()) {
81             if (name.startsWith(dottedPrefix)) {
82                 String[] components = name.substring(pfxlen).split("[.]");
83                 setProperty(map, components, properties.getProperty(name));
84             }
85         }
86
87         return map;
88     }
89
90     /**
91      * Sets a property within a hierarchical map.
92      *
93      * @param map map into which the value should be placed
94      * @param names property name components
95      * @param value value to be placed into the map
96      */
97     private static void setProperty(Map<String, Object> map, String[] names, String value) {
98         Map<String, Object> node = map;
99
100         final int lastComp = names.length - 1;
101
102         // process all but the final component
103         for (int comp = 0; comp < lastComp; ++comp) {
104             node = getNode(node, names[comp]);
105         }
106
107         // process the final component
108         String name = names[lastComp];
109         Matcher matcher = NAME_PAT.matcher(name);
110
111         if (!matcher.matches()) {
112             // no subscript
113             node.put(name, value);
114             return;
115         }
116
117         // subscripted
118         List<Object> array = getArray(node, matcher.group(1));
119         int index = Integer.parseInt(matcher.group(2));
120         expand(array, index);
121         array.set(index, value);
122     }
123
124     /**
125      * Gets a node.
126      *
127      * @param map map from which to get the object
128      * @param name name of the element to get from the map, with an optional subscript
129      * @return a Map
130      */
131     @SuppressWarnings("unchecked")
132     private static Map<String, Object> getNode(Map<String, Object> map, String name) {
133         Matcher matcher = NAME_PAT.matcher(name);
134
135         if (!matcher.matches()) {
136             // no subscript
137             return getObject(map, name);
138         }
139
140         // subscripted
141         List<Object> array = getArray(map, matcher.group(1));
142         int index = Integer.parseInt(matcher.group(2));
143         expand(array, index);
144
145         Object item = array.get(index);
146         if (item instanceof Map) {
147             return (Map<String, Object>) item;
148
149         } else {
150             LinkedHashMap<String, Object> result = new LinkedHashMap<>();
151             array.set(index, result);
152             return result;
153         }
154     }
155
156     /**
157      * Ensures that an array's size is large enough to hold the specified element.
158      *
159      * @param array array to be expanded
160      * @param index index of the desired element
161      */
162     private static void expand(List<Object> array, int index) {
163         while (array.size() <= index) {
164             array.add(null);
165         }
166     }
167
168     /**
169      * Gets an object (i.e., Map) from a map. If the particular element is not a Map, then
170      * it is replaced with an empty Map.
171      *
172      * @param map map from which to get the object
173      * @param name name of the element to get from the map, without any subscript
174      * @return a Map
175      */
176     private static Map<String, Object> getObject(Map<String, Object> map, String name) {
177         @SuppressWarnings("unchecked")
178         Map<String, Object> result = (Map<String, Object>) map.compute(name, (key, value) -> {
179             if (value instanceof Map) {
180                 return value;
181             } else {
182                 return new LinkedHashMap<>();
183             }
184         });
185
186         return result;
187     }
188
189     /**
190      * Gets an array from a map. If the particular element is not an array, then it is
191      * replaced with an empty array.
192      *
193      * @param map map from which to get the array
194      * @param name name of the element to get from the map, without any subscript
195      * @return an array
196      */
197     private static List<Object> getArray(Map<String, Object> map, String name) {
198         @SuppressWarnings("unchecked")
199         List<Object> result = (List<Object>) map.compute(name, (key, value) -> {
200             if (value instanceof List) {
201                 return value;
202             } else {
203                 return new ArrayList<>();
204             }
205         });
206
207         return result;
208     }
209
210     /**
211      * Compresses lists contained within a generic object, removing all {@code null}
212      * items.
213      *
214      * @param object object to be compressed
215      * @return the original object, modified in place
216      */
217     public static Object compressLists(Object object) {
218         if (object instanceof Map) {
219             @SuppressWarnings("unchecked")
220             Map<String, Object> asMap = (Map<String, Object>) object;
221             compressMapValues(asMap);
222
223         } else if (object instanceof List) {
224             @SuppressWarnings("unchecked")
225             List<Object> asList = (List<Object>) object;
226             compressListItems(asList);
227         }
228
229         // else: ignore anything else
230
231         return object;
232     }
233
234     /**
235      * Walks a hierarchical map and removes {@code null} items found in any Lists.
236      *
237      * @param map map whose lists are to be compressed
238      */
239     private static void compressMapValues(Map<String, Object> map) {
240         for (Object value : map.values()) {
241             compressLists(value);
242         }
243     }
244
245     /**
246      * Removes {@code null} items from the list. In addition, it walks the items within
247      * the list, compressing them, as well.
248      *
249      * @param list the list to be compressed
250      */
251     private static void compressListItems(List<Object> list) {
252         Iterator<Object> iter = list.iterator();
253         while (iter.hasNext()) {
254             Object item = iter.next();
255             if (item == null) {
256                 // null item - remove it
257                 iter.remove();
258
259             } else {
260                 compressLists(item);
261             }
262         }
263     }
264 }