Upgrade SDC from Titan to Janus Graph
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / property / DefaultPropertyDeclarator.java
1 package org.openecomp.sdc.be.components.property;
2
3 import static org.openecomp.sdc.common.api.Constants.GET_INPUT;
4 import static org.openecomp.sdc.common.api.Constants.GET_POLICY;
5
6 import com.google.gson.Gson;
7 import fj.data.Either;
8 import java.util.ArrayList;
9 import java.util.Collection;
10 import java.util.HashMap;
11 import java.util.HashSet;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Arrays;
15 import java.util.Objects;
16 import java.util.Optional;
17 import java.util.Set;
18 import java.util.stream.Collectors;
19 import org.apache.commons.collections.CollectionUtils;
20 import org.apache.commons.collections.MapUtils;
21 import org.apache.commons.lang.StringUtils;
22 import org.json.simple.JSONObject;
23 import org.openecomp.sdc.be.components.utils.PropertiesUtils;
24 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
25 import org.openecomp.sdc.be.datatypes.elements.GetInputValueDataDefinition;
26 import org.openecomp.sdc.be.datatypes.elements.GetPolicyValueDataDefinition;
27 import org.openecomp.sdc.be.datatypes.elements.PropertiesOwner;
28 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
29 import org.openecomp.sdc.be.impl.ComponentsUtils;
30 import org.openecomp.sdc.be.model.CapabilityDefinition;
31 import org.openecomp.sdc.be.model.Component;
32 import org.openecomp.sdc.be.model.ComponentInstancePropInput;
33 import org.openecomp.sdc.be.model.IComponentInstanceConnectedElement;
34 import org.openecomp.sdc.be.model.InputDefinition;
35 import org.openecomp.sdc.be.model.PolicyDefinition;
36 import org.openecomp.sdc.be.model.PropertyDefinition;
37 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
38 import org.openecomp.sdc.be.model.operations.impl.DaoStatusConverter;
39 import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
40 import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
41 import org.openecomp.sdc.common.log.wrappers.Logger;
42 import org.openecomp.sdc.exception.ResponseFormat;
43 import org.yaml.snakeyaml.Yaml;
44
45 public abstract class DefaultPropertyDeclarator<PROPERTYOWNER extends PropertiesOwner, PROPERTYTYPE extends PropertyDataDefinition> implements PropertyDeclarator {
46
47     private static final Logger log = Logger.getLogger(DefaultPropertyDeclarator.class);
48     private static final short LOOP_PROTECTION_LEVEL = 10;
49     private static final String UNDERSCORE = "_";
50     private final Gson gson = new Gson();
51     private ComponentsUtils componentsUtils;
52     private PropertyOperation propertyOperation;
53     private static final String GET_INPUT_INDEX = "INDEX";
54
55     public DefaultPropertyDeclarator(ComponentsUtils componentsUtils, PropertyOperation propertyOperation) {
56         this.componentsUtils = componentsUtils;
57         this.propertyOperation = propertyOperation;
58     }
59
60     @Override
61     public Either<List<InputDefinition>, StorageOperationStatus> declarePropertiesAsInputs(Component component, String propertiesOwnerId, List<ComponentInstancePropInput> propsToDeclare) {
62         log.debug("#declarePropertiesAsInputs - declaring properties as inputs for component {} from properties owner {}", component.getUniqueId(), propertiesOwnerId);
63         return resolvePropertiesOwner(component, propertiesOwnerId)
64                 .map(propertyOwner -> declarePropertiesAsInputs(component, propertyOwner, propsToDeclare))
65                 .orElse(Either.right(onPropertiesOwnerNotFound(component.getUniqueId(), propertiesOwnerId)));
66     }
67
68     protected abstract PROPERTYTYPE createDeclaredProperty(PropertyDataDefinition prop);
69
70     protected abstract Either<?, StorageOperationStatus> updatePropertiesValues(Component component, String propertiesOwnerId, List<PROPERTYTYPE> properties);
71
72     protected abstract Optional<PROPERTYOWNER> resolvePropertiesOwner(Component component, String propertiesOwnerId);
73
74     protected abstract void addPropertiesListToInput(PROPERTYTYPE declaredProp, InputDefinition input);
75
76     @Override
77     public Either<List<PolicyDefinition>, StorageOperationStatus> declarePropertiesAsPolicies(Component component,
78             String propertiesOwnerId,
79             List<ComponentInstancePropInput> propsToDeclare) {
80         log.debug("#declarePropertiesAsPolicies - declaring properties as policies for component {} from properties owner {}", component.getUniqueId(), propertiesOwnerId);
81         return resolvePropertiesOwner(component, propertiesOwnerId)
82                        .map(propertyOwner -> declarePropertiesAsPolicies(component, propertyOwner, propsToDeclare))
83                        .orElse(Either.right(onPropertiesOwnerNotFound(component.getUniqueId(), propertiesOwnerId)));
84     }
85
86     @Override
87     public Either<InputDefinition, StorageOperationStatus> declarePropertiesAsListInput(Component component, String propertiesOwnerId, List<ComponentInstancePropInput> propsToDeclare, InputDefinition input) {
88         log.debug("#declarePropertiesAsListInput - declaring properties as inputs for component {} from properties owner {}", component.getUniqueId(), propertiesOwnerId);
89         Optional<PROPERTYOWNER> propertyOwner = resolvePropertiesOwner(component, propertiesOwnerId);
90         if (propertyOwner.isPresent()) {
91             return declarePropertiesAsListInput(component, propertyOwner.get(), propsToDeclare, input);
92         } else {
93             return Either.right(onPropertiesOwnerNotFound(component.getUniqueId(), propertiesOwnerId));
94         }
95     }
96
97     public StorageOperationStatus unDeclarePropertiesAsPolicies(Component component, PolicyDefinition policy) {
98         return StorageOperationStatus.OK;
99     }
100
101     private Either<List<PolicyDefinition>, StorageOperationStatus> declarePropertiesAsPolicies(Component component, PROPERTYOWNER propertiesOwner, List<ComponentInstancePropInput> propsToDeclare) {
102         PropertiesDeclarationData policyProperties = createPoliciesAndOverridePropertiesValues(propertiesOwner.getUniqueId(), propertiesOwner, propsToDeclare);
103         return updatePropertiesValues(component, propertiesOwner.getUniqueId(), policyProperties.getPropertiesToUpdate())
104                        .left()
105                        .map(updatePropsRes -> policyProperties.getPoliciesToCreate());
106     }
107
108     private StorageOperationStatus onPropertiesOwnerNotFound(String componentId, String propertiesOwnerId) {
109         log.debug("#declarePropertiesAsInputs - properties owner {} was not found on component {}", propertiesOwnerId, componentId);
110         return StorageOperationStatus.NOT_FOUND;
111     }
112
113     private Either<List<InputDefinition>, StorageOperationStatus> declarePropertiesAsInputs(Component component, PROPERTYOWNER propertiesOwner, List<ComponentInstancePropInput> propsToDeclare) {
114         PropertiesDeclarationData inputsProperties = createInputsAndOverridePropertiesValues(component, propertiesOwner, propsToDeclare);
115         return updatePropertiesValues(component, propertiesOwner.getUniqueId(), inputsProperties.getPropertiesToUpdate())
116                 .left()
117                 .map(updatePropsRes -> inputsProperties.getInputsToCreate());
118     }
119     private PropertiesDeclarationData createPoliciesAndOverridePropertiesValues(String componentId, PROPERTYOWNER propertiesOwner, List<ComponentInstancePropInput> propsToDeclare) {
120         List<PROPERTYTYPE> declaredProperties = new ArrayList<>();
121         List<PolicyDefinition> policies = new ArrayList<>();
122         propsToDeclare.forEach(property -> policies.add(declarePropertyPolicy(componentId, declaredProperties, property)));
123         return new PropertiesDeclarationData(null, policies, declaredProperties);
124     }
125
126     private PolicyDefinition declarePropertyPolicy(String componentId, List<PROPERTYTYPE> declaredProperties,
127             ComponentInstancePropInput propInput) {
128         PropertyDataDefinition prop = resolveProperty(declaredProperties, propInput);
129         propInput.setOwnerId(null);
130         propInput.setParentUniqueId(null);
131
132         PolicyDefinition policyDefinition = new PolicyDefinition(prop);
133         policyDefinition.setUniqueId(UniqueIdBuilder.buildPolicyUniqueId(componentId, prop.getName()));
134         policyDefinition.setInputPath(prop.getName());
135         policyDefinition.setInstanceUniqueId(componentId);
136
137         changePropertyValueToGetPolicy(prop, policyDefinition);
138         PROPERTYTYPE declaredProperty = createDeclaredProperty(prop);
139
140
141         if(!declaredProperties.contains(declaredProperty)){
142             declaredProperties.add(declaredProperty);
143         }
144
145         return policyDefinition;
146     }
147
148     private void changePropertyValueToGetPolicy(PropertyDataDefinition prop, PolicyDefinition policyDefinition) {
149         JSONObject jsonObject = new JSONObject();
150
151         String origValue = Objects.isNull(prop.getValue()) ? prop.getDefaultValue() : prop.getValue();
152         jsonObject.put(GET_POLICY, null);
153         prop.setValue(jsonObject.toJSONString());
154         policyDefinition.setValue(jsonObject.toJSONString());
155
156         if(CollectionUtils.isEmpty(prop.getGetPolicyValues())){
157             prop.setGetPolicyValues(new ArrayList<>());
158         }
159         List<GetPolicyValueDataDefinition> getPolicyValues = prop.getGetPolicyValues();
160
161         GetPolicyValueDataDefinition getPolicyValueDataDefinition = new GetPolicyValueDataDefinition();
162         getPolicyValueDataDefinition.setPolicyId(policyDefinition.getUniqueId());
163         getPolicyValueDataDefinition.setPropertyName(prop.getName());
164
165         getPolicyValueDataDefinition.setOrigPropertyValue(origValue);
166
167         getPolicyValues.add(getPolicyValueDataDefinition);
168
169         policyDefinition.setGetPolicyValues(getPolicyValues);
170
171     }
172
173
174     private Either<InputDefinition, StorageOperationStatus> declarePropertiesAsListInput(Component component, PROPERTYOWNER propertiesOwner, List<ComponentInstancePropInput> propsToDeclare, InputDefinition input) {
175         List<PROPERTYTYPE> declaredProperties = new ArrayList<>();
176         for (ComponentInstancePropInput propInput : propsToDeclare) {
177             if (StringUtils.isNotEmpty(propInput.getPropertiesName()) && propInput.getInput() != null) {
178                 // sub-property in complex type is checked on UI. currently not supported.
179                 log.debug("skip propInput (propertiesName={}) currently not supported.", propInput.getPropertiesName());
180                 continue;
181             }
182             PROPERTYTYPE declaredProperty = createDeclaredProperty(propInput);
183
184             JSONObject jsonObject = new JSONObject();
185             jsonObject.put(GET_INPUT, Arrays.asList(input.getName(), GET_INPUT_INDEX, propInput.getName()));
186             declaredProperty.setValue(jsonObject.toJSONString());
187
188             GetInputValueDataDefinition getInputValueDataDefinition = new GetInputValueDataDefinition();
189             getInputValueDataDefinition.setInputId(input.getUniqueId());
190             getInputValueDataDefinition.setInputName(input.getName());
191             List<GetInputValueDataDefinition> getInputValues = declaredProperty.getGetInputValues();
192             if (getInputValues == null) {
193                 getInputValues = new ArrayList<>();
194                 declaredProperty.setGetInputValues(getInputValues);
195             }
196             getInputValues.add(getInputValueDataDefinition);
197
198             if (!declaredProperties.contains(declaredProperty)) {
199                 // Add property to the list if not contain in declareProperties.
200                 declaredProperties.add(declaredProperty);
201             }
202         }
203         return updatePropertiesValues(component, propertiesOwner.getUniqueId(), declaredProperties)
204                 .left().map(x -> input);
205     }
206
207     private PropertiesDeclarationData createInputsAndOverridePropertiesValues(Component component, PROPERTYOWNER propertiesOwner, List<ComponentInstancePropInput> propsToDeclare) {
208         List<PROPERTYTYPE> declaredProperties = new ArrayList<>();
209         List<InputDefinition> createdInputs = propsToDeclare.stream()
210                 .map(propInput -> declarePropertyInput(component, propertiesOwner, declaredProperties, propInput))
211                 .collect(Collectors.toList());
212         return new PropertiesDeclarationData(createdInputs, null, declaredProperties);
213     }
214
215     private InputDefinition declarePropertyInput(Component component, PROPERTYOWNER propertiesOwner, List<PROPERTYTYPE> declaredProperties, ComponentInstancePropInput propInput) {
216         PropertyDataDefinition prop = resolveProperty(declaredProperties, propInput);
217         InputDefinition inputDefinition = createInput(component, propertiesOwner, propInput, prop);
218         PROPERTYTYPE declaredProperty = createDeclaredProperty(prop);
219         if(!declaredProperties.contains(declaredProperty)){
220             declaredProperties.add(declaredProperty);
221         }
222         addPropertiesListToInput(declaredProperty, inputDefinition);
223         return inputDefinition;
224     }
225
226     private InputDefinition createInput(Component component, PROPERTYOWNER propertiesOwner,
227                                         ComponentInstancePropInput propInput, PropertyDataDefinition prop) {
228         String generatedInputPrefix = propertiesOwner.getNormalizedName();
229         if (propertiesOwner.getUniqueId().equals(propInput.getParentUniqueId())) {
230             //Creating input from property create on self using add property..Do not add the prefix
231             generatedInputPrefix = null;
232         }
233
234         Optional<CapabilityDefinition> propertyCapability = PropertiesUtils.getPropertyCapabilityOfChildInstance(propInput
235                 .getParentUniqueId(), component.getCapabilities());
236         if (propertyCapability.isPresent()) {
237             String capName = propertyCapability.get().getName();
238             if(capName.contains(".")) {
239                 capName = capName.replaceAll("\\.", UNDERSCORE);
240             }
241             generatedInputPrefix = generatedInputPrefix ==  null || generatedInputPrefix.isEmpty()?
242                     capName : generatedInputPrefix + UNDERSCORE + capName;
243         }
244
245         String generatedInputName = generateInputName(generatedInputPrefix, propInput);
246         log.debug("createInput: propOwner.uniqueId={}, propInput.parentUniqueId={}", propertiesOwner.getUniqueId(), propInput.getParentUniqueId());
247         return createInputFromProperty(component.getUniqueId(), propertiesOwner, generatedInputName, propInput, prop);
248     }
249
250     private String generateInputName(String inputName, ComponentInstancePropInput propInput) {
251         String declaredInputName;
252         String[] parsedPropNames = propInput.getParsedPropNames();
253
254         if(parsedPropNames != null){
255             declaredInputName = handleInputName(inputName, parsedPropNames);
256         } else {
257             String[] propName = {propInput.getName()};
258             declaredInputName = handleInputName(inputName, propName);
259         }
260
261         return declaredInputName;
262     }
263
264     private String handleInputName(String inputName, String[] parsedPropNames) {
265         StringBuilder prefix = new StringBuilder();
266         int startingIndex;
267
268         if(Objects.isNull(inputName)) {
269             prefix.append(parsedPropNames[0]);
270             startingIndex = 1;
271         } else {
272             prefix.append(inputName);
273             startingIndex = 0;
274         }
275
276         while(startingIndex < parsedPropNames.length){
277             prefix.append(UNDERSCORE);
278             prefix.append(parsedPropNames[startingIndex]);
279             startingIndex ++;
280         }
281
282         return prefix.toString();
283     }
284
285     private PropertyDataDefinition resolveProperty(List<PROPERTYTYPE> propertiesToCreate, ComponentInstancePropInput propInput) {
286         Optional<PROPERTYTYPE> resolvedProperty = propertiesToCreate.stream()
287                 .filter(p -> p.getName().equals(propInput.getName()))
288                 .findFirst();
289         return resolvedProperty.isPresent() ? resolvedProperty.get() : propInput;
290     }
291
292     InputDefinition createInputFromProperty(String componentId, PROPERTYOWNER propertiesOwner, String inputName, ComponentInstancePropInput propInput, PropertyDataDefinition prop) {
293         String propertiesName = propInput.getPropertiesName() ;
294         PropertyDefinition selectedProp = propInput.getInput();
295         String[] parsedPropNames = propInput.getParsedPropNames();
296         InputDefinition input;
297         boolean complexProperty = false;
298         if(propertiesName != null && !propertiesName.isEmpty() && selectedProp != null){
299             complexProperty = true;
300             input = new InputDefinition(selectedProp);
301             input.setDefaultValue(selectedProp.getValue());
302         }else{
303             input = new InputDefinition(prop);
304             input.setDefaultValue(prop.getValue());
305         }
306         input.setName(inputName);
307         input.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(componentId, input.getName()));
308         input.setInputPath(propertiesName);
309         input.setInstanceUniqueId(propertiesOwner.getUniqueId());
310         input.setPropertyId(propInput.getUniqueId());
311                 if (Objects.isNull(input.getSubPropertyInputPath())
312                                 || (Objects.nonNull(propertiesName)
313                                 && input.getSubPropertyInputPath().substring(input.getSubPropertyInputPath().lastIndexOf('#'))
314                                 .equals(propertiesName.substring(propertiesName.lastIndexOf('#'))))) {
315                         input.setParentPropertyType(propInput.getType());
316                         input.setSubPropertyInputPath(propertiesName);
317                 }
318
319         changePropertyValueToGetInputValue(inputName, parsedPropNames, input, prop, complexProperty);
320
321         if(prop instanceof IComponentInstanceConnectedElement) {
322             ((IComponentInstanceConnectedElement) prop)
323                 .setComponentInstanceId(propertiesOwner.getUniqueId());
324             ((IComponentInstanceConnectedElement) prop)
325                 .setComponentInstanceName(propertiesOwner.getName());
326         }
327         return input;
328     }
329
330     private void changePropertyValueToGetInputValue(String inputName, String[] parsedPropNames, InputDefinition input, PropertyDataDefinition prop, boolean complexProperty) {
331         JSONObject jsonObject = new JSONObject();
332         String value = prop.getValue();
333         if(value == null || value.isEmpty()){
334             if(complexProperty){
335
336                 jsonObject = createJSONValueForProperty(parsedPropNames.length -1, parsedPropNames, jsonObject, inputName);
337                 prop.setValue(jsonObject.toJSONString());
338
339             }else{
340
341                 jsonObject.put(GET_INPUT, input.getName());
342                 prop.setValue(jsonObject.toJSONString());
343
344             }
345
346         }else{
347
348             Object objValue =  new Yaml().load(value);
349             if( objValue instanceof Map || objValue  instanceof List){
350                 if(!complexProperty){
351                     jsonObject.put(GET_INPUT, input.getName());
352                     prop.setValue(jsonObject.toJSONString());
353
354
355                 }else{
356                     Map<String, Object> mappedToscaTemplate = (Map<String, Object>) objValue;
357                     createInputValue(mappedToscaTemplate, 1, parsedPropNames, inputName);
358
359                     String json = gson.toJson(mappedToscaTemplate);
360                     prop.setValue(json);
361
362                 }
363
364             }else{
365                 jsonObject.put(GET_INPUT, input.getName());
366                 prop.setValue(jsonObject.toJSONString());
367
368             }
369
370         }
371
372         if(CollectionUtils.isEmpty(prop.getGetInputValues())){
373             prop.setGetInputValues(new ArrayList<>());
374         }
375         List<GetInputValueDataDefinition> getInputValues = prop.getGetInputValues();
376
377         GetInputValueDataDefinition getInputValueDataDefinition = new GetInputValueDataDefinition();
378         getInputValueDataDefinition.setInputId(input.getUniqueId());
379         getInputValueDataDefinition.setInputName(input.getName());
380         getInputValues.add(getInputValueDataDefinition);
381     }
382
383     private  JSONObject createJSONValueForProperty (int i, String [] parsedPropNames, JSONObject ooj, String inputName){
384
385         while(i >= 1){
386             if( i == parsedPropNames.length -1){
387                 JSONObject jobProp = new JSONObject();
388                 jobProp.put(GET_INPUT, inputName);
389                 ooj.put(parsedPropNames[i], jobProp);
390                 i--;
391                 return createJSONValueForProperty (i, parsedPropNames, ooj, inputName);
392             }else{
393                 JSONObject res = new JSONObject();
394                 res.put(parsedPropNames[i], ooj);
395                 i --;
396                 res =  createJSONValueForProperty (i, parsedPropNames, res, inputName);
397                 return res;
398             }
399         }
400
401         return ooj;
402     }
403
404     private  Map<String, Object> createInputValue(Map<String, Object> lhm1, int index, String[] inputNames, String inputName){
405         while(index < inputNames.length){
406             if(lhm1.containsKey(inputNames[index])){
407                 Object value = lhm1.get(inputNames[index]);
408                 if (value instanceof Map){
409                     if(index == inputNames.length -1){
410                         ((Map) value).put(GET_INPUT, inputName);
411                         return (Map) value;
412
413                     }else{
414                         index++;
415                         return  createInputValue((Map)value, index, inputNames, inputName);
416                     }
417                 }else{
418                     Map<String, Object> jobProp = new HashMap<>();
419                     if(index == inputNames.length -1){
420                         jobProp.put(GET_INPUT, inputName);
421                         lhm1.put(inputNames[index], jobProp);
422                         return lhm1;
423                     }else{
424                         lhm1.put(inputNames[index], jobProp);
425                         index++;
426                         return  createInputValue(jobProp, index, inputNames, inputName);
427                     }
428                 }
429             }else{
430                 Map<String, Object> jobProp = new HashMap<>();
431                 lhm1.put(inputNames[index], jobProp);
432                 if(index == inputNames.length -1){
433                     jobProp.put(GET_INPUT, inputName);
434                     return jobProp;
435                 }else{
436                     index++;
437                     return  createInputValue(jobProp, index, inputNames, inputName);
438                 }
439             }
440         }
441         return lhm1;
442     }
443
444     private class PropertiesDeclarationData {
445         private List<InputDefinition> inputsToCreate;
446         private List<PolicyDefinition> policiesToCreate;
447         private List<PROPERTYTYPE> propertiesToUpdate;
448
449         PropertiesDeclarationData(List<InputDefinition> inputsToCreate, List<PolicyDefinition> policiesToCreate, List<PROPERTYTYPE> propertiesToUpdate) {
450             this.inputsToCreate = inputsToCreate;
451             this.policiesToCreate = policiesToCreate;
452             this.propertiesToUpdate = propertiesToUpdate;
453         }
454
455         List<InputDefinition> getInputsToCreate() {
456             return inputsToCreate;
457         }
458
459         public List<PolicyDefinition> getPoliciesToCreate() { return policiesToCreate; }
460
461         List<PROPERTYTYPE> getPropertiesToUpdate() {
462             return propertiesToUpdate;
463         }
464     }
465
466     Either<InputDefinition, ResponseFormat>  prepareValueBeforeDelete(InputDefinition inputForDelete, PropertyDataDefinition inputValue, List<String> pathOfComponentInstances) {
467         Either<InputDefinition, ResponseFormat> deleteEither = prepareValueBeforeDelete(inputForDelete, inputValue);
468
469         Either<String, JanusGraphOperationStatus> findDefaultValue = propertyOperation
470             .findDefaultValueFromSecondPosition(pathOfComponentInstances, inputValue.getUniqueId(),
471                 (String) inputValue.getDefaultValue());
472         if (findDefaultValue.isRight()) {
473             deleteEither = Either.right(componentsUtils.getResponseFormat(componentsUtils
474                 .convertFromStorageResponse(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(findDefaultValue.right().value()))));
475             return deleteEither;
476
477         }
478         String defaultValue = findDefaultValue.left().value();
479         inputValue.setDefaultValue(defaultValue);
480         log.debug("The returned default value in ResourceInstanceProperty is {}", defaultValue);
481         return deleteEither;
482     }
483
484     Either<InputDefinition, ResponseFormat>  prepareValueBeforeDeleteOfCapProp(InputDefinition inputForDelete,
485                                                                                PropertyDataDefinition inputValue) {
486         Either<InputDefinition, ResponseFormat> deleteEither = prepareValueBeforeDelete(inputForDelete, inputValue);
487         inputValue.setDefaultValue(inputForDelete.getDefaultValue());
488         log.debug("The returned default value in ResourceInstanceProperty is {}", inputForDelete.getDefaultValue());
489         return deleteEither;
490     }
491
492     private Either<InputDefinition, ResponseFormat> prepareValueBeforeDelete(InputDefinition inputForDelete,
493                                                                              PropertyDataDefinition inputValue) {
494         Either<InputDefinition, ResponseFormat> deleteEither = Either.left(inputForDelete);
495         String value = inputValue.getValue();
496         Map<String, Object> mappedToscaTemplate = (Map<String, Object>) new Yaml().load(value);
497
498         resetInputName(mappedToscaTemplate, inputForDelete.getName());
499
500         value = "";
501         if (!mappedToscaTemplate.isEmpty()) {
502             Either result = cleanNestedMap(mappedToscaTemplate, true);
503             Map modifiedMappedToscaTemplate = mappedToscaTemplate;
504             if (result.isLeft()) {
505                 modifiedMappedToscaTemplate = (Map) result.left().value();
506             } else {
507                 log.warn("Map cleanup failed -> " + result.right().value()
508                         .toString());    //continue, don't break operation
509             }
510             value = gson.toJson(modifiedMappedToscaTemplate);
511         }
512         inputValue.setValue(value);
513
514
515         List<GetInputValueDataDefinition> getInputsValues = inputValue.getGetInputValues();
516         if (getInputsValues != null && !getInputsValues.isEmpty()) {
517             Optional<GetInputValueDataDefinition> op =
518                     getInputsValues.stream().filter(gi -> gi.getInputId().equals(inputForDelete.getUniqueId()))
519                             .findAny();
520             op.ifPresent(getInputsValues::remove);
521         }
522         inputValue.setGetInputValues(getInputsValues);
523         return deleteEither;
524     }
525
526     private void resetInputName(Map<String, Object> lhm1, String inputName){
527         for (Map.Entry<String, Object> entry : lhm1.entrySet()) {
528             String key = entry.getKey();
529             Object value = entry.getValue();
530             if (value instanceof String && ((String) value).equalsIgnoreCase(inputName) && key.equals(GET_INPUT)) {
531                 value = "";
532                 lhm1.remove(key);
533             } else if (value instanceof Map) {
534                 Map<String, Object> subMap = (Map<String, Object>)value;
535                 resetInputName(subMap, inputName);
536             } else if (value instanceof List && ((List) value).contains(inputName) && key.equals(GET_INPUT)) {
537                 value = "";
538                 lhm1.remove(key);
539             } else {
540                 continue;
541             }
542
543         }
544     }
545
546     private Either cleanNestedMap( Map mappedToscaTemplate , boolean deepClone  ){
547         if (MapUtils.isNotEmpty( mappedToscaTemplate ) ){
548             if (deepClone){
549                 if (!(mappedToscaTemplate instanceof HashMap))
550                     return Either.right("expecting mappedToscaTemplate as HashMap ,recieved "+ mappedToscaTemplate.getClass().getSimpleName() );
551                 else
552                     mappedToscaTemplate = (HashMap)((HashMap) mappedToscaTemplate).clone();
553             }
554             return Either.left( (Map) cleanEmptyNestedValuesInMap( mappedToscaTemplate , LOOP_PROTECTION_LEVEL ) );
555         }
556         else {
557             log.debug("mappedToscaTemplate is empty ");
558             return Either.right("mappedToscaTemplate is empty ");
559         }
560     }
561
562     /*        Mutates the object
563      *        Tail recurse -> traverse the tosca elements and remove nested empty map properties
564      *        this only handles nested maps, other objects are left untouched (even a Set containing a map) since behaviour is unexpected
565      *
566      *        @param  toscaElement - expected map of tosca values
567      *        @return mutated @param toscaElement , where empty maps are deleted , return null for empty map.
568      **/
569     private Object cleanEmptyNestedValuesInMap(Object toscaElement , short loopProtectionLevel ){
570         if (loopProtectionLevel<=0 || toscaElement==null || !(toscaElement instanceof  Map))
571             return toscaElement;
572         if ( MapUtils.isNotEmpty( (Map)toscaElement ) ) {
573             Object ret;
574             Set<Object> keysToRemove = new HashSet<>();                                                                 // use different set to avoid ConcurrentModificationException
575             for( Object key : ((Map)toscaElement).keySet() ) {
576                 Object value = ((Map) toscaElement).get(key);
577                 ret = cleanEmptyNestedValuesInMap(value , --loopProtectionLevel );
578                 if ( ret == null )
579                     keysToRemove.add(key);
580             }
581             Collection set = ((Map) toscaElement).keySet();
582             if (CollectionUtils.isNotEmpty(set))
583                 set.removeAll(keysToRemove);
584
585             if ( isEmptyNestedMap(toscaElement) )
586                 return null;
587         }
588         else
589             return null;
590         return toscaElement;
591     }
592
593     //@returns true iff map nested maps are all empty
594     //ignores other collection objects
595     private boolean isEmptyNestedMap(Object element){
596         boolean isEmpty = true;
597         if (element != null){
598             if ( element instanceof Map ){
599                 if (MapUtils.isEmpty((Map)element))
600                     isEmpty = true;
601                 else
602                 {
603                     for( Object key : ((Map)(element)).keySet() ){
604                         Object value =  ((Map)(element)).get(key);
605                         isEmpty &= isEmptyNestedMap( value );
606                     }
607                 }
608             } else {
609                 isEmpty = false;
610             }
611         }
612         return isEmpty;
613     }
614
615 }