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