1 package org.openecomp.sdc.be.components.merge.property;
3 import java.util.ArrayList;
4 import java.util.Collections;
7 import java.util.Optional;
8 import java.util.stream.Collectors;
10 import javax.annotation.Resource;
12 import org.openecomp.sdc.be.components.impl.ImportUtils;
13 import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
14 import org.openecomp.sdc.be.datatypes.elements.GetInputValueDataDefinition;
15 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
16 import org.openecomp.sdc.be.model.DataTypeDefinition;
17 import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache;
18 import org.openecomp.sdc.be.model.tosca.ToscaFunctions;
19 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
20 import org.openecomp.sdc.be.tosca.PropertyConvertor;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23 import org.springframework.stereotype.Component;
25 import com.google.gson.Gson;
27 import fj.data.Either;
30 public class PropertyDataValueMergeBusinessLogic {
32 private static final Logger LOGGER = LoggerFactory.getLogger(PropertyDataValueMergeBusinessLogic.class);
34 private final PropertyConvertor propertyConvertor = PropertyConvertor.getInstance();
36 private PropertyValueMerger complexPropertyValueMerger = ComplexPropertyValueMerger.getInstance();
38 private PropertyValueMerger scalarPropertyValueMerger = ScalarPropertyValueMerger.getInstance();
41 private ApplicationDataTypeCache dataTypeCache;
43 private final Gson gson = new Gson();
47 * @param oldProp the old property to merge value from
48 * @param newProp the new property to merge value into
49 * @param getInputNamesToMerge inputs names which their corresponding get_input values are allowed to be merged
51 public void mergePropertyValue(PropertyDataDefinition oldProp, PropertyDataDefinition newProp, List<String> getInputNamesToMerge) {
52 Either<Map<String, DataTypeDefinition>, TitanOperationStatus> dataTypesEither = dataTypeCache.getAll();
53 if (dataTypesEither.isRight()) {
54 LOGGER.debug("failed to fetch data types, skip merging of previous property values. status: {}", dataTypesEither.right().value());
56 mergePropertyValue(oldProp, newProp, dataTypesEither.left().value(), getInputNamesToMerge);
57 mergeComplexPropertyGetInputsValues(oldProp, newProp);
60 private void mergePropertyValue(PropertyDataDefinition oldProp, PropertyDataDefinition newProp, Map<String, DataTypeDefinition> dataTypes, List<String> getInputNamesToMerge) {
61 Object oldValAsObject = convertPropertyStrValueToObject(oldProp, dataTypes);
62 Object newValAsObject = convertPropertyStrValueToObject(newProp, dataTypes);
63 PropertyValueMerger propertyValueMerger = getPropertyValueMerger(newProp);
64 if(oldValAsObject != null){
65 Object mergedValue = propertyValueMerger.mergeValues(oldValAsObject, newValAsObject, getInputNamesToMerge);
66 newProp.setValue(convertPropertyValueObjectToString(mergedValue));
70 private PropertyValueMerger getPropertyValueMerger(PropertyDataDefinition newProp) {
71 if (ToscaPropertyType.isPrimitiveType(newProp.getType()) || ToscaPropertyType.isPrimitiveType(newProp.getSchemaType())) {
72 return scalarPropertyValueMerger;
74 return complexPropertyValueMerger;
77 private String convertPropertyValueObjectToString(Object mergedValue) {
78 if (isEmptyValue(mergedValue)) {
81 return mergedValue instanceof String? mergedValue.toString() : gson.toJson(mergedValue);
84 private Object convertPropertyStrValueToObject(PropertyDataDefinition propertyDataDefinition, Map<String, DataTypeDefinition> dataTypes) {
85 String propValue = propertyDataDefinition.getValue() == null ? "": propertyDataDefinition.getValue();
86 String propertyType = propertyDataDefinition.getType();
87 String innerType = propertyDataDefinition.getSchemaType();
88 return propertyConvertor.convertToToscaObject(propertyType, propValue, innerType, dataTypes);
92 @SuppressWarnings("unchecked")
93 private Object removeUnwantedGetInputValues(Object val, List<String> getInputNamesToMerge) {
94 if (val instanceof Map) {
95 return removeUnwantedGetInputValues((Map<String, Object>) val, getInputNamesToMerge);
97 if (val instanceof List) {
98 return removeUnwantedGetInputValues((List<Object>)val, getInputNamesToMerge);
103 private List<Object> removeUnwantedGetInputValues(List<Object> listVal, List<String> getInputNamesToMerge) {
104 return listVal.stream().map(val -> removeUnwantedGetInputValues(val, getInputNamesToMerge)).collect(Collectors.toList());
107 private Map<String, Object> removeUnwantedGetInputValues(Map<String, Object> val, List<String> getInputNamesToMerge) {
108 return val.entrySet().stream().filter(entry -> !isGetInputEntry(entry) || isGetInputToMerge(getInputNamesToMerge, entry))
109 .collect(Collectors.toMap(Map.Entry::getKey, entry -> removeUnwantedGetInputValues(entry.getValue(), getInputNamesToMerge)));
112 private boolean isGetInputToMerge(List<String> getInputNamesToMerge, Map.Entry<String, Object> entry) {
113 return getInputNamesToMerge.contains(retrieveGetInputInputName(entry.getValue()));
116 private String retrieveGetInputInputName(Object getInputValue) {
117 return getInputValue instanceof List ? (String)((List) getInputValue).get(0) : (String)getInputValue;
120 private boolean isGetInputEntry(Map.Entry<String, Object> oldValEntry) {
121 return oldValEntry.getKey().equals(ImportUtils.ToscaTagNamesEnum.GET_INPUT.getElementName());
124 private boolean isEmptyValue(Object val) {
125 return val == null ||
126 val instanceof Map && ((Map) val).isEmpty() ||
127 val instanceof List && ((List) val).isEmpty();
130 private void mergeComplexPropertyGetInputsValues(PropertyDataDefinition oldProp, PropertyDataDefinition newProp) {
131 if (!oldProp.isGetInputProperty()) {
134 List<GetInputValueDataDefinition> getInputsToMerge = findOldGetInputValuesToMerge(oldProp, newProp);
135 List<GetInputValueDataDefinition> newPropGetInputValues = Optional.ofNullable(newProp.getGetInputValues()).orElse(new ArrayList<>());
136 newPropGetInputValues.addAll(getInputsToMerge);
137 newProp.setGetInputValues(newPropGetInputValues);
140 private List<GetInputValueDataDefinition> findOldGetInputValuesToMerge(PropertyDataDefinition oldProp, PropertyDataDefinition newProp) {
141 List<GetInputValueDataDefinition> oldGetInputValues = oldProp.getGetInputValues();
142 List<GetInputValueDataDefinition> newGetInputValues = Optional.ofNullable(newProp.getGetInputValues()).orElse(Collections.emptyList());
143 List<String> newGetInputNames = newGetInputValues.stream().map(GetInputValueDataDefinition::getInputName).collect(Collectors.toList());
144 return oldGetInputValues.stream()
145 .filter(getInput -> !newGetInputNames.contains(getInput.getInputName()))
146 .filter(getInput -> isValueContainsGetInput(getInput.getInputName(), newProp.getValue()))
147 .collect(Collectors.toList());
150 private boolean isValueContainsGetInput(String inputName, String value) {
151 String getInputEntry = "\"%s\":\"%s\"";
152 return value != null && value.contains(String.format(getInputEntry, ToscaFunctions.GET_INPUT.getFunctionName(), inputName));