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