e49d2b2c870111660708b2a440b6a4d1a836b00b
[sdc.git] /
1 package org.openecomp.sdc.be.components.merge.input;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.HashMap;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.stream.Collectors;
9
10 import org.apache.commons.lang.StringUtils;
11 import org.openecomp.sdc.be.dao.utils.MapUtil;
12 import org.openecomp.sdc.be.datatypes.elements.GetInputValueDataDefinition;
13 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
14 import org.openecomp.sdc.be.model.Component;
15 import org.openecomp.sdc.be.model.InputDefinition;
16
17 @org.springframework.stereotype.Component
18 public class InputsValuesMergingBusinessLogic {
19
20     /**
21      * Merge old inputs values into the updated inputs
22      * An input value is merged if the input previous version had a user defined value and its value is empty in current version
23      * @param oldInputs the currently persisted inputs mapped by their names
24      * @param updatedInputs the currently being update inputs mapped by their names
25      */
26     public void mergeComponentInputs(Map<String, InputDefinition> oldInputs, Map<String, InputDefinition> updatedInputs) {
27         updatedInputs.forEach((inputName, input) -> mergeInputsValues(oldInputs.get(inputName), input));
28     }
29
30     /**
31      * @param oldComponent the old state of {@link Component} that is being updated
32      * @param newComponent the new state of {@link Component} that is being updated
33      * @return a list of all inputs that were previously declared and need to be merged to the updating component
34      * An input needs to merged if a property was declared as an input (by the user) in previous component version and the declared input not exist in new version
35      */
36     public List<InputDefinition> getPreviouslyDeclaredInputsToMerge(Component oldComponent, Component newComponent) {
37         if (oldComponent == null || oldComponent.getInputs() == null || newComponent == null ) {
38             return Collections.emptyList();
39         }
40         Map<String, List<PropertyDataDefinition>> getInputProperties = getAllGetInputPropertyData(newComponent);
41         List<RedeclareInputData> inputsToRedeclareData = buildRedeclareInputData(newComponent, getInputProperties);
42         return findPrevDeclaredInputs(oldComponent.getInputs(), inputsToRedeclareData);
43     }
44
45     /**
46      * @param oldInputs list of previous inputs to find inputs to redeclare from
47      * @param newComponent the new state of {@link Component} that is being updated
48      * @param instanceId instance id
49      * @return a list of all inputs that were previously declared and need to be merged to the updating component
50      * An input needs to merged if an instance property was declared as an input (by the user) in previous component version and the declared input not exist in new version
51      */
52     public List<InputDefinition> getPreviouslyDeclaredInputsToMerge(List<InputDefinition> oldInputs, Component newComponent, String instanceId) {
53         if (oldInputs == null || newComponent == null ) {
54             return Collections.emptyList();
55         }
56         Map<String, List<PropertyDataDefinition>> getInputProperties = getAllGetInputPropertyData(newComponent, instanceId);
57         List<RedeclareInputData> inputsToRedeclareData = buildRedeclareInputData(newComponent, getInputProperties);
58         return findPrevDeclaredInputs(oldInputs, inputsToRedeclareData);
59     }
60
61     private List<InputDefinition> findPrevDeclaredInputs(List<InputDefinition> oldInputs, List<RedeclareInputData> inputsToRedeclareData) {
62         Map<String, InputDefinition> oldInputsById = MapUtil.toMap(oldInputs, InputDefinition::getUniqueId);
63         List<InputDefinition> inputsToRedeclare = new ArrayList<>();
64         inputsToRedeclareData.forEach(redeclareInputData -> {
65             List<InputDefinition> inputDefinitions = prepareInputsForRedeclaration(oldInputsById, redeclareInputData);
66             inputsToRedeclare.addAll(inputDefinitions);
67         });
68         return inputsToRedeclare;
69     }
70
71     private List<InputDefinition> prepareInputsForRedeclaration(Map<String, InputDefinition> oldInputsById, RedeclareInputData redeclareInputData) {
72         List<InputDefinition> inputsForRedeclaration = redeclareInputData.declaredInputIds.stream().map(oldInputsById::get).collect(Collectors.toList());
73         inputsForRedeclaration.forEach(input -> {
74             input.setPropertyId(redeclareInputData.propertyId);
75             input.setInstanceUniqueId(redeclareInputData.instanceId);
76         });
77         return inputsForRedeclaration;
78     }
79
80     private <T extends PropertyDataDefinition> Map<String, List<PropertyDataDefinition>> findGetInputPropsDefinitions(Map<String, List<T>> instancesPropDefinitions) {
81         Map<String, List<PropertyDataDefinition>> getInputProps = new HashMap<>();
82         if (instancesPropDefinitions == null) {
83             return getInputProps;
84         }
85         return instancesPropDefinitions.entrySet()
86                 .stream()
87                 .collect(Collectors.toMap(Map.Entry::getKey,
88                         entry -> this.filterGetInputProps(entry.getValue())));
89     }
90
91     private <T extends PropertyDataDefinition> List<PropertyDataDefinition> filterGetInputProps(List<T> propDefinitions) {
92         return propDefinitions
93                 .stream()
94                 .filter(PropertyDataDefinition::isGetInputProperty)
95                 .collect(Collectors.toList());
96     }
97
98     private void mergeInputsValues(InputDefinition oldInput, InputDefinition updatedInput) {
99         if (shouldMergeOldValue(oldInput, updatedInput)) {
100             updatedInput.setDefaultValue(oldInput.getDefaultValue());
101         }
102     }
103
104     private boolean shouldMergeOldValue(InputDefinition oldInput, InputDefinition newInput) {
105         return isNonEmptyDefaultValue(oldInput) && isEmptyDefaultValue(newInput) && isSameType(oldInput, newInput);
106     }
107
108     private boolean isSameType(InputDefinition oldInput, InputDefinition updatedInput) {
109         return oldInput.typeEquals(updatedInput);
110     }
111
112     private boolean isEmptyDefaultValue(InputDefinition input) {
113         return input != null && StringUtils.isEmpty(input.getDefaultValue());
114     }
115
116     private boolean isNonEmptyDefaultValue(InputDefinition input) {
117         return input != null && !isEmptyDefaultValue(input);
118     }
119
120     private List<RedeclareInputData> buildRedeclareInputData(Component newComponent, Map<String, List<PropertyDataDefinition>> getInputProperties) {
121         Map<String, InputDefinition> inputsById = MapUtil.toMap(newComponent.getInputs(), InputDefinition::getUniqueId);
122         List<RedeclareInputData> redeclareInputData = new ArrayList<>();
123         getInputProperties.forEach((instanceId, getInputProps) ->  {
124             redeclareInputData.addAll(findInputsToRedeclare(inputsById, instanceId, getInputProps));
125         });
126         return redeclareInputData;
127
128     }
129
130     private Map<String, List<PropertyDataDefinition>> getAllGetInputPropertyData(Component newComponent) {
131         Map<String, List<PropertyDataDefinition>> getInputInstanceProps = findGetInputPropsDefinitions(newComponent.getComponentInstancesProperties());
132         Map<String, List<PropertyDataDefinition>> getInputInstanceInputs = findGetInputPropsDefinitions(newComponent.getComponentInstancesInputs());
133         getInputInstanceInputs.putAll(getInputInstanceProps);
134         return getInputInstanceInputs;
135     }
136
137     private Map<String, List<PropertyDataDefinition>> getAllGetInputPropertyData(Component newComponent, String instanceId) {
138         List<PropertyDataDefinition> getInputInstanceProps = this.filterGetInputProps(newComponent.safeGetComponentInstanceProperties(instanceId));
139         List<PropertyDataDefinition> getInputInstanceInputs = this.filterGetInputProps(newComponent.safeGetComponentInstanceInput(instanceId));
140         getInputInstanceInputs.addAll(getInputInstanceProps);
141         return Collections.singletonMap(instanceId, getInputInstanceInputs);
142     }
143
144     private List<RedeclareInputData> findInputsToRedeclare(Map<String, InputDefinition> inputsById, String instanceId, List<PropertyDataDefinition> getInputProps) {
145         List<RedeclareInputData> redeclareInputDataList = new ArrayList<>();
146         getInputProps.forEach(property -> {
147             List<String> inputsToRedeclareIds = findInputsToRedeclareIds(inputsById, property);
148             RedeclareInputData redeclareInputData = new RedeclareInputData(property.getUniqueId(), inputsToRedeclareIds, instanceId);
149             redeclareInputDataList.add(redeclareInputData);
150         });
151         return redeclareInputDataList;
152     }
153
154     private List<String> findInputsToRedeclareIds(Map<String, InputDefinition> inputsById, PropertyDataDefinition property) {
155         List<GetInputValueDataDefinition> getInputValues = property.getGetInputValues();
156         return getInputValues.stream()
157                 .filter(getInputVal -> getInputValueWithNoCorrespondingInput(getInputVal, inputsById))
158                 .map(GetInputValueDataDefinition::getInputId)
159                 .collect(Collectors.toList());
160     }
161
162     private boolean getInputValueWithNoCorrespondingInput(GetInputValueDataDefinition getInputVal, Map<String, InputDefinition> inputsById) {
163         return !inputsById.containsKey(getInputVal.getInputId());
164     }
165
166     private class RedeclareInputData {
167         private String propertyId;
168         private List<String> declaredInputIds;
169         private String instanceId;
170
171         public RedeclareInputData(String propertyId, List<String> declaredInputIds, String instanceId) {
172             this.propertyId = propertyId;
173             this.declaredInputIds = declaredInputIds;
174             this.instanceId = instanceId;
175         }
176
177     }
178
179
180 }