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