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