Support for defining attributes on a node_type
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / impl / ImportUtils.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2017 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
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
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=========================================================
19  */
20
21 package org.openecomp.sdc.be.components.impl;
22
23 import com.google.gson.Gson;
24 import com.google.gson.GsonBuilder;
25 import com.google.gson.JsonParseException;
26 import com.google.gson.reflect.TypeToken;
27 import fj.data.Either;
28 import org.apache.commons.collections.CollectionUtils;
29 import org.apache.commons.lang3.StringEscapeUtils;
30 import org.apache.tinkerpop.gremlin.structure.T;
31 import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException;
32 import org.openecomp.sdc.be.config.BeEcompErrorManager;
33 import org.openecomp.sdc.be.dao.api.ActionStatus;
34 import org.openecomp.sdc.be.datatypes.elements.Annotation;
35 import org.openecomp.sdc.be.datatypes.elements.AttributeDataDefinition;
36 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
37 import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
38 import org.openecomp.sdc.be.datatypes.enums.JsonPresentationFields;
39 import org.openecomp.sdc.be.datatypes.tosca.ToscaDataDefinition;
40 import org.openecomp.sdc.be.impl.ComponentsUtils;
41 import org.openecomp.sdc.be.model.AnnotationTypeDefinition;
42 import org.openecomp.sdc.be.model.AttributeDefinition;
43 import org.openecomp.sdc.be.model.HeatParameterDefinition;
44 import org.openecomp.sdc.be.model.InputDefinition;
45 import org.openecomp.sdc.be.model.LifecycleStateEnum;
46 import org.openecomp.sdc.be.model.PropertyConstraint;
47 import org.openecomp.sdc.be.model.PropertyDefinition;
48 import org.openecomp.sdc.be.model.heat.HeatParameterType;
49 import org.openecomp.sdc.be.model.operations.impl.AnnotationTypeOperations;
50 import org.openecomp.sdc.be.model.operations.impl.PropertyOperation.PropertyConstraintDeserialiser;
51 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
52 import org.openecomp.sdc.be.model.tosca.constraints.ConstraintType;
53 import org.openecomp.sdc.be.model.tosca.constraints.ValidValuesConstraint;
54 import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintValueDoNotMatchPropertyTypeException;
55 import org.openecomp.sdc.be.utils.TypeUtils;
56 import org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum;
57 import org.openecomp.sdc.common.api.ArtifactTypeEnum;
58 import org.openecomp.sdc.common.log.wrappers.Logger;
59 import org.openecomp.sdc.exception.ResponseFormat;
60 import org.springframework.beans.factory.annotation.Autowired;
61 import org.springframework.beans.factory.config.YamlProcessor;
62 import org.springframework.stereotype.Component;
63 import org.yaml.snakeyaml.DumperOptions;
64 import org.yaml.snakeyaml.Yaml;
65 import org.yaml.snakeyaml.constructor.Constructor;
66 import org.yaml.snakeyaml.nodes.Tag;
67 import org.yaml.snakeyaml.representer.Representer;
68 import org.yaml.snakeyaml.resolver.Resolver;
69
70 import java.lang.reflect.Type;
71 import java.util.ArrayList;
72 import java.util.Collection;
73 import java.util.Collections;
74 import java.util.HashMap;
75 import java.util.Iterator;
76 import java.util.LinkedHashMap;
77 import java.util.List;
78 import java.util.Map;
79 import java.util.Map.Entry;
80 import java.util.function.Consumer;
81 import java.util.function.Function;
82
83 import static org.apache.commons.collections.CollectionUtils.isEmpty;
84 import static org.openecomp.sdc.be.components.impl.ResourceImportManager.PROPERTY_NAME_PATTERN_IGNORE_LENGTH;
85 import static org.openecomp.sdc.be.datatypes.elements.Annotation.setAnnotationsName;
86
87 @Component
88 public final class ImportUtils {
89
90     private static final CustomResolver customResolver = new CustomResolver();
91     private static final Yaml strictYamlLoader =  new YamlLoader().getStrictYamlLoader();
92
93     private static ComponentsUtils componentsUtils;
94
95     private static final Logger log = Logger.getLogger(ImportUtils.class);
96
97     private ImportUtils() {
98     }
99
100     @Autowired
101     public static void setComponentsUtils(ComponentsUtils componentsUtils) {
102         componentsUtils = componentsUtils;
103     }
104
105     private static class CustomResolver extends Resolver {
106         @Override
107         protected void addImplicitResolvers() {
108             // avoid implicit resolvers for strings that can be interpreted as boolean values
109             addImplicitResolver(Tag.STR, EMPTY, "");
110             addImplicitResolver(Tag.STR, NULL, null);
111             addImplicitResolver(Tag.NULL, NULL, "~nN\0");
112             addImplicitResolver(Tag.NULL, EMPTY, null);
113             addImplicitResolver(Tag.INT, INT, "-+0123456789");
114             addImplicitResolver(Tag.FLOAT, FLOAT, "-+0123456789.");
115             addImplicitResolver(Tag.YAML, YAML, "!&*");
116         }
117     }
118
119     private static void buildMap(Map<String, Object> output, Map<String, Object> map) {
120         for (Entry<String, Object> entry : map.entrySet()) {
121             String key = entry.getKey();
122             Object value = entry.getValue();
123             if (value instanceof Map) {
124                 Map<String, Object> result = new LinkedHashMap<>();
125                 buildMap(result, (Map) value);
126                 output.put(key, result);
127             }
128             else if (value instanceof Collection) {
129                 Map<String, Object> result = new LinkedHashMap<>();
130                 int i = 0;
131                 for(Object item : (Collection<Object>) value) {
132                     buildMap(result, Collections.singletonMap("[" + (i++) + "]", item));
133                 }
134                 output.put(key, new ArrayList<>(result.values()));
135             }
136             else {
137                 output.put(key, value);
138             }
139         }
140     }
141
142     public static Map<String, Object> loadYamlAsStrictMap(String content){
143         Map<String, Object> result = new LinkedHashMap<>();
144         Object map = strictYamlLoader.load(content);
145         buildMap(result, (Map<String, Object>)map);
146         return result;
147     }
148
149     private static class YamlLoader extends YamlProcessor {
150         public Yaml getStrictYamlLoader() {
151             return createYaml();
152         }
153     }
154
155     @SuppressWarnings("unchecked")
156     public static Either<List<HeatParameterDefinition>, ResultStatusEnum> getHeatParamsWithoutImplicitTypes(String heatDecodedPayload, String artifactType) {
157         Map<String, Object> heatData = (Map<String, Object>) new Yaml(new Constructor(), new Representer(), new DumperOptions(), customResolver).load(heatDecodedPayload);
158         return getHeatParameters(heatData, artifactType);
159     }
160
161     public static class Constants {
162         public static final String FIRST_NON_CERTIFIED_VERSION = "0.1";
163         public static final String VENDOR_NAME = "ONAP (Tosca)";
164         public static final String VENDOR_RELEASE = "1.0.0.wd03";
165         public static final LifecycleStateEnum NORMATIVE_TYPE_LIFE_CYCLE = LifecycleStateEnum.CERTIFIED;
166         public static final LifecycleStateEnum NORMATIVE_TYPE_LIFE_CYCLE_NOT_CERTIFIED_CHECKOUT = LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT;
167         public static final boolean NORMATIVE_TYPE_HIGHEST_VERSION = true;
168         public static final String ABSTRACT_CATEGORY_NAME = "Generic";
169         public static final String ABSTRACT_SUBCATEGORY = "Abstract";
170         public static final String DEFAULT_ICON = "defaulticon";
171         public static final String INNER_VFC_DESCRIPTION = "Not reusable inner VFC";
172         public static final String USER_DEFINED_RESOURCE_NAMESPACE_PREFIX = "org.openecomp.resource.";
173         public static final String UI_JSON_PAYLOAD_NAME = "payloadName";
174         public static final String CVFC_DESCRIPTION = "Complex node type that is used as nested type in VF";
175         public static final String ESCAPED_DOUBLE_QUOTE = "\"";
176         public static final String QUOTE = "'";
177
178         private Constants() {}
179     }
180
181     public enum ResultStatusEnum {
182         ELEMENT_NOT_FOUND, GENERAL_ERROR, OK, INVALID_PROPERTY_DEFAULT_VALUE, INVALID_PROPERTY_TYPE, INVALID_PROPERTY_VALUE, MISSING_ENTRY_SCHEMA_TYPE, INVALID_PROPERTY_NAME
183     }
184
185     public enum ToscaElementTypeEnum {
186         BOOLEAN, STRING, MAP, LIST, ALL
187     }
188
189     @SuppressWarnings("unchecked")
190     private static void handleElementNameNotFound(String elementName, Object elementValue, ToscaElementTypeEnum elementType, List<Object> returnedList) {
191         if (elementValue instanceof Map) {
192             findToscaElements((Map<String, Object>) elementValue, elementName, elementType, returnedList);
193         } else if (elementValue instanceof List) {
194             findAllToscaElementsInList((List<Object>) elementValue, elementName, elementType, returnedList);
195         }
196     }
197
198     @SuppressWarnings("unchecked")
199     private static void addFoundElementAccordingToItsType(String elementName, ToscaElementTypeEnum elementType, List<Object> returnedList, Object elementValue) {
200
201         if (elementValue instanceof Boolean) {
202             if (elementType == ToscaElementTypeEnum.BOOLEAN || elementType == ToscaElementTypeEnum.ALL) {
203                 returnedList.add(elementValue);
204             }
205         }
206
207         else if (elementValue instanceof String) {
208             if (elementType == ToscaElementTypeEnum.STRING || elementType == ToscaElementTypeEnum.ALL) {
209                 returnedList.add(elementValue);
210             }
211         } else if (elementValue instanceof Map) {
212             if (elementType == ToscaElementTypeEnum.MAP || elementType == ToscaElementTypeEnum.ALL) {
213                 returnedList.add(elementValue);
214             }
215             findToscaElements((Map<String, Object>) elementValue, elementName, elementType, returnedList);
216
217         } else if (elementValue instanceof List) {
218             if (elementType == ToscaElementTypeEnum.LIST || elementType == ToscaElementTypeEnum.ALL) {
219                 returnedList.add(elementValue);
220             }
221             findAllToscaElementsInList((List<Object>) elementValue, elementName, elementType, returnedList);
222
223         }
224         // For Integer, Double etc...
225         else if (elementType == ToscaElementTypeEnum.ALL && elementValue != null) {
226             returnedList.add(String.valueOf(elementValue));
227         }
228     }
229
230     private static void findAllToscaElementsInList(List<Object> list, String elementName, ToscaElementTypeEnum elementType, List<Object> returnedList) {
231         list.forEach(elementValue -> handleElementNameNotFound(elementName, elementValue, elementType, returnedList));
232     }
233
234     public static Either<Object, ResultStatusEnum> findToscaElement(Map<String, Object> toscaJson, TypeUtils.ToscaTagNamesEnum elementName, ToscaElementTypeEnum elementType) {
235         List<Object> foundElements = new ArrayList<>();
236         findToscaElements(toscaJson, elementName.getElementName(), elementType, foundElements);
237         if (!isEmpty(foundElements)) {
238             return Either.left(foundElements.get(0));
239         }
240         return Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
241     }
242
243     /**
244      * Recursively searches for all tosca elements with key equals to elementName and value equals to elementType. <br>
245      * Returns Either element with:<br>
246      * List with all value if values found<br>
247      * Or ELEMENT_NOT_FOUND ActionStatus
248      *
249      * @param toscaJson
250      * @return
251      */
252     public static Either<List<Object>, ResultStatusEnum> findToscaElements(Map<String, Object> toscaJson, String elementName, ToscaElementTypeEnum elementType, List<Object> returnedList) {
253         Either<List<Object>, ResultStatusEnum> returnedElement = Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
254         String skipKey = null;
255         if (toscaJson.containsKey(elementName)) {
256             skipKey = handleFoundElement(toscaJson, elementName, elementType, returnedList);
257         }
258
259
260         Iterator<Entry<String, Object>> keyValItr = toscaJson.entrySet().iterator();
261         while (keyValItr.hasNext()) {
262             Entry<String, Object> keyValEntry = keyValItr.next();
263             if (!String.valueOf(keyValEntry.getKey()).equals(skipKey)) {
264                 handleElementNameNotFound(elementName, keyValEntry.getValue(), elementType, returnedList);
265             }
266         }
267
268         if (!isEmpty(returnedList)) {
269             returnedElement = Either.left(returnedList);
270         }
271
272         return returnedElement;
273     }
274
275     private static String handleFoundElement(Map<String, Object> toscaJson, String elementName, ToscaElementTypeEnum elementType, List<Object> returnedList) {
276         Object elementValue = toscaJson.get(elementName);
277         addFoundElementAccordingToItsType(elementName, elementType, returnedList, elementValue);
278         return elementName;
279
280     }
281
282     @SuppressWarnings("unchecked")
283     public static <T> Either<List<T>, ResultStatusEnum> findFirstToscaListElement(Map<String, Object> toscaJson, TypeUtils.ToscaTagNamesEnum toscaTagName) {
284         Either<List<T>, ResultStatusEnum> returnedElement = Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
285         Either<Object, ResultStatusEnum> findFirstToscaElement = findToscaElement(toscaJson, toscaTagName, ToscaElementTypeEnum.LIST);
286         if (findFirstToscaElement.isLeft()) {
287             returnedElement = Either.left((List<T>) findFirstToscaElement.left().value());
288         }
289         return returnedElement;
290
291     }
292
293     @SuppressWarnings("unchecked")
294     public static <T> Either<Map<String, T>, ResultStatusEnum> findFirstToscaMapElement(Map<String, Object> toscaJson, TypeUtils.ToscaTagNamesEnum toscaTagName) {
295         Either<Map<String, T>, ResultStatusEnum> returnedElement = Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
296         Either<Object, ResultStatusEnum> findFirstToscaElement = findToscaElement(toscaJson, toscaTagName, ToscaElementTypeEnum.MAP);
297         if (findFirstToscaElement.isLeft()) {
298             returnedElement = Either.left((Map<String, T>) findFirstToscaElement.left().value());
299         }
300         return returnedElement;
301
302     }
303
304     public static Either<String, ResultStatusEnum> findFirstToscaStringElement(Map<String, Object> toscaJson, TypeUtils.ToscaTagNamesEnum toscaTagName) {
305         Either<String, ResultStatusEnum> returnedElement = Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
306         Either<Object, ResultStatusEnum> findFirstToscaElements = findToscaElement(toscaJson, toscaTagName, ToscaElementTypeEnum.STRING);
307         if (findFirstToscaElements.isLeft()) {
308             returnedElement = Either.left((String) findFirstToscaElements.left().value());
309         }
310         return returnedElement;
311     }
312
313     /**
314      * searches for first Tosca in Json map (toscaJson) boolean element by name (toscaTagName) returns found element or ELEMENT_NOT_FOUND status
315      *
316      * @param toscaJson
317      * @param toscaTagName
318      * @return
319      */
320     public static Either<String, ResultStatusEnum> findFirstToscaBooleanElement(Map<String, Object> toscaJson, TypeUtils.ToscaTagNamesEnum toscaTagName) {
321         Either<String, ResultStatusEnum> returnedElement = Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
322         Either<Object, ResultStatusEnum> findFirstToscaElements = findToscaElement(toscaJson, toscaTagName, ToscaElementTypeEnum.BOOLEAN);
323         if (findFirstToscaElements.isLeft()) {
324             returnedElement = Either.left(String.valueOf(findFirstToscaElements.left().value()));
325         }
326         return returnedElement;
327     }
328
329     private static void setPropertyConstraints(Map<String, Object> propertyValue, PropertyDefinition property) {
330         List<PropertyConstraint> constraints = getPropertyConstraints(propertyValue, property.getType());
331         if (CollectionUtils.isNotEmpty(constraints)) {
332             property.setConstraints(constraints);
333         }
334     }
335
336     private static List<PropertyConstraint> getPropertyConstraints(Map<String, Object> propertyValue, String propertyType) {
337         List<Object> propertyFieldConstraints = findCurrentLevelConstraintsElement(propertyValue);
338         if (CollectionUtils.isNotEmpty(propertyFieldConstraints)) {
339             List<PropertyConstraint> constraintList = new ArrayList<>();
340             Type constraintType = new TypeToken<PropertyConstraint>() {
341             }.getType();
342             Gson gson = new GsonBuilder().registerTypeAdapter(constraintType, new PropertyConstraintDeserialiser()).create();
343
344             for (Object constraintJson : propertyFieldConstraints) {
345                 PropertyConstraint propertyConstraint = validateAndGetPropertyConstraint(propertyType, constraintType, gson, constraintJson);
346                 constraintList.add(propertyConstraint);
347             }
348             return constraintList;
349         }
350         return null;
351     }
352
353     private static List<Object> findCurrentLevelConstraintsElement(Map<String, Object> toscaJson) {
354         List<Object> constraints = null;
355         if (toscaJson.containsKey(TypeUtils.ToscaTagNamesEnum.CONSTRAINTS.getElementName())) {
356             try {
357                 constraints = (List<Object>) toscaJson.get(TypeUtils.ToscaTagNamesEnum.CONSTRAINTS.getElementName());
358             } catch (ClassCastException e){
359                 throw new ByActionStatusComponentException(ActionStatus.INVALID_PROPERTY_CONSTRAINTS_FORMAT, toscaJson.get(TypeUtils.ToscaTagNamesEnum.CONSTRAINTS.getElementName()).toString());
360             }
361         }
362         return constraints;
363
364     }
365
366     private static PropertyConstraint validateAndGetPropertyConstraint(String propertyType, Type constraintType, Gson gson, Object constraintJson) {
367         PropertyConstraint propertyConstraint;
368         try{
369             propertyConstraint = gson.fromJson(gson.toJson(constraintJson), constraintType);
370         } catch (ClassCastException|JsonParseException e){
371             throw new ByActionStatusComponentException(ActionStatus.INVALID_PROPERTY_CONSTRAINTS_FORMAT, constraintJson.toString());
372         }
373         if(propertyConstraint!= null && propertyConstraint instanceof ValidValuesConstraint){
374             try {
375                 ((ValidValuesConstraint)propertyConstraint).validateType(propertyType);
376             } catch (ConstraintValueDoNotMatchPropertyTypeException e) {
377                 BeEcompErrorManager.getInstance().logInternalFlowError("GetInitializedPropertyConstraint",
378                         e.getMessage(), BeEcompErrorManager.ErrorSeverity.ERROR);
379                 throw new ByActionStatusComponentException(ActionStatus.INVALID_PROPERTY_CONSTRAINTS, ConstraintType.VALID_VALUES.name(),
380                         ((ValidValuesConstraint) propertyConstraint).getValidValues().toString(), propertyType);
381             }
382         }
383         return propertyConstraint;
384     }
385
386     public static PropertyDefinition createModuleProperty(Map<String, Object> propertyValue) {
387
388         PropertyDefinition propertyDef = new PropertyDefinition();
389         setField(propertyValue, TypeUtils.ToscaTagNamesEnum.TYPE, propertyDef::setType);
390         setFieldBoolean(propertyValue, ToscaTagNamesEnum.REQUIRED, req -> propertyDef.setRequired(Boolean.parseBoolean(req)));
391         setField(propertyValue, TypeUtils.ToscaTagNamesEnum.DESCRIPTION, propertyDef::setDescription);
392
393         setJsonStringField(propertyValue, TypeUtils.ToscaTagNamesEnum.DEFAULT_VALUE, propertyDef.getType(), propertyDef::setDefaultValue);
394         setJsonStringField(propertyValue, TypeUtils.ToscaTagNamesEnum.VALUE, propertyDef.getType(), propertyDef::setValue);
395
396         setFieldBoolean(propertyValue, TypeUtils.ToscaTagNamesEnum.IS_PASSWORD, pass -> propertyDef.setPassword(Boolean.parseBoolean(pass)));
397         setField(propertyValue, TypeUtils.ToscaTagNamesEnum.STATUS, propertyDef::setStatus);
398         setScheme(propertyValue, propertyDef);
399         setPropertyConstraints(propertyValue, propertyDef);
400
401         return propertyDef;
402     }
403
404     private static void setJsonStringField(Map<String, Object> propertyValue, ToscaTagNamesEnum elementName, String type, Consumer<String> setter) {
405         Either<Object, ResultStatusEnum> eitherValue = findToscaElement(propertyValue, elementName, ToscaElementTypeEnum.ALL);
406         if (eitherValue.isLeft()) {
407             String propertyJsonStringValue = getPropertyJsonStringValue(eitherValue.left().value(), type);
408             setter.accept(propertyJsonStringValue);
409         }
410     }
411
412     public static Annotation createModuleAnnotation(Map<String, Object> annotationMap, AnnotationTypeOperations annotationTypeOperations) {
413         String parsedAnnotationType = findFirstToscaStringElement(annotationMap, TypeUtils.ToscaTagNamesEnum.TYPE).left().value();
414         AnnotationTypeDefinition annotationTypeObject = annotationTypeOperations.getLatestType(parsedAnnotationType);
415         if (annotationTypeObject != null) {
416             Annotation annotation = new Annotation();
417             setField(annotationMap, TypeUtils.ToscaTagNamesEnum.TYPE, annotation::setType);
418             setField(annotationMap, TypeUtils.ToscaTagNamesEnum.DESCRIPTION, annotation::setDescription);
419             Either<Map<String, PropertyDefinition>, ResultStatusEnum> properties = getProperties(annotationMap);
420             modifyPropertiesKeysToProperForm(properties, annotation);
421             return annotation;
422         }
423         return null;
424     }
425
426     private static Either<Boolean, ResponseFormat> modifyPropertiesKeysToProperForm(Either<Map<String, PropertyDefinition>, ResultStatusEnum> properties, Annotation annotation) {
427         Either<Boolean, ResponseFormat> result = Either.left(true);
428         if (properties.isLeft()) {
429             List<PropertyDataDefinition> propertiesList = new ArrayList<>();
430             Map<String, PropertyDefinition> value = properties.left().value();
431             if (value != null) {
432                 for (Entry<String, PropertyDefinition> entry : value.entrySet()) {
433                     String name = entry.getKey();
434                     if (!PROPERTY_NAME_PATTERN_IGNORE_LENGTH.matcher(name).matches()) {
435                         log.debug("The property with invalid name {} occurred upon import resource {}. ", name, annotation.getName());
436                         result = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromResultStatusEnum(ResultStatusEnum.INVALID_PROPERTY_NAME, JsonPresentationFields.PROPERTY)));
437                     }
438                     PropertyDefinition propertyDefinition = entry.getValue();
439                     propertyDefinition.setValue(propertyDefinition.getName());
440                     propertyDefinition.setName(name);
441                     propertiesList.add(propertyDefinition);
442                 }
443             }
444             annotation.setProperties(propertiesList);
445         }
446         else if (properties.right().value() != ResultStatusEnum.ELEMENT_NOT_FOUND) {
447             result = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromResultStatusEnum(properties
448                     .right()
449                     .value(), JsonPresentationFields.PROPERTY)));
450         }
451         return result;
452     }
453
454     public static InputDefinition createModuleInput(Map<String, Object> inputValue, AnnotationTypeOperations annotationTypeOperations) {
455
456         InputDefinition inputDef = new InputDefinition();
457         setField(inputValue, TypeUtils.ToscaTagNamesEnum.TYPE, inputDef::setType);
458         setFieldBoolean(inputValue, ToscaTagNamesEnum.REQUIRED, req -> inputDef.setRequired(Boolean.parseBoolean(req)));
459         setField(inputValue, TypeUtils.ToscaTagNamesEnum.DESCRIPTION, inputDef::setDescription);
460
461         setJsonStringField(inputValue, TypeUtils.ToscaTagNamesEnum.DEFAULT_VALUE, inputDef.getType(), inputDef::setDefaultValue);
462
463         setFieldBoolean(inputValue, TypeUtils.ToscaTagNamesEnum.IS_PASSWORD, pass -> inputDef.setPassword(Boolean.parseBoolean(pass)));
464         setField(inputValue, TypeUtils.ToscaTagNamesEnum.STATUS, inputDef::setStatus);
465         setField(inputValue, TypeUtils.ToscaTagNamesEnum.LABEL, inputDef::setLabel);
466         setFieldBoolean(inputValue, TypeUtils.ToscaTagNamesEnum.HIDDEN, hidden -> inputDef.setHidden(Boolean.parseBoolean(hidden)));
467         setFieldBoolean(inputValue, TypeUtils.ToscaTagNamesEnum.IMMUTABLE, immutable -> inputDef.setImmutable(Boolean.parseBoolean(immutable)));
468
469         setScheme(inputValue, inputDef);
470         setPropertyConstraints(inputValue, inputDef);
471
472         return parseAnnotationsAndAddItToInput(inputDef, inputValue, annotationTypeOperations);
473
474     }
475
476     public static InputDefinition parseAnnotationsAndAddItToInput(InputDefinition inputDef, Map<String, Object> inputValue, AnnotationTypeOperations annotationTypeOperations){
477         Function<String, Annotation> elementGenByName = ImportUtils::createAnnotation;
478         Function<Map<String, Object>, Annotation> func = annotation -> createModuleAnnotation(annotation, annotationTypeOperations);
479         return getElements(inputValue, TypeUtils.ToscaTagNamesEnum.ANNOTATIONS, elementGenByName, func).
480                 left().map( annotations -> modifyInputWithAnnotations(inputDef, annotations)).
481                 left().on(err -> { log.error("Parsing annotations or adding them to the PropertyDataDefinition object failed");
482                                     return inputDef;});
483     }
484
485     private static InputDefinition modifyInputWithAnnotations(InputDefinition inputDef, Map<String, Annotation> annotationsMap) {
486         setAnnotationsName(annotationsMap);
487         inputDef.setAnnotationsToInput(annotationsMap.values());
488         return inputDef;
489     }
490
491     public static AttributeDefinition createModuleAttribute(Map<String, Object> attributeMap) {
492
493         AttributeDefinition attributeDef = new AttributeDefinition();
494         setField(attributeMap, TypeUtils.ToscaTagNamesEnum.TYPE, attributeDef::setType);
495         setField(attributeMap, TypeUtils.ToscaTagNamesEnum.DESCRIPTION, attributeDef::setDescription);
496         setField(attributeMap, TypeUtils.ToscaTagNamesEnum.STATUS, attributeDef::setStatus);
497
498         setJsonStringField(attributeMap, TypeUtils.ToscaTagNamesEnum.DEFAULT_VALUE, attributeDef.getType(), attributeDef::set_default);
499
500         setScheme(attributeMap, attributeDef);
501         return attributeDef;
502     }
503
504     private static void setScheme(Map<String, Object> propertyValue,
505                                   ToscaDataDefinition toscaDataDefinition) {
506         Either<Object, ResultStatusEnum> schemaElementRes = findSchemaElement(propertyValue);
507         if (schemaElementRes.isLeft()) {
508             SchemaDefinition schemaDef = getSchema(schemaElementRes.left().value());
509             toscaDataDefinition.setSchema(schemaDef);
510         }
511     }
512
513     private static Either<Object, ResultStatusEnum> findSchemaElement(Map<String, Object> propertyValue) {
514         return findToscaElement(propertyValue, TypeUtils.ToscaTagNamesEnum.ENTRY_SCHEMA, ToscaElementTypeEnum.ALL);
515     }
516
517     private static SchemaDefinition getSchema(Object propertyFieldEntryScheme) {
518         SchemaDefinition schema = new SchemaDefinition();
519         if (propertyFieldEntryScheme instanceof String) {
520             String schemaType = (String) propertyFieldEntryScheme;
521             PropertyDefinition schemeProperty = new PropertyDefinition();
522             schemeProperty.setType(schemaType);
523             schema.setProperty(schemeProperty);
524         } else if (propertyFieldEntryScheme instanceof Map) {
525             PropertyDefinition schemeProperty = createModuleProperty((Map<String, Object>) propertyFieldEntryScheme);
526             schema.setProperty(schemeProperty);
527         }
528         return schema;
529     }
530
531     public static void setField(Map<String, Object> toscaJson, TypeUtils.ToscaTagNamesEnum tagName, Consumer<String> setter) {
532         Either<String, ResultStatusEnum> fieldStringValue = findFirstToscaStringElement(toscaJson, tagName);
533         if (fieldStringValue.isLeft()) {
534             setter.accept(fieldStringValue.left().value());
535         }
536
537     }
538
539     public static void setFieldBoolean(Map<String, Object> toscaJson, TypeUtils.ToscaTagNamesEnum tagName, Consumer<String> setter) {
540         Either<String, ResultStatusEnum> fieldStringValue = findFirstToscaBooleanElement(toscaJson, tagName);
541         if (fieldStringValue.isLeft()) {
542             setter.accept(fieldStringValue.left().value());
543         }
544
545     }
546
547     public static Either<Map<String, PropertyDefinition>, ResultStatusEnum> getProperties(
548         Map<String, Object> toscaJson) {
549         Function<String, PropertyDefinition> elementGenByName = ImportUtils::createProperties;
550         Function<Map<String, Object>, PropertyDefinition> func = ImportUtils::createModuleProperty;
551
552         return getElements(toscaJson, TypeUtils.ToscaTagNamesEnum.PROPERTIES, elementGenByName, func);
553
554     }
555
556     public static Either<Map<String, InputDefinition>, ResultStatusEnum> getInputs(Map<String, Object> toscaJson,
557                                                                                    AnnotationTypeOperations annotationTypeOperations) {
558         Function<String, InputDefinition> elementGenByName = ImportUtils::createInputs;
559         Function<Map<String, Object>, InputDefinition> func = object -> createModuleInput(object,
560             annotationTypeOperations);
561
562         return getElements(toscaJson, TypeUtils.ToscaTagNamesEnum.INPUTS, elementGenByName, func);
563
564     }
565
566     public static Either<Map<String, AttributeDataDefinition>, ResultStatusEnum> getAttributes(
567         Map<String, Object> toscaJson) {
568         Function<String, AttributeDataDefinition> elementGenByName = ImportUtils::createAttribute;
569         Function<Map<String, Object>, AttributeDataDefinition> func = ImportUtils::createModuleAttribute;
570
571         return getElements(toscaJson, TypeUtils.ToscaTagNamesEnum.ATTRIBUTES, elementGenByName, func);
572     }
573
574     public static <T> Either<Map<String, T>, ResultStatusEnum> getElements(Map<String, Object> toscaJson,
575                                                                            TypeUtils.ToscaTagNamesEnum elementTagName,
576                                                                            Function<String, T> elementGenByName,
577                                                                            Function<Map<String, Object>, T> func) {
578         Either<Map<String, T>, ResultStatusEnum> eitherResult = Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
579         Either<Map<String, Object>, ResultStatusEnum> toscaAttributes = findFirstToscaMapElement(toscaJson,
580             elementTagName);
581         if (toscaAttributes.isLeft()) {
582             Map<String, Object> jsonAttributes = toscaAttributes.left().value();
583             Map<String, T> moduleAttributes = new HashMap<>();
584             Iterator<Entry<String, Object>> propertiesNameValue = jsonAttributes.entrySet().iterator();
585             while (propertiesNameValue.hasNext()) {
586                 Entry<String, Object> attributeNameValue = propertiesNameValue.next();
587                 if (attributeNameValue.getValue() instanceof Map) {
588                     @SuppressWarnings("unchecked")
589                     T attribute = func.apply((Map<String, Object>) attributeNameValue.getValue());
590                     if (attribute != null) {
591                         moduleAttributes.put(String.valueOf(attributeNameValue.getKey()), attribute);
592                     }
593                 }
594                 else {
595                     T element = elementGenByName.apply(String.valueOf(attributeNameValue.getValue()));
596                     moduleAttributes.put(String.valueOf(attributeNameValue.getKey()), element);
597                 }
598             }
599             if (moduleAttributes.size() > 0) {
600                 eitherResult = Either.left(moduleAttributes);
601             }
602
603         }
604         return eitherResult;
605
606     }
607
608     private static AttributeDefinition createAttribute(String name) {
609         AttributeDefinition attribute = new AttributeDefinition();
610
611         attribute.setName(name);
612         return attribute;
613     }
614
615     private static PropertyDefinition createProperties(String name) {
616         PropertyDefinition property = new PropertyDefinition();
617         property.setDefaultValue(name);
618         property.setName(name);
619         return property;
620     }
621
622     private static InputDefinition createInputs(String name) {
623         InputDefinition input = new InputDefinition();
624
625         input.setName(name);
626         return input;
627     }
628
629     private static Annotation createAnnotation(String name) {
630         Annotation annotation = new Annotation();
631         annotation.setName(name);
632         return annotation;
633     }
634
635     public static Either<List<HeatParameterDefinition>, ResultStatusEnum> getHeatParameters(Map<String, Object> heatData, String artifactType) {
636
637         Either<List<HeatParameterDefinition>, ResultStatusEnum> eitherResult = Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
638         Either<Map<String, Object>, ResultStatusEnum> toscaProperties = findFirstToscaMapElement(heatData, TypeUtils.ToscaTagNamesEnum.PARAMETERS);
639         if (toscaProperties.isLeft()) {
640             Map<String, Object> jsonProperties = toscaProperties.left().value();
641             List<HeatParameterDefinition> moduleProperties = new ArrayList<>();
642             Iterator<Entry<String, Object>> propertiesNameValue = jsonProperties.entrySet().iterator();
643             while (propertiesNameValue.hasNext()) {
644                 Entry<String, Object> propertyNameValue = propertiesNameValue.next();
645                 if (propertyNameValue.getValue() instanceof Map || propertyNameValue.getValue() instanceof List) {
646                     if (!artifactType.equals(ArtifactTypeEnum.HEAT_ENV.getType())) {
647                         @SuppressWarnings("unchecked")
648                         Either<HeatParameterDefinition, ResultStatusEnum> propertyStatus = createModuleHeatParameter((Map<String, Object>) propertyNameValue.getValue());
649                         if (propertyStatus.isRight()) {
650                             return Either.right(propertyStatus.right().value());
651                         }
652                         HeatParameterDefinition property = propertyStatus.left().value();
653                         property.setName(String.valueOf(propertyNameValue.getKey()));
654                         moduleProperties.add(property);
655                     } else {
656                         addHeatParamDefinition(moduleProperties, propertyNameValue, true);
657                     }
658                 } else {
659                     addHeatParamDefinition(moduleProperties, propertyNameValue, false);
660                 }
661
662             }
663
664             if (!isEmpty(moduleProperties)) {
665                 eitherResult = Either.left(moduleProperties);
666             }
667
668         }
669         return eitherResult;
670
671     }
672
673     private static void addHeatParamDefinition(List<HeatParameterDefinition> moduleProperties, Entry<String, Object> propertyNameValue, boolean isJson) {
674         HeatParameterDefinition property = new HeatParameterDefinition();
675         Object value = propertyNameValue.getValue();
676         if (value != null) {
677             property.setDefaultValue(isJson ? new Gson().toJson(value) : StringEscapeUtils.escapeJava(String.valueOf(value)));
678         }
679         property.setName(String.valueOf(propertyNameValue.getKey()));
680         moduleProperties.add(property);
681     }
682
683     private static Either<HeatParameterDefinition, ResultStatusEnum> createModuleHeatParameter(Map<String, Object> propertyValue) {
684         HeatParameterDefinition propertyDef = new HeatParameterDefinition();
685         String type;
686         Either<String, ResultStatusEnum> propertyFieldType = findFirstToscaStringElement(propertyValue, TypeUtils.ToscaTagNamesEnum.TYPE);
687         if (propertyFieldType.isLeft()) {
688             type = propertyFieldType.left().value();
689             propertyDef.setType(type);
690         } else {
691             return Either.right(ResultStatusEnum.INVALID_PROPERTY_TYPE);
692         }
693         Either<String, ResultStatusEnum> propertyFieldDescription = findFirstToscaStringElement(propertyValue, TypeUtils.ToscaTagNamesEnum.DESCRIPTION);
694         if (propertyFieldDescription.isLeft()) {
695             propertyDef.setDescription(propertyFieldDescription.left().value());
696         }
697
698         Either<Object, ResultStatusEnum> propertyFieldDefaultVal = findToscaElement(propertyValue, TypeUtils.ToscaTagNamesEnum.DEFAULT_VALUE, ToscaElementTypeEnum.ALL);
699         if (propertyFieldDefaultVal.isLeft()) {
700             if (propertyFieldDefaultVal.left().value() == null) {
701                 return Either.right(ResultStatusEnum.INVALID_PROPERTY_VALUE);
702             }
703             Object value = propertyFieldDefaultVal.left().value();
704             String defaultValue = type.equals(HeatParameterType.JSON.getType()) ? new Gson().toJson(value) : StringEscapeUtils.escapeJava(String.valueOf(value));
705             propertyDef.setDefaultValue(defaultValue);
706             propertyDef.setCurrentValue(defaultValue);
707         }
708
709         return Either.left(propertyDef);
710     }
711     public static boolean containsGetInput(Object propValue) {
712         String value = getPropertyJsonStringValue(propValue, ToscaPropertyType.MAP.getType());
713         return value != null && value.contains(TypeUtils.ToscaTagNamesEnum.GET_INPUT.getElementName());
714     }
715
716     public static String getPropertyJsonStringValue(Object value, String type) {
717         Gson gson = new Gson();
718         if (type == null) {
719             return null;
720         }
721         ToscaPropertyType validType = ToscaPropertyType.isValidType(type);
722         if (validType == null ||  validType == ToscaPropertyType.JSON || validType == ToscaPropertyType.MAP || validType == ToscaPropertyType.LIST) {
723             return gson.toJson(value);
724         }
725         return value.toString();
726     }
727
728     /**
729      * removes from Json map (toscaJson) first element found by name (elementName) note that this method could update the received argument toscaJson
730      *
731      * @param toscaJson
732      * @param elementName
733      */
734     public static void removeElementFromJsonMap(Map<String, Object> toscaJson, String elementName) {
735         for (Entry<String, Object> entry : toscaJson.entrySet()) {
736             String key = entry.getKey();
737             Object value = entry.getValue();
738             if (key.equals(elementName)) {
739                 toscaJson.remove(elementName);
740                 return;
741             } else if (value instanceof Map) {
742                 removeElementFromJsonMap((Map<String, Object>) value, elementName);
743             }
744         }
745     }
746 }