vFW and vDNS support added to azure-plugin
[multicloud/azure.git] / azure / aria / aria-extension-cloudify / src / aria / aria / parser / presentation / presentation.py
1 # Licensed to the Apache Software Foundation (ASF) under one or more
2 # contributor license agreements.  See the NOTICE file distributed with
3 # this work for additional information regarding copyright ownership.
4 # The ASF licenses this file to You under the Apache License, Version 2.0
5 # (the "License"); you may not use this file except in compliance with
6 # the License.  You may obtain a copy of the License at
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 from ...utils.caching import HasCachedMethods
17 from ...utils.collections import deepcopy_with_locators
18 from ...utils.formatting import safe_repr
19 from ...utils.type import full_type_name
20 from ...utils.console import puts
21 from ..validation import Issue
22 from .null import none_to_null
23 from .utils import (get_locator, validate_no_short_form, validate_no_unknown_fields,
24                     validate_known_fields, validate_primitive)
25
26
27 class Value(object):
28     """
29     Encapsulates a typed value assignment.
30     """
31
32     def __init__(self, type_name, value, description, required):
33         self.type = deepcopy_with_locators(type_name)
34         self.value = deepcopy_with_locators(value)
35         self.description = deepcopy_with_locators(description)
36         self.required = deepcopy_with_locators(required)
37
38     def _dump(self, context):
39         if self.type is not None:
40             puts(context.style.type_style(self.type))
41         if self.value is not None:
42             puts(context.style.literal_style(self.value))
43         if self.description is not None:
44             puts(context.style.meta_style(self.description))
45         if self.required is not None:
46             puts(context.style.required_style(self.required))
47
48
49 class PresentationBase(HasCachedMethods):
50     """
51     Base class for ARIA presentation classes.
52     """
53
54     def __init__(self, name=None, raw=None, container=None):
55         self._name = name
56         self._raw = raw
57         self._container = container
58         super(PresentationBase, self).__init__()
59
60     @property
61     def as_raw(self):
62         return self._raw
63
64     def _validate(self, context):
65         """
66         Validates the presentation while reporting errors in the validation context but *not*
67         raising exceptions.
68
69         The base class does not thing, but subclasses may override this for specialized validation.
70         """
71
72     @property
73     def _fullname(self):
74         """
75         Always returns a usable full name of the presentation, whether it itself is named, or
76         recursing to its container, and finally defaulting to the class name.
77         """
78
79         if self._name is not None:
80             return self._name
81         elif self._container is not None:
82             return self._container._fullname
83         return full_type_name(self)
84
85     @property
86     def _locator(self):
87         """
88         Attempts to return the most relevant locator, whether we have one, or recursing to our
89         container.
90
91         :rtype: :class:`aria.parser.reading.Locator`
92         """
93
94         return get_locator(self._raw, self._container)
95
96     def _get(self, *names):
97         """
98         Gets attributes recursively.
99         """
100
101         obj = self
102         if (obj is not None) and names:
103             for name in names:
104                 obj = getattr(obj, name, None)
105                 if obj is None:
106                     break
107         return obj
108
109     def _get_from_dict(self, *names):
110         """
111         Gets attributes recursively, except for the last name which is used to get a value from the
112         last dict.
113         """
114
115         if names:
116             obj = self._get(*names[:-1])
117             if isinstance(obj, dict):
118                 return obj.get(names[-1])  # pylint: disable=no-member
119         return None
120
121     def _get_child_locator(self, *names):
122         """
123         Attempts to return the locator of one our children. Will default to our locator if not
124         found.
125
126         :rtype: :class:`aria.parser.reading.Locator`
127         """
128
129         if hasattr(self._raw, '_locator'):
130             locator = self._raw._locator
131             if locator is not None:
132                 return locator.get_child(*names)
133         return self._locator
134
135     def _dump(self, context):
136         """
137         Emits a colorized representation.
138
139         The base class will emit a sensible default representation of the fields, (by calling
140         ``_dump_content``), but subclasses may override this for specialized dumping.
141         """
142
143         if self._name:
144             puts(context.style.node(self._name))
145             with context.style.indent():
146                 self._dump_content(context)
147         else:
148             self._dump_content(context)
149
150     def _dump_content(self, context, field_names=None):
151         """
152         Emits a colorized representation of the contents.
153
154         The base class will call ``_dump_field`` on all the fields, but subclasses may override
155         this for specialized dumping.
156         """
157
158         if field_names:
159             for field_name in field_names:
160                 self._dump_field(context, field_name)
161         elif hasattr(self, '_iter_field_names'):
162             for field_name in self._iter_field_names():  # pylint: disable=no-member
163                 self._dump_field(context, field_name)
164         else:
165             puts(context.style.literal_style(self._raw))
166
167     def _dump_field(self, context, field_name):
168         """
169         Emits a colorized representation of the field.
170
171         According to the field type, this may trigger nested recursion. The nested types will
172         delegate to their ``_dump`` methods.
173         """
174
175         field = self.FIELDS[field_name]  # pylint: disable=no-member
176         field.dump(self, context)
177
178     def _clone(self, container=None):
179         """
180         Creates a clone of this presentation, optionally allowing for a new container.
181         """
182
183         raw = deepcopy_with_locators(self._raw)
184         if container is None:
185             container = self._container
186         return self.__class__(name=self._name, raw=raw, container=container)
187
188
189 class Presentation(PresentationBase):
190     """
191     Base class for ARIA presentations. A presentation is a Pythonic wrapper around agnostic raw
192     data, adding the ability to read and modify the data with proper validation.
193
194     ARIA presentation classes will often be decorated with :func:`has_fields`, as that mechanism
195     automates a lot of field-specific validation. However, that is not a requirement.
196
197     Make sure that your utility property and method names begin with a ``_``, because those names
198     without a ``_`` prefix are normally reserved for fields.
199     """
200
201     def _validate(self, context):
202         validate_no_short_form(context, self)
203         validate_no_unknown_fields(context, self)
204         validate_known_fields(context, self)
205
206
207 class AsIsPresentation(PresentationBase):
208     """
209     Base class for trivial ARIA presentations that provide the raw value as is.
210     """
211
212     def __init__(self, name=None, raw=None, container=None, cls=None):
213         super(AsIsPresentation, self).__init__(name, raw, container)
214         self.cls = cls
215
216     @property
217     def value(self):
218         return none_to_null(self._raw)
219
220     @value.setter
221     def value(self, value):
222         self._raw = value
223
224     @property
225     def _full_cls_name(self):
226         name = full_type_name(self.cls) if self.cls is not None else None
227         if name == 'unicode':
228             # For simplicity, display "unicode" as "str"
229             name = 'str'
230         return name
231
232     def _validate(self, context):
233         try:
234             validate_primitive(self._raw, self.cls, context.validation.allow_primitive_coersion)
235         except ValueError as e:
236             context.validation.report('"%s" is not a valid "%s": %s'
237                                       % (self._fullname, self._full_cls_name, safe_repr(self._raw)),
238                                       locator=self._locator,
239                                       level=Issue.FIELD,
240                                       exception=e)
241
242     def _dump(self, context):
243         if hasattr(self._raw, '_dump'):
244             puts(context.style.node(self._name))
245             with context.style.indent():
246                 self._raw._dump(context)
247         else:
248             super(AsIsPresentation, self)._dump(context)