4ab3d6f860f7d854a305f7f8f1cde54d4948c7e3
[ccsdk/cds.git] / ms / blueprintsprocessor / modules / blueprints / blueprint-core / src / main / kotlin / org / onap / ccsdk / cds / controllerblueprints / core / CustomFunctions.kt
1 /*
2  * Copyright © 2017-2018 AT&T Intellectual Property.
3  * Modifications Copyright © 2019 IBM.
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  */
17
18 package org.onap.ccsdk.cds.controllerblueprints.core
19
20 import com.fasterxml.jackson.databind.JsonNode
21 import com.fasterxml.jackson.databind.node.ArrayNode
22 import com.fasterxml.jackson.databind.node.BooleanNode
23 import com.fasterxml.jackson.databind.node.DoubleNode
24 import com.fasterxml.jackson.databind.node.IntNode
25 import com.fasterxml.jackson.databind.node.MissingNode
26 import com.fasterxml.jackson.databind.node.NullNode
27 import com.fasterxml.jackson.databind.node.ObjectNode
28 import com.fasterxml.jackson.databind.node.TextNode
29 import org.apache.commons.lang3.ObjectUtils
30 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
31 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JsonParserUtils
32 import org.slf4j.LoggerFactory
33 import org.slf4j.helpers.MessageFormatter
34 import java.util.UUID
35 import kotlin.reflect.KClass
36
37 /**
38  *
39  *
40  * @author Brinda Santh
41  */
42
43 fun <T : Any> logger(clazz: T) = LoggerFactory.getLogger(clazz.javaClass)!!
44
45 fun <T : KClass<*>> logger(clazz: T) = LoggerFactory.getLogger(clazz.java)!!
46
47 fun <T : Any> T?.defaultToEmpty(): String {
48     return this?.toString() ?: ""
49 }
50
51 fun <T : Any> T?.defaultToUUID(): String {
52     return this?.toString() ?: UUID.randomUUID().toString()
53 }
54
55 fun <T : Any> T.bpClone(): T {
56     return ObjectUtils.clone(this)
57 }
58
59 fun String.splitCommaAsList(): List<String> {
60     return this.split(",").map { it.trim() }.toList()
61 }
62
63 fun String.isJson(): Boolean {
64     return ((this.trim().startsWith("{") && this.trim().endsWith("}")) ||
65         (this.trim().startsWith("[") && this.trim().endsWith("]")))
66 }
67
68 fun Any.asJsonString(intend: Boolean? = false): String {
69     return JacksonUtils.getJson(this, intend!!)
70 }
71
72 fun String.asJsonPrimitive(): TextNode {
73     return TextNode(this)
74 }
75
76 inline fun <reified T : Any> String.jsonAsType(): T {
77     return JacksonUtils.readValue<T>(this.trim())
78 }
79
80 // If you know the string is json content, then use the function directly
81 fun String.jsonAsJsonType(): JsonNode {
82     return JacksonUtils.jsonNode(this.trim())
83 }
84
85 fun Boolean.asJsonPrimitive(): BooleanNode {
86     return BooleanNode.valueOf(this)
87 }
88
89 fun Int.asJsonPrimitive(): IntNode {
90     return IntNode.valueOf(this)
91 }
92
93 fun Double.asJsonPrimitive(): DoubleNode {
94     return DoubleNode.valueOf(this)
95 }
96
97 /**
98  * Utility to convert Primitive object to Json Type Primitive.
99  */
100 fun <T : Any?> T.asJsonPrimitive(): JsonNode {
101     return if (this == null || this is MissingNode || this is NullNode) {
102         NullNode.instance
103     } else {
104         when (this) {
105             is String ->
106                 this.asJsonPrimitive()
107             is Boolean ->
108                 this.asJsonPrimitive()
109             is Int ->
110                 this.asJsonPrimitive()
111             is Double ->
112                 this.asJsonPrimitive()
113             else ->
114                 throw BluePrintException("$this type is not supported")
115         }
116     }
117 }
118
119 /** Based on Blueprint DataType Convert string value to JsonNode Type **/
120 fun String.asJsonType(bpDataType: String): JsonNode {
121     return when (bpDataType.toLowerCase()) {
122         BluePrintConstants.DATA_TYPE_STRING -> this.asJsonPrimitive()
123         BluePrintConstants.DATA_TYPE_BOOLEAN -> this.toBoolean().asJsonPrimitive()
124         BluePrintConstants.DATA_TYPE_INTEGER -> this.toInt().asJsonPrimitive()
125         BluePrintConstants.DATA_TYPE_FLOAT -> this.toFloat().asJsonPrimitive()
126         BluePrintConstants.DATA_TYPE_DOUBLE -> this.toDouble().asJsonPrimitive()
127         // For List, Map and Complex Types.
128         else -> this.jsonAsJsonType()
129     }
130 }
131
132 /**
133  * Utility to convert Complex or Primitive object or ByteArray to Json Type.
134  */
135 fun <T : Any?> T.asJsonType(): JsonNode {
136     return if (this == null || this is MissingNode || this is NullNode) {
137         NullNode.instance
138     } else {
139         when (this) {
140             is JsonNode -> this
141             is ByteArray -> JacksonUtils.objectMapper.reader().readTree(this.inputStream())
142             is String -> {
143                 if (this.isJson())
144                     this.jsonAsJsonType()
145                 else
146                     TextNode(this)
147             }
148             is Boolean -> BooleanNode.valueOf(this)
149             is Int -> IntNode.valueOf(this.toInt())
150             is Double -> DoubleNode.valueOf(this.toDouble())
151             else -> JacksonUtils.jsonNodeFromObject(this)
152         }
153     }
154 }
155
156 fun Map<String, *>.asJsonNode(): JsonNode {
157     return JacksonUtils.jsonNodeFromObject(this)
158 }
159
160 fun Map<String, *>.asObjectNode(): ObjectNode {
161     return JacksonUtils.objectNodeFromObject(this)
162 }
163
164 fun format(message: String, vararg args: Any?): String {
165     if (args != null && args.isNotEmpty()) {
166         return MessageFormatter.arrayFormat(message, args).message
167     }
168     return message
169 }
170
171 fun <T : Any> Map<String, *>.castOptionalValue(key: String, valueType: KClass<T>): T? {
172     return if (containsKey(key)) {
173         get(key) as? T
174     } else {
175         null
176     }
177 }
178
179 fun <T : Any> Map<String, *>.castValue(key: String, valueType: KClass<T>): T {
180     if (containsKey(key)) {
181         return get(key) as T
182     } else {
183         throw BluePrintException("couldn't find the key $key")
184     }
185 }
186
187 fun ArrayNode.asListOfString(): List<String> {
188     return JacksonUtils.getListFromJsonNode(this, String::class.java)
189 }
190
191 fun JsonNode.asByteArray(): ByteArray {
192     val writer = JacksonUtils.objectMapper.writer()
193     return writer.writeValueAsBytes(this)
194 }
195
196 fun <T> JsonNode.asType(clazzType: Class<T>): T {
197     return JacksonUtils.readValue(this, clazzType)
198         ?: throw BluePrintException("couldn't convert JsonNode of type $clazzType")
199 }
200
201 fun JsonNode.asListOfString(): List<String> {
202     check(this is ArrayNode) { "JsonNode is not of type ArrayNode" }
203     return this.asListOfString()
204 }
205
206 fun <T : JsonNode> T?.returnNullIfMissing(): JsonNode? {
207     return if (this == null || this is NullNode || this is MissingNode) {
208         null
209     } else this
210 }
211
212 fun <T : JsonNode> T?.isNullOrMissing(): Boolean {
213     return this == null || this is NullNode || this is MissingNode
214 }
215
216 /**
217  * Convert Json to map of json node, the root fields will be map keys
218  */
219 fun JsonNode.rootFieldsToMap(): MutableMap<String, JsonNode> {
220     if (this is ObjectNode) {
221         val propertyMap: MutableMap<String, JsonNode> = linkedMapOf()
222         this.fields().forEach {
223             propertyMap[it.key] = it.value
224         }
225         return propertyMap
226     } else {
227         throw BluePrintException("json node should be Object Node Type")
228     }
229 }
230
231 fun JsonNode.removeNullNode() {
232     val it = this.iterator()
233     while (it.hasNext()) {
234         val child = it.next()
235         if (child.isNull) {
236             it.remove()
237         } else {
238             child.removeNullNode()
239         }
240     }
241 }
242
243 fun MutableMap<String, JsonNode>.putJsonElement(key: String, value: Any) {
244     val convertedValue = value.asJsonType()
245     this[key] = convertedValue
246 }
247
248 fun Map<String, JsonNode>.getAsString(key: String): String {
249     return this[key]?.asText() ?: throw BluePrintException("couldn't find value for key($key)")
250 }
251
252 fun Map<String, JsonNode>.getAsBoolean(key: String): Boolean {
253     return this[key]?.asBoolean() ?: throw BluePrintException("couldn't find value for key($key)")
254 }
255
256 fun Map<String, JsonNode>.getAsInt(key: String): Int {
257     return this[key]?.asInt() ?: throw BluePrintException("couldn't find value for key($key)")
258 }
259
260 fun Map<String, JsonNode>.getAsDouble(key: String): Double {
261     return this[key]?.asDouble() ?: throw BluePrintException("couldn't find value for key($key)")
262 }
263
264 fun Map<String, JsonNode>.getOptionalAsString(key: String): String? {
265     return if (this.containsKey(key)) this[key]!!.asText() else null
266 }
267
268 fun Map<String, JsonNode>.getOptionalAsBoolean(key: String): Boolean? {
269     return if (this.containsKey(key)) this[key]!!.asBoolean() else null
270 }
271
272 fun Map<String, JsonNode>.getOptionalAsInt(key: String): Int? {
273     return if (this.containsKey(key)) this[key]!!.asInt() else null
274 }
275
276 fun Map<String, JsonNode>.getOptionalAsDouble(key: String): Double? {
277     return if (this.containsKey(key)) this[key]!!.asDouble() else null
278 }
279
280 // Checks
281
282 inline fun checkEquals(value1: String?, value2: String?, lazyMessage: () -> Any): Boolean {
283     if (value1.equals(value2, ignoreCase = true)) {
284         return true
285     } else {
286         throw BluePrintException(lazyMessage().toString())
287     }
288 }
289
290 inline fun checkNotEmpty(value: String?, lazyMessage: () -> Any): String {
291     if (value == null || value.isEmpty()) {
292         val message = lazyMessage()
293         throw IllegalStateException(message.toString())
294     } else {
295         return value
296     }
297 }
298
299 inline fun checkNotBlank(value: String?, lazyMessage: () -> Any): String {
300     if (value == null || value.isBlank()) {
301         val message = lazyMessage()
302         throw IllegalStateException(message.toString())
303     } else {
304         return value
305     }
306 }
307
308 fun isNotEmpty(value: String?): Boolean {
309     return value != null && value.isNotEmpty()
310 }
311
312 fun isNotBlank(value: String?): Boolean {
313     return value != null && value.isNotBlank()
314 }
315
316 fun <T : String> T?.emptyTONull(): String? {
317     return if (this == null || this.isEmpty()) null else this
318 }
319
320 fun nullToEmpty(value: String?): String {
321     return if (isNotEmpty(value)) value!! else ""
322 }
323
324 inline fun <reified T : JsonNode> T.isComplexType(): Boolean {
325     return this is ObjectNode || this is ArrayNode
326 }
327
328 // Json Parsing Extensions
329 fun JsonNode.jsonPathParse(expression: String): JsonNode {
330     check(this.isComplexType()) { "$this is not complex or array node to apply expression" }
331     return JsonParserUtils.parse(this, expression)
332 }
333
334 // Json Path Extensions
335 fun JsonNode.jsonPaths(expression: String): List<String> {
336     check(this.isComplexType()) { "$this is not complex or array node to apply expression" }
337     return JsonParserUtils.paths(this, expression)
338 }