Back to index

salome-kernel  6.5.0
datamodeler.py
Go to the documentation of this file.
00001 # -*- coding: iso-8859-1 -*-
00002 # Copyright (C) 2010-2012  CEA/DEN, EDF R&D, OPEN CASCADE
00003 #
00004 # This library is free software; you can redistribute it and/or
00005 # modify it under the terms of the GNU Lesser General Public
00006 # License as published by the Free Software Foundation; either
00007 # version 2.1 of the License.
00008 #
00009 # This library is distributed in the hope that it will be useful,
00010 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012 # Lesser General Public License for more details.
00013 #
00014 # You should have received a copy of the GNU Lesser General Public
00015 # License along with this library; if not, write to the Free Software
00016 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
00017 #
00018 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
00019 #
00020 
00021 __author__="gboulant"
00022 __date__ ="$15 avr. 2010 19:44:17$"
00023 
00024 from uiexception import DevelException
00025 
00026 # Most usable class types
00027 TypeString= "".__class__
00028 __ref_integer = 0
00029 TypeInteger = __ref_integer.__class__
00030 __ref_double = 0.0
00031 TypeDouble = __ref_double.__class__
00032 __ref_list = []
00033 TypeList = __ref_list.__class__
00034 __ref_dict = {}
00035 TypeDictionnary = __ref_dict.__class__
00036 
00037 # There is no control to do for these attributes. They are attributes for the
00038 # class management and not data of the model.
00039 UNCHECKED_ATTRIBUTES = [
00040     "_typemap",
00041     "_rangemap",
00042     "_defaultmap",
00043     "_voidmap"
00044 ]
00045 
00046 class DataModeler:
00047     """
00048     This class is a placeholder for modeling data. An object based on this class
00049     (particular instance or specialized derived class) can defined attributes with
00050     the following properties:
00051 
00052     - a type : the class or the type of the attribute. Setting an attribute to
00053       a value whose type is not the specified type raises an exception.
00054     - a range : a list of the possible values for the attribute. Setting an
00055       attribute to a value not in the range raises an exception
00056     - a default: the default value of an attribute when an instance is created
00057     - a void flag: the attribute can be authorized to be None or not using this
00058       flag. Setting an attribute to a None value while the flag is not set to
00059       True raises an exception. By default, a None value is not allowed.
00060 
00061     These properties are dictionnaries mapping the attribute name to its
00062     associated value for the property.
00063 
00064     A typical usage is to derived this class in a specialized form where the
00065     attributes names and there properties are defined in the constructor. See
00066     use cases at the end of this file.
00067 
00068     """
00069     def __init__(self, typemap=None, rangemap=None, defaultmap=None, voidmap=None):
00070         self._typemap = {}
00071         self._rangemap   = {} # possible values
00072         self._defaultmap = {} # defaults values
00073         self._voidmap    = {}    # None values are allowed
00074         
00075         if typemap is not None:
00076             self._typemap.update(typemap)
00077         if rangemap is not None:
00078             self._rangemap.update(rangemap)
00079         if voidmap is not None:
00080             self._voidmap.update(voidmap)
00081 
00082         # Default initialization (if any)
00083         if defaultmap is not None:
00084             self._defaultmap.update(defaultmap)
00085             for name in self._defaultmap.keys():
00086                 self.__setattr__(name,self._defaultmap[name])
00087 
00088     def addAttribute(self, name, type=None, range=None, default=None, void=None):
00089         """
00090         A None argument means that no entry is created in the associated maps.
00091         """
00092         self._typemap[name] = type
00093 
00094         if range is not None:
00095             self._rangemap[name] = range
00096 
00097         if void is not None:
00098             self._voidmap[name] = void
00099 
00100         if (not void) and (default is None):
00101             return
00102         
00103         self.__setattr__(name,default)
00104 
00105     def __setattr__(self, name, val):
00106         if name in UNCHECKED_ATTRIBUTES:
00107             self.__dict__[name] = val
00108             return
00109 
00110         #__GBO_DEBUG_
00111         if name == "_typemap":
00112             print "WARNING WARNING WARNING : changing value of _typemap by ",val
00113 
00114         if name not in self._typemap.keys():
00115             raise DevelException("The class "+str(self.__class__)+" has no attribute "+str(name))
00116 
00117         if val is None:
00118             if not self.__isVoidAllowed(name):
00119                 raise DevelException("The attribute "+str(name)+" can't be None")
00120             else:
00121                 # We can stop here and set the value to None
00122                 self.__dict__[name] = None
00123                 return
00124 
00125         if self.__isNotValidType(name,val):
00126             raise DevelException("The attribute "+str(name)+" must be an instance of "+str(self._typemap[name]))
00127 
00128         if self.__isNotValidRange(name,val):
00129             raise DevelException("The attribute "+str(name)+" must be a value in :"+str(self._rangemap[name]))
00130 
00131         self.__dict__[name] = val
00132     
00133     def __getattribute__(self, name):
00134         if name in UNCHECKED_ATTRIBUTES:
00135             return self.__dict__[name]
00136 
00137         if name not in self._typemap.keys():
00138             raise DevelException("The class "+str(self.__class__)+" has no attribute "+str(name))
00139         # The attribute coulb be requested while it has not been created yet (for
00140         # example if we did't call the setter before).
00141         if not self.__dict__.has_key(name):
00142             return None
00143         
00144         return self.__dict__[name]
00145 
00146     def __isNotValidType(self, name, val):
00147         isNotValid = (
00148             ( self._typemap[name] is not None) and
00149             ( not isinstance(val,self._typemap[name]) ) )
00150 
00151         return isNotValid
00152 
00153     def __isNotValidRange(self, name, val):
00154         isNotValid = (
00155             ( self._rangemap is not None) and
00156             ( self._rangemap.has_key(name) ) and
00157             ( self._rangemap[name] is not None ) and
00158             ( val not in self._rangemap[name] ) )
00159 
00160         return isNotValid
00161 
00162     def __isVoidAllowed(self,name):
00163         isVoidAllowed = (
00164             ( self._voidmap is not None) and
00165             ( self._voidmap.has_key(name) ) and
00166             ( self._voidmap[name] is True ) )
00167             
00168         return isVoidAllowed
00169 
00170     def log(self):
00171         print "DATAMODELER ["+str(self.__class__)+"]: self._typemap.keys() = "+str(self._typemap.keys())
00172 
00173 
00174 
00175 
00176 #
00177 # ==============================================================================
00178 # Basic use cases and unit tests
00179 # ==============================================================================
00180 #
00181 def TEST_usecase():
00182     typemap={}
00183     typemap["stringdata"] = TypeString
00184     typemap["integerdata"] = TypeInteger
00185     typemap["anydata"] = None # can be anything
00186 
00187     data = DataModeler(typemap)
00188 
00189     sdata = "toto"
00190     idata = 3
00191     data.stringdata = sdata
00192     data.integerdata = idata
00193 
00194     data.anydata = 5.3
00195     data.anydata = "any value"
00196     data.anydata = True
00197 
00198     print data.integerdata
00199     return True
00200 
00201 def TEST_addAttribute():
00202     typemap={}
00203     typemap["stringdata"] = TypeString
00204     typemap["integerdata"] = TypeInteger
00205     data = DataModeler(typemap)
00206     data.stringdata = "a string value"
00207 
00208     ref_value = 1.3
00209     data.addAttribute(
00210         name    = "myAttr",
00211         type    = TypeDouble,
00212         range   = None,
00213         default = ref_value,
00214         void    = False)
00215 
00216     try:
00217         if data.myAttr != ref_value:
00218             return False
00219         data.myAttr = 5.3
00220         #data.myAttr = 5
00221     except Exception, e:
00222         print e
00223         return False
00224 
00225     try:
00226         data.myAttr = "bad type value"
00227         return False
00228     except Exception, e:
00229         print e
00230         return True
00231 
00232 def TEST_badAttributeName():
00233     map={}
00234     map["stringdata"] = TypeString
00235     map["integerdata"] = TypeInteger
00236 
00237     data = DataModeler(map)
00238 
00239     # this should raise an exception
00240     try:
00241         data.myatt = 3
00242         return False
00243     except Exception, e:
00244         print "OK : "+str(e)
00245         return True
00246 
00247 def TEST_badAttributeType():
00248     map={}
00249     map["stringdata"] = TypeString
00250     map["integerdata"] = TypeInteger
00251 
00252     data = DataModeler(map)
00253     # this should raise an exception
00254     try:
00255         data.stringdata = 2
00256         return False
00257     except Exception, e:
00258         print "OK : "+str(e)
00259         return True
00260 
00261 def TEST_badAttributeRange():
00262     map={}
00263     map["stringdata"] = TypeString
00264     map["integerdata"] = TypeInteger
00265 
00266     range={}
00267     ref_integervalue = 3
00268     range["integerdata"] = [1,ref_integervalue,7]
00269 
00270     data = DataModeler(map,range)
00271     # this should not raise an exception
00272     try:
00273         data.integerdata = ref_integervalue
00274         data.stringdata = "anything (no restriction has been defined)"
00275     except Exception, e:
00276         print e
00277         return False
00278 
00279     # this should raise an exception
00280     try:
00281         data.integerdata = 9999 # a value not in the range
00282         return False
00283     except Exception, e:
00284         print e
00285         return True
00286 
00287 def TEST_voidAttributeAllowed():
00288     map={}
00289     map["stringdata"] = TypeString
00290     map["integerdata"] = TypeInteger
00291 
00292     voidmap={}
00293     voidmap["stringdata"] = True
00294 
00295     data = DataModeler(typemap=map,voidmap=voidmap)
00296     try:
00297         # this should not raise an exception
00298         data.stringdata = None
00299         print data.stringdata
00300     except Exception, e:
00301         print e
00302         return False
00303     
00304     try:
00305         # this should raise an exception
00306         data.integerdata = None
00307         return False
00308     except Exception, e:
00309         print e
00310         return True
00311 
00312 def TEST_defaultValues():
00313     typemap={}
00314     typemap["stringdata"] = TypeString
00315     typemap["integerdata"] = TypeInteger
00316 
00317     ref_value = "my initial value"
00318     defaultmap={}
00319     defaultmap["stringdata"] = ref_value
00320 
00321     data = DataModeler(typemap=typemap,defaultmap=defaultmap)
00322     print data.stringdata
00323     if data.stringdata != ref_value:
00324         return False
00325     else:
00326         return True
00327 
00328 if __name__ == "__main__":
00329     from unittester import run
00330     run("salome/kernel/datamodeler","TEST_usecase")
00331     run("salome/kernel/datamodeler","TEST_addAttribute")
00332     run("salome/kernel/datamodeler","TEST_badAttributeName")
00333     run("salome/kernel/datamodeler","TEST_badAttributeType")
00334     run("salome/kernel/datamodeler","TEST_badAttributeRange")
00335     run("salome/kernel/datamodeler","TEST_voidAttributeAllowed")
00336     run("salome/kernel/datamodeler","TEST_defaultValues")