re base code
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / merge / property / PropertyValueMerger.java
1 package org.openecomp.sdc.be.components.merge.property;
2
3 import java.util.ArrayList;
4 import java.util.List;
5 import java.util.Map;
6 import java.util.Optional;
7 import java.util.stream.Collectors;
8
9 import org.apache.commons.lang.StringUtils;
10 import org.apache.commons.lang3.tuple.ImmutablePair;
11 import org.openecomp.sdc.be.model.DataTypeDefinition;
12 import org.openecomp.sdc.be.model.PropertyDefinition;
13 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
14 import org.openecomp.sdc.be.utils.TypeUtils;
15 import org.springframework.stereotype.Component;
16
17 @Component
18 public class PropertyValueMerger {
19     
20     @SuppressWarnings("unchecked")
21     /**
22      * merges property value oldVal into property value newVal recursively
23      * @param oldVal - cannot be {@code Null}
24      */
25     protected Object merge(Object oldVal, Object newVal, List<String> inputNamesToMerge, String type, String innerType, Map<String, DataTypeDefinition> dataTypes) {
26         if (isEmptyValue(newVal)) {
27             return removeUnwantedGetInputValues(oldVal, inputNamesToMerge);
28         }
29         if (isMapTypeValues(oldVal, newVal)) {
30             return mergeMapValue((Map<String, Object>) oldVal, (Map<String, Object>) newVal, inputNamesToMerge, type, innerType, dataTypes);
31         }
32         if (isListTypeValues(oldVal, newVal)) {
33             return mergeListValue((List<Object>) oldVal, (List<Object>) newVal, inputNamesToMerge, innerType, dataTypes);
34         }
35         if (isSameTypeValues(oldVal, newVal)) {
36             return mergeScalarValue(oldVal, newVal);
37         }
38         return newVal;
39     }
40     
41     private Map<String, Object> mergeMapValue(Map<String, Object> oldValMap, Map<String, Object> newValMap, List<String> inputNamesToMerge, String type, String innertType, Map<String, DataTypeDefinition> dataTypes) {
42         mergeEntriesExistInOldValue(oldValMap, newValMap, inputNamesToMerge, type, innertType, dataTypes);//continue the recursion
43         if (type != null && !type.equals("map")) {
44             setOldEntriesNotExistInNewValue(oldValMap, newValMap, inputNamesToMerge);
45         }
46         
47         return newValMap;
48     }
49
50     private void mergeEntriesExistInOldValue(Map<String, Object> oldValMap, Map<String, Object> newValMap, List<String> inputNamesToMerge, String type, String innerType, Map<String, DataTypeDefinition> dataTypes) {
51         for (Map.Entry<String, Object> newValEntry : newValMap.entrySet()) {
52             Object oldVal = oldValMap.get(newValEntry.getKey());
53             if (oldVal != null) {
54                 ImmutablePair<String, String> types = getTypeAndInnerTypePair(newValEntry.getKey(), type, innerType, dataTypes);
55                 newValMap.put(newValEntry.getKey(), merge(oldVal, newValEntry.getValue(), inputNamesToMerge, types.getLeft(), types.getRight(), dataTypes));
56             }
57         }
58     }
59     
60     private void setOldEntriesNotExistInNewValue(Map<String, Object> oldVal, Map<String, Object> newVal, List<String> getInputNamesToMerge) {
61         for (Map.Entry<String, Object> oldValEntry : oldVal.entrySet()) {
62             if (!isInputEntry(oldValEntry) || isInputToMerge(getInputNamesToMerge, oldValEntry)) {
63                 Object oldValObj = oldValEntry.getValue();
64                 newVal.computeIfAbsent(oldValEntry.getKey(), key -> removeUnwantedGetInputValues(oldValObj, getInputNamesToMerge));
65             }
66         }
67     }
68     
69     private ImmutablePair<String, String> getTypeAndInnerTypePair(String propName, String type, String innerType, Map<String, DataTypeDefinition> dataTypes) {
70         if (type == null || (ToscaPropertyType.isScalarType(type) && ToscaPropertyType.isScalarType(innerType))) {
71             return ImmutablePair.of(innerType, null);
72         }
73         
74         String newInnerType = null;
75         DataTypeDefinition innerTypeDef = dataTypes.get(type);
76         if (innerTypeDef != null) {
77             List<PropertyDefinition> properties = innerTypeDef.getProperties();
78             if (properties!= null) {
79                 Optional<PropertyDefinition> optionalProperty = findProperty(properties, propName);
80                 
81                 innerType = optionalProperty.map(PropertyDefinition::getType)
82                                             .orElse(innerType);
83                 
84                 newInnerType = optionalProperty.map(PropertyDefinition::getSchemaType)
85                                                .orElse(null);
86             }
87         }
88                 
89         return ImmutablePair.of(innerType, newInnerType);
90     }
91     
92     private Optional<PropertyDefinition> findProperty(List<PropertyDefinition> properties, String propName) {
93         return properties.stream()
94                 .filter(p -> propName.equals(p.getName()))
95                 .findFirst();
96     }
97
98     private List<Object> mergeListValue(List<Object> oldVal, List<Object> newVal, List<String> inputNamesToMerge, String innerType, Map<String, DataTypeDefinition> dataTypes) {
99         List<Object> mergedList = newVal;
100     
101         if (oldVal.size() == newVal.size()) {
102             mergedList = mergeLists(oldVal, newVal, inputNamesToMerge, innerType, dataTypes);
103         }
104
105         return mergedList;
106     }
107     
108
109     private List<Object> mergeLists(List<Object> oldVal, List<Object> newVal, List<String> inputNamesToMerge, String innerType, Map<String, DataTypeDefinition> dataTypes) {
110         int minListSize = Math.min(oldVal.size(), newVal.size());
111         List<Object> mergedList = new ArrayList<>();
112         for (int i = 0; i < minListSize; i++) {
113             Object mergedVal = merge(oldVal.get(i), newVal.get(i), inputNamesToMerge, innerType, null, dataTypes);
114             mergedList.add(mergedVal);
115         }
116         return mergedList;
117     }
118     
119     
120     private Object mergeScalarValue(Object oldVal, Object newVal) {
121         return isEmptyValue(newVal) ? oldVal : newVal;
122     }
123     
124     static boolean isEmptyValue(Object val) {
125         return val == null ||
126                val instanceof String && StringUtils.isEmpty((String)val) ||
127                val instanceof Map && ((Map<?,?>) val).isEmpty() ||
128                val instanceof List && ((List<?>) val).isEmpty();
129
130     }
131     
132     @SuppressWarnings("unchecked")
133     Object removeUnwantedGetInputValues(Object val, List<String> inputNamesToMerge) {
134         if (val instanceof  Map) {
135             return removeUnwantedGetInputValues((Map<String, Object>) val, inputNamesToMerge);
136         }
137         if (val instanceof List) {
138             return removeUnwantedGetInputValues((List<Object>)val, inputNamesToMerge);
139         }
140         return val;
141     }
142
143     private List<Object> removeUnwantedGetInputValues(List<Object> listVal, List<String> inputNamesToMerge) {
144         return listVal.stream().map(val -> removeUnwantedGetInputValues(val, inputNamesToMerge)).collect(Collectors.toList());
145     }
146
147     private Map<String, Object> removeUnwantedGetInputValues(Map<String, Object> val, List<String> inputNamesToMerge) {
148         return val.entrySet().stream().filter(entry -> !isInputEntry(entry) || isInputToMerge(inputNamesToMerge, entry))
149                 .collect(Collectors.toMap(Map.Entry::getKey, entry -> removeUnwantedGetInputValues(entry.getValue(), inputNamesToMerge)));
150     }
151
152     private boolean isInputToMerge(List<String> inputNamesToMerge, Map.Entry<String, Object> entry) {
153         return inputNamesToMerge.contains(retrieveInputName(entry.getValue()));
154     }
155
156     private boolean isMapTypeValues(Object oldVal, Object newVal) {
157         return newVal instanceof Map && oldVal instanceof Map;
158     }
159
160     private boolean isListTypeValues(Object oldVal, Object newVal) {
161         return newVal instanceof List && oldVal instanceof List;
162     }
163
164     private boolean isSameTypeValues(Object oldVal, Object newVal) {
165         return oldVal.getClass().equals(newVal.getClass());
166     }
167
168     private String retrieveInputName(Object inputValue) {
169         return inputValue instanceof List ? (String)((List<?>) inputValue).get(0) : (String)inputValue;
170     }
171
172     protected boolean isInputEntry(Map.Entry<String, Object> oldValEntry) {
173         return oldValEntry.getKey().equals(TypeUtils.ToscaTagNamesEnum.GET_INPUT.getElementName());
174     }
175     
176     
177 }