Update tosca lib to version 0.5
[parser.git] / tosca2heat / tosca-parser / toscaparser / utils / validateutils.py
1 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
2 #    not use this file except in compliance with the License. You may obtain
3 #    a copy of the License at
4 #
5 #         http://www.apache.org/licenses/LICENSE-2.0
6 #
7 #    Unless required by applicable law or agreed to in writing, software
8 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10 #    License for the specific language governing permissions and limitations
11 #    under the License.
12
13 import collections
14 import dateutil.parser
15 import logging
16 import numbers
17 import re
18 import six
19
20 from toscaparser.common.exception import ExceptionCollector
21 from toscaparser.common.exception import InvalidTOSCAVersionPropertyException
22 from toscaparser.utils.gettextutils import _
23 log = logging.getLogger('tosca')
24
25
26 def str_to_num(value):
27     '''Convert a string representation of a number into a numeric type.'''
28     if isinstance(value, numbers.Number):
29         return value
30     try:
31         return int(value)
32     except ValueError:
33         return float(value)
34
35
36 def validate_number(value):
37     return str_to_num(value)
38
39
40 def validate_integer(value):
41     if not isinstance(value, int):
42         try:
43             value = int(value)
44         except Exception:
45             ExceptionCollector.appendException(
46                 ValueError(_('"%s" is not an integer.') % value))
47     return value
48
49
50 def validate_float(value):
51     if not isinstance(value, float):
52         ExceptionCollector.appendException(
53             ValueError(_('"%s" is not a float.') % value))
54     return validate_number(value)
55
56
57 def validate_string(value):
58     if not isinstance(value, six.string_types):
59         ExceptionCollector.appendException(
60             ValueError(_('"%s" is not a string.') % value))
61     return value
62
63
64 def validate_list(value):
65     if not isinstance(value, list):
66         ExceptionCollector.appendException(
67             ValueError(_('"%s" is not a list.') % value))
68     return value
69
70
71 def validate_range(value):
72     validate_list(value)
73     if isinstance(value, list):
74         if len(value) != 2 or not (value[0] <= value[1]):
75             ExceptionCollector.appendException(
76                 ValueError(_('"%s" is not a valid range.') % value))
77         validate_integer(value[0])
78         if not value[1] == "UNBOUNDED":
79             validate_integer(value[1])
80     return value
81
82
83 def validate_map(value):
84     if not isinstance(value, collections.Mapping):
85         ExceptionCollector.appendException(
86             ValueError(_('"%s" is not a map.') % value))
87     return value
88
89
90 def validate_boolean(value):
91     if isinstance(value, bool):
92         return value
93
94     if isinstance(value, str):
95         normalised = value.lower()
96         if normalised in ['true', 'false']:
97             return normalised == 'true'
98
99     ExceptionCollector.appendException(
100         ValueError(_('"%s" is not a boolean.') % value))
101
102
103 def validate_timestamp(value):
104     try:
105         # Note: we must return our own exception message
106         # as dateutil's parser returns different types / values on
107         # different systems. OSX, for example, returns a tuple
108         # containing a different error message than Linux
109         dateutil.parser.parse(value)
110     except Exception as e:
111         original_err_msg = str(e)
112         log.error(original_err_msg)
113         ExceptionCollector.appendException(
114             ValueError(_('"%(val)s" is not a valid timestamp. "%(msg)s"') %
115                        {'val': value, 'msg': original_err_msg}))
116     return
117
118
119 class TOSCAVersionProperty(object):
120
121     VERSION_RE = re.compile('^(?P<major_version>([0-9][0-9]*))'
122                             '(\.(?P<minor_version>([0-9][0-9]*)))?'
123                             '(\.(?P<fix_version>([0-9][0-9]*)))?'
124                             '(\.(?P<qualifier>([0-9A-Za-z]+)))?'
125                             '(\-(?P<build_version>[0-9])*)?$')
126
127     def __init__(self, version):
128         self.version = str(version)
129         match = self.VERSION_RE.match(self.version)
130         if not match:
131             ExceptionCollector.appendException(
132                 InvalidTOSCAVersionPropertyException(what=(self.version)))
133             return
134         ver = match.groupdict()
135         if self.version in ['0', '0.0', '0.0.0']:
136             log.warning(_('Version assumed as not provided'))
137             self.version = None
138         self.minor_version = ver['minor_version']
139         self.major_version = ver['major_version']
140         self.fix_version = ver['fix_version']
141         self.qualifier = self._validate_qualifier(ver['qualifier'])
142         self.build_version = self._validate_build(ver['build_version'])
143         self._validate_major_version(self.major_version)
144
145     def _validate_major_version(self, value):
146         """Validate major version
147
148         Checks if only major version is provided and assumes
149         minor version as 0.
150         Eg: If version = 18, then it returns version = '18.0'
151         """
152
153         if self.minor_version is None and self.build_version is None and \
154             value != '0':
155             log.warning(_('Minor version assumed "0".'))
156             self.version = '.'.join([value, '0'])
157         return value
158
159     def _validate_qualifier(self, value):
160         """Validate qualifier
161
162            TOSCA version is invalid if a qualifier is present without the
163            fix version or with all of major, minor and fix version 0s.
164
165            For example, the following versions are invalid
166               18.0.abc
167               0.0.0.abc
168         """
169         if (self.fix_version is None and value) or \
170             (self.minor_version == self.major_version ==
171              self.fix_version == '0' and value):
172             ExceptionCollector.appendException(
173                 InvalidTOSCAVersionPropertyException(what=(self.version)))
174         return value
175
176     def _validate_build(self, value):
177         """Validate build version
178
179            TOSCA version is invalid if build version is present without the
180            qualifier.
181            Eg: version = 18.0.0-1 is invalid.
182         """
183         if not self.qualifier and value:
184             ExceptionCollector.appendException(
185                 InvalidTOSCAVersionPropertyException(what=(self.version)))
186         return value
187
188     def get_version(self):
189         return self.version