Fix checkstyle violations in sdc/jtosca
[sdc/sdc-tosca.git] / src / main / java / org / onap / sdc / toscaparser / api / DataEntity.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 org.onap.sdc.toscaparser.api.common.JToscaValidationIssue;
24 import org.onap.sdc.toscaparser.api.elements.DataType;
25 import org.onap.sdc.toscaparser.api.elements.PortSpec;
26 import org.onap.sdc.toscaparser.api.elements.PropertyDef;
27 import org.onap.sdc.toscaparser.api.elements.ScalarUnitFrequency;
28 import org.onap.sdc.toscaparser.api.elements.ScalarUnitSize;
29 import org.onap.sdc.toscaparser.api.elements.ScalarUnitTime;
30 import org.onap.sdc.toscaparser.api.elements.constraints.Constraint;
31 import org.onap.sdc.toscaparser.api.elements.constraints.Schema;
32 import org.onap.sdc.toscaparser.api.functions.Function;
33 import org.onap.sdc.toscaparser.api.utils.TOSCAVersionProperty;
34 import org.onap.sdc.toscaparser.api.utils.ThreadLocalsHolder;
35 import org.onap.sdc.toscaparser.api.utils.ValidateUtils;
36
37 import java.util.ArrayList;
38 import java.util.LinkedHashMap;
39 import java.util.List;
40
41 public class DataEntity {
42     // A complex data value entity
43
44     private LinkedHashMap<String, Object> customDef;
45     private DataType dataType;
46     private LinkedHashMap<String, PropertyDef> schema;
47     private Object value;
48     private String propertyName;
49
50     public DataEntity(String _dataTypeName, Object _valueDict,
51                       LinkedHashMap<String, Object> _customDef, String _propName) {
52
53         customDef = _customDef;
54         dataType = new DataType(_dataTypeName, _customDef);
55         schema = dataType.getAllProperties();
56         value = _valueDict;
57         propertyName = _propName;
58     }
59
60     @SuppressWarnings("unchecked")
61     public Object validate() {
62         // Validate the value by the definition of the datatype
63
64         // A datatype can not have both 'type' and 'properties' definitions.
65         // If the datatype has 'type' definition
66         if (dataType.getValueType() != null) {
67             value = DataEntity.validateDatatype(dataType.getValueType(), value, null, customDef, null);
68             Schema schemaCls = new Schema(propertyName, dataType.getDefs());
69             for (Constraint constraint : schemaCls.getConstraints()) {
70                 constraint.validate(value);
71             }
72         }
73         // If the datatype has 'properties' definition
74         else {
75             if (!(value instanceof LinkedHashMap)) {
76                 //ERROR under investigation
77                 String checkedVal = value != null ? value.toString() : null;
78
79                 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE001", String.format(
80                         "TypeMismatchError: \"%s\" is not a map. The type is \"%s\"",
81                         checkedVal, dataType.getType())));
82
83                 if (value instanceof List && ((List) value).size() > 0) {
84                     value = ((List) value).get(0);
85                 }
86
87                 if (!(value instanceof LinkedHashMap)) {
88                     return value;
89                 }
90             }
91
92
93             LinkedHashMap<String, Object> valueDict = (LinkedHashMap<String, Object>) value;
94             ArrayList<String> allowedProps = new ArrayList<>();
95             ArrayList<String> requiredProps = new ArrayList<>();
96             LinkedHashMap<String, Object> defaultProps = new LinkedHashMap<>();
97             if (schema != null) {
98                 allowedProps.addAll(schema.keySet());
99                 for (String name : schema.keySet()) {
100                     PropertyDef propDef = schema.get(name);
101                     if (propDef.isRequired()) {
102                         requiredProps.add(name);
103                     }
104                     if (propDef.getDefault() != null) {
105                         defaultProps.put(name, propDef.getDefault());
106                     }
107                 }
108             }
109
110             // check allowed field
111             for (String valueKey : valueDict.keySet()) {
112                 //1710 devlop JSON validation
113                 if (!("json").equals(dataType.getType()) && !allowedProps.contains(valueKey)) {
114                     ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE100", String.format(
115                             "UnknownFieldError: Data value of type \"%s\" contains unknown field \"%s\"",
116                             dataType.getType(), valueKey)));
117                 }
118             }
119
120             // check default field
121             for (String defKey : defaultProps.keySet()) {
122                 Object defValue = defaultProps.get(defKey);
123                 if (valueDict.get(defKey) == null) {
124                     valueDict.put(defKey, defValue);
125                 }
126
127             }
128
129             // check missing field
130             ArrayList<String> missingProp = new ArrayList<>();
131             for (String reqKey : requiredProps) {
132                 if (!valueDict.keySet().contains(reqKey)) {
133                     missingProp.add(reqKey);
134                 }
135             }
136             if (missingProp.size() > 0) {
137                 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE003", String.format(
138                         "MissingRequiredFieldError: Data value of type \"%s\" is missing required field(s) \"%s\"",
139                         dataType.getType(), missingProp.toString())));
140             }
141
142             // check every field
143             for (String vname : valueDict.keySet()) {
144                 Object vvalue = valueDict.get(vname);
145                 LinkedHashMap<String, Object> schemaName = _findSchema(vname);
146                 if (schemaName == null) {
147                     continue;
148                 }
149                 Schema propSchema = new Schema(vname, schemaName);
150                 // check if field value meets type defined
151                 DataEntity.validateDatatype(propSchema.getType(),
152                         vvalue,
153                         propSchema.getEntrySchema(),
154                         customDef,
155                         null);
156
157                 // check if field value meets constraints defined
158                 if (propSchema.getConstraints() != null) {
159                     for (Constraint constraint : propSchema.getConstraints()) {
160                         if (vvalue instanceof ArrayList) {
161                             for (Object val : (ArrayList<Object>) vvalue) {
162                                 constraint.validate(val);
163                             }
164                         } else {
165                             constraint.validate(vvalue);
166                         }
167                     }
168                 }
169             }
170         }
171         return value;
172     }
173
174     private LinkedHashMap<String, Object> _findSchema(String name) {
175         if (schema != null && schema.get(name) != null) {
176             return schema.get(name).getSchema();
177         }
178         return null;
179     }
180
181     public static Object validateDatatype(String type,
182                                           Object value,
183                                           LinkedHashMap<String, Object> entrySchema,
184                                           LinkedHashMap<String, Object> customDef,
185                                           String propName) {
186         // Validate value with given type
187
188         // If type is list or map, validate its entry by entry_schema(if defined)
189         // If type is a user-defined complex datatype, custom_def is required.
190
191         if (Function.isFunction(value)) {
192             return value;
193         } else if (type == null) {
194             //NOT ANALYZED
195             ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE002", String.format(
196                     "MissingType: Type is missing for value \"%s\"",
197                     value.toString())));
198             return value;
199         } else if (type.equals(Schema.STRING)) {
200             return ValidateUtils.validateString(value);
201         } else if (type.equals(Schema.INTEGER)) {
202             return ValidateUtils.validateInteger(value);
203         } else if (type.equals(Schema.FLOAT)) {
204             return ValidateUtils.validateFloat(value);
205         } else if (type.equals(Schema.NUMBER)) {
206             return ValidateUtils.validateNumeric(value);
207         } else if (type.equals(Schema.BOOLEAN)) {
208             return ValidateUtils.validateBoolean(value);
209         } else if (type.equals(Schema.RANGE)) {
210             return ValidateUtils.validateRange(value);
211         } else if (type.equals(Schema.TIMESTAMP)) {
212             ValidateUtils.validateTimestamp(value);
213             return value;
214         } else if (type.equals(Schema.LIST)) {
215             ValidateUtils.validateList(value);
216             if (entrySchema != null) {
217                 DataEntity.validateEntry(value, entrySchema, customDef);
218             }
219             return value;
220         } else if (type.equals(Schema.SCALAR_UNIT_SIZE)) {
221             return (new ScalarUnitSize(value)).validateScalarUnit();
222         } else if (type.equals(Schema.SCALAR_UNIT_FREQUENCY)) {
223             return (new ScalarUnitFrequency(value)).validateScalarUnit();
224         } else if (type.equals(Schema.SCALAR_UNIT_TIME)) {
225             return (new ScalarUnitTime(value)).validateScalarUnit();
226         } else if (type.equals(Schema.VERSION)) {
227             return (new TOSCAVersionProperty(value.toString())).getVersion();
228         } else if (type.equals(Schema.MAP)) {
229             ValidateUtils.validateMap(value);
230             if (entrySchema != null) {
231                 DataEntity.validateEntry(value, entrySchema, customDef);
232             }
233             return value;
234         } else if (type.equals(Schema.PORTSPEC)) {
235             // tODO(TBD) bug 1567063, validate source & target as PortDef type
236             // as complex types not just as integers
237             PortSpec.validateAdditionalReq(value, propName, customDef);
238         } else {
239             DataEntity data = new DataEntity(type, value, customDef, null);
240             return data.validate();
241         }
242
243         return value;
244     }
245
246     @SuppressWarnings("unchecked")
247     public static Object validateEntry(Object value,
248                                        LinkedHashMap<String, Object> entrySchema,
249                                        LinkedHashMap<String, Object> customDef) {
250
251         // Validate entries for map and list
252         Schema schema = new Schema(null, entrySchema);
253         Object valueob = value;
254         ArrayList<Object> valueList = null;
255         if (valueob instanceof LinkedHashMap) {
256             valueList = new ArrayList<Object>(((LinkedHashMap<String, Object>) valueob).values());
257         } else if (valueob instanceof ArrayList) {
258             valueList = (ArrayList<Object>) valueob;
259         }
260         if (valueList != null) {
261             for (Object v : valueList) {
262                 DataEntity.validateDatatype(schema.getType(), v, schema.getEntrySchema(), customDef, null);
263                 if (schema.getConstraints() != null) {
264                     for (Constraint constraint : schema.getConstraints()) {
265                         constraint.validate(v);
266                     }
267                 }
268             }
269         }
270         return value;
271     }
272
273     @Override
274     public String toString() {
275         return "DataEntity{" +
276                 "customDef=" + customDef +
277                 ", dataType=" + dataType +
278                 ", schema=" + schema +
279                 ", value=" + value +
280                 ", propertyName='" + propertyName + '\'' +
281                 '}';
282     }
283 }
284
285 /*python
286
287 from toscaparser.common.exception import ValidationIssueCollector
288 from toscaparser.common.exception import MissingRequiredFieldError
289 from toscaparser.common.exception import TypeMismatchError
290 from toscaparser.common.exception import UnknownFieldError
291 from toscaparser.elements.constraints import Schema
292 from toscaparser.elements.datatype import DataType
293 from toscaparser.elements.portspectype import PortSpec
294 from toscaparser.elements.scalarunit import ScalarUnit_Frequency
295 from toscaparser.elements.scalarunit import ScalarUnit_Size
296 from toscaparser.elements.scalarunit import ScalarUnit_Time
297 from toscaparser.utils.gettextutils import _
298 from toscaparser.utils import validateutils
299
300
301 class DataEntity(object):
302     '''A complex data value entity.'''
303
304     def __init__(self, datatypename, value_dict, custom_def=None,
305                  prop_name=None):
306         self.custom_def = custom_def
307         self.datatype = DataType(datatypename, custom_def)
308         self.schema = self.datatype.get_all_properties()
309         self.value = value_dict
310         self.property_name = prop_name
311
312     def validate(self):
313         '''Validate the value by the definition of the datatype.'''
314
315         # A datatype can not have both 'type' and 'properties' definitions.
316         # If the datatype has 'type' definition
317         if self.datatype.value_type:
318             self.value = DataEntity.validate_datatype(self.datatype.value_type,
319                                                       self.value,
320                                                       None,
321                                                       self.custom_def)
322             schema = Schema(self.property_name, self.datatype.defs)
323             for constraint in schema.constraints:
324                 constraint.validate(self.value)
325         # If the datatype has 'properties' definition
326         else:
327             if not isinstance(self.value, dict):
328                 ValidationIssueCollector.appendException(
329                     TypeMismatchError(what=self.value,
330                                       type=self.datatype.type))
331             allowed_props = []
332             required_props = []
333             default_props = {}
334             if self.schema:
335                 allowed_props = self.schema.keys()
336                 for name, prop_def in self.schema.items():
337                     if prop_def.required:
338                         required_props.append(name)
339                     if prop_def.default:
340                         default_props[name] = prop_def.default
341
342             # check allowed field
343             for value_key in list(self.value.keys()):
344                 if value_key not in allowed_props:
345                     ValidationIssueCollector.appendException(
346                         UnknownFieldError(what=(_('Data value of type "%s"')
347                                                 % self.datatype.type),
348                                           field=value_key))
349
350             # check default field
351             for def_key, def_value in list(default_props.items()):
352                 if def_key not in list(self.value.keys()):
353                     self.value[def_key] = def_value
354
355             # check missing field
356             missingprop = []
357             for req_key in required_props:
358                 if req_key not in list(self.value.keys()):
359                     missingprop.append(req_key)
360             if missingprop:
361                 ValidationIssueCollector.appendException(
362                     MissingRequiredFieldError(
363                         what=(_('Data value of type "%s"')
364                               % self.datatype.type), required=missingprop))
365
366             # check every field
367             for name, value in list(self.value.items()):
368                 schema_name = self._find_schema(name)
369                 if not schema_name:
370                     continue
371                 prop_schema = Schema(name, schema_name)
372                 # check if field value meets type defined
373                 DataEntity.validate_datatype(prop_schema.type, value,
374                                              prop_schema.entry_schema,
375                                              self.custom_def)
376                 # check if field value meets constraints defined
377                 if prop_schema.constraints:
378                     for constraint in prop_schema.constraints:
379                         if isinstance(value, list):
380                             for val in value:
381                                 constraint.validate(val)
382                         else:
383                             constraint.validate(value)
384
385         return self.value
386
387     def _find_schema(self, name):
388         if self.schema and name in self.schema.keys():
389             return self.schema[name].schema
390
391     @staticmethod
392     def validate_datatype(type, value, entry_schema=None, custom_def=None,
393                           prop_name=None):
394         '''Validate value with given type.
395
396         If type is list or map, validate its entry by entry_schema(if defined)
397         If type is a user-defined complex datatype, custom_def is required.
398         '''
399         from toscaparser.functions import is_function
400         if is_function(value):
401             return value
402         if type == Schema.STRING:
403             return validateutils.validate_string(value)
404         elif type == Schema.INTEGER:
405             return validateutils.validate_integer(value)
406         elif type == Schema.FLOAT:
407             return validateutils.validate_float(value)
408         elif type == Schema.NUMBER:
409             return validateutils.validate_numeric(value)
410         elif type == Schema.BOOLEAN:
411             return validateutils.validate_boolean(value)
412         elif type == Schema.RANGE:
413             return validateutils.validate_range(value)
414         elif type == Schema.TIMESTAMP:
415             validateutils.validate_timestamp(value)
416             return value
417         elif type == Schema.LIST:
418             validateutils.validate_list(value)
419             if entry_schema:
420                 DataEntity.validate_entry(value, entry_schema, custom_def)
421             return value
422         elif type == Schema.SCALAR_UNIT_SIZE:
423             return ScalarUnit_Size(value).validate_scalar_unit()
424         elif type == Schema.SCALAR_UNIT_FREQUENCY:
425             return ScalarUnit_Frequency(value).validate_scalar_unit()
426         elif type == Schema.SCALAR_UNIT_TIME:
427             return ScalarUnit_Time(value).validate_scalar_unit()
428         elif type == Schema.VERSION:
429             return validateutils.TOSCAVersionProperty(value).get_version()
430         elif type == Schema.MAP:
431             validateutils.validate_map(value)
432             if entry_schema:
433                 DataEntity.validate_entry(value, entry_schema, custom_def)
434             return value
435         elif type == Schema.PORTSPEC:
436             # tODO(TBD) bug 1567063, validate source & target as PortDef type
437             # as complex types not just as integers
438             PortSpec.validate_additional_req(value, prop_name, custom_def)
439         else:
440             data = DataEntity(type, value, custom_def)
441             return data.validate()
442
443     @staticmethod
444     def validate_entry(value, entry_schema, custom_def=None):
445         '''Validate entries for map and list.'''
446         schema = Schema(None, entry_schema)
447         valuelist = value
448         if isinstance(value, dict):
449             valuelist = list(value.values())
450         for v in valuelist:
451             DataEntity.validate_datatype(schema.type, v, schema.entry_schema,
452                                          custom_def)
453             if schema.constraints:
454                 for constraint in schema.constraints:
455                     constraint.validate(v)
456         return value
457 */