Fix checkstyle violations in sdc/jtosca
[sdc/sdc-tosca.git] / src / main / java / org / onap / sdc / toscaparser / api / utils / ValidateUtils.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.utils;
22
23 import org.onap.sdc.toscaparser.api.common.JToscaValidationIssue;
24
25 import java.util.ArrayList;
26 import java.util.Date;
27 import java.util.LinkedHashMap;
28
29 public class ValidateUtils {
30
31     private static final String RANGE_UNBOUNDED = "UNBOUNDED";
32
33     private ValidateUtils() {
34     }
35
36     public static Object strToNum(Object value) {
37         // Convert a string representation of a number into a numeric type
38         // TODO(TBD) we should not allow numeric values in, input should be str
39         if (value instanceof Number) {
40             return value;
41         }
42         try {
43             return Integer.parseInt((String) value);
44         } catch (NumberFormatException e) {
45         }
46         try {
47             return Float.parseFloat((String) value);
48         } catch (Exception e) {
49         }
50         return null;
51     }
52
53     public static Object validateNumeric(Object value) {
54         if (value != null) {
55             if (!(value instanceof Number)) {
56                 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE257", String.format(
57                         "ValueError: \"%s\" is not a numeric", value.toString())));
58             }
59         }
60         return value;
61     }
62
63     public static Object validateInteger(Object value) {
64         if (value != null) {
65             if (!(value instanceof Integer)) {
66                 // allow "true" and "false"
67                 if (value instanceof Boolean) {
68                     return (Boolean) value ? 1 : 0;
69                 }
70                 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE258", String.format(
71                         "ValueError: \"%s\" is not an integer", value.toString())));
72             }
73         }
74         return value;
75     }
76
77     public static Object validateFloat(Object value) {
78         if (value != null) {
79             if (!(value instanceof Float || value instanceof Double)) {
80                 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE259", String.format(
81                         "ValueError: \"%s\" is not a float", value.toString())));
82             }
83         }
84         return value;
85     }
86
87     public static Object validateString(Object value) {
88         if (value != null) {
89             if (!(value instanceof String)) {
90                 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE260", String.format(
91                         "ValueError: \'%s\' is not a string", value.toString())));
92             }
93         }
94         return value;
95     }
96
97     public static Object validateList(Object value) {
98         if (value != null) {
99             if (!(value instanceof ArrayList)) {
100                 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE261", String.format(
101                         "ValueError: \"%s\" is not a list", value.toString())));
102             }
103         }
104         return value;
105     }
106
107
108     @SuppressWarnings("unchecked")
109     public static Object validateRange(Object range) {
110         // list class check
111         validateList(range);
112         // validate range list has a min and max
113         if (range instanceof ArrayList && ((ArrayList<Object>) range).size() != 2) {
114             ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE262", String.format(
115                     "ValueError: \"%s\" is not a valid range", range.toString())));
116             // too dangerous to continue...
117             return range;
118         }
119         // validate min and max are numerics or the keyword UNBOUNDED
120         boolean minTest = false;
121         boolean maxTest = false;
122         Object r0 = ((ArrayList<Object>) range).get(0);
123         Object r1 = ((ArrayList<Object>) range).get(1);
124
125         if (!(r0 instanceof Integer) && !(r0 instanceof Float)
126                 || !(r1 instanceof Integer) && !(r1 instanceof Float)) {
127             ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE263", String.format(
128                     "ValueError: \"%s\" is not a valid range", range.toString())));
129             // too dangerous to continue...
130             return range;
131         }
132
133         Float min = 0.0F;
134         Float max = 0.0F;
135         if (r0 instanceof String && ((String) r0).equals(RANGE_UNBOUNDED)) {
136             minTest = true;
137         } else {
138             min = r0 instanceof Integer ? ((Integer) r0).floatValue() : (Float) r0;
139         }
140         if (r1 instanceof String && ((String) r1).equals(RANGE_UNBOUNDED)) {
141             maxTest = true;
142         } else {
143             max = r1 instanceof Integer ? ((Integer) r1).floatValue() : (Float) r1;
144         }
145
146         // validate the max > min (account for UNBOUNDED)
147         if (!minTest && !maxTest) {
148             // Note: min == max is allowed
149             if (min > max) {
150                 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE264", String.format(
151                         "ValueError:\"%s\" is not a valid range", range.toString())));
152             }
153         }
154         return range;
155     }
156
157     @SuppressWarnings("unchecked")
158     public static Object validateValueInRange(Object value, Object range, String propName) {
159         // verify all 3 are numeric and convert to Floats
160         if (!(value instanceof Integer || value instanceof Float)) {
161             ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE265", String.format(
162                     "ValueError: validateInRange: \"%s\" is not a number", range.toString())));
163             return value;
164         }
165         Float fval = value instanceof Integer ? ((Integer) value).floatValue() : (Float) value;
166
167         //////////////////////////
168         //"validateRange(range);"
169         //////////////////////////
170         // better safe than sorry...
171         // validate that range list has a min and max
172         if (range instanceof ArrayList && ((ArrayList<Object>) range).size() != 2) {
173             ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE266", String.format(
174                     "ValueError: \"%s\" is not a valid range", range.toString())));
175             // too dangerous to continue...
176             return value;
177         }
178         // validate min and max are numerics or the keyword UNBOUNDED
179         boolean minTest = false;
180         boolean maxTest = false;
181         Object r0 = ((ArrayList<Object>) range).get(0);
182         Object r1 = ((ArrayList<Object>) range).get(1);
183
184         if (!(r0 instanceof Integer) && !(r0 instanceof Float)
185                 || !(r1 instanceof Integer) && !(r1 instanceof Float)) {
186             ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE267", String.format(
187                     "ValueError: \"%s\" is not a valid range", range.toString())));
188             // too dangerous to continue...
189             return value;
190         }
191
192         Float min = 0.0F;
193         Float max = 0.0F;
194         if (r0 instanceof String && ((String) r0).equals(RANGE_UNBOUNDED)) {
195             minTest = true;
196         } else {
197             min = r0 instanceof Integer ? ((Integer) r0).floatValue() : (Float) r0;
198         }
199         if (r1 instanceof String && ((String) r1).equals(RANGE_UNBOUNDED)) {
200             maxTest = true;
201         } else {
202             max = r1 instanceof Integer ? ((Integer) r1).floatValue() : (Float) r1;
203         }
204
205         // validate the max > min (account for UNBOUNDED)
206         if (!minTest && !maxTest) {
207             // Note: min == max is allowed
208             if (min > max) {
209                 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE268", String.format(
210                         "ValueError:\"%s\" is not a valid range", range.toString())));
211             }
212         }
213         // finally...
214         boolean bError = false;
215         //Note: value is valid if equal to min
216         if (!minTest) {
217             if (fval < min) {
218                 bError = true;
219             }
220         }
221         // Note: value is valid if equal to max
222         if (!maxTest) {
223             if (fval > max) {
224                 bError = true;
225             }
226         }
227         if (bError) {
228             ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE269", String.format(
229                     "RangeValueError: Property \"%s\", \"%s\" not in range [\"%s\" - \"%s\"",
230                     propName, value.toString(), r0.toString(), r1.toString())));
231         }
232         return value;
233     }
234
235     public static Object validateMap(Object ob) {
236         if (ob != null) {
237             if (!(ob instanceof LinkedHashMap)) {
238                 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE270", String.format(
239                         "ValueError\"%s\" is not a map.", ob.toString())));
240             }
241         }
242         return ob;
243     }
244
245     public static Object validateBoolean(Object value) {
246         if (value != null) {
247             if (value instanceof Boolean) {
248                 return value;
249             }
250             if (value instanceof String) {
251                 String normalized = ((String) value).toLowerCase();
252                 if (normalized.equals("true") || normalized.equals("false")) {
253                     return normalized.equals("true");
254                 }
255             }
256             ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE271", String.format(
257                     "ValueError: \"%s\" is not a boolean", value.toString())));
258         }
259         return value;
260     }
261
262     public static Object validateTimestamp(Object value) {
263
264         /*
265             try:
266                 # Note: we must return our own exception message
267                 # as dateutil's parser returns different types / values on
268                 # different systems. OSX, for example, returns a tuple
269                 # containing a different error message than Linux
270                 dateutil.parser.parse(value)
271             except Exception as e:
272                 original_err_msg = str(e)
273                 log.error(original_err_msg)
274                 ValidationIssueCollector.appendException(
275                     ValueError(_('"%(val)s" is not a valid timestamp. "%(msg)s"') %
276                                {'val': value, 'msg': original_err_msg}))
277                 */
278         // timestamps are loaded as Date objects by the YAML parser
279         if (value != null) {
280             if (!(value instanceof Date)) {
281                 ThreadLocalsHolder.getCollector().appendValidationIssue(new JToscaValidationIssue("JE272", String.format(
282                         "ValueError: \"%s\" is not a valid timestamp",
283                         value.toString())));
284
285             }
286         }
287         return value;
288     }
289
290 }
291
292 /*python
293
294 from toscaparser.elements import constraints
295 from toscaparser.common.exception import ValidationIssueCollector
296 from toscaparser.common.exception import InvalidTOSCAVersionPropertyException
297 from toscaparser.common.exception import RangeValueError
298 from toscaparser.utils.gettextutils import _
299
300 log = logging.getLogger('tosca')
301
302 RANGE_UNBOUNDED = 'UNBOUNDED'
303
304
305 def str_to_num(value):
306     '''Convert a string representation of a number into a numeric type.'''
307     # tODO(TBD) we should not allow numeric values in, input should be str
308     if isinstance(value, numbers.Number):
309         return value
310     try:
311         return int(value)
312     except ValueError:
313         return float(value)
314
315
316 def validate_numeric(value):
317     if not isinstance(value, numbers.Number):
318         ValidationIssueCollector.appendException(
319             ValueError(_('"%s" is not a numeric.') % value))
320     return value
321
322
323 def validate_integer(value):
324     if not isinstance(value, int):
325         try:
326             value = int(value)
327         except Exception:
328             ValidationIssueCollector.appendException(
329                 ValueError(_('"%s" is not an integer.') % value))
330     return value
331
332
333 def validate_float(value):
334     if not isinstance(value, float):
335         ValidationIssueCollector.appendException(
336             ValueError(_('"%s" is not a float.') % value))
337     return value
338
339
340 def validate_string(value):
341     if not isinstance(value, six.string_types):
342         ValidationIssueCollector.appendException(
343             ValueError(_('"%s" is not a string.') % value))
344     return value
345
346
347 def validate_list(value):
348     if not isinstance(value, list):
349         ValidationIssueCollector.appendException(
350             ValueError(_('"%s" is not a list.') % value))
351     return value
352
353
354 def validate_range(range):
355     # list class check
356     validate_list(range)
357     # validate range list has a min and max
358     if len(range) != 2:
359         ValidationIssueCollector.appendException(
360             ValueError(_('"%s" is not a valid range.') % range))
361     # validate min and max are numerics or the keyword UNBOUNDED
362     min_test = max_test = False
363     if not range[0] == RANGE_UNBOUNDED:
364         min = validate_numeric(range[0])
365     else:
366         min_test = True
367     if not range[1] == RANGE_UNBOUNDED:
368         max = validate_numeric(range[1])
369     else:
370         max_test = True
371     # validate the max > min (account for UNBOUNDED)
372     if not min_test and not max_test:
373         # Note: min == max is allowed
374         if min > max:
375             ValidationIssueCollector.appendException(
376                 ValueError(_('"%s" is not a valid range.') % range))
377
378     return range
379
380
381 def validate_value_in_range(value, range, prop_name):
382     validate_numeric(value)
383     validate_range(range)
384
385     # Note: value is valid if equal to min
386     if range[0] != RANGE_UNBOUNDED:
387         if value < range[0]:
388             ValidationIssueCollector.appendException(
389                 RangeValueError(pname=prop_name,
390                                 pvalue=value,
391                                 vmin=range[0],
392                                 vmax=range[1]))
393     # Note: value is valid if equal to max
394     if range[1] != RANGE_UNBOUNDED:
395         if value > range[1]:
396             ValidationIssueCollector.appendException(
397                 RangeValueError(pname=prop_name,
398                                 pvalue=value,
399                                 vmin=range[0],
400                                 vmax=range[1]))
401     return value
402
403
404 def validate_map(value):
405     if not isinstance(value, collections.Mapping):
406         ValidationIssueCollector.appendException(
407             ValueError(_('"%s" is not a map.') % value))
408     return value
409
410
411 def validate_boolean(value):
412     if isinstance(value, bool):
413         return value
414
415     if isinstance(value, str):
416         normalised = value.lower()
417         if normalised in ['true', 'false']:
418             return normalised == 'true'
419
420     ValidationIssueCollector.appendException(
421         ValueError(_('"%s" is not a boolean.') % value))
422
423
424 def validate_timestamp(value):
425     try:
426         # Note: we must return our own exception message
427         # as dateutil's parser returns different types / values on
428         # different systems. OSX, for example, returns a tuple
429         # containing a different error message than Linux
430         dateutil.parser.parse(value)
431     except Exception as e:
432         original_err_msg = str(e)
433         log.error(original_err_msg)
434         ValidationIssueCollector.appendException(
435             ValueError(_('"%(val)s" is not a valid timestamp. "%(msg)s"') %
436                        {'val': value, 'msg': original_err_msg}))
437     return
438
439 */