1 package org.openecomp.sdc.be.components.property;
3 import static org.openecomp.sdc.common.api.Constants.GET_INPUT;
4 import static org.openecomp.sdc.common.api.Constants.GET_POLICY;
6 import com.google.gson.Gson;
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;
14 import java.util.Arrays;
15 import java.util.Objects;
16 import java.util.Optional;
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;
45 public abstract class DefaultPropertyDeclarator<PROPERTYOWNER extends PropertiesOwner, PROPERTYTYPE extends PropertyDataDefinition> implements PropertyDeclarator {
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";
55 public DefaultPropertyDeclarator(ComponentsUtils componentsUtils, PropertyOperation propertyOperation) {
56 this.componentsUtils = componentsUtils;
57 this.propertyOperation = propertyOperation;
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)));
68 protected abstract PROPERTYTYPE createDeclaredProperty(PropertyDataDefinition prop);
70 protected abstract Either<?, StorageOperationStatus> updatePropertiesValues(Component component, String propertiesOwnerId, List<PROPERTYTYPE> properties);
72 protected abstract Optional<PROPERTYOWNER> resolvePropertiesOwner(Component component, String propertiesOwnerId);
74 protected abstract void addPropertiesListToInput(PROPERTYTYPE declaredProp, InputDefinition input);
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)));
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);
93 return Either.right(onPropertiesOwnerNotFound(component.getUniqueId(), propertiesOwnerId));
97 public StorageOperationStatus unDeclarePropertiesAsPolicies(Component component, PolicyDefinition policy) {
98 return StorageOperationStatus.OK;
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())
105 .map(updatePropsRes -> policyProperties.getPoliciesToCreate());
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;
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())
117 .map(updatePropsRes -> inputsProperties.getInputsToCreate());
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);
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);
132 PolicyDefinition policyDefinition = new PolicyDefinition(prop);
133 policyDefinition.setUniqueId(UniqueIdBuilder.buildPolicyUniqueId(componentId, prop.getName()));
134 policyDefinition.setInputPath(prop.getName());
135 policyDefinition.setInstanceUniqueId(componentId);
137 changePropertyValueToGetPolicy(prop, policyDefinition);
138 PROPERTYTYPE declaredProperty = createDeclaredProperty(prop);
141 if(!declaredProperties.contains(declaredProperty)){
142 declaredProperties.add(declaredProperty);
145 return policyDefinition;
148 private void changePropertyValueToGetPolicy(PropertyDataDefinition prop, PolicyDefinition policyDefinition) {
149 JSONObject jsonObject = new JSONObject();
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());
156 if(CollectionUtils.isEmpty(prop.getGetPolicyValues())){
157 prop.setGetPolicyValues(new ArrayList<>());
159 List<GetPolicyValueDataDefinition> getPolicyValues = prop.getGetPolicyValues();
161 GetPolicyValueDataDefinition getPolicyValueDataDefinition = new GetPolicyValueDataDefinition();
162 getPolicyValueDataDefinition.setPolicyId(policyDefinition.getUniqueId());
163 getPolicyValueDataDefinition.setPropertyName(prop.getName());
165 getPolicyValueDataDefinition.setOrigPropertyValue(origValue);
167 getPolicyValues.add(getPolicyValueDataDefinition);
169 policyDefinition.setGetPolicyValues(getPolicyValues);
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());
182 PROPERTYTYPE declaredProperty = createDeclaredProperty(propInput);
184 JSONObject jsonObject = new JSONObject();
185 jsonObject.put(GET_INPUT, Arrays.asList(input.getName(), GET_INPUT_INDEX, propInput.getName()));
186 declaredProperty.setValue(jsonObject.toJSONString());
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);
196 getInputValues.add(getInputValueDataDefinition);
198 if (!declaredProperties.contains(declaredProperty)) {
199 // Add property to the list if not contain in declareProperties.
200 declaredProperties.add(declaredProperty);
203 return updatePropertiesValues(component, propertiesOwner.getUniqueId(), declaredProperties)
204 .left().map(x -> input);
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);
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);
222 addPropertiesListToInput(declaredProperty, inputDefinition);
223 return inputDefinition;
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;
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);
241 generatedInputPrefix = generatedInputPrefix == null || generatedInputPrefix.isEmpty()?
242 capName : generatedInputPrefix + UNDERSCORE + capName;
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);
250 private String generateInputName(String inputName, ComponentInstancePropInput propInput) {
251 String declaredInputName;
252 String[] parsedPropNames = propInput.getParsedPropNames();
254 if(parsedPropNames != null){
255 declaredInputName = handleInputName(inputName, parsedPropNames);
257 String[] propName = {propInput.getName()};
258 declaredInputName = handleInputName(inputName, propName);
261 return declaredInputName;
264 private String handleInputName(String inputName, String[] parsedPropNames) {
265 StringBuilder prefix = new StringBuilder();
268 if(Objects.isNull(inputName)) {
269 prefix.append(parsedPropNames[0]);
272 prefix.append(inputName);
276 while(startingIndex < parsedPropNames.length){
277 prefix.append(UNDERSCORE);
278 prefix.append(parsedPropNames[startingIndex]);
282 return prefix.toString();
285 private PropertyDataDefinition resolveProperty(List<PROPERTYTYPE> propertiesToCreate, ComponentInstancePropInput propInput) {
286 Optional<PROPERTYTYPE> resolvedProperty = propertiesToCreate.stream()
287 .filter(p -> p.getName().equals(propInput.getName()))
289 return resolvedProperty.isPresent() ? resolvedProperty.get() : propInput;
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());
303 input = new InputDefinition(prop);
304 input.setDefaultValue(prop.getValue());
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);
319 changePropertyValueToGetInputValue(inputName, parsedPropNames, input, prop, complexProperty);
321 if(prop instanceof IComponentInstanceConnectedElement) {
322 ((IComponentInstanceConnectedElement) prop)
323 .setComponentInstanceId(propertiesOwner.getUniqueId());
324 ((IComponentInstanceConnectedElement) prop)
325 .setComponentInstanceName(propertiesOwner.getName());
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()){
336 jsonObject = createJSONValueForProperty(parsedPropNames.length -1, parsedPropNames, jsonObject, inputName);
337 prop.setValue(jsonObject.toJSONString());
341 jsonObject.put(GET_INPUT, input.getName());
342 prop.setValue(jsonObject.toJSONString());
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());
356 Map<String, Object> mappedToscaTemplate = (Map<String, Object>) objValue;
357 createInputValue(mappedToscaTemplate, 1, parsedPropNames, inputName);
359 String json = gson.toJson(mappedToscaTemplate);
365 jsonObject.put(GET_INPUT, input.getName());
366 prop.setValue(jsonObject.toJSONString());
372 if(CollectionUtils.isEmpty(prop.getGetInputValues())){
373 prop.setGetInputValues(new ArrayList<>());
375 List<GetInputValueDataDefinition> getInputValues = prop.getGetInputValues();
377 GetInputValueDataDefinition getInputValueDataDefinition = new GetInputValueDataDefinition();
378 getInputValueDataDefinition.setInputId(input.getUniqueId());
379 getInputValueDataDefinition.setInputName(input.getName());
380 getInputValues.add(getInputValueDataDefinition);
383 private JSONObject createJSONValueForProperty (int i, String [] parsedPropNames, JSONObject ooj, String inputName){
386 if( i == parsedPropNames.length -1){
387 JSONObject jobProp = new JSONObject();
388 jobProp.put(GET_INPUT, inputName);
389 ooj.put(parsedPropNames[i], jobProp);
391 return createJSONValueForProperty (i, parsedPropNames, ooj, inputName);
393 JSONObject res = new JSONObject();
394 res.put(parsedPropNames[i], ooj);
396 res = createJSONValueForProperty (i, parsedPropNames, res, inputName);
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);
415 return createInputValue((Map)value, index, inputNames, inputName);
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);
424 lhm1.put(inputNames[index], jobProp);
426 return createInputValue(jobProp, index, inputNames, inputName);
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);
437 return createInputValue(jobProp, index, inputNames, inputName);
444 private class PropertiesDeclarationData {
445 private List<InputDefinition> inputsToCreate;
446 private List<PolicyDefinition> policiesToCreate;
447 private List<PROPERTYTYPE> propertiesToUpdate;
449 PropertiesDeclarationData(List<InputDefinition> inputsToCreate, List<PolicyDefinition> policiesToCreate, List<PROPERTYTYPE> propertiesToUpdate) {
450 this.inputsToCreate = inputsToCreate;
451 this.policiesToCreate = policiesToCreate;
452 this.propertiesToUpdate = propertiesToUpdate;
455 List<InputDefinition> getInputsToCreate() {
456 return inputsToCreate;
459 public List<PolicyDefinition> getPoliciesToCreate() { return policiesToCreate; }
461 List<PROPERTYTYPE> getPropertiesToUpdate() {
462 return propertiesToUpdate;
466 Either<InputDefinition, ResponseFormat> prepareValueBeforeDelete(InputDefinition inputForDelete, PropertyDataDefinition inputValue, List<String> pathOfComponentInstances) {
467 Either<InputDefinition, ResponseFormat> deleteEither = prepareValueBeforeDelete(inputForDelete, inputValue);
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()))));
478 String defaultValue = findDefaultValue.left().value();
479 inputValue.setDefaultValue(defaultValue);
480 log.debug("The returned default value in ResourceInstanceProperty is {}", defaultValue);
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());
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);
498 resetInputName(mappedToscaTemplate, inputForDelete.getName());
501 if (!mappedToscaTemplate.isEmpty()) {
502 Either result = cleanNestedMap(mappedToscaTemplate, true);
503 Map modifiedMappedToscaTemplate = mappedToscaTemplate;
504 if (result.isLeft()) {
505 modifiedMappedToscaTemplate = (Map) result.left().value();
507 log.warn("Map cleanup failed -> " + result.right().value()
508 .toString()); //continue, don't break operation
510 value = gson.toJson(modifiedMappedToscaTemplate);
512 inputValue.setValue(value);
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()))
520 op.ifPresent(getInputsValues::remove);
522 inputValue.setGetInputValues(getInputsValues);
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)) {
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)) {
546 private Either cleanNestedMap( Map mappedToscaTemplate , boolean deepClone ){
547 if (MapUtils.isNotEmpty( mappedToscaTemplate ) ){
549 if (!(mappedToscaTemplate instanceof HashMap))
550 return Either.right("expecting mappedToscaTemplate as HashMap ,recieved "+ mappedToscaTemplate.getClass().getSimpleName() );
552 mappedToscaTemplate = (HashMap)((HashMap) mappedToscaTemplate).clone();
554 return Either.left( (Map) cleanEmptyNestedValuesInMap( mappedToscaTemplate , LOOP_PROTECTION_LEVEL ) );
557 log.debug("mappedToscaTemplate is empty ");
558 return Either.right("mappedToscaTemplate is empty ");
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
566 * @param toscaElement - expected map of tosca values
567 * @return mutated @param toscaElement , where empty maps are deleted , return null for empty map.
569 private Object cleanEmptyNestedValuesInMap(Object toscaElement , short loopProtectionLevel ){
570 if (loopProtectionLevel<=0 || toscaElement==null || !(toscaElement instanceof Map))
572 if ( MapUtils.isNotEmpty( (Map)toscaElement ) ) {
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 );
579 keysToRemove.add(key);
581 Collection set = ((Map) toscaElement).keySet();
582 if (CollectionUtils.isNotEmpty(set))
583 set.removeAll(keysToRemove);
585 if ( isEmptyNestedMap(toscaElement) )
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))
603 for( Object key : ((Map)(element)).keySet() ){
604 Object value = ((Map)(element)).get(key);
605 isEmpty &= isEmptyNestedMap( value );