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