Fix NPE in toJsonString()
[ccsdk/sli/core.git] / sli / common / src / main / java / org / onap / ccsdk / sli / core / sli / SvcLogicContext.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP : CCSDK
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights
6  *                                              reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.ccsdk.sli.core.sli;
23
24 import java.util.*;
25
26 import com.google.gson.*;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29 import org.w3c.dom.Document;
30 import org.w3c.dom.Element;
31 import org.w3c.dom.Node;
32 import org.w3c.dom.NodeList;
33 import org.w3c.dom.Text;
34
35
36 public class SvcLogicContext {
37
38     private static final Logger LOG = LoggerFactory.getLogger(SvcLogicContext.class);
39     private final SecurePrinter securePrinter = new SecurePrinter();
40     public static final String CTX_NULL_VALUE="";
41     private static final String LENGTH="_length";
42
43     private HashMap<String, String> attributes;
44
45     private String status = SvcLogicConstants.SUCCESS;
46
47     public SvcLogicContext() {
48         this.attributes = new HashMap<>();
49
50     }
51
52     public SvcLogicContext(Properties props) {
53         this.attributes = new HashMap<>();
54
55         if (props.containsKey(CommonConstants.SERVICE_LOGIC_STATUS)) {
56             this.status = props.getProperty(CommonConstants.SERVICE_LOGIC_STATUS);
57         }
58
59         for (Object nameObj : props.keySet()) {
60             String propName = (String) nameObj;
61             attributes.put(propName, props.getProperty(propName));
62         }
63     }
64
65     public String getAttribute(String name) {
66         if (attributes.containsKey(name)) {
67             return attributes.get(name);
68         } else {
69             return null;
70         }
71     }
72
73     public void setAttribute(String name, String value) {
74         if (value == null) {
75             if (attributes.containsKey(name)) {
76                 attributes.remove(name);
77             }
78         } else {
79             attributes.put(name, value);
80         }
81     }
82
83     public Set<String> getAttributeKeySet() {
84         return attributes.keySet();
85     }
86
87     public Boolean isSuccess() {
88         return status.equals(SvcLogicConstants.SUCCESS);
89     }
90
91     @Deprecated
92     public String getStatus() {
93         return status;
94     }
95
96     @Deprecated
97     public void setStatus(String status) {
98         this.status = status;
99     }
100
101     public void markFailed() {
102         this.status = SvcLogicConstants.FAILURE;
103     }
104
105     public void markSuccess() {
106         this.status = SvcLogicConstants.SUCCESS;
107     }
108
109     public Properties toProperties() {
110         Properties props = new Properties();
111
112         if (status != null) {
113             props.setProperty(CommonConstants.SERVICE_LOGIC_STATUS, status);
114         }
115
116         String attrName;
117         String attrVal;
118         for (Map.Entry<String, String> entry : attributes.entrySet()) {
119             attrName = entry.getKey();
120             attrVal = entry.getValue();
121             if (attrVal == null) {
122                 LOG.warn("attribute {} value is null - setting to empty string", attrName);
123                 props.setProperty(attrName, "");
124             } else {
125                 props.setProperty(attrName, attrVal);
126             }
127         }
128
129         return props;
130     }
131
132     public void mergeDocument(String pfx, Document doc) {
133         String prefix = "";
134
135         if (pfx != null) {
136             prefix = pfx;
137         }
138
139         Element root = doc.getDocumentElement();
140
141         mergeElement(prefix, root, null);
142     }
143
144     public void mergeElement(String pfx, Element element, Map<String, Integer> nodeMap) {
145
146         // In XML, cannot tell the difference between containers and lists.
147         // So, have to treat each element as both (ugly but necessary).
148         // We do this by passing a nodeMap to be used to count instance of each tag,
149         // which will be used to set _length and to set index
150
151         LOG.trace("mergeElement({},{},{})", pfx, element.getTagName(), nodeMap);
152
153         String curTagName = element.getTagName();
154         String prefix = curTagName;
155
156         if (pfx != null) {
157             prefix = pfx + "." + prefix;
158         }
159
160         int myIdx = 0;
161
162         if (nodeMap != null) {
163             if (nodeMap.containsKey(curTagName)) {
164                 myIdx = nodeMap.get(curTagName);
165             }
166
167             nodeMap.put(curTagName, myIdx + 1);
168             this.setAttribute(prefix + "_length", Integer.toString(myIdx + 1));
169         }
170
171         NodeList children = element.getChildNodes();
172
173         int numChildren = children.getLength();
174
175         Map<String, Integer> childMap = new HashMap<>();
176         Map<String, Integer> idxChildMap = new HashMap<>();
177
178         for (int i = 0; i < numChildren; i++) {
179             Node curNode = children.item(i);
180
181             if (curNode instanceof Text) {
182                 Text curText = (Text) curNode;
183                 String curTextValue = curText.getTextContent();
184                 LOG.trace("Setting ctx variable {} = {}", prefix, curTextValue);
185                 this.setAttribute(prefix, curText.getTextContent());
186
187
188             } else if (curNode instanceof Element) {
189                 mergeElement(prefix, (Element) curNode, childMap);
190                 if (nodeMap != null) {
191
192                     mergeElement(prefix + "[" + myIdx + "]", (Element) curNode, idxChildMap);
193
194                 }
195             }
196         }
197
198     }
199
200     public void mergeJson(String pfx, String jsonString) {
201
202         JsonParser jp = new JsonParser();
203         JsonElement element = jp.parse(jsonString);
204         String root = "";
205         if ((pfx != null) && (pfx.length() > 0)) {
206             root = pfx + ".";
207         }
208         if (element.isJsonObject()) {
209             writeJsonObject(element.getAsJsonObject(), root);
210         } else if (element.isJsonArray()) {
211             handleJsonArray("", element.getAsJsonArray(), root);
212         }
213     }
214
215
216     protected void writeJsonObject(JsonObject obj, String root) {
217         for (Map.Entry<String, JsonElement> entry : obj.entrySet()) {
218             String key = entry.getKey();
219             if (entry.getValue().isJsonObject()) {
220                 writeJsonObject(entry.getValue().getAsJsonObject(), root + key + ".");
221             } else if (entry.getValue().isJsonArray()) {
222                 JsonArray array = entry.getValue().getAsJsonArray();
223                 handleJsonArray(key, array, root);
224             } else {
225                 //Handles when a JSON obj is nested within a JSON obj
226                 if(!root.endsWith(".")){
227                     root = root + ".";
228                 }
229                 if(entry.getValue().isJsonNull()) {
230                     this.setAttribute(root + key, CTX_NULL_VALUE);
231                 }else {
232                     this.setAttribute(root + key, entry.getValue().getAsString());
233                 }
234             }
235         }
236     }
237
238     protected void handleJsonArray(String key, JsonArray array,  String root) {
239         this.setAttribute(root + key + LENGTH, String.valueOf(array.size()));
240         Integer arrayIdx = 0;
241         for (JsonElement element : array) {
242             String prefix = root + key + "[" + arrayIdx + "]";
243
244             if (element.isJsonArray()) {
245                 handleJsonArray(key, element.getAsJsonArray(), prefix);
246             } else if (element.isJsonObject()) {
247                 writeJsonObject(element.getAsJsonObject(),  prefix + ".");
248             } else if (element.isJsonNull()) {
249                 this.setAttribute(prefix, CTX_NULL_VALUE);
250             } else if (element.isJsonPrimitive()) {
251                 this.setAttribute(prefix, element.getAsString());
252             }
253             arrayIdx++;
254         }
255     }
256
257
258     public String resolve(String ctxVarName) {
259
260         if (ctxVarName.indexOf('[') == -1) {
261             // Ctx variable contains no arrays
262             return getAttribute(ctxVarName);
263         }
264
265         // Resolve any array references
266         StringBuilder sbuff = new StringBuilder();
267         String[] ctxVarParts = ctxVarName.split("\\[");
268         sbuff.append(ctxVarParts[0]);
269         for (int i = 1; i < ctxVarParts.length; i++) {
270             if (ctxVarParts[i].startsWith("$")) {
271                 int endBracketLoc = ctxVarParts[i].indexOf(']');
272                 if (endBracketLoc == -1) {
273                     // Missing end bracket ... give up parsing
274                     LOG.warn("Variable reference {} seems to be missing a ']'", ctxVarName);
275                     return getAttribute(ctxVarName);
276                 }
277
278                 String idxVarName = ctxVarParts[i].substring(1, endBracketLoc);
279                 String remainder = ctxVarParts[i].substring(endBracketLoc);
280
281                 sbuff.append("[");
282                 sbuff.append(this.getAttribute(idxVarName));
283                 sbuff.append(remainder);
284
285             } else {
286                 // Index is not a variable reference
287                 sbuff.append("[");
288                 sbuff.append(ctxVarParts[i]);
289             }
290         }
291
292         return getAttribute(sbuff.toString());
293     }
294
295     public String toJsonString(String pfx) {
296         JsonParser jp = new JsonParser();
297
298         String jsonString = this.toJsonString();
299         JsonObject jsonRoot = (JsonObject) jp.parse(jsonString);
300         JsonObject targetJson = jsonRoot.getAsJsonObject(pfx);
301         if (targetJson == null) {
302             return("");
303         } else {
304             return(targetJson.toString());
305         }
306     }
307
308     public String toJsonString() {
309         JsonObject root = new JsonObject();
310         JsonElement lastJsonObject = root;
311         JsonElement currJsonLeaf = root;
312
313         String attrName = null;
314         String attrVal = null;
315
316         // Sort properties so that arrays will be reconstructed in proper order
317         TreeMap<String, String> sortedAttributes = new TreeMap<>();
318         sortedAttributes.putAll(attributes);
319
320         // Loop through properties, sorted by key
321         for (Map.Entry<String, String> entry : sortedAttributes.entrySet()) {
322             attrName = entry.getKey();
323             attrVal = entry.getValue();
324
325             currJsonLeaf = root;
326             String curFieldName = null;
327             JsonArray curArray = null;
328             lastJsonObject = null;
329             boolean addNeeded = false;
330
331             // Split property names by period and iterate through parts
332             for (String attrNamePart : attrName.split("\\.")) {
333
334                 // Add last object found to JSON tree.  Need to handle
335                                 // this way because last element found (leaf) needs to be
336                                 // assigned the property value.
337                 if (lastJsonObject != null) {
338                     if (addNeeded) {
339                         if (currJsonLeaf.isJsonArray()) {
340                             ((JsonArray) currJsonLeaf).add(lastJsonObject);
341                         } else {
342                             ((JsonObject) currJsonLeaf).add(curFieldName, lastJsonObject);
343                         }
344                     }
345                     currJsonLeaf = (JsonObject) lastJsonObject;
346                 }
347                 addNeeded = false;
348                 // See if current level should be a JsonArray or JsonObject based on
349                                 // whether name part contains square brackets.
350                 if (!attrNamePart.contains("[")) {
351                         // This level should be inserted as a JsonObject
352                     curFieldName = attrNamePart;
353                     lastJsonObject = ((JsonObject) currJsonLeaf).get(curFieldName);
354                     if (lastJsonObject == null) {
355                         lastJsonObject = new JsonObject();
356                         addNeeded = true;
357                     } else if (!lastJsonObject.isJsonObject()) {
358                         LOG.error("Unexpected condition - expecting to find JsonObject, but found " + lastJsonObject.getClass().getName());
359                         lastJsonObject = new JsonObject();
360                         addNeeded = true;
361                     }
362                 } else {
363                         // This level should be inserted as a JsonArray.
364
365                     String[] curFieldNameParts = attrNamePart.split("[\\[\\]]");
366                     curFieldName = curFieldNameParts[0];
367                     int curIndex = Integer.parseInt(curFieldNameParts[1]);
368
369
370                     curArray = ((JsonObject) currJsonLeaf).getAsJsonArray(curFieldName);
371
372                     if (curArray == null) {
373                         // This is the first time we see this array.
374                                                 // Create a new JsonArray and add it to current
375                                                 // leaf
376                         curArray = new JsonArray();
377                         ((JsonObject) currJsonLeaf).add(curFieldName, curArray);
378                     }
379
380                     // Current leaf should point to the JsonArray for this level.
381                                         // lastJsonObject should point to the array item entry to append
382                                         // the next level to - which is a new one if the index value
383                                         // isn't the end of the current array.
384                     currJsonLeaf = curArray;
385                     if (curArray.size() == curIndex + 1) {
386                         lastJsonObject = curArray.get(curArray.size() - 1);
387                     } else {
388                         lastJsonObject = new JsonObject();
389                         addNeeded = true;
390                     }
391                 }
392             }
393
394             // Done parsing property name.  Add the value of this
395                         // property to the current json leaf, either as a property
396                         // or as a string (if the current leaf is a JsonArray)
397
398             if (!curFieldName.endsWith("_length")) {
399                 if (currJsonLeaf.isJsonArray()) {
400                     if ("true".equals(attrVal) || "false".equals(attrVal)) {
401                         ((JsonArray) currJsonLeaf).add(Boolean.valueOf(attrVal));
402                     } else if ("null".equals(attrVal)) {
403                         ((JsonArray) currJsonLeaf).add(new JsonNull());
404                     } else {
405                         ((JsonArray) currJsonLeaf).add(attrVal);
406                     }
407                 } else {
408                     if (("true".equals(attrVal) || "false".equals(attrVal))) {
409                         ((JsonObject) currJsonLeaf).addProperty(curFieldName, Boolean.valueOf(attrVal));
410                     } else if ("null".equals(attrVal)){
411
412                         ((JsonObject) currJsonLeaf).add(curFieldName, new JsonNull());
413                     } else {
414                         ((JsonObject) currJsonLeaf).addProperty(curFieldName, attrVal);
415                     }
416                 }
417             }
418         }
419
420         return (root.toString());
421     }
422
423     public void printProperties(Properties props) {
424         securePrinter.printProperties(props);
425     }
426
427     public void printAttributes() {
428         securePrinter.printAttributes(attributes);
429     }
430
431     public void printProperties(Properties props, String subpath) {
432         securePrinter.printProperties(props, subpath);
433     }
434
435     public void printAttributes(String subpath) {
436         securePrinter.printAttributes(attributes, subpath);
437     }
438 }