py::Configuration::Configuration Class Reference

List of all members.

Public Member Functions

def __init__
def upgrade
def init_locale
def get_scanner
def set_scanner
def check_scanner
def check_profile
def load
def save
def get_dtd_dir
def get_locale_dir
def get_home_dir
def get_user_config
def get_xml_dirs
def get_xml_elements
def get_xml_element
def get_overlay_xml
def get_overlay_files
def get_overlay_archive
def get_backup_dir
def get_splash_img
def get_covers_ext
def get_cover_overlay
def get_cover_official
def get_cover_path
def get_copy_comment_path
def get_comment_path
def get_updateXML_index
def get_updateCOVERS_mirrors
def get_updateCOVERS_dir
def get_icon
def set_autosave_comics
def get_autosave_comics
def set_autofetch_covers
def get_autofetch_covers
def set_autosavepoint
def get_autosavepoint
def set_autosend_overlay
def get_autosend_overlay
def set_expand_series
def get_expand_series
def set_mirrors_update
def set_mirrors_covers
def get_latestOut_time
def get_color
def set_color
def get_debug
def get_version_str
def get_program
def get_backend_domain
def get_backend_path
def set_profile_registered
def get_profile_registered
def get_profile_username
def get_profile_password
def get_db_engine
def get_db_name
def get_db_user
def get_db_password
def get_db_host
def get_db_port
def set_db_engine
def set_db_name
def set_db_user
def set_db_password
def set_db_host
def set_db_port
def is_first_run
def get_distribution

Public Attributes

 APPLICATION
 USER
 cache
 cfg

Static Public Attributes

tuple xml_dir = property(lambda self: self.USER['xml_dir'])
tuple mirrors_update = property(lambda self: self.USER['config'].getlist("General", 'mirrors_update'))

Private Member Functions

def __init_core_configuration
def __init_user_configuration
def __read_user_configuration
def __check_cmdline
def __apply_user_configuration
def __save_user_configuration


Member Function Documentation

def py::Configuration::Configuration::__init__ (   self,
  append_root = '' 
)

00106                                               :
00107                 self.APPLICATION = {}
00108                 self.USER = {}
00109                 self.cache = {}
00110                 self.__init_core_configuration( append_root )
00111 
        def __init_core_configuration (self, append_root = '' ):

def py::Configuration::Configuration::__init_core_configuration (   self,
  append_root = '' 
) [private]

00112                                                                :
00113                 self.APPLICATION ["name"] = "Qomics"
00114                 self.APPLICATION ["version"] = "0.5"
00115                 self.APPLICATION ["extra_version"] = "beta4"
00116 
00117                 ## version of catalog files
00118                 self.APPLICATION[ "catalog_version" ] = "1.0"
00119 
00120                 ## sys configuration
00121                 if not LOCALRUN :
00122                         self.APPLICATION["locale"] = LOCALEDIR
00123                 else :
00124                         self.APPLICATION["locale"] = os.path.join( os.path.abspath(append_root + ".."), "locale" )
00125                 self.init_locale()
00126 
00127                 ## application configuration
00128                 if not LOCALRUN :
00129                         self.APPLICATION["share_dir"] = PKGDATADIR
00130                 else :
00131                         self.APPLICATION["share_dir"] = os.path.abspath( append_root + ".." )
00132 
00133                 self.APPLICATION ["gfx_dir"] = os.path.join( self.APPLICATION ["share_dir"], "gfx" ) + os.sep
00134                 self.APPLICATION ["dtd_dir"] = os.path.join( self.APPLICATION ["share_dir"], "dtd" )
00135                 self.APPLICATION[ "ui_xml" ] = os.path.join( self.APPLICATION[ "gfx_dir" ], "ui.xml" )
00136 
00137                 self.APPLICATION ["splash_img"] = os.path.join( self.APPLICATION ["gfx_dir"], "splash.png" )
00138 
00139                 self.APPLICATION[ "cover_ext" ] = ".jpg"
00140                 self.APPLICATION ["xml_elements"] = {
00141                         'editor': 'EDITOR',
00142                         'collection': 'COLLECTION',
00143                         'comics': 'COMIC',
00144                         'author': 'AUTHOR',
00145                         'series': 'ASERIES'
00146                 }
00147 
00148                 ## official mirrors
00149                 self.APPLICATION[ "mirrors_update" ] = [ 
00150                         "http://download.gna.org/qomics",
00151                 ]
00152                 self.APPLICATION[ "mirrors_covers" ] = [ 
00153                         "http://download.gna.org/qomics",
00154                 ]
00155                 self.APPLICATION[ "update_root" ] = "/catalog/" + self.APPLICATION[ "catalog_version" ]
00156                 self.APPLICATION[ "update_index" ] = self.APPLICATION[ "update_root" ] + "/index.xml.gz"
00157                 self.APPLICATION[ "mirror_covers_dir" ] = "/covers/"
00158 
00159                 self.APPLICATION[ "backend_domain" ] = "qomics.backzone.net"
00160                 self.APPLICATION[ "backend_path" ] = "/backend/"
00161                 #self.APPLICATION[ "backend_domain" ] = "localhost"
00162                 #self.APPLICATION[ "backend_path" ] = "/~panard/qomics/backend/"
00163 
00164                 # django
00165                 os.environ['DJANGO_SETTINGS_MODULE'] = 'qodb.settings'
00166 
00167 
        def __init_user_configuration (self):

def py::Configuration::Configuration::__init_user_configuration (   self  )  [private]

00168                                             :
00169                 if sys.platform.startswith( "win" ) :
00170                         self.USER["dir"] = os.path.join(os.getenv("APPDATA") or "C:\\Windows\\Application Data", "qomics")
00171                         self.USER["home"] = os.getenv("USERPROFILE") or "C:\\Mes Documents"
00172                         self.USER["name"] = os.getenv("USERNAME") or "user"
00173                 else :
00174                         self.USER[ "dir" ] = os.path.join( os.getenv( "HOME" ), ".qomics" )
00175                         self.USER["home"] = os.getenv("HOME")
00176                         self.USER["name"] = os.getenv("USER")
00177                 self.USER["dir"] = os.getenv("QOMICS_HOME") or self.USER["dir"]
00178 
00179                 self.USER["dist"] = None
00180                 if sys.platform.startswith("win") :
00181                         self.USER["dist"] = "windows"
00182                 else :
00183                         for dist in ["gentoo","debian"] :
00184                                 for pattern in ["/etc/%s-release", "/etc/%s_version"] :
00185                                         if os.path.exists(pattern%dist) :
00186                                                 self.USER["dist"] = dist
00187                                                 break
00188                                 if self.USER["dist"] :
00189                                         break
00190 
00191                 self.cfg = QomicsConfig(self)
00192                 self.USER[ "conffile" ] = os.path.join( self.USER[ "dir" ], "config" )
00193                 self.USER[ "config" ] = self.cfg
00194 
00195                 self.USER[ "xml_dir" ] = os.path.join( self.USER[ "dir" ], "xml" ) + os.sep
00196                 self.USER["comments_dir"] = os.path.join(self.USER["dir"],"comments")
00197                 self.USER["covers_dir"] = os.path.join(self.USER["dir"], "covers")
00198                 self.USER["backup_dir"] = os.path.join(self.USER["dir"], "backup")
00199                 self.USER[ "overlay" ] = os.path.join( self.USER["dir"], "overlay" )
00200                 self.USER["covers_overlay_dir"] = os.path.join(self.USER["overlay"],"covers")
00201                 self.USER[ "overlay_xml" ] = os.path.join( self.USER["overlay"], "xml" )
00202                 self.USER[ "overlay_data" ] = os.path.join( self.USER["overlay"], "data" )
00203                 self.USER["overlay_archive"] = os.path.join(self.USER["overlay"], "overlay.qopak")
00204 
00205                 self.USER[ "latestout_time" ] = datetime.timedelta( 60 )
00206 
00207 
        def __read_user_configuration( self ) :

def py::Configuration::Configuration::__read_user_configuration (   self  )  [private]

00208                                               :
00209                 config = self.USER[ "config" ]
00210                 if os.path.exists( self.USER[ "conffile" ] ) :
00211                         config.read( [ self.USER[ 'conffile' ] ] )
00212 
00213                 self.check_scanner()
00214                 self.check_profile()
00215 
        def upgrade( self, notifier ) :

def py::Configuration::Configuration::upgrade (   self,
  notifier 
)

00216                                       :
00217                 config = self.USER[ "config" ]
00218                 cur_version = self.APPLICATION[ "version" ] + "," + self.APPLICATION[ "extra_version" ]
00219                 conf_version = config.get( "General", "version" ) + "," +  config.get( "General", "extra_version" )
00220                 import qo.utils
00221                 import qo.upgrade
00222                 if qo.utils.is_version_newer(cur_version, conf_version) :
00223                         qo.upgrade.all(conf_version, notifier)
00224                 config.set( "General", "version", self.APPLICATION[ "version" ] )
00225                 config.set( "General", "extra_version", self.APPLICATION[ "extra_version" ] )
00226 
00227                 self.check_scanner()
00228                 self.check_profile()
00229 
        def init_locale( self ) :

def py::Configuration::Configuration::init_locale (   self  ) 

00230                                 :
00231                 if locale.getlocale() == (None,None) :
00232                         locale.setlocale(locale.LC_ALL, "fr_FR")
00233                 gettext.install( 'qomics', self.APPLICATION["locale"], unicode=1 )
00234                 if sys.platform.startswith('win') :
00235                         try :
00236                                 gettext.translation( 'qomics', self.APPLICATION["locale"], [ locale.getlocale()[0] ] ).install( unicode=1 )
00237                         except IOError :
00238                                 pass
00239 
        def __check_cmdline( self, cmdline, fallbacks, f_set ) :

def py::Configuration::Configuration::__check_cmdline (   self,
  cmdline,
  fallbacks,
  f_set 
) [private]

00240                                                                :
00241                 program = cmdline.split()
00242                 if len( program ) == 0 :
00243                         program = ""
00244                 else :
00245                         program = program[0]
00246                 if len(program) == 0 or self.get_program( program ) is None :
00247                         program = None
00248                         for x in fallbacks :
00249                                 if self.get_program( x ) is not None :
00250                                         program = x
00251                                         break
00252                         if program is not None :
00253                                 f_set( program )
00254 
        def get_scanner( self ) :

def py::Configuration::Configuration::get_scanner (   self  ) 

00255                                 :
00256                 return self.USER['config'].get( "General", "scanner" )
        def set_scanner( self, program ) :

def py::Configuration::Configuration::set_scanner (   self,
  program 
)

00257                                          :
00258                 if program == "xsane" :
00259                         program = "xsane --save -n -N %%s"
00260                 self.USER['config'].set( "General", "scanner", program )
00261 
        def check_scanner( self ) :

def py::Configuration::Configuration::check_scanner (   self  ) 

00262                                   :
00263                 self.__check_cmdline( self.get_scanner(), ['xsane'], self.set_scanner )
00264 
        def check_profile( self ) :

def py::Configuration::Configuration::check_profile (   self  ) 

00265                                   :
00266                 if self.USER["config"].get( "Profile", "username" ) == "NOTDEF" :
00267                         username = self.USER["name"] + str( random.randrange( 0, 9999 ) ).zfill( 4 )
00268                         password = md5.md5( str(random.random()) ).hexdigest()
00269                         self.USER['config'].set( "Profile", "username", username )
00270                         self.USER['config'].set( "Profile", "password", password )
00271 
        def __apply_user_configuration( self ) :

def py::Configuration::Configuration::__apply_user_configuration (   self  )  [private]

00272                                                :
00273                 for path in [ "dir", "xml_dir", "comments_dir", "covers_dir", "backup_dir", "overlay", "covers_overlay_dir", "overlay_data" ] :
00274                         path = self.USER[ path ]
00275                         if not os.path.exists( path ) : 
00276                                 os.makedirs( path, 0700 )
00277 
        def __save_user_configuration( self ) :

def py::Configuration::Configuration::__save_user_configuration (   self  )  [private]

00278                                               :
00279                 fd = open( self.USER[ "conffile" ], "w" )
00280                 self.USER[ "config" ].write( fd )
00281                 fd.close()
00282 
        def load( self ) :

def py::Configuration::Configuration::load (   self  ) 

00283                          :
00284                 self.__init_user_configuration()
00285                 self.__read_user_configuration()
00286                 self.__apply_user_configuration()
00287 
        def save( self ) :

def py::Configuration::Configuration::save (   self  ) 

00288                          :
00289                 self.__save_user_configuration()
00290 
        def get_dtd_dir( self ) :               return self.APPLICATION[ "dtd_dir" ]

def py::Configuration::Configuration::get_dtd_dir (   self  ) 

00291 :               return self.APPLICATION[ "dtd_dir" ]

def py::Configuration::Configuration::get_locale_dir (   self  ) 

00292                                    :            return self.APPLICATION[ "locale" ]
00293 

def py::Configuration::Configuration::get_home_dir (   self  ) 

00294                                  :
00295                 return self.USER["home"]
        def get_user_config( self ) :           return self.USER[ "config" ]

def py::Configuration::Configuration::get_user_config (   self  ) 

00296                                     :           return self.USER[ "config" ]
00297 

def py::Configuration::Configuration::get_xml_dirs (   self  ) 

00299 :               return [ self.USER[ 'xml_dir' ] ]

def py::Configuration::Configuration::get_xml_elements (   self  ) 

00300 :               return self.APPLICATION[ "xml_elements" ].values ()

def py::Configuration::Configuration::get_xml_element (   self,
  name 
)

00301                                           :     return self.APPLICATION[ "xml_elements" ][ name ]
00302 

def py::Configuration::Configuration::get_overlay_xml (   self,
  name 
)

00303                                           :
00304                 return os.path.join( self.USER["overlay_xml"], name + ".xml" )
        def get_overlay_files( self ) :

def py::Configuration::Configuration::get_overlay_files (   self  ) 

00305                                       :
00306                 ret = []
00307                 for x in self.APPLICATION['xml_elements'].keys() :
00308                         ret.append( self.get_overlay_xml(x) )
00309                 return ret
        def get_overlay_archive( self ) :

def py::Configuration::Configuration::get_overlay_archive (   self  ) 

00310                                         :
00311                 return self.USER[ "overlay_archive" ]
00312 
        def get_backup_dir( self ) :

def py::Configuration::Configuration::get_backup_dir (   self  ) 

00313                                    :
00314                 return self.USER["backup_dir"]
00315 
        def get_splash_img( self ) :            return self.APPLICATION[ "splash_img" ]

def py::Configuration::Configuration::get_splash_img (   self  ) 

00316 :               return self.APPLICATION[ "splash_img" ]

def py::Configuration::Configuration::get_covers_ext (   self  ) 

00317 :               return self.APPLICATION[ "cover_ext" ]

def py::Configuration::Configuration::get_cover_overlay (   self,
  cover 
)

00318                                              :
00319                 return os.path.join(self.USER["covers_overlay_dir"], qo.utils.encode(cover)+self.APPLICATION["cover_ext"])
        def get_cover_official( self, cover ) :

def py::Configuration::Configuration::get_cover_official (   self,
  cover 
)

00320                                               :
00321                 return os.path.join(self.USER["covers_dir"], qo.utils.encode(cover)+self.APPLICATION["cover_ext"])
        def get_cover_path( self, cover ) :     

def py::Configuration::Configuration::get_cover_path (   self,
  cover 
)

00322                                           :     
00323                 overlay_path = self.get_cover_overlay(cover)
00324                 if os.path.exists(overlay_path) :
00325                         return overlay_path
00326                 return self.get_cover_official(cover)
00327 

def py::Configuration::Configuration::get_copy_comment_path (   self,
  qoid,
  copyid 
)

00328                                                         :
00329                 return os.path.join(self.USER["comments_dir"], "%s#%s#comment.txt"%(qoid, copyid))
00330 
        def get_comment_path( self, qoid, copyid=None ) :

def py::Configuration::Configuration::get_comment_path (   self,
  qoid,
  copyid = None 
)

00331                                                         :
00332                 if copyid :
00333                         path = self.get_copy_comment_path(qoid, copyid)
00334                         if os.path.exists(path) :
00335                                 return path
00336                 return os.path.join(self.USER["comments_dir"], "%s.txt"%qoid)
00337 
00338 
        def get_updateXML_index( self ) :       return self.APPLICATION[ 'update_index' ]

def py::Configuration::Configuration::get_updateXML_index (   self  ) 

00339 :       return self.APPLICATION[ 'update_index' ]

def py::Configuration::Configuration::get_updateCOVERS_mirrors (   self  ) 

00341 :       return self.USER[ 'config' ].getlist( "General", 'mirrors_covers' )

def py::Configuration::Configuration::get_updateCOVERS_dir (   self  ) 

00342                                          :      return self.APPLICATION[ 'mirror_covers_dir' ]
00343 

def py::Configuration::Configuration::get_icon (   self,
  name 
)

00344                                    :
00345                 return qo.utils.decode( os.path.join( self.APPLICATION[ 'gfx_dir' ], name + '.png' ) )
00346 
        def set_autosave_comics( self, value ) :

def py::Configuration::Configuration::set_autosave_comics (   self,
  value 
)

00347                                                :
00348                 self.USER[ "config" ].setboolean( "General", "autosave_comics", value )
        def get_autosave_comics( self ) : 

def py::Configuration::Configuration::get_autosave_comics (   self  ) 

00349                                         : 
00350                 return self.USER[ "config" ].getboolean( "General", "autosave_comics" )
00351 

def py::Configuration::Configuration::set_autofetch_covers (   self,
  value 
)

00352                                                 :
00353                 self.USER[ "config" ].setboolean( "General", "autofetch_covers", value )
        def get_autofetch_covers( self ) :

def py::Configuration::Configuration::get_autofetch_covers (   self  ) 

00354                                          :
00355                 return self.USER[ "config" ].getboolean( "General", "autofetch_covers" )
00356 
        def set_autosavepoint( self, value ) :

def py::Configuration::Configuration::set_autosavepoint (   self,
  value 
)

00357                                              :
00358                 self.USER[ "config" ].setboolean("General", "autosavepoint", value)
        def get_autosavepoint( self ) :

def py::Configuration::Configuration::get_autosavepoint (   self  ) 

00359                                       :
00360                 return self.USER[ "config" ].getboolean("General", "autosavepoint")
00361 
        def set_autosend_overlay( self, value ) :

def py::Configuration::Configuration::set_autosend_overlay (   self,
  value 
)

00362                                                 :
00363                 self.USER[ "config" ].setboolean( "General", "autosend_overlay", value )
        def get_autosend_overlay( self ) :

def py::Configuration::Configuration::get_autosend_overlay (   self  ) 

00364                                          :
00365                 return self.USER[ "config" ].getboolean( "General", "autosend_overlay" )
00366 
        def set_expand_series( self, value ) :

def py::Configuration::Configuration::set_expand_series (   self,
  value 
)

00367                                              :
00368                 self.USER[ "config" ].setboolean( "General", "expand_series", value )
        def get_expand_series( self ) :

def py::Configuration::Configuration::get_expand_series (   self  ) 

00369                                       :
00370                 return self.USER[ "config" ].getboolean( "General", "expand_series" )
00371 
        def set_mirrors_update( self, mirrors ) :

def py::Configuration::Configuration::set_mirrors_update (   self,
  mirrors 
)

00372                                                 :
00373                 self.USER[ "config" ].setlist( "General", "mirrors_update", mirrors )
        def set_mirrors_covers( self, mirrors ) :

def py::Configuration::Configuration::set_mirrors_covers (   self,
  mirrors 
)

00374                                                 :
00375                 self.USER[ "config" ].setlist( "General", "mirrors_covers", mirrors )
00376 
        def get_latestOut_time( self ) :        return self.USER[ 'latestout_time' ]

def py::Configuration::Configuration::get_latestOut_time (   self  ) 

00377                                        :        return self.USER[ 'latestout_time' ]
00378 

def py::Configuration::Configuration::get_color (   self,
  name 
)

00379                                     : # return ( bg, fg )
00380                 colors = self.USER[ "config" ].get( "Colors", name ).split( ";" )
00381                 return tuple( colors[ : 2 ] )

def py::Configuration::Configuration::set_color (   self,
  name,
  bg,
  fg 
)

00382                                             :   self.USER[ "config" ].set( "Colors", name, bg + ";" + fg )
00383 

def py::Configuration::Configuration::get_debug (   self  ) 

00384 : return self.USER[ "config" ].getboolean( "General", "debug" )

def py::Configuration::Configuration::get_version_str (   self  ) 

00385                                     :
00386                 return self.APPLICATION[ "version" ] + " " + self.APPLICATION[ "extra_version" ]
00387 
        def get_program( self, name ) :

def py::Configuration::Configuration::get_program (   self,
  name 
)

00388                                       :
00389                 if os.path.exists(name) :
00390                         return name
00391                 path_join = ':'
00392                 if sys.platform.startswith('win') :
00393                         path_join = ';'
00394                         name += '.exe'
00395                 for x in os.environ['PATH'].split( path_join ) :
00396                         _progpath = os.path.join( x, name )
00397                         if os.path.exists( _progpath ) :
00398                                 return _progpath
00399 
        def get_backend_domain( self ) :

def py::Configuration::Configuration::get_backend_domain (   self  ) 

00400                                        :
00401                 return self.APPLICATION[ "backend_domain" ]
        def get_backend_path( self ) :

def py::Configuration::Configuration::get_backend_path (   self  ) 

00402                                      :
00403                 return self.APPLICATION[ "backend_path" ]
00404 
        def set_profile_registered( self, value ) :

def py::Configuration::Configuration::set_profile_registered (   self,
  value 
)

00405                                                   :
00406                 self.USER[ "config" ].setboolean( "Profile", "registered", value )
        def get_profile_registered( self ) :

def py::Configuration::Configuration::get_profile_registered (   self  ) 

00407                                            :
00408                 return self.USER[ "config" ].getboolean( "Profile", "registered" )
        def get_profile_username( self ) :

def py::Configuration::Configuration::get_profile_username (   self  ) 

00409                                          :
00410                 return self.USER[ "config" ].get( "Profile", "username" )
        def get_profile_password( self ) :

def py::Configuration::Configuration::get_profile_password (   self  ) 

00411                                          :
00412                 return self.USER[ "config" ].get( "Profile", "password" )
00413         
        def get_db_engine( self ) :

def py::Configuration::Configuration::get_db_engine (   self  ) 

00414                                   :
00415                 return self.USER["config"].get("Database","engine")
        def get_db_name( self ) :

def py::Configuration::Configuration::get_db_name (   self  ) 

00416                                 :
00417                 return self.USER["config"].get("Database","name") or os.path.join(self.USER["dir"],"qodb.dat")
        def get_db_user( self ) :

def py::Configuration::Configuration::get_db_user (   self  ) 

00418                                 :
00419                 return self.USER["config"].get("Database","user")
        def get_db_password( self ) :

def py::Configuration::Configuration::get_db_password (   self  ) 

00420                                     :
00421                 return self.USER["config"].get("Database","password")
        def get_db_host( self ) :

def py::Configuration::Configuration::get_db_host (   self  ) 

00422                                 :
00423                 return self.USER["config"].get("Database","host")
        def get_db_port( self ) :

def py::Configuration::Configuration::get_db_port (   self  ) 

00424                                 :
00425                 return self.USER["config"].get("Database","port")
00426 
        def set_db_engine( self, value ) :

def py::Configuration::Configuration::set_db_engine (   self,
  value 
)

00427                                          :
00428                 return self.USER["config"].set("Database","engine", value)
        def set_db_name( self, value ) :

def py::Configuration::Configuration::set_db_name (   self,
  value 
)

00429                                        :
00430                 return self.USER["config"].set("Database","name", value)
        def set_db_user( self, value ) :

def py::Configuration::Configuration::set_db_user (   self,
  value 
)

00431                                        :
00432                 return self.USER["config"].set("Database","user", value)
        def set_db_password( self, value ) :

def py::Configuration::Configuration::set_db_password (   self,
  value 
)

00433                                            :
00434                 return self.USER["config"].set("Database","password", value)
        def set_db_host( self, value ) :

def py::Configuration::Configuration::set_db_host (   self,
  value 
)

00435                                        :
00436                 return self.USER["config"].set("Database","host", value)
        def set_db_port( self, value ) :

def py::Configuration::Configuration::set_db_port (   self,
  value 
)

00437                                        :
00438                 return self.USER["config"].set("Database","port", value)
00439         
        def is_first_run( self ) :

def py::Configuration::Configuration::is_first_run (   self  ) 

00440                                  :
00441                 return self.USER["config"].has_option("General","configured")
00442         
        def get_distribution( self ) :

def py::Configuration::Configuration::get_distribution (   self  ) 

00443                                      :
00444                 return self.USER["dist"]
00445 
00446 


Member Data Documentation

tuple py::Configuration::Configuration::xml_dir = property(lambda self: self.USER['xml_dir']) [static]