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