3de13cff464fb0a0164fa3abab47bdce4be2c8b3
[nepi.git] / src / nepi / core / attributes.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 class Attribute(object):
5     ### Attribute types
6     STRING  = "STRING"
7     BOOL    = "BOOL"
8     ENUM    = "ENUM"
9     DOUBLE  = "DOUBLE"
10     INTEGER = "INTEGER"
11
12     types = [
13         STRING, 
14         BOOL, 
15         ENUM, 
16         DOUBLE, 
17         INTEGER
18     ]
19     
20     type_parsers = {
21         STRING : str,
22         BOOL : lambda x : str(x).lower() in ("1","on","yes","true"),
23         ENUM : str,
24         DOUBLE : float,
25         INTEGER : int,
26     }
27
28     ### Attribute Flags
29     NoFlags          = 0x00
30     # Read-only attribute at design time
31     DesignReadOnly   = 0x01
32     # Invisible attribute at design time
33     DesignInvisible  = 0x02
34     # Read-only attribute at execution time
35     ExecReadOnly     = 0x04
36     # Invisible attribute at execution time
37     ExecInvisible    = 0x08
38     # Attribute doesn't change value during execution time
39     ExecImmutable    = 0x10
40     # Attribute has no default value in the testbed
41     NoDefaultValue   = 0x20
42     # Metadata attribute (is not directly reflected by a real object attribute)
43     Metadata         = 0x40
44
45     def __init__(self, name, help, type, value = None, range = None,
46         allowed = None, flags = None, validation_function = None, 
47         category = None):
48         if not type in Attribute.types:
49             raise AttributeError("invalid type %s " % type)
50         self._name = name
51         self._type = type
52         self._help = help
53         self._value = value
54         self._flags = flags if flags != None else Attribute.NoFlags
55         # range: max and min possible values
56         self._range = range
57         # list of possible values
58         self._allowed = allowed
59         self._validation_function = validation_function
60         self._modified = False
61         self._category = category
62
63     @property
64     def name(self):
65         return self._name
66
67     @property
68     def type(self):
69         return self._type
70
71     @property
72     def help(self):
73         return self._help
74
75     @property
76     def flags(self):
77         return self._flags
78
79     @property
80     def is_design_invisible(self):
81         return self.has_flag(Attribute.DesignInvisible)
82
83     @property
84     def is_design_read_only(self):
85         return self.has_flag(Attribute.DesignReadOnly)
86
87     @property
88     def is_exec_invisible(self):
89         return self.has_flag(Attribute.ExecInvisible)
90
91     @property
92     def is_exec_read_only(self):
93         return self.has_flag(Attribute.ExecReadOnly)
94
95     @property
96     def is_exec_immutable(self):
97         return self.has_flag(Attribute.ExecImmutable)
98
99     @property
100     def is_metadata(self):
101         return self.has_flag(Attribute.Metadata)
102
103     @property
104     def has_no_default_value(self):
105         return self.has_flag(Attribute.NoDefaultValue)
106
107     @property
108     def modified(self):
109         return self._modified
110
111     @property
112     def category(self):
113         return self._category
114
115     @property
116     def range(self):
117         return self._range
118
119     @property
120     def allowed(self):
121         return self._allowed
122
123     @property
124     def validation_function(self):
125         return self._validation_function
126
127     def has_flag(self, flag):
128         return (self._flags & flag) == flag
129
130     def get_value(self):
131         return self._value
132
133     def set_value(self, value):
134         if self.is_valid_value(value):
135             self._value = value
136             self._modified = True
137         else:
138             raise ValueError("Invalid value %s for attribute %s" %
139                     (str(value), self.name))
140
141     value = property(get_value, set_value)
142
143     def is_valid_value(self, value):
144         return self._is_in_range(value) and \
145             self._is_in_allowed_values(value) and \
146                 self._is_valid(value)    
147
148     def _is_in_range(self, value):
149         return not self.range or \
150                 (value >= self.range[0] and value <= self.range[1])
151
152     def _is_in_allowed_values(self, value):
153         return not self._allowed or value in self._allowed
154
155     def _is_valid(self, value):
156         return not self._validation_function or \
157                 self._validation_function(self, value)
158
159 class AttributesMap(object):
160     """AttributesMap is the base class for every object whose attributes 
161     are going to be manipulated by the end-user in a script or GUI.
162     """
163     def __init__(self):
164         super(AttributesMap, self).__init__()
165         self._attributes = dict()
166
167     @property
168     def attributes(self):
169         return self._attributes.values()
170
171     def get_attribute_list(self, filter_flags = None, exclude = False):
172         """
173         Returns the list of attributes.
174         
175         Params:
176             filter_flags: if given, only attributes with (all) the specified
177                 flags will be returned.
178             
179             exclude: if True, only attributes without (any of) the specified
180                 flags will be returned.
181         """
182         attributes = self._attributes
183         if filter_flags != None:
184             def filter_attrs(attr_data):
185                 (attr_id, attr) = attr_data
186                 return attr.has_flag(filter_flags) == (not exclude)
187             attributes = dict(filter(filter_attrs, attributes.iteritems()))
188         return attributes.keys()
189
190     def set_attribute_value(self, name, value):
191         self._attributes[name].value = value
192
193     def get_attribute_value(self, name):
194         return self._attributes[name].value
195
196     def get_attribute_help(self, name):
197         return self._attributes[name].help
198
199     def get_attribute_type(self, name):
200         return self._attributes[name].type
201
202     def get_attribute_range(self, name):
203         if not self._attributes[name].range:
204             return (None, None)
205         return self._attributes[name].range
206
207     def get_attribute_allowed(self, name):
208         return self._attributes[name].allowed
209
210     def get_attribute_category(self, name):
211         return self._attributes[name].category
212
213     def is_attribute_design_invisible(self, name):
214         return self._attributes[name].is_design_invisible
215
216     def is_attribute_design_read_only(self, name):
217         return self._attributes[name].is_design_read_only
218
219     def is_attribute_exec_invisible(self, name):
220         return self._attributes[name].is_exec_invisible
221
222     def is_attribute_exec_read_only(self, name):
223         return self._attributes[name].is_exec_read_only
224
225     def is_attribute_exec_immutable(self, name):
226         return self._attributes[name].is_exec_immutable
227
228     def is_attribute_metadata(self, name):
229         return self._attributes[name].is_metadata
230
231     def has_attribute_no_default_value(self, name):
232         return self._attributes[name].has_no_default_value
233
234     def is_attribute_modified(self, name):
235         return self._attributes[name].modified
236
237     def is_attribute_value_valid(self, name, value):
238         return self._attributes[name].is_valid_value(value)
239
240     def add_attribute(self, name, help, type, value = None, range = None,
241         allowed = None, flags = Attribute.NoFlags, validation_function = None,
242         category = None):
243         if name in self._attributes:
244             raise AttributeError("Attribute %s already exists" % name)
245         attribute = Attribute(name, help, type, value, range, allowed, flags,
246                 validation_function, category)
247         self._attributes[name] = attribute
248
249     def del_attribute(self, name):
250         del self._attributes[name]
251
252     def has_attribute(self, name):
253         return name in self._attributes    
254     
255     def destroy(self):
256         self._attributes = dict()
257