2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.openecomp.sdc.be.components.merge.property;
23 import java.util.ArrayList;
24 import java.util.List;
26 import java.util.Optional;
27 import java.util.stream.Collectors;
29 import org.apache.commons.lang.StringUtils;
30 import org.apache.commons.lang3.tuple.ImmutablePair;
31 import org.openecomp.sdc.be.model.DataTypeDefinition;
32 import org.openecomp.sdc.be.model.PropertyDefinition;
33 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
34 import org.openecomp.sdc.be.utils.TypeUtils;
35 import org.springframework.stereotype.Component;
38 public class PropertyValueMerger {
40 @SuppressWarnings("unchecked")
42 * merges property value oldVal into property value newVal recursively
43 * @param oldVal - cannot be {@code Null}
45 protected Object merge(Object oldVal, Object newVal, List<String> inputNamesToMerge, String type, String innerType, Map<String, DataTypeDefinition> dataTypes) {
46 if (isEmptyValue(newVal)) {
47 return removeUnwantedGetInputValues(oldVal, inputNamesToMerge);
49 if (isMapTypeValues(oldVal, newVal)) {
50 return mergeMapValue((Map<String, Object>) oldVal, (Map<String, Object>) newVal, inputNamesToMerge, type, innerType, dataTypes);
52 if (isListTypeValues(oldVal, newVal)) {
53 return mergeListValue((List<Object>) oldVal, (List<Object>) newVal, inputNamesToMerge, innerType, dataTypes);
55 if (isSameTypeValues(oldVal, newVal)) {
56 return mergeScalarValue(oldVal, newVal);
61 private Map<String, Object> mergeMapValue(Map<String, Object> oldValMap, Map<String, Object> newValMap, List<String> inputNamesToMerge, String type, String innertType, Map<String, DataTypeDefinition> dataTypes) {
62 mergeEntriesExistInOldValue(oldValMap, newValMap, inputNamesToMerge, type, innertType, dataTypes);//continue the recursion
63 if (type != null && !type.equals("map")) {
64 setOldEntriesNotExistInNewValue(oldValMap, newValMap, inputNamesToMerge);
70 private void mergeEntriesExistInOldValue(Map<String, Object> oldValMap, Map<String, Object> newValMap, List<String> inputNamesToMerge, String type, String innerType, Map<String, DataTypeDefinition> dataTypes) {
71 for (Map.Entry<String, Object> newValEntry : newValMap.entrySet()) {
72 Object oldVal = oldValMap.get(newValEntry.getKey());
74 ImmutablePair<String, String> types = getTypeAndInnerTypePair(newValEntry.getKey(), type, innerType, dataTypes);
75 newValMap.put(newValEntry.getKey(), merge(oldVal, newValEntry.getValue(), inputNamesToMerge, types.getLeft(), types.getRight(), dataTypes));
80 private void setOldEntriesNotExistInNewValue(Map<String, Object> oldVal, Map<String, Object> newVal, List<String> getInputNamesToMerge) {
81 for (Map.Entry<String, Object> oldValEntry : oldVal.entrySet()) {
82 if (!isInputEntry(oldValEntry) || isInputToMerge(getInputNamesToMerge, oldValEntry)) {
83 Object oldValObj = oldValEntry.getValue();
84 newVal.computeIfAbsent(oldValEntry.getKey(), key -> removeUnwantedGetInputValues(oldValObj, getInputNamesToMerge));
89 private ImmutablePair<String, String> getTypeAndInnerTypePair(String propName, String type, String innerType, Map<String, DataTypeDefinition> dataTypes) {
90 if (type == null || (ToscaPropertyType.isScalarType(type) && ToscaPropertyType.isScalarType(innerType))) {
91 return ImmutablePair.of(innerType, null);
94 String newInnerType = null;
95 DataTypeDefinition innerTypeDef = dataTypes.get(type);
96 if (innerTypeDef != null) {
97 List<PropertyDefinition> properties = innerTypeDef.getProperties();
98 if (properties!= null) {
99 Optional<PropertyDefinition> optionalProperty = findProperty(properties, propName);
101 innerType = optionalProperty.map(PropertyDefinition::getType)
104 newInnerType = optionalProperty.map(PropertyDefinition::getSchemaType)
109 return ImmutablePair.of(innerType, newInnerType);
112 private Optional<PropertyDefinition> findProperty(List<PropertyDefinition> properties, String propName) {
113 return properties.stream()
114 .filter(p -> propName.equals(p.getName()))
118 private List<Object> mergeListValue(List<Object> oldVal, List<Object> newVal, List<String> inputNamesToMerge, String innerType, Map<String, DataTypeDefinition> dataTypes) {
119 List<Object> mergedList = newVal;
121 if (oldVal.size() == newVal.size()) {
122 mergedList = mergeLists(oldVal, newVal, inputNamesToMerge, innerType, dataTypes);
129 private List<Object> mergeLists(List<Object> oldVal, List<Object> newVal, List<String> inputNamesToMerge, String innerType, Map<String, DataTypeDefinition> dataTypes) {
130 int minListSize = Math.min(oldVal.size(), newVal.size());
131 List<Object> mergedList = new ArrayList<>();
132 for (int i = 0; i < minListSize; i++) {
133 Object mergedVal = merge(oldVal.get(i), newVal.get(i), inputNamesToMerge, innerType, null, dataTypes);
134 mergedList.add(mergedVal);
140 private Object mergeScalarValue(Object oldVal, Object newVal) {
141 return isEmptyValue(newVal) ? oldVal : newVal;
144 static boolean isEmptyValue(Object val) {
145 return val == null ||
146 val instanceof String && StringUtils.isEmpty((String)val) ||
147 val instanceof Map && ((Map<?,?>) val).isEmpty() ||
148 val instanceof List && ((List<?>) val).isEmpty();
152 @SuppressWarnings("unchecked")
153 Object removeUnwantedGetInputValues(Object val, List<String> inputNamesToMerge) {
154 if (val instanceof Map) {
155 return removeUnwantedGetInputValues((Map<String, Object>) val, inputNamesToMerge);
157 if (val instanceof List) {
158 return removeUnwantedGetInputValues((List<Object>)val, inputNamesToMerge);
163 private List<Object> removeUnwantedGetInputValues(List<Object> listVal, List<String> inputNamesToMerge) {
164 return listVal.stream().map(val -> removeUnwantedGetInputValues(val, inputNamesToMerge)).collect(Collectors.toList());
167 private Map<String, Object> removeUnwantedGetInputValues(Map<String, Object> val, List<String> inputNamesToMerge) {
168 return val.entrySet().stream().filter(entry -> !isInputEntry(entry) || isInputToMerge(inputNamesToMerge, entry))
169 .collect(Collectors.toMap(Map.Entry::getKey, entry -> removeUnwantedGetInputValues(entry.getValue(), inputNamesToMerge)));
172 private boolean isInputToMerge(List<String> inputNamesToMerge, Map.Entry<String, Object> entry) {
173 return inputNamesToMerge.contains(retrieveInputName(entry.getValue()));
176 private boolean isMapTypeValues(Object oldVal, Object newVal) {
177 return newVal instanceof Map && oldVal instanceof Map;
180 private boolean isListTypeValues(Object oldVal, Object newVal) {
181 return newVal instanceof List && oldVal instanceof List;
184 private boolean isSameTypeValues(Object oldVal, Object newVal) {
185 return oldVal.getClass().equals(newVal.getClass());
188 private String retrieveInputName(Object inputValue) {
189 return inputValue instanceof List ? (String)((List<?>) inputValue).get(0) : (String)inputValue;
192 protected boolean isInputEntry(Map.Entry<String, Object> oldValEntry) {
193 return oldValEntry.getKey().equals(TypeUtils.ToscaTagNamesEnum.GET_INPUT.getElementName());