Fix checkstyle violations in sdc/jtosca
[sdc/sdc-tosca.git] / src / main / java / org / onap / sdc / toscaparser / api / Property.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
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
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.onap.sdc.toscaparser.api;
22
23 import com.google.common.collect.Lists;
24 import org.onap.sdc.toscaparser.api.elements.constraints.Constraint;
25 import org.onap.sdc.toscaparser.api.elements.constraints.Schema;
26 import org.onap.sdc.toscaparser.api.functions.Function;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.LinkedHashMap;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Objects;
36 import java.util.stream.Collectors;
37
38 public class Property {
39     // TOSCA built-in Property type
40     private static final Logger LOGGER = LoggerFactory.getLogger(Property.class.getName());
41
42     private static final String TYPE = "type";
43     private static final String REQUIRED = "required";
44     private static final String DESCRIPTION = "description";
45     private static final String DEFAULT = "default";
46     private static final String CONSTRAINTS = "constraints";
47     private static String entrySchema = "entry_schema";
48     private static String dataType = "datatypes";
49
50     private static final String[] PROPERTY_KEYS = {
51             TYPE, REQUIRED, DESCRIPTION, DEFAULT, CONSTRAINTS};
52
53     private static final String ENTRYTYPE = "type";
54     private static final String ENTRYPROPERTIES = "properties";
55     private static final String PATH_DELIMITER = "#";
56     private static final String[] ENTRY_SCHEMA_KEYS = {
57             ENTRYTYPE, ENTRYPROPERTIES};
58
59     private String name;
60     private Object value;
61     private Schema schema;
62     private LinkedHashMap<String, Object> customDef;
63
64     public Property(Map.Entry<String, Object> propertyEntry) {
65         name = propertyEntry.getKey();
66         value = propertyEntry.getValue();
67     }
68
69     public Property(String propname,
70                     Object propvalue,
71                     LinkedHashMap<String, Object> propschemaDict,
72                     LinkedHashMap<String, Object> propcustomDef) {
73
74         name = propname;
75         value = propvalue;
76         customDef = propcustomDef;
77         schema = new Schema(propname, propschemaDict);
78     }
79
80     public String getType() {
81         return schema.getType();
82     }
83
84     public boolean isRequired() {
85         return schema.isRequired();
86     }
87
88     public String getDescription() {
89         return schema.getDescription();
90     }
91
92     public Object getDefault() {
93         return schema.getDefault();
94     }
95
96     public ArrayList<Constraint> getConstraints() {
97         return schema.getConstraints();
98     }
99
100     public LinkedHashMap<String, Object> getEntrySchema() {
101         return schema.getEntrySchema();
102     }
103
104
105     public String getName() {
106         return name;
107     }
108
109     public Object getValue() {
110         return value;
111     }
112
113     // setter
114     public Object setValue(Object vob) {
115         value = vob;
116         return value;
117     }
118
119     public void validate() {
120         // Validate if not a reference property
121         if (!Function.isFunction(value)) {
122             if (getType().equals(Schema.STRING)) {
123                 value = value.toString();
124             }
125             value = DataEntity.validateDatatype(getType(), value,
126                     getEntrySchema(),
127                     customDef,
128                     name);
129             validateConstraints();
130         }
131     }
132
133     private void validateConstraints() {
134         if (getConstraints() != null) {
135             for (Constraint constraint : getConstraints()) {
136                 constraint.validate(value);
137             }
138         }
139     }
140
141     @Override
142     public String toString() {
143         return "Property{"
144                 + "name='" + name + '\''
145                 + ", value=" + value
146                 + ", schema=" + schema
147                 + ", customDef=" + customDef
148                 + '}';
149     }
150
151     /**
152      * Retrieves property value as list of strings if<br>
153      * - the value is simple<br>
154      * - the value is list of simple values<br>
155      * - the provided path refers to a simple property inside a data type<br>
156      *
157      * @param propertyPath valid name of property for search.<br>
158      *                     If a name refers to a simple field inside a datatype, the property name should be defined with # delimiter.<br>
159      * @return List of property values. If not found, empty list will be returned.<br>
160      * If property value is a list either of simple fields or of simple fields inside a datatype, all values from the list should be returned
161      */
162     public List<String> getLeafPropertyValue(String propertyPath) {
163         List<String> propertyValueList = Collections.emptyList();
164
165         if (LOGGER.isDebugEnabled()) {
166             LOGGER.debug("getLeafPropertyValue=> A new request: propertyPath: {}, value: {}", propertyPath, getValue());
167         }
168         if (propertyPath == null || getValue() == null
169                 //if entry_schema disappears, it is datatype,
170                 // otherwise it is map of simple types - should be ignored
171                 || isValueMapOfSimpleTypes()) {
172             LOGGER.error("It is a wrong request - ignoring! propertyPath: {}, value: {}", propertyPath, getValue());
173             return propertyValueList;
174         }
175         String[] path = propertyPath.split(PATH_DELIMITER);
176
177         if (Schema.isRequestedTypeSimple(getPropertyTypeByPath(path))) {
178             //the internal property type in the path is either simple or list of simple types
179             if (isValueInsideDataType()) {
180                 if (LOGGER.isDebugEnabled()) {
181                     LOGGER.debug("The requested is an internal simple property inside of a data type");
182                 }
183                 //requested value is an internal simple property inside of a data type
184                 propertyValueList = getSimplePropertyValueForComplexType(path);
185             } else {
186                 if (LOGGER.isDebugEnabled()) {
187                     LOGGER.debug("The requested property has simple type or list of simple types");
188                 }
189                 //the requested property is simple type or list of simple types
190                 propertyValueList = getSimplePropertyValueForSimpleType();
191             }
192         }
193         return propertyValueList;
194     }
195
196     private boolean isValueMapOfSimpleTypes() {
197         if (getValue() instanceof Map && getEntrySchema() != null) {
198             LOGGER.warn("This property value is a map of simple types");
199             return true;
200         }
201         return false;
202     }
203
204     private boolean isValueInsideDataType() {
205         //value is either a list of values for data type
206         //or data type
207         return (Schema.LIST.equals(getType()) && isDataTypeInEntrySchema())
208                 || (getEntrySchema() == null && getType().contains(dataType));
209     }
210
211     private Object getSimpleValueFromComplexObject(Object current, String[] path) {
212         if (current == null) {
213             return null;
214         }
215         int index = 0;
216
217         if (path.length > index) {
218             for (int i = index; i < path.length; i++) {
219                 if (current instanceof Map) {
220                     current = ((Map<String, Object>) current).get(path[i]);
221                 } else if (current instanceof List) {
222                     current = ((List) current).get(0);
223                     i--;
224                 } else {
225                     return null;
226                 }
227             }
228         }
229         if (current != null) {
230             return current;
231         }
232         return null;
233     }
234
235     private List<String> getSimplePropertyValueForSimpleType() {
236         if (getValue() instanceof List || getValue() instanceof Map) {
237             return getSimplePropertyValueForComplexType(null);
238         }
239         return Lists.newArrayList(String.valueOf(value));
240     }
241
242     private List<String> getSimplePropertyValueForComplexType(String[] path) {
243         if (getValue() instanceof List) {
244             return ((List<Object>) getValue()).stream()
245                     .map(v -> {
246                         if (path != null) {
247                             return getSimpleValueFromComplexObject(v, path);
248                         } else {
249                             return v;
250                         }
251                     })
252                     //it might be null when get_input can't be resolved
253                     // e.g.:
254                     // - get_input has two parameters: 1. list and 2. index in this list
255                     //and list has no value
256                     // - neither value no default is defined for get_input
257                     .filter(Objects::nonNull)
258                     .map(String::valueOf)
259                     .collect(Collectors.toList());
260         }
261         //it is data type
262         List<String> valueList = Lists.newArrayList();
263         String valueString = String.valueOf(getSimpleValueFromComplexObject(getValue(), path));
264         if (Objects.nonNull(valueString)) {
265             valueList.add(valueString);
266         }
267         return valueList;
268     }
269
270     private String getPropertyTypeByPath(String[] path) {
271         String propertyType = calculatePropertyType();
272
273         if (path.length > 0 && !path[0].isEmpty()) {
274             return getInternalPropertyType(propertyType, path, 0);
275         }
276         return propertyType;
277     }
278
279     private String calculatePropertyType() {
280         String propertyType = getType();
281         if (Schema.LIST.equals(propertyType)) {
282             //if it is list, return entry schema type
283             return (String) getEntrySchema().get(ENTRYTYPE);
284         }
285         return propertyType;
286     }
287
288     private String calculatePropertyType(LinkedHashMap<String, Object> property) {
289         String type = (String) property.get(TYPE);
290         if (Schema.LIST.equals(type)) {
291             //it might be a data type
292             return getEntrySchemaType(property);
293         }
294         return type;
295     }
296
297     private String getInternalPropertyType(String dataTypeName, String[] path, int index) {
298         if (path.length > index) {
299             LinkedHashMap<String, Object> complexProperty = (LinkedHashMap<String, Object>) customDef.get(dataTypeName);
300             if (complexProperty != null) {
301                 LinkedHashMap<String, Object> dataTypeProperties = (LinkedHashMap<String, Object>) complexProperty.get(ENTRYPROPERTIES);
302                 return getPropertyTypeFromCustomDefDeeply(path, index, dataTypeProperties);
303             }
304         }
305         //stop searching - seems as wrong flow: the path is finished but the value is not found yet
306         return null;
307     }
308
309     private String getEntrySchemaType(LinkedHashMap<String, Object> property) {
310         LinkedHashMap<String, Object> entrySchema = (LinkedHashMap<String, Object>) property.get(Property.entrySchema);
311         if (entrySchema != null) {
312             return (String) entrySchema.get(TYPE);
313         }
314         return null;
315     }
316
317     private String getPropertyTypeFromCustomDefDeeply(String[] path, int index, LinkedHashMap<String, Object> properties) {
318         if (properties != null) {
319             LinkedHashMap<String, Object> foundProperty = (LinkedHashMap<String, Object>) (properties).get(path[index]);
320             if (foundProperty != null) {
321                 String propertyType = calculatePropertyType(foundProperty);
322                 if (propertyType == null || index == path.length - 1) {
323                     return propertyType;
324                 }
325                 return getInternalPropertyType(propertyType, path, index + 1);
326             }
327         }
328         return null;
329     }
330
331     private boolean isDataTypeInEntrySchema() {
332         String entrySchemaType = (String) getEntrySchema().get(ENTRYTYPE);
333         return entrySchemaType != null && entrySchemaType.contains(dataType);
334     }
335
336
337 }
338
339 /*python
340
341 class Property(object):
342     '''TOSCA built-in Property type.'''
343
344     PROPERTY_KEYS = (
345         TYPE, REQUIRED, DESCRIPTION, DEFAULT, CONSTRAINTS
346     ) = (
347         'type', 'required', 'description', 'default', 'constraints'
348     )
349
350     ENTRY_SCHEMA_KEYS = (
351         ENTRYTYPE, ENTRYPROPERTIES
352     ) = (
353         'type', 'properties'
354     )
355
356     def __init__(self, property_name, value, schema_dict, custom_def=None):
357         self.name = property_name
358         self.value = value
359         self.custom_def = custom_def
360         self.schema = Schema(property_name, schema_dict)
361
362     @property
363     def type(self):
364         return self.schema.type
365
366     @property
367     def required(self):
368         return self.schema.required
369
370     @property
371     def description(self):
372         return self.schema.description
373
374     @property
375     def default(self):
376         return self.schema.default
377
378     @property
379     def constraints(self):
380         return self.schema.constraints
381
382     @property
383     def entry_schema(self):
384         return self.schema.entry_schema
385
386     def validate(self):
387         '''Validate if not a reference property.'''
388         if not is_function(self.value):
389             if self.type == Schema.STRING:
390                 self.value = str(self.value)
391             self.value = DataEntity.validate_datatype(self.type, self.value,
392                                                       self.entry_schema,
393                                                       self.custom_def,
394                                                       self.name)
395             self._validate_constraints()
396
397     def _validate_constraints(self):
398         if self.constraints:
399             for constraint in self.constraints:
400                 constraint.validate(self.value)
401 */