Salome HOME
Add functions to know that SALOME module version is development one
[modules/kernel.git] / src / KERNEL_PY / kernel / datamodeler.py
1 # -*- coding: iso-8859-1 -*-
2 #  Copyright (C) 2010  EDF R&D
3 #
4 #  This library is free software; you can redistribute it and/or
5 #  modify it under the terms of the GNU Lesser General Public
6 #  License as published by the Free Software Foundation; either
7 #  version 2.1 of the License.
8 #
9 #  This library is distributed in the hope that it will be useful,
10 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 #  Lesser General Public License for more details.
13 #
14 #  You should have received a copy of the GNU Lesser General Public
15 #  License along with this library; if not, write to the Free Software
16 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17 #
18 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19 #
20 # -* Makefile *- 
21 #
22
23 __author__="gboulant"
24 __date__ ="$15 avr. 2010 19:44:17$"
25
26 from uiexception import DevelException
27
28 # Most usable class types
29 TypeString= "".__class__
30 __ref_integer = 0
31 TypeInteger = __ref_integer.__class__
32 __ref_double = 0.0
33 TypeDouble = __ref_double.__class__
34 __ref_list = []
35 TypeList = __ref_list.__class__
36 __ref_dict = {}
37 TypeDictionnary = __ref_dict.__class__
38
39 # There is no control to do for these attributes. They are attributes for the
40 # class management and not data of the model.
41 UNCHECKED_ATTRIBUTES = [
42     "_typemap",
43     "_rangemap",
44     "_defaultmap",
45     "_voidmap"
46 ]
47
48 class DataModeler:
49     """
50     This class is a placeholder for modeling data. An object based on this class
51     (particular instance or specialized derived class) can defined attributes with
52     the following properties:
53
54     - a type : the class or the type of the attribute. Setting an attribute to
55       a value whose type is not the specified type raises an exception.
56     - a range : a list of the possible values for the attribute. Setting an
57       attribute to a value not in the range raises an exception
58     - a default: the default value of an attribute when an instance is created
59     - a void flag: the attribute can be authorized to be None or not using this
60       flag. Setting an attribute to a None value while the flag is not set to
61       True raises an exception. By default, a None value is not allowed.
62
63     These properties are dictionnaries mapping the attribute name to its
64     associated value for the property.
65
66     A typical usage is to derived this class in a specialized form where the
67     attributes names and there properties are defined in the constructor. See
68     use cases at the end of this file.
69
70     """
71     def __init__(self, typemap=None, rangemap=None, defaultmap=None, voidmap=None):
72         self._typemap = {}
73         self._rangemap   = {} # possible values
74         self._defaultmap = {} # defaults values
75         self._voidmap    = {}    # None values are allowed
76         
77         if typemap is not None:
78             self._typemap.update(typemap)
79         if rangemap is not None:
80             self._rangemap.update(rangemap)
81         if voidmap is not None:
82             self._voidmap.update(voidmap)
83
84         # Default initialization (if any)
85         if defaultmap is not None:
86             self._defaultmap.update(defaultmap)
87             for name in self._defaultmap.keys():
88                 self.__setattr__(name,self._defaultmap[name])
89
90     def addAttribute(self, name, type=None, range=None, default=None, void=None):
91         """
92         A None argument means that no entry is created in the associated maps.
93         """
94         self._typemap[name] = type
95
96         if range is not None:
97             self._rangemap[name] = range
98
99         if void is not None:
100             self._voidmap[name] = void
101
102         if (not void) and (default is None):
103             return
104         
105         self.__setattr__(name,default)
106
107     def __setattr__(self, name, val):
108         if name in UNCHECKED_ATTRIBUTES:
109             self.__dict__[name] = val
110             return
111
112         #__GBO_DEBUG_
113         if name == "_typemap":
114             print "WARNING WARNING WARNING : changing value of _typemap by ",val
115
116         if name not in self._typemap.keys():
117             raise DevelException("The class "+str(self.__class__)+" has no attribute "+str(name))
118
119         if val is None:
120             if not self.__isVoidAllowed(name):
121                 raise DevelException("The attribute "+str(name)+" can't be None")
122             else:
123                 # We can stop here and set the value to None
124                 self.__dict__[name] = None
125                 return
126
127         if self.__isNotValidType(name,val):
128             raise DevelException("The attribute "+str(name)+" must be an instance of "+str(self._typemap[name]))
129
130         if self.__isNotValidRange(name,val):
131             raise DevelException("The attribute "+str(name)+" must be a value in :"+str(self._rangemap[name]))
132
133         self.__dict__[name] = val
134     
135     def __getattribute__(self, name):
136         if name in UNCHECKED_ATTRIBUTES:
137             return self.__dict__[name]
138
139         if name not in self._typemap.keys():
140             raise DevelException("The class "+str(self.__class__)+" has no attribute "+str(name))
141         # The attribute coulb be requested while it has not been created yet (for
142         # example if we did't call the setter before).
143         if not self.__dict__.has_key(name):
144             return None
145         
146         return self.__dict__[name]
147
148     def __isNotValidType(self, name, val):
149         isNotValid = (
150             ( self._typemap[name] is not None) and
151             ( not isinstance(val,self._typemap[name]) ) )
152
153         return isNotValid
154
155     def __isNotValidRange(self, name, val):
156         isNotValid = (
157             ( self._rangemap is not None) and
158             ( self._rangemap.has_key(name) ) and
159             ( self._rangemap[name] is not None ) and
160             ( val not in self._rangemap[name] ) )
161
162         return isNotValid
163
164     def __isVoidAllowed(self,name):
165         isVoidAllowed = (
166             ( self._voidmap is not None) and
167             ( self._voidmap.has_key(name) ) and
168             ( self._voidmap[name] is True ) )
169             
170         return isVoidAllowed
171
172     def log(self):
173         print "DATAMODELER ["+str(self.__class__)+"]: self._typemap.keys() = "+str(self._typemap.keys())
174
175
176
177
178 #
179 # ==============================================================================
180 # Basic use cases and unit tests
181 # ==============================================================================
182 #
183 def TEST_usecase():
184     typemap={}
185     typemap["stringdata"] = TypeString
186     typemap["integerdata"] = TypeInteger
187     typemap["anydata"] = None # can be anything
188
189     data = DataModeler(typemap)
190
191     sdata = "toto"
192     idata = 3
193     data.stringdata = sdata
194     data.integerdata = idata
195
196     data.anydata = 5.3
197     data.anydata = "any value"
198     data.anydata = True
199
200     print data.integerdata
201     return True
202
203 def TEST_addAttribute():
204     typemap={}
205     typemap["stringdata"] = TypeString
206     typemap["integerdata"] = TypeInteger
207     data = DataModeler(typemap)
208     data.stringdata = "a string value"
209
210     ref_value = 1.3
211     data.addAttribute(
212         name    = "myAttr",
213         type    = TypeDouble,
214         range   = None,
215         default = ref_value,
216         void    = False)
217
218     try:
219         if data.myAttr != ref_value:
220             return False
221         data.myAttr = 5.3
222         #data.myAttr = 5
223     except Exception, e:
224         print e
225         return False
226
227     try:
228         data.myAttr = "bad type value"
229         return False
230     except Exception, e:
231         print e
232         return True
233
234 def TEST_badAttributeName():
235     map={}
236     map["stringdata"] = TypeString
237     map["integerdata"] = TypeInteger
238
239     data = DataModeler(map)
240
241     # this should raise an exception
242     try:
243         data.myatt = 3
244         return False
245     except Exception, e:
246         print "OK : "+str(e)
247         return True
248
249 def TEST_badAttributeType():
250     map={}
251     map["stringdata"] = TypeString
252     map["integerdata"] = TypeInteger
253
254     data = DataModeler(map)
255     # this should raise an exception
256     try:
257         data.stringdata = 2
258         return False
259     except Exception, e:
260         print "OK : "+str(e)
261         return True
262
263 def TEST_badAttributeRange():
264     map={}
265     map["stringdata"] = TypeString
266     map["integerdata"] = TypeInteger
267
268     range={}
269     ref_integervalue = 3
270     range["integerdata"] = [1,ref_integervalue,7]
271
272     data = DataModeler(map,range)
273     # this should not raise an exception
274     try:
275         data.integerdata = ref_integervalue
276         data.stringdata = "anything (no restriction has been defined)"
277     except Exception, e:
278         print e
279         return False
280
281     # this should raise an exception
282     try:
283         data.integerdata = 9999 # a value not in the range
284         return False
285     except Exception, e:
286         print e
287         return True
288
289 def TEST_voidAttributeAllowed():
290     map={}
291     map["stringdata"] = TypeString
292     map["integerdata"] = TypeInteger
293
294     voidmap={}
295     voidmap["stringdata"] = True
296
297     data = DataModeler(typemap=map,voidmap=voidmap)
298     try:
299         # this should not raise an exception
300         data.stringdata = None
301         print data.stringdata
302     except Exception, e:
303         print e
304         return False
305     
306     try:
307         # this should raise an exception
308         data.integerdata = None
309         return False
310     except Exception, e:
311         print e
312         return True
313
314 def TEST_defaultValues():
315     typemap={}
316     typemap["stringdata"] = TypeString
317     typemap["integerdata"] = TypeInteger
318
319     ref_value = "my initial value"
320     defaultmap={}
321     defaultmap["stringdata"] = ref_value
322
323     data = DataModeler(typemap=typemap,defaultmap=defaultmap)
324     print data.stringdata
325     if data.stringdata != ref_value:
326         return False
327     else:
328         return True
329
330 if __name__ == "__main__":
331     from unittester import run
332     run("salome/kernel/datamodeler","TEST_usecase")
333     run("salome/kernel/datamodeler","TEST_addAttribute")
334     run("salome/kernel/datamodeler","TEST_badAttributeName")
335     run("salome/kernel/datamodeler","TEST_badAttributeType")
336     run("salome/kernel/datamodeler","TEST_badAttributeRange")
337     run("salome/kernel/datamodeler","TEST_voidAttributeAllowed")
338     run("salome/kernel/datamodeler","TEST_defaultValues")