[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[gnue] r6938 - in trunk/gnue-appserver: scripts src src/gcd src/gld
From: |
johannes |
Subject: |
[gnue] r6938 - in trunk/gnue-appserver: scripts src src/gcd src/gld |
Date: |
Wed, 2 Feb 2005 08:48:04 -0600 (CST) |
Author: johannes
Date: 2005-02-02 08:48:02 -0600 (Wed, 02 Feb 2005)
New Revision: 6938
Modified:
trunk/gnue-appserver/scripts/gnue-readgcd
trunk/gnue-appserver/scripts/gnue-readgld
trunk/gnue-appserver/src/gcd/GCParser.py
trunk/gnue-appserver/src/gcd/readgcd.py
trunk/gnue-appserver/src/geasConfiguration.py
trunk/gnue-appserver/src/geasRpcServer.py
trunk/gnue-appserver/src/geasSessionManager.py
trunk/gnue-appserver/src/gld/GLParser.py
trunk/gnue-appserver/src/gld/readgld.py
Log:
Added 'modulepath' to appserver , which get's scanned for gcds and glds on
startup (and SIGHUP). Improved gnue-readgcd and gnue-readgld (speed and
fault-tolerance) as well as some bug-fixes (regarding schema-generation).
Modified: trunk/gnue-appserver/scripts/gnue-readgcd
===================================================================
--- trunk/gnue-appserver/scripts/gnue-readgcd 2005-02-02 12:01:48 UTC (rev
6937)
+++ trunk/gnue-appserver/scripts/gnue-readgcd 2005-02-02 14:48:02 UTC (rev
6938)
@@ -29,5 +29,5 @@
from gnue.appserver.gcd import readgcd
if __name__ == '__main__':
- readgcd = readgcd.gcdConverter ()
+ readgcd = readgcd.gcdClient ()
readgcd.run ();
Modified: trunk/gnue-appserver/scripts/gnue-readgld
===================================================================
--- trunk/gnue-appserver/scripts/gnue-readgld 2005-02-02 12:01:48 UTC (rev
6937)
+++ trunk/gnue-appserver/scripts/gnue-readgld 2005-02-02 14:48:02 UTC (rev
6938)
@@ -29,4 +29,4 @@
from gnue.appserver.gld import readgld
if __name__ == '__main__':
- readgld.gldReader ().run ()
+ readgld.gldClient ().run ()
Modified: trunk/gnue-appserver/src/gcd/GCParser.py
===================================================================
--- trunk/gnue-appserver/src/gcd/GCParser.py 2005-02-02 12:01:48 UTC (rev
6937)
+++ trunk/gnue-appserver/src/gcd/GCParser.py 2005-02-02 14:48:02 UTC (rev
6938)
@@ -39,34 +39,48 @@
class Error (errors.ApplicationError):
pass
-class NoModuleError (Error):
+class GCDError (errors.UserError):
+ pass
+
+class NoModuleError (GCDError):
def __init__ (self, classname):
msg = u_("Class '%s' has no module") % classname
- Error.__init__ (self, msg)
+ GCDError.__init__ (self, msg)
-class DuplicateDefinitionError (Error):
+class DuplicateDefinitionError (GCDError):
def __init__ (self, name, asLength):
if asLength:
msg = u_("'%s' has a length in type and an extra length attribute") %
name
else:
msg = u_("'%s' has a scale in type and an extra scale attribute") % name
- Error.__init__ (self, msg)
+ GCDError.__init__ (self, msg)
-class ModuleMismatchError (Error):
+class ModuleMismatchError (GCDError):
def __init__ (self, classname, module):
msg = u_("The class '%(class)s' mismatches the given module attribute "
"'%(module)s'") % {'class': classname, 'module': module}
- Error.__init__ (self, msg)
+ GCDError.__init__ (self, msg)
-class InvalidCalculatedError (Error):
+class InvalidCalculatedError (GCDError):
def __init__ (self, classname, propName):
msg = u_("Calculated property '%(class)s.%(property)s' has parameters") \
% {'class': classname, 'property': propName}
- Error.__init__ (self, msg)
+ GCDError.__init__ (self, msg)
-class MissingNameError (Error):
+class MissingNameError (GCDError):
pass
+class ModuleNameError (GCDError):
+ def __init__ (self, name):
+ msg = u_("The module name '%s' contains underscores") % name
+ GCDError.__init__ (self, msg)
+
+class ReferenceNotAllowedError (GCDError):
+ def __init__ (self, refname, typename, name):
+ msg = u_("Reference '%(ref)s' not allowed for '%(type)s' instance "
+ "%(name)s") % {'ref': refname, 'type': typename, 'name': name}
+ GCDError.__init__ (self, msg)
+
# =============================================================================
# load an XML object tree from a given stream and return it's root object
# =============================================================================
@@ -242,13 +256,14 @@
# =============================================================================
class GCTypeDefinition (GCObject):
- def __init__ (self, parent = None, objType = None):
+ def __init__ (self, parent = None, objType = None, refsAllowed = True):
GCObject.__init__ (self, parent, type = objType)
- self.datatype = None
- self.length = None
- self.scale = None
- self.isReference = False
+ self.datatype = None
+ self.length = None
+ self.scale = None
+ self.isReference = False
+ self._refsAllowed = refsAllowed
self._inits.extend ([None, self._validate])
@@ -286,6 +301,9 @@
if typename in helpers.BASE_TYPES:
helpers.verifyBasetype (typename, self.length, self.scale)
else:
+ if not self._refsAllowed:
+ raise ReferenceNotAllowedError, (typename, self._type, self.name)
+
self.isReference = True
if self.length:
@@ -319,7 +337,10 @@
if not len (self.name):
raise MissingNameError, u_("Module has no name")
+ if '_' in self.name:
+ raise ModuleNameError, self.name
+
# =============================================================================
# The class object
# =============================================================================
@@ -486,7 +507,7 @@
class GCParameter (GCTypeDefinition):
def __init__ (self, parent):
- GCTypeDefinition.__init__ (self, parent, objType = 'GCParameter')
+ GCTypeDefinition.__init__ (self, parent, 'GCParameter', False)
# =============================================================================
@@ -496,16 +517,20 @@
class GCIndex (GCObject):
def __init__ (self, parent):
GCObject.__init__ (self, parent, type = 'GCIndex')
- self.fields = []
- self.fullName = None
- self.module = None
- self._inits.extend ([None, self._complete])
+ self.fields = []
+ self.fullName = None
+ self.module = None
+ self.fieldNames = []
+ self._inits.extend ([None, self._complete, self._getFieldNames])
def _complete (self):
- self.module = self.findParentOfType ('GCModule').name
- self.fullName = "ix_%s" % Namespace.createName (self.module, self.name)
- self.fields = self.findChildrenOfType ('GCIndexField')
+ self.module = self.findParentOfType ('GCModule').name
+ self.fullName = "ix_%s" % Namespace.createName (self.module, self.name)
+ self.fields = self.findChildrenOfType ('GCIndexField')
+ def _getFieldNames (self):
+ self.fieldNames = [f.fullName for f in self.fields]
+
# =============================================================================
# The index field object
# =============================================================================
Modified: trunk/gnue-appserver/src/gcd/readgcd.py
===================================================================
--- trunk/gnue-appserver/src/gcd/readgcd.py 2005-02-02 12:01:48 UTC (rev
6937)
+++ trunk/gnue-appserver/src/gcd/readgcd.py 2005-02-02 14:48:02 UTC (rev
6938)
@@ -23,44 +23,85 @@
import sys
import os
+import string
import whrandom
from gnue.common.apps import i18n, errors
from gnue.common.apps.GClientApp import *
from gnue.common.utils.FileUtils import openResource
-from gnue.common.datasources import GDataSource, GConditions
+from gnue.common.datasources import GDataSource
from gnue.appserver import VERSION
from gnue.appserver.gcd import GCParser
-from gnue.appserver.classrep import Namespace
+from gnue.appserver.classrep import Namespace, helpers
# =============================================================================
# Exceptions
# =============================================================================
-class Error (errors.ApplicationError):
- pass
+class ReferenceLookupError (errors.SystemError):
+ def __init__ (self, key, value):
+ msg = u_("Cannot find '%(key)s' named '%(value)s in lookup dictionary") \
+ % {'key': key, 'value': value}
+ errors.SystemError.__init__ (self, msg)
+
class StartupError (errors.UserError):
pass
+class Error (errors.ApplicationError):
+ def __init__ (self, message, files = None):
+ errors.ApplicationError.__init__ (self, message)
+ text = []
+
+ if files:
+ text.append (u_("In file(s) '%s':") % string.join (files, ', '))
+
+ text.append (message)
+ self.detail = string.join (text, os.linesep)
+
+
class ModuleNotFoundError (Error):
- def __init__ (self, module):
+ def __init__ (self, module, files = None):
msg = u_("Module '%s' not found in class repository") % module
- Error.__init__ (self, msg)
+ Error.__init__ (self, msg, files)
-class ProcedureNotFoundError (Error):
- def __init__ (self, procedure):
- msg = u_("Procedure '%s' not found in class repository") % procedure
- Error.__init__ (self, msg)
+class ClassNotFoundError (Error):
+ def __init__ (self, classname, files = None):
+ msg = u_("Class '%s' not found in class repository") % classname
+ Error.__init__ (self, msg, files)
+class FilterNotFoundError (Error):
+ def __init__ (self, classname, filtername, files = None):
+ msg = u_("Filter '%(filter)s' of class '%(class)s' not found in class "
+ "repository") \
+ % {'filter': filtername, 'class' : classname}
+ Error.__init__ (self, msg, files)
+class FilterChangeError (Error):
+ def __init__ (self, name, files = None):
+ msg = u_("Changing filter of existing class '%s' is not allowed") % name
+ Error.__init__ (self, msg, files)
+
+class NullableError (Error):
+ def __init__ (self, pName, files = None):
+ msg = u_("Property '%s' cannot be added to existing class with "
+ "'NOT NULL' constraint") % pName
+ Error.__init__ (self, msg, files)
+
+class DuplicateClassError (Error):
+ def __init__ (self, classname, file1, file2):
+ msg = u_("Class '%s' is defined multiple times") % classname
+ Error.__init__ (self, msg, [file1, file2])
+
+
+
# =============================================================================
# Update a database schema from GNUe Class Definitions and update classrep.
# =============================================================================
-class gcdConverter (GClientApp):
+class gcdClient (GClientApp):
NAME = "readgcd"
VERSION = VERSION
@@ -100,592 +141,932 @@
# ---------------------------------------------------------------------------
- # Verify the given commandline options
+ # Main program
# ---------------------------------------------------------------------------
- def __checkOptions (self):
+ def run (self):
"""
- This function checks wether the given command line arguments and options
- are usefull or not.
+ Create a new instance of a gcdReader and pass all options to it. Before
+ starting the process username and password will be set for the connection.
"""
- self._args = [unicode (a, i18n.encoding) for a in self.ARGUMENTS]
- if not len (self._args):
- raise StartupError, u_("No input file specified.")
+ reader = gcdReader (self.connections,
+ self.OPTIONS ['connection'],
+ [unicode (a, i18n.encoding) for a in self.ARGUMENTS],
+ self.OPTIONS ['output'],
+ self.OPTIONS ['file-only'])
- if not self.OPTIONS ['connection']:
- raise StartupError, u_("No connection specified.")
+ self._prepareConnection ()
- try:
- self._files = []
+ reader.run ()
- for filename in self._args:
- self._files.append (openResource (filename))
- except IOError, err:
- raise StartupError, u_("Unable to open input file: %s") % \
- errors.getException () [2]
+ # ---------------------------------------------------------------------------
+ # Prepare the connection
+ # ---------------------------------------------------------------------------
+ def _prepareConnection (self):
+ """
+ This function makes sure the connection will have the proper username and
+ password set.
+ """
- self.outfile = self.OPTIONS ['output']
+ connection = self.connections.getConnection (self.OPTIONS ['connection'])
- if self.OPTIONS ['file-only'] and self.outfile is None:
- raise StartupError, \
- u_("Output to file only requested, but no filename specified.")
+ if not connection.parameters.has_key ('username'):
+ connection.parameters ['username'] = 'gnue'
+ if not connection.parameters.has_key ('password'):
+ connection.parameters ['password'] = 'gnue'
+ if self.OPTIONS ['username'] is not None:
+ connection.parameters ['username'] = self.OPTIONS ['username']
+ if self.OPTIONS ['password'] is not None:
+ connection.parameters ['password'] = self.OPTIONS ['password']
+
+
+# =============================================================================
+# This class implements an integrator for GNUe Class Definition files
+# =============================================================================
+
+class gcdReader:
+
# ---------------------------------------------------------------------------
- # Main program
+ # Constructor
# ---------------------------------------------------------------------------
- def run (self):
+ def __init__ (self, connections, database, files, dump = None, fonly =
False):
"""
- This is the main function of the whole process. It verifies the given
- options, loads all schema definitions and then logs into the connection to
- perform all actions requested.
+ Create a new instance of a gcd reader
+
+ @param connections: GConnections instance to be used
+ @param database: name of the connection to use (in connection.conf)
+ @param files: sequence of filenames to integerate
+ @param dump: If not None, the SQL-code of the schema-update will be stored
+ into a file with this name
+ @param fonly: If True, only the SQL-dump will be done, no change of the
+ backend will happen.
"""
- self.__checkOptions ()
+ self._connections = connections
+ self._database = database
+ self._filenames = files
+ self._files = []
+ self._dump = dump
+ self._fileonly = fonly
- self.tables = []
+ # ResultSets
+ self.moduleRS = None
+ self.classRS = None
+ self.propRS = None
+ self.procRS = None
+ self.parmRS = None
+
+ # Lookup dictionaries
+ self.__lpModule = {} # gnue_id -> name, name -> gnue_id
+ self.__lpClass = {} # fqName -> gnue_id
+ self.__lpFilter = {} # fqName -> gnue_id
+ self.__lpProperty = {} # class-id.module-id_name -> gnue_id
+ self.__lpProcedure= {} # class-name.module-name_name -> gnue_id
+ self.__duplos = {} # fqName
+
+ # Data from gcd files
+ self.schema = {}
self.modules = {}
self.classes = {}
self.properties = {}
self.procedures = {}
self.parameters = {}
- for item in range (len (self._files)):
- print o (u_("Loading gcd file '%s' ...") % self._args [item])
+ if not len (self._filenames):
+ raise StartupError, u_("No input file specified.")
+ if not self._database:
+ raise StartupError, u_("No connection specified.")
+
+ if self._fileonly and self._dump is None:
+ raise StartupError, \
+ u_("Output to file only requested, but no filename specified.")
+
+ try:
+ for filename in files:
+ self._files.append (openResource (filename))
+
+ except IOError, err:
+ for item in self._files:
+ item.close ()
+
+ raise StartupError, u_("Unable to open input file: %s") % \
+ errors.getException () [2]
+
+
+ # ---------------------------------------------------------------------------
+ # Process the given GCD-files
+ # ---------------------------------------------------------------------------
+
+ def run (self):
+ """
+ This is the main function of the whole process. It loads all schema
+ definitions and then logs into the connection to perform all actions
+ requested.
+ """
+
+ self.__getModuleLookup ()
+ self.__getClassLookup ()
+ self.__getPropertyLookup ()
+ self.__getProcedureLookup ()
+
+ print o (u_("Loading gcd files ..."))
+
+ for item in xrange (len (self._files)):
try:
+ self.__currentFile = self._filenames [item]
+
schema = GCParser.loadFile (self._files [item])
schema.walk (self.__iterateObjects)
finally:
self._files [item].close ()
+ self.__integrityCheck ()
+
self.executeAndGenerateCode ()
-
self.updateRepository ()
# ---------------------------------------------------------------------------
+ # Execute and generate the code
+ # ---------------------------------------------------------------------------
+
+ def executeAndGenerateCode (self):
+ """
+ This function logs into the given connection and calls it for an update of
+ it's schema according to the loaded table definitions. Additionally the
+ schema creation code is generated by this call, which will be stored in the
+ given output file (if requested by options).
+ """
+
+ connection = self._connections.getConnection (self._database)
+ self._connections.loginToConnection (connection)
+
+ print _("Updating schema ...")
+ code = connection.updateSchema (self.schema.values (), self._fileonly)
+
+ if self._dump is not None:
+ dest = open (self._dump, 'w')
+
+ for item in code:
+ for line in item:
+ dest.write (line + os.linesep)
+
+ dest.close ()
+
+
+ # ---------------------------------------------------------------------------
+ # Update the class repository
+ # ---------------------------------------------------------------------------
+
+ def updateRepository (self):
+ """
+ This function updates all parts of the class repository.
+ """
+
+ print _("Updating class repository ...")
+
+ commit = self.__updateModules ()
+ commit |= self.__updateClasses ()
+ commit |= self.__updateProperties ()
+ commit |= self.__updateProcedures ()
+ commit |= self.__updateParameter ()
+
+ if commit:
+ self._connections.commitAll ()
+
+
+ # ---------------------------------------------------------------------------
# Iterate over all top level elements
# ---------------------------------------------------------------------------
def __iterateObjects (self, sObject):
"""
This function iterates over all objects of a GCD tree and processes the
- GCModule and GCClass instances.
+ instances.
@param sObject: current GCD object to be processed
"""
+
if sObject._type == 'GCModule':
- self.__translateModule (sObject)
+ self.__currentModule = sObject
+ self.__addModuleToRepository (sObject)
elif sObject._type == 'GCClass':
- self.__translateClass (sObject)
+ self.__currentClass = sObject
+ self.__addClassToSchema (sObject)
+ self.__addClassToRepository (sObject)
+ elif sObject._type == 'GCProperty':
+ self.__addPropertyToSchema (sObject)
+ self.__addPropertyToRepository (sObject)
+
+ elif sObject._type == 'GCProcedure':
+ self.__addProcedureToRepository (sObject)
+
+ elif sObject._type == 'GCParameter':
+ self.__addParameterToRepository (sObject)
+
+ elif sObject._type == 'GCIndex':
+ self.__addIndexToSchema (sObject)
+
+
-
# ---------------------------------------------------------------------------
- # A module translates to a gnue_module data entry only
+ # Create a lookup dictionary for modules
# ---------------------------------------------------------------------------
- def __translateModule (self, aModule):
+ def __getModuleLookup (self):
"""
- This function adds a dictionary for the given module to the modules data
- block for later update of the class repository.
+ This function creates a lookup dictionary for modules, where it maps names
+ to ids and vice versa
+ """
- @param aModule: GCD Module object to be processed.
+ self.__lpModule = {}
+
+ if self.moduleRS is None:
+ self.moduleRS = self.__openSource ('gnue_module',
+ ['gnue_id', 'gnue_name', 'gnue_comment']).createResultSet ()
+
+ rec = self.moduleRS.firstRecord ()
+ while rec is not None:
+ gid = rec.getField ('gnue_id')
+ name = rec.getField ('gnue_name')
+
+ self.__lpModule [gid] = name
+ self.__lpModule [name.lower ()] = gid
+
+ rec = self.moduleRS.nextRecord ()
+
+
+
+ # ---------------------------------------------------------------------------
+ # Create a lookup dictionary for classes
+ # ---------------------------------------------------------------------------
+
+ def __getClassLookup (self):
"""
+ This function creates a lookup dictionary for classes and filters. The
+ class lookup maps names to ids and vice versa. If a class has a filter
+ defined it will be set into the filter dictionary.
+ """
- self.modules [aModule.name] = self.fetchTags (aModule, ['name', 'comment'])
- self.modules [aModule.name] ['gnue_id'] = None
+ self.__lpClass = {}
+ self.__lpFilter = {}
+ if self.classRS is None:
+ self.classRS = self.__openSource ('gnue_class', ['gnue_id', 'gnue_name',
+ 'gnue_module', 'gnue_comment', 'gnue_filter']).createResultSet ()
+ rec = self.classRS.firstRecord ()
+ while rec is not None:
+ gid = rec.getField ('gnue_id')
+ name = rec.getField ('gnue_name')
+ mid = rec.getField ('gnue_module')
+ fid = rec.getField ('gnue_filter')
+
+ fqName = Namespace.createName (self.__lpModule [mid], name)
+
+ self.__lpClass [gid] = fqName
+ self.__lpClass [fqName.lower ()] = gid
+
+ if fid is not None:
+ self.__lpFilter [fqName.lower ()] = fid
+
+ rec = self.classRS.nextRecord ()
+
+
+
# ---------------------------------------------------------------------------
- # A class translation needs a table creation/modification and a data entry
+ # Build a lookup-dictionary for properties
# ---------------------------------------------------------------------------
- def __translateClass (self, aClass):
+ def __getPropertyLookup (self):
"""
- This function creates an entry for schema creation of the given class, as
- well as a dictionary for the class repository update.
+ This function creates a lookup dictionary for properties where the key is
+ constructed from "classid.moduleid_propertyname".
+ """
- @param aClass: GCD Class object to be processed.
+ self.__lpProperty = {}
+
+ if self.propRS is None:
+ self.propRS = self.__openSource ('gnue_property', ['gnue_id',
+ 'gnue_module', 'gnue_class', 'gnue_name', 'gnue_type', 'gnue_length',
+ 'gnue_scale', 'gnue_nullable', 'gnue_comment']).createResultSet ()
+
+ rec = self.propRS.firstRecord ()
+ while rec is not None:
+ mid = rec.getField ('gnue_module')
+ cid = rec.getField ('gnue_class')
+ name = rec.getField ('gnue_name')
+ key = "%s.%s_%s" % (cid, mid, name.lower ())
+
+ self.__lpProperty [key] = rec.getField ('gnue_id')
+
+ rec = self.propRS.nextRecord ()
+
+
+ # ---------------------------------------------------------------------------
+ # Build a lookup-dictionary for procedures
+ # ---------------------------------------------------------------------------
+
+ def __getProcedureLookup (self):
"""
+ This function creates a procedure lookup dictionary where the key is built
+ from "classname.modulename_procedurename".
+ """
- self.tables.append ({'name': aClass.fullName, 'fields': []})
+ self.__lpProcedure = {}
- if aClass.action == 'create':
- self.tables [-1]['primarykey'] = {
- 'name' : 'pk_%s' % aClass.fullName,
- 'fields': ['gnue_id']}
+ if self.procRS is None:
+ self.procRS = self.__openSource ('gnue_procedure', ['gnue_id',
+ 'gnue_module', 'gnue_class', 'gnue_name', 'gnue_type', 'gnue_length',
+ 'gnue_scale', 'gnue_nullable', 'gnue_comment', 'gnue_code',
+ 'gnue_language']).createResultSet ()
- cDef = self.fetchTags (aClass, ['name', 'module', 'comment', 'filter'])
- cDef ['gnue_id'] = None
+ rec = self.procRS.firstRecord ()
+ while rec is not None:
+ mName = self.__lpModule.get (rec.getField ('gnue_module'))
+ cName = self.__lpClass.get (rec.getField ('gnue_class'))
+ pName = rec.getField ('gnue_name')
+ key = "%s.%s_%s" % (cName, mName, pName)
- self.classes [aClass.fullName] = cDef
+ self.__lpProcedure [key.lower ()] = rec.getField ('gnue_id')
- # After processing the class definition, iterate over all it's items
- aClass.walk (self.__iterateClassObjects, defs = self.tables [-1])
+ rec = self.procRS.nextRecord ()
# ---------------------------------------------------------------------------
- # Iterate over all elements of a class definition
+ # Add a module to the repository dictionary
# ---------------------------------------------------------------------------
- def __iterateClassObjects (self, sObject, defs):
+ def __addModuleToRepository (self, item):
"""
- This function processes all child objects of a GCD class instance. For all
- properties a schema creation dictionary will be created. Properties,
- Procedures and Parameters will be added to the class repository update
- dictionaries.
+ This function adds a given module to the dictionary used for repository
+ update later.
- @param sObject: current GCD object to be processed
- @param defs: schema creation dictionary describing the table definition for
- the class.
+ @param item: GCModule instance of the module to be added
"""
- if sObject._type == 'GCProperty':
- fDef = {'name' : sObject.fullName,
- 'type' : sObject.datatype,
- 'nullable': sObject.nullable}
-
- if sObject.length is not None and sObject.length:
- fDef ['length'] = sObject.length
- if sObject.scale is not None and sObject.scale:
- fDef ['precision'] = sObject.scale
+ mkey = item.name.lower ()
+ mDef = self.__fetchTags (item, ['name', 'comment'])
+ mDef ['gnue_id'] = self.__lpModule.get (mkey)
- defs ['fields'].append (fDef)
+ if not self.modules.has_key (mkey):
+ self.modules [mkey] = {}
- # Create a foreign key constraint for class references
- if sObject.isReference:
- cDef = {'name' : "fk_%s_%s" % (defs ['name'], fDef ['name']),
- 'fields' : [fDef ['name']],
- 'reftable' : sObject.type,
- 'reffields': ['gnue_id']}
+ self.modules [mkey] = mDef
- if not defs.has_key ('constraints'):
- defs ['constraints'] = [cDef]
- else:
- defs ['constraints'].append (cDef)
- fqName = "%s.%s" % (sObject._parent.fullName, sObject.fullName)
- propDef = self.fetchTags (sObject, ['name', 'length', 'scale',
- 'nullable', 'comment'])
- if sObject.isReference:
- propDef ['gnue_type'] = sObject.type
- propDef ['gnue_length'] = None
+ # ---------------------------------------------------------------------------
+ # Add a class to the schema definition
+ # ---------------------------------------------------------------------------
- elif sObject.fullName == 'gnue_id':
- propDef ['gnue_type'] = 'id'
- propDef ['gnue_length'] = None
- else:
- propDef ['gnue_type'] = sObject.datatype
+ def __addClassToSchema (self, item):
+ """
+ This function adds a class to the schema definition used for backend schema
+ generation later on. If the same class should be added twice a
+ 'DuplicateClassError' will be raised.
- propDef ['gnue_id'] = None
- propDef ['gnue_class'] = sObject._parent.fullName
- propDef ['gnue_module'] = sObject.module
- self.properties [fqName] = propDef
+ @param item: GCClass instance to be added to schema definition
+ """
+ ckey = item.fullName.lower ()
+ if not self.schema.has_key (ckey):
+ self.schema [ckey] = {'name': item.fullName, 'fields': []}
- # Process a procedure of the class
- elif sObject._type == 'GCProcedure':
- fqName = "%s.%s" % (sObject._parent.fullName, sObject.fullName)
- pDef = self.fetchTags (sObject, ['name', 'module', 'nullable',
- 'language', 'length', 'scale', 'comment'])
- pDef ['gnue_id'] = None
- pDef ['gnue_class'] = sObject._parent.fullName
- pDef ['gnue_type'] = sObject.datatype
- pDef ['gnue_code'] = sObject.getChildrenAsContent ()
- self.procedures [fqName] = pDef
+ if item.action == 'create':
+ self.schema [ckey] ['primarykey'] = { \
+ 'name' : 'pk_%s' % item.fullName,
+ 'fields': ['gnue_id']}
- sObject.walk (self.__iterateProcedure)
+ if self.__duplos.has_key (ckey):
+ raise DuplicateClassError, \
+ (item.fullName, self.__currentFile, self.__duplos [ckey])
- elif sObject._type == 'GCIndex':
- if not defs.has_key ('indices'):
- defs ['indices'] = []
+ self.__duplos [ckey] = self.__currentFile
- defs ['indices'].append ({'name' : sObject.fullName,
- 'unique': sObject.unique,
- 'fields': [f.fullName for f in
sObject.fields]})
# ---------------------------------------------------------------------------
- # Iterate over all child elements of a procedure
+ # Add an index to the schema definition
# ---------------------------------------------------------------------------
- def __iterateProcedure (self, sObject):
+ def __addIndexToSchema (self, item):
"""
- This function processes any parameter definitions for a GCD procedure
- instance. A dictionary for class repository update will be created.
+ This function adds an index to the schema definition
- @param sObject: the GCD parameter object to be processed
+ @param item: GCIndex instance with the index to be added
"""
- if sObject._type == 'GCParameter':
- pDef = self.fetchTags (sObject, ['name', 'comment', 'length', 'scale'])
- pDef ['gnue_type'] = sObject.datatype
- pDef ['gnue_procedure'] = "%s.%s" % (sObject._parent._parent.fullName,
- sObject._parent.fullName)
- fqName = "%s.%s" % (pDef ['gnue_procedure'], sObject.name)
- self.parameters [fqName] = pDef
+ classDef = self.schema [self.__currentClass.fullName.lower ()]
+ if not classDef.has_key ('indices'):
+ classDef ['indices'] = []
+ classDef ['indices'].append ({'name' : item.fullName,
+ 'unique': item.unique,
+ 'fields': item.fieldNames})
+
+
# ---------------------------------------------------------------------------
- # Get a dictionary with all keys listed in tags and values from sObject
+ # Add a class instance to the repository dictionary
# ---------------------------------------------------------------------------
- def fetchTags (self, sObject, tags):
+ def __addClassToRepository (self, item):
"""
- This function creates a dictionary with all attributes from sObject listed
- in tags, where the keys are constructed by 'gnue_%s' % attributename.
+ This function adds a class to the class dictionary used for repository
+ update later on.
- @param sObject: Schema object to retriev attributes from
- @param tags: list of all attributes to retrieve
- @return: dictionary with the attribute names as keys and their values
+ @param item: GCClass instance of the class to add
"""
- res = {}
- for item in tags:
- if hasattr (sObject, item):
- res ["gnue_%s" % item] = getattr (sObject, item)
- return res
+ key = item.fullName.lower ()
+ cDef = self.__fetchTags (item, ['module', 'name', 'comment', 'filter'])
+ cDef ['gnue_id'] = self.__lpClass.get (key)
+ if not self.classes.has_key (key):
+ self.classes [key] = {'_files': []}
+
+ self.classes [key].update (cDef)
+ self.classes [key]['_files'].append (self.__currentFile)
+
+
# ---------------------------------------------------------------------------
- # Execute and generate the code
+ # Add a property to the schema definition
# ---------------------------------------------------------------------------
- def executeAndGenerateCode (self):
+ def __addPropertyToSchema (self, item):
"""
- This function logs into the given connection and calls it for an update of
- it's schema according to the loaded table definitions. Additionally the
- schema creation code is generated by this call, which will be stored in the
- given output file (if requested by options).
+ This function adds another property to the schema definition used for
+ backend schema generation.
+
+ @param item: GCProperty instance to add
"""
- connection = self.connections.getConnection (self.OPTIONS ['connection'])
+ classDef = self.schema [item._parent.fullName.lower ()]
+ pDef = {'name' : item.fullName,
+ 'type' : item.datatype,
+ 'nullable': item.nullable}
- if not connection.parameters.has_key ('username'):
- connection.parameters ['username'] = 'gnue'
- if not connection.parameters.has_key ('password'):
- connection.parameters ['password'] = 'gnue'
+ if item.length: pDef ['length'] = item.length
+ if item.scale: pDef ['precision'] = item.scale
- if self.OPTIONS ['username'] is not None:
- connection.parameters ['username'] = self.OPTIONS ['username']
+ classDef ['fields'].append (pDef)
- if self.OPTIONS ['password'] is not None:
- connection.parameters ['password'] = self.OPTIONS ['password']
+ # If the property is a reference to another class, add a constraint to the
+ # class definition
+ if item.isReference:
+ if not classDef.has_key ('constraints'):
+ classDef ['constraints'] = []
- self.connections.loginToConnection (connection)
+ classDef ['constraints'].append ( \
+ {'name' : "fk_%s_%s" % (item._parent.fullName, item.fullName),
+ 'fields' : [item.fullName],
+ 'reftable' : item.type,
+ 'reffields': ['gnue_id']})
- print _("Updating schema ...")
- code = connection.updateSchema (self.tables, self.OPTIONS ['file-only'])
- if self.outfile is not None:
- dest = open (self.outfile, 'w')
+ # ---------------------------------------------------------------------------
+ # Add a property to the repository dictionary
+ # ---------------------------------------------------------------------------
- for item in code:
- for line in item:
- dest.write (line + "\n")
+ def __addPropertyToRepository (self, item):
+ """
+ This function adds a property to the property dictionary used for
+ repository update later on.
- dest.close ()
+ @param item: GCProperty instance to add
+ """
+ fqName = "%s.%s" % (item._parent.fullName, item.fullName)
+ pkey = "%s.%s_%s" % (self.__lpClass.get (item._parent.fullName.lower ()),
+ self.__lpModule.get (item.module.lower ()), item.name)
+ pDef = {'gnue_id' : self.__lpProperty.get (pkey.lower ()),
+ 'gnue_class': item._parent.fullName,
+ 'gnue_type' : item.datatype}
+
+ pDef.update (self.__fetchTags (item,
+ ['module', 'name', 'length', 'scale', 'nullable', 'comment']))
+
+ if item.isReference:
+ pDef.update ({'gnue_type': item.type, 'gnue_length': None})
+
+ elif item.fullName.lower () == 'gnue_id':
+ pDef.update ({'gnue_type': 'id', 'gnue_length': None})
+
+ self.properties [fqName.lower ()] = pDef
+
+
# ---------------------------------------------------------------------------
- # Update the class repository
+ # Add a procedure to the repository dictionary
# ---------------------------------------------------------------------------
- def updateRepository (self):
- print _("Updating class repository ...")
+ def __addProcedureToRepository (self, item):
+ """
+ This function adds a procedure to the dictionary used for repository update
+ later on.
- self._updateModules ()
- self._updateClasses ()
- self._updateProperties ()
- self._updateProcedures ()
- self._updateParameter ()
+ @param item: GCProcedure instance to add
+ """
+ fqName = "%s.%s" % (item._parent.fullName, item.fullName)
+ pDef = {'gnue_id' : None,
+ 'gnue_class': item._parent.fullName,
+ 'gnue_type' : item.datatype,
+ 'gnue_code' : item.getChildrenAsContent ()}
+ pDef.update (self.__fetchTags (item, ['module', 'name', 'length', 'scale',
+ 'nullable', 'comment', 'language']))
+
+ self.procedures [fqName.lower ()] = pDef
+
+
# ---------------------------------------------------------------------------
- # Create a new datasource for a given class
+ # Add a parameter to the repository dictionary
# ---------------------------------------------------------------------------
- def __openSource (self, classname, fieldList):
- return GDataSource.DataSourceWrapper (connections = self.connections,
- attributes = {'name' : "dts_%s" % classname,
- 'database' : self.OPTIONS ['connection'],
- 'table' : classname,
- 'primarykey': 'gnue_id'},
- fields = fieldList, unicodeMode = True)
+ def __addParameterToRepository (self, item):
+ """
+ This function adds a parameter to the dictionary used for repository update
+ later on.
+ @param item: GCParameter instance to add
+ """
+ pName = "%s.%s" % (self.__currentClass.fullName, item._parent.fullName)
+ fqName = "%s.%s" % (pName, item.name)
+
+ par = {'gnue_procedure': pName, 'gnue_type': item.datatype}
+ par.update (self.__fetchTags (item, ['name', 'length', 'scale',
'comment']))
+
+ self.parameters [fqName.lower ()] = par
+
+
# ---------------------------------------------------------------------------
- # Update/add modules to the class repository
+ # Verify the built dictionaries
# ---------------------------------------------------------------------------
- def _updateModules (self):
+ def __integrityCheck (self):
"""
+ This function does some integrity checking on the dictionaries built from
+ all the gcd files.
"""
- self._dtsModules = self.__openSource ('gnue_module', ['gnue_id',
- 'gnue_name', 'gnue_comment'])
+ for (name, item) in self.classes.items ():
+ # Make sure the module referenced is a valid module, either an existing
+ # or a new one
+ mname = item ['gnue_module'].lower ()
+ if not self.__lpModule.get (mname) and not self.modules.get (mname):
+ raise ModuleNotFoundError, (item ['gnue_module'], item ['_files'])
- stat = [0, 0, 0] # inserted, updated, unchanged
+ # Make sure the filter class will be available if one is defined
+ if item.get ('gnue_filter') is not None:
+ fname = item ['gnue_filter']
+ if not self.__lpClass.get (fname) and not self.classes.get (fname):
+ raise FilterNotFoundError, (name, fname, item ['_files'])
- for module in self.modules.values ():
- cond = GConditions.buildConditionFromDict ( \
- {'gnue_name': module ['gnue_name']})
- resultSet = self._dtsModules.createResultSet (cond)
+ # For an existing class we need to make sure the filter-attribute won't
+ # get changed, because we cannot add a not NULL field to an existing
class
+ if item ['gnue_id'] is not None:
+ iFilter = item.get ('gnue_filter') and \
+ self.__lpClass [item ['gnue_filter'].lower ()]
- if resultSet.firstRecord () is None:
- resultSet.insertRecord ()
- modifier = 0
- module ['gnue_id'] = self.__generateId ()
- else:
- module ['gnue_id'] = resultSet.current.getField ('gnue_id')
- modifier = 1
+ if iFilter != self.__lpFilter.get (name):
+ raise FilterChangeError, (name, item ['_files'])
- if not self.doUpdate (resultSet, module):
- modifier += 1
- stat [modifier] += 1
+ # Integrity checks on properties
+ for (name, item) in self.properties.items ():
+ classDef = self.classes [item ['gnue_class'].lower ()]
- if stat [0] + stat [1]:
- self.connections.commitAll ()
+ # If a property is a reference, make sure the referenced class exists
+ if not item ['gnue_type'] in helpers.BASE_TYPES:
+ refname = item ['gnue_type'].lower ()
+ if not self.__lpClass.get (refname) and not self.classes.get (refname):
+ raise ClassNotFoundError, (item ['gnue_type'], classDef ['_files'])
+ # For an existing class make sure to *not* add NOT NULL properties
+ if classDef ['gnue_id'] is not None:
+ if item ['gnue_id'] is None and not item ['gnue_nullable']:
+ raise NullableError, (name, classDef ['_files'])
+
+
+ # remove obsolete keys from dictionaries
+ for item in self.classes.values ():
+ del item ['_files']
+
+
+ # ===========================================================================
+
+ # ---------------------------------------------------------------------------
+ # Update/add modules to the class repository
+ # ---------------------------------------------------------------------------
+
+ def __updateModules (self):
+ """
+ This function updates all modules listed in 'gnue_modules' and populates a
+ lookup-dictionary for *all* modules available.
+
+ @return: True if a commit is needed, False otherwise
+ """
+
+ # Update the result set and the lookup dictionary
+ stat = self.__processResultSet (self.moduleRS, self.modules, ['gnue_name'])
+ self.__getModuleLookup ()
+
print o (u_(" Modules : %(ins)3d inserted, %(upd)3d updated, %(kept)3d "
"unchanged.") \
% {'ins': stat [0], 'upd': stat [1], 'kept': stat [2]})
+ return (stat [0] + stat [1]) > 0
+
# ---------------------------------------------------------------------------
# Update/add classes to the class repository
# ---------------------------------------------------------------------------
- def _updateClasses (self):
+ def __updateClasses (self):
"""
+ This function updates all classes listed in the table 'gnue_class' and it
+ updates the lookup dictionary for all classes available.
+
+ @return: True if a commit is needed, False otherwise
"""
- self._dtsClass = self.__openSource ('gnue_class', ['gnue_id', 'gnue_name',
- 'gnue_module', 'gnue_comment', 'gnue_filter'])
+ # First make sure we have valid 'gnue_module' entries for all classes
+ self.__replaceReferences (self.classes, self.__lpModule, 'gnue_module')
- stat = [0, 0, 0] # inserted, updated, unchanged
-
+ # Then have a look at the filters defined on the classes
filterStack = []
- for (fullName, klass) in self.classes.items ():
- moduleId = self.__findModule (klass ['gnue_module'])
- cond = GConditions.buildConditionFromDict ( \
- {'gnue_name': klass ['gnue_name'],
- 'gnue_module': moduleId})
- resultSet = self._dtsClass.createResultSet (cond)
+ for (fullName, item) in self.classes.items ():
+ if item.has_key ('gnue_filter'):
+ filterName = item ['gnue_filter']
+ filterId = self.__lpClass.get (filterName)
- if resultSet.firstRecord () is None:
- resultSet.insertRecord ()
- modifier = 0
- klass ['gnue_id'] = self.__generateId ()
- else:
- klass ['gnue_id'] = resultSet.current.getField ('gnue_id')
- modifier = 1
-
- # replace the module's name by it's gnue_id
- klass ['gnue_module'] = moduleId
-
- if klass.has_key ('gnue_filter'):
- filterId = self.__findClass (klass ['gnue_filter'])
-
- # The filter class is defined but has no gnue_id yet. So we defer
- # updating the filter-reference
- if filterId is None:
- filterStack.append ((klass ['gnue_id'], klass ['gnue_filter'],
- fullName))
- del klass ['gnue_filter']
+ # If the filter-class is available we can use it
+ if filterId is not None:
+ item ['gnue_filter'] = filterId
else:
- klass ['gnue_filter'] = filterId
+ # since the filter-id will be created later, we add it to the queue
+ # and remove the attribute from the class.
+ filterStack.append ((item, filterName, fullName))
+ del item ['gnue_filter']
- if not self.doUpdate (resultSet, klass):
- modifier += 1
+ # Merge all changes into the current result set
+ cond = ['gnue_module', 'gnue_name']
+ stat = self.__processResultSet (self.classRS, self.classes, cond)
+ needCommit = (stat [0] + stat [1]) > 0
- stat [modifier] += 1
- doCommit = (stat [0] + stat [1]) > 0
+ # Update the lookup dictionary for classes
+ self.__getClassLookup ()
- for (gnue_id, filterName, fullName) in filterStack:
- cond = GConditions.buildConditionFromDict ({'gnue_id': gnue_id})
- resultSet = self._dtsClass.createResultSet (cond)
- if resultSet.firstRecord () is None:
- raise ClassNotFoundError, (fullName)
+ # Process all classes with a 'new' filter class
+ filterChanged = False
- filterId = self.__findClass (filterName)
-
- if self.doUpdate (resultSet, {'gnue_filter': filterId}):
- doCommit = True
+ if filterStack:
+ for (item, filterName, fullName) in filterStack:
+ item ['gnue_filter'] = self.__lpClass.get (filterName)
- if doCommit:
- self.connections.commitAll ()
+ fst = self.__processResultSet (self.classRS, self.classes, cond)
+ filterChanged = fst [1] > 0
print o (u_(" Classes : %(ins)3d inserted, %(upd)3d updated, %(kept)3d "
"unchanged.") \
% {'ins': stat [0], 'upd': stat [1], 'kept': stat [2]})
+ return needCommit or filterChanged
+
# ---------------------------------------------------------------------------
# Update/add Properties to the class repository
# ---------------------------------------------------------------------------
- def _updateProperties (self):
+ def __updateProperties (self):
"""
+ This function updates all properties listed in 'gnue_property'.
+
+ @return: True if a commit is needed, False otherwise
"""
- datasource = self.__openSource ('gnue_property', ['gnue_id', 'gnue_module',
- 'gnue_class', 'gnue_name', 'gnue_type', 'gnue_length', 'gnue_scale',
- 'gnue_nullable', 'gnue_comment'])
+ # Make sure all properties have a valid 'gnue_module' and 'gnue_class'
+ self.__replaceReferences (self.properties, self.__lpModule, 'gnue_module')
+ self.__replaceReferences (self.properties, self.__lpClass, 'gnue_class')
- stat = [0, 0, 0] # inserted, updated, unchanged
+ # Load and update all properties
+ cond = ['gnue_module', 'gnue_class', 'gnue_name']
+ stat = self.__processResultSet (self.propRS, self.properties, cond)
- for prop in self.properties.values ():
- # make sure we have a valid gnue_id for the referenced module
- moduleId = self.__findModule (prop ['gnue_module'])
+ print o (u_(" Properties: %(ins)3d inserted, %(upd)3d updated, %(kept)3d "
+ "unchanged.") \
+ % {'ins': stat [0], 'upd': stat [1], 'kept': stat [2]})
- cond = GConditions.buildConditionFromDict ( \
- {'gnue_name' : prop ['gnue_name'],
- 'gnue_module': moduleId,
- 'gnue_class' : self.classes [prop ['gnue_class']] ['gnue_id']})
- resultSet = datasource.createResultSet (cond)
+ return (stat [0] + stat [1]) > 0
- if resultSet.firstRecord () is None:
- resultSet.insertRecord ()
- modifier = 0
- prop ['gnue_id'] = self.__generateId ()
- else:
- prop ['gnue_id'] = resultSet.current.getField ('gnue_id')
- modifier = 1
- # replace module-name and class-name by their gnue_id's
- prop ['gnue_module'] = moduleId
- prop ['gnue_class'] = self.classes [prop ['gnue_class']] ['gnue_id']
+ # ---------------------------------------------------------------------------
+ # Update/add Procedures to the class repository
+ # ---------------------------------------------------------------------------
- if not self.doUpdate (resultSet, prop):
- modifier += 1
+ def __updateProcedures (self):
+ """
+ This function updates all procedures listed in 'gnue_procedure' and updates
+ the lookup dictionary for them.
- stat [modifier] += 1
+ @return: True if a commit is needed, False otherwise
+ """
- if stat [0] + stat [1]:
- self.connections.commitAll ()
+ # Make sure all procedures have a valid 'gnue_module' and 'gnue_class'
+ self.__replaceReferences (self.procedures, self.__lpModule, 'gnue_module')
+ self.__replaceReferences (self.procedures, self.__lpClass, 'gnue_class')
- print o (u_(" Properties: %(ins)3d inserted, %(upd)3d updated, %(kept)3d "
+ # Load and update all procedures
+ cond = ['gnue_module', 'gnue_class', 'gnue_name']
+ stat = self.__processResultSet (self.procRS, self.procedures, cond)
+ self.__getProcedureLookup ()
+
+ print o (u_(" Procedures: %(ins)3d inserted, %(upd)3d updated, %(kept)3d "
"unchanged.") \
- % {'ins': stat [0], 'upd': stat [1], 'kept': stat [2]})
+ % {'ins': stat [0], 'upd': stat [1], 'kept': stat [2]})
+ return (stat [0] + stat [1]) > 0
+
# ---------------------------------------------------------------------------
- # Update/add Procedures to the class repository
+ # Update/add parameters to the class repository
# ---------------------------------------------------------------------------
- def _updateProcedures (self):
+ def __updateParameter (self):
+ """
+ This function updates all parameters listed in 'gnue_parameter'.
- self._dtsProcedure = self.__openSource ('gnue_procedure', ['gnue_id',
- 'gnue_module', 'gnue_class', 'gnue_name', 'gnue_type', 'gnue_length',
- 'gnue_scale', 'gnue_nullable', 'gnue_comment', 'gnue_code',
- 'gnue_language'])
+ @return: True if a commit is needed, False otherwise
+ """
- stat = [0, 0, 0] # inserted, updated, unchanged
+ # Make sure all parameters have a valid 'gnue_procedure'
+ self.__replaceReferences (self.parameters, self.__lpProcedure,
+ 'gnue_procedure')
- for proc in self.procedures.values ():
- moduleId = self.__findModule (proc ['gnue_module'])
- cond = GConditions.buildConditionFromDict ( \
- {'gnue_name' : proc ['gnue_name'],
- 'gnue_module': moduleId,
- 'gnue_class' : self.classes [proc ['gnue_class']] ['gnue_id']})
- resultSet = self._dtsProcedure.createResultSet (cond)
+ self.parmRS = self.__openSource ('gnue_parameter', ['gnue_id',
+ 'gnue_procedure', 'gnue_name', 'gnue_type', 'gnue_scale',
+ 'gnue_length', 'gnue_comment']).createResultSet ()
- if resultSet.firstRecord () is None:
- resultSet.insertRecord ()
- modifier = 0
- proc ['gnue_id'] = self.__generateId ()
+ # Load and update all parameters
+ cond = ['gnue_procedure', 'gnue_name']
+ stat = self.__processResultSet (self.parmRS, self.parameters, cond)
+
+ print o (u_(" Parameters: %(ins)3d inserted, %(upd)3d updated, %(kept)3d "
+ "unchanged.") \
+ % {'ins': stat [0], 'upd': stat [1], 'kept': stat [2]})
+
+ return (stat [0] + stat [1]) > 0
+
+
+ # ---------------------------------------------------------------------------
+ # Update references in a dictionary
+ # ---------------------------------------------------------------------------
+
+ def __replaceReferences (self, dictionary, lookup, dname):
+ """
+ This function replaces all references in the 'dictionary' using a lookup
+ dictionary. If an item is not available in the lookup dictionary a
+ 'ReferenceLookupError' will be raised (this would point us to a bug).
+
+ @param dictionary: the dictionary to update references in
+ @param lookup: the dictionary to search for the replacement values
+ @param dname: the key to use in 'dictionary' and 'lookup'
+ """
+
+ for item in dictionary.values ():
+ if lookup.has_key (item [dname].lower ()):
+ item [dname] = lookup [item [dname].lower ()]
else:
- proc ['gnue_id'] = resultSet.current.getField ('gnue_id')
- modifier = 1
+ raise ReferenceLookupError, (dname, item [dname])
- # replace module-name and class-name by their gnue_id's
- proc ['gnue_module'] = moduleId
- proc ['gnue_class'] = self.classes [proc ['gnue_class']] ['gnue_id']
+ # ===========================================================================
- if not self.doUpdate (resultSet, proc):
- modifier += 1
+ # ---------------------------------------------------------------------------
+ # Create a new datasource for a given class
+ # ---------------------------------------------------------------------------
- stat [modifier] += 1
+ def __openSource (self, classname, fieldList):
+ """
+ This function creates a new datasource for the given classname with the
+ given fieldlist. The primary key is always 'gnue_id'
- if stat [0] + stat [1]:
- self.connections.commitAll ()
+ @param classname: name of the table to create a datasource for
+ @param fieldList: sequence of fieldnames to use
- print o (u_(" Procedures: %(ins)3d inserted, %(upd)3d updated, %(kept)3d "
- "unchanged.") \
- % {'ins': stat [0], 'upd': stat [1], 'kept': stat [2]})
+ @return: datasource instance
+ """
+ return GDataSource.DataSourceWrapper (self._connections,
+ fieldList,
+ {'name' : "dts_%s" % classname,
+ 'database' : self._database,
+ 'table' : classname,
+ 'primarykey': 'gnue_id'},
+ unicodeMode = True)
+
# ---------------------------------------------------------------------------
- # Update/add Procedures to the class repository
+ # Get a dictionary with all keys listed in tags and values from sObject
# ---------------------------------------------------------------------------
- def _updateParameter (self):
+ def __fetchTags (self, sObject, tags):
+ """
+ This function creates a dictionary with all attributes from sObject listed
+ in tags, where the keys are constructed by 'gnue_%s' % attributename.
- self._dtsParameter = self.__openSource ('gnue_parameter', ['gnue_id',
- 'gnue_procedure', 'gnue_name', 'gnue_type', 'gnue_scale',
- 'gnue_length', 'gnue_comment'])
+ @param sObject: Schema object to retriev attributes from
+ @param tags: list of all attributes to retrieve
- stat = [0, 0, 0] # inserted, updated, unchanged
+ @return: dictionary with the attribute names as keys and their values
+ """
+ res = {}
+ for item in tags:
+ if hasattr (sObject, item):
+ res ["gnue_%s" % item] = getattr (sObject, item)
- for param in self.parameters.values ():
+ return res
- if self.procedures.has_key (param ['gnue_procedure']):
- procId = self.procedures [param ['gnue_procedure']] ['gnue_id']
- else:
- raise ProcedureNotFoundError, (param ['gnue_procedure'])
- cond = GConditions.buildConditionFromDict ( \
- {'gnue_name' : param ['gnue_name'],
- 'gnue_procedure': procId})
- resultSet = self._dtsParameter.createResultSet (cond)
+ # ---------------------------------------------------------------------------
+ # Update a given resultset using a dictionary of records
+ # ---------------------------------------------------------------------------
- if resultSet.firstRecord () is None:
- resultSet.insertRecord ()
- modifier = 0
- param ['gnue_id'] = self.__generateId ()
- else:
- param ['gnue_id'] = resultSet.current.getField ('gnue_id')
- modifier = 1
+ def __processResultSet (self, resultSet, items, condition):
+ """
+ This function iterates over the given resultset and updates all records
+ listed in the given dictionary. All items of the dictionary not listed in
+ the resultset will be inserted as new records.
- # replace module-name and class-name by their gnue_id's
- param ['gnue_procedure'] = procId
+ @param resultSet: the resultset to update
+ @param items: dictionary of records to update the resultset with
+ @param condition: sequence of key-fields used for comparison.
+ @return: triple with number of (inserted, modified, unchanged) records
+ """
- if not self.doUpdate (resultSet, param):
- modifier += 1
+ stat = [0, 0, 0]
+ needPost = False
- stat [modifier] += 1
+ # First we build a mapping using the condition as key for all items
+ mapping = {}
+ for item in items.values ():
+ key = tuple ([item [field] for field in condition])
+ mapping [key] = item
- if stat [0] + stat [1]:
- self.connections.commitAll ()
+ # Now run over all existing records and update the records if necessary
+ rec = resultSet.firstRecord ()
+ while rec is not None:
+ key = tuple ([rec.getField (field) for field in condition])
+ if mapping.has_key (key):
+ mapping [key]['gnue_id'] = rec.getField ('gnue_id')
- print o (u_(" Parameters: %(ins)3d inserted, %(upd)3d updated, %(kept)3d "
- "unchanged.") \
- % {'ins': stat [0], 'upd': stat [1], 'kept': stat [2]})
+ post = self.doUpdate (resultSet, mapping [key])
+ needPost |= post
+ stat [1 + [1, 0][post]] += 1
+ # Remove the record from the mapping
+ del mapping [key]
+ rec = resultSet.nextRecord ()
+ # If there are keys left in the mapping, they must be new records
+ for item in mapping.values ():
+ item ['gnue_id'] = self.__generateId ()
+ resultSet.insertRecord ()
+ self.doUpdate (resultSet, item)
+ needPost = True
+ stat [0] += 1
+
+ if needPost:
+ resultSet.post ()
+
+ return stat
+
+
# ---------------------------------------------------------------------------
# Perform an update on the given resultset using a given data dictionary
# ---------------------------------------------------------------------------
def doUpdate (self, resultSet, data):
"""
- This function sets all fields in the current record of the resultset base
- on the key/values given by the data dictionary. It returns TRUE, if a field
- value has been changed, otherwise FALSE. If a field was changed, the record
- gets posted.
+ This function sets all fields in the current record of the resultset based
+ on the key/values given by the data dictionary. It returns True, if a field
+ value has been changed, otherwise False.
@param resultSet: resultset with the current record to be updated
@param data: dictionary with keys and values used for updates
- @return: TRUE if a field has been changed, FALSE if no field has been
+
+ @return: True if a field has been changed, False if no field has been
changed.
"""
doPost = False
@@ -695,45 +1076,10 @@
resultSet.current.setField (key, data [key])
doPost = True
- if doPost:
- resultSet.post ()
-
return doPost
# ---------------------------------------------------------------------------
- # Find a module by name
- # ---------------------------------------------------------------------------
-
- def __findModule (self, module):
- if self.modules.has_key (module):
- return self.modules [module]['gnue_id']
- else:
- mc = GConditions.buildConditionFromDict ({'gnue_name': module})
- rs = self._dtsModules.createResultSet (mc)
- if rs.firstRecord () is None:
- raise ModuleNotFoundError, (module)
- return rs.current.getField ('gnue_id')
-
-
- # ---------------------------------------------------------------------------
- # Find the gnue-id of a given class
- # ---------------------------------------------------------------------------
-
- def __findClass (self, classname):
- if self.classes.has_key (classname):
- return self.classes [classname]['gnue_id']
- else:
- (module, name) = Namespace.splitName (classname)
- mc = GConditions.buildConditionFromDict ( \
- {'gnue_module': self.__findModule (module), 'gnue_name': name})
- rs = self._dtsClass.createResultSet (mc)
- if rs.firstRecord () is None:
- raise ClassNotFoundError, (classname)
-
- return rs.current.getField ('gnue_id')
-
- # ---------------------------------------------------------------------------
# Generate a new object id
# ---------------------------------------------------------------------------
@@ -749,4 +1095,4 @@
if __name__ == "__main__":
- gcdConverter ().run ()
+ gcdClient ().run ()
Modified: trunk/gnue-appserver/src/geasConfiguration.py
===================================================================
--- trunk/gnue-appserver/src/geasConfiguration.py 2005-02-02 12:01:48 UTC
(rev 6937)
+++ trunk/gnue-appserver/src/geasConfiguration.py 2005-02-02 14:48:02 UTC
(rev 6938)
@@ -21,7 +21,10 @@
#
# $Id$
+import os.path
+
from gnue.common.formatting import GTypecast
+from gnue import paths
ConfigOptions = (
@@ -68,6 +71,13 @@
'Typecast' : GTypecast.boolean,
'Default' : True },
+ { 'Name' : 'modulepath',
+ 'Type' : 'Setting',
+ 'Comment' : _('Semicolon-separated list of paths to load modules from'),
+ 'Description': _('Semicolon-separated list of paths to load modules from'),
+ 'Typecast' : GTypecast.text,
+ 'Default' : os.path.join (paths.data, "share", "gnue", "appserver") },
+
{ 'Name' : 'httpdir',
'Type' : 'Setting',
'Comment' : _('Directory for webfrontend http documents'),
Modified: trunk/gnue-appserver/src/geasRpcServer.py
===================================================================
--- trunk/gnue-appserver/src/geasRpcServer.py 2005-02-02 12:01:48 UTC (rev
6937)
+++ trunk/gnue-appserver/src/geasRpcServer.py 2005-02-02 14:48:02 UTC (rev
6938)
@@ -24,6 +24,7 @@
import time
import os
import sys
+import signal
import gnue.paths
@@ -86,7 +87,11 @@
webfrontend is at the same port as XMLRPC.""")],
['loglevel', None, 'loglevel', 1, None, 'loglevel', _(
-"""If set to 1, provides information on data dispatched to the RPC
interface.""")]]
+"""If set to 1, provides information on data dispatched to the RPC
+interface.""")],
+ ['modulepath', 'm', 'modulepath', True, None, 'pathlist',
+ _("Semicolon-separated list of paths to load modules from")]
+ ]
# USE_DATABASE_OPTIONS = 1 # Conflicts with the existing username and password
@@ -95,6 +100,7 @@
# ---------------------------------------------------------------------------
def __init__ (self, connections=None):
+
GServerApp.__init__ (self, connections, 'appserver',
geasConfiguration.ConfigOptions)
self.configurationManager.registerAlias ('gConfig', 'appserver')
@@ -111,6 +117,10 @@
if self.OPTIONS["database"] != None:
cparser.set ('appserver', 'database', self.OPTIONS ["database"])
+ if self.OPTIONS ["modulepath"] is not None:
+ cparser.set ('appserver', 'modulepath', self.OPTIONS ['modulepath'])
+
+
# ---------------------------------------------------------------------------
# Set a list of transports
# ---------------------------------------------------------------------------
@@ -187,6 +197,12 @@
# Daemonize (if appropriate)
GServerApp.run (self)
+ try:
+ signal.signal (signal.SIGHUP, self._hangupServer)
+
+ except AttributeError:
+ pass
+
# Start the server for the different protocolls
for key in servers.keys ():
servers [key].serveAsNewThread ()
@@ -211,10 +227,25 @@
loginhandler.setLoginData (self.OPTIONS ["username"],
self.OPTIONS ["password"])
self.connections.setLoginHandler (loginhandler)
- self.sm = geasSessionManager.geasSessionManager (self.connections)
+ self.sm = geasSessionManager.geasSessionManager (self.connections,
+ gConfig ('modulepath'))
return self.sm
+
# ---------------------------------------------------------------------------
+ # Handle a 'Hangup' signal
+ # ---------------------------------------------------------------------------
+
+ def _hangupServer (self, signal, frame):
+ """
+ This function handles a SIGHUP signal and reloads the class repository.
+ """
+
+ if self.sm:
+ self.sm.updateRepository ()
+
+
+ # ---------------------------------------------------------------------------
# Self test
# ---------------------------------------------------------------------------
Modified: trunk/gnue-appserver/src/geasSessionManager.py
===================================================================
--- trunk/gnue-appserver/src/geasSessionManager.py 2005-02-02 12:01:48 UTC
(rev 6937)
+++ trunk/gnue-appserver/src/geasSessionManager.py 2005-02-02 14:48:02 UTC
(rev 6938)
@@ -27,10 +27,15 @@
import geasAuthentication
import classrep
import geasFilter
+import os.path
+import fnmatch
+import time
-from gnue import appserver
+from gnue import appserver, paths
from gnue.common.datasources import GConnections
-from gnue.common.apps import i18n, GConfig
+from gnue.common.apps import i18n, GConfig, errors
+from gnue.appserver.gcd import readgcd
+from gnue.appserver.gld import readgld
# =============================================================================
# Exceptions
@@ -51,14 +56,16 @@
# Initalize
# ---------------------------------------------------------------------------
- def __init__ (self, connections):
+ def __init__ (self, connections, modulepath = None):
self._connections = connections
self._sessNo = 0
self._sessions = {}
self._buildInternalSession ()
- classrep.init (self)
- self._filter = geasFilter.geasFilter (self)
+ self._modulepath = modulepath or \
+ os.path.join (paths.data, "share", "gnue", "appserver")
+ self.updateRepository (haltOnError = True)
+
cfg = gConfigDict (section = 'appserver')
dbauth = cfg.get ('authentication', 'False')
if dbauth.lower () in ['true', 'yes', 'y']:
@@ -91,6 +98,109 @@
else:
raise SessionNotFoundError, (sess_id)
+
+ # ---------------------------------------------------------------------------
+ # Update the class repository
+ # ---------------------------------------------------------------------------
+
+ def updateRepository (self, haltOnError = False):
+ """
+ This function updates the class repository used by the session manager.
+ First the modulepath will be scanned for gcd- and gld-files. All files
+ found will be integrated and finally the repository get's updated.
+ """
+
+ try:
+ (gcd, gld) = self._scanModulePath ()
+
+ if len (gcd):
+ reader = readgcd.gcdReader (self._connections,
+ gConfig ('database'), gcd)
+ reader.run ()
+
+ modRS = reader.moduleRS
+ clsRS = reader.classRS
+ prpRS = reader.propRS
+ prcRS = reader.procRS
+ else:
+ (modRS, clsRS, prpRS, prcRS) = (None, None, None, None)
+
+ if len (gld):
+ reader = readgld.gldReader (self._connections,
+ gConfig ('database'), gld,
+ modRS, clsRS, prpRS, prcRS)
+ reader.run ()
+
+ except:
+ if haltOnError:
+ raise
+
+ msg = u_("Failed reloading repository: %s") % errors.getException () [3]
+ gDebug (1, msg)
+ print o(msg)
+
+ else:
+ print o(u_("Reloading class repository ..."))
+
+ self._internalSession.rollback ()
+
+ classrep.init (self)
+
+ self._filter = geasFilter.geasFilter (self)
+
+ print o(u_("Class repository loaded"))
+
+
+ # ---------------------------------------------------------------------------
+ # Scan the module path for all GCD- and GLD-files
+ # ---------------------------------------------------------------------------
+
+ def _scanModulePath (self):
+ """
+ This function splits the modulepath by semicolons and scans all parts for
+ gcd- and gld-files.
+
+ @return: tuple of sequences (gcd, gld)
+ """
+
+ parts = [os.path.normcase (p) for p in self._modulepath.split (';')]
+ gcds, glds = [], []
+
+ for root in parts:
+ gDebug (1, "Scanning path '%s'" % root)
+
+ arg = {'gcd': [], 'gld': []}
+ os.path.walk (root, self.__visitPath, arg)
+ gcds.extend (arg ['gcd'])
+ glds.extend (arg ['gld'])
+
+ return (gcds, glds)
+
+
+ # ---------------------------------------------------------------------------
+ # Search a given path for gcd- and gld-files
+ # ---------------------------------------------------------------------------
+
+ def __visitPath (self, arg, dirname, files):
+ """
+ This function get's called by os.path.walk () and processes all files in a
+ given directory.
+ """
+
+ for name in files:
+ fullname = os.path.normpath (os.path.join (dirname, name))
+
+ # Since os.path.walk () does *not* walk symbolic links we start another
+ # run for every linked directory.
+ if os.path.islink (fullname) and os.path.isdir (fullname):
+ os.path.walk (fullname, self.__visitPath, arg)
+ else:
+ if fnmatch.fnmatch (name, '*.gcd'):
+ arg ['gcd'].append (fullname)
+ elif fnmatch.fnmatch (name, '*.gld'):
+ arg ['gld'].append (fullname)
+
+
# ===========================================================================
# official API functions
# ===========================================================================
Modified: trunk/gnue-appserver/src/gld/GLParser.py
===================================================================
--- trunk/gnue-appserver/src/gld/GLParser.py 2005-02-02 12:01:48 UTC (rev
6937)
+++ trunk/gnue-appserver/src/gld/GLParser.py 2005-02-02 14:48:02 UTC (rev
6938)
@@ -217,6 +217,8 @@
if not hasattr (self, 'search'): self.search = None
if not hasattr (self, 'info'): self.info = None
+ if hasattr (self, 'pos'): self.position = self.pos
+
if self.search is not None and self.info is not None:
raise RefStyleError, (self.fullName)
Modified: trunk/gnue-appserver/src/gld/readgld.py
===================================================================
--- trunk/gnue-appserver/src/gld/readgld.py 2005-02-02 12:01:48 UTC (rev
6937)
+++ trunk/gnue-appserver/src/gld/readgld.py 2005-02-02 14:48:02 UTC (rev
6938)
@@ -23,6 +23,7 @@
import sys
import os
+import string
import whrandom
from gnue.common.apps import GClientApp, i18n, errors
@@ -31,6 +32,7 @@
from gnue.appserver import VERSION
from gnue.appserver.gld import GLParser
+from gnue.appserver.classrep import Namespace
from gnue.common.apps import GBaseApp
@@ -42,39 +44,46 @@
pass
class Error (errors.ApplicationError):
- pass
+ def __init__ (self, message, files = None):
+ errors.ApplicationError.__init__ (self, message)
+ text = []
-class ModuleNotFoundError (Error):
- def __init__ (self, module):
- msg = u_("Module '%s' not found in class repository.") % module
- Error.__init__ (self, msg)
+ if files:
+ text.append (u_("In file(s) '%s':") % string.join (files, ', '))
+ text.append (message)
+ self.detail = string.join (text, os.linesep)
+
class ClassNotFoundError (Error):
- def __init__ (self, module, classname):
- name = "%s_%s" % (module, classname)
- msg = u_("Class '%s' not found in class repository") % name
- Error.__init__ (self, msg)
+ def __init__ (self, classname, filename = None):
+ msg = u_("Class '%s' not found in class repository") % classname
+ Error.__init__ (self, msg, [filename])
+class DuplicateItemError (Error):
+ def __init__ (self, language, itemname, files):
+ msg = u_("Duplicate definition of element '%(itemname)s' "
+ "for language '%(language)s'") \
+ % {'itemname' : itemname, 'language' : language}
+ Error.__init__ (self, msg, files)
+
class PropertyNotFoundError (Error):
- def __init__ (self, pName, classname):
+ def __init__ (self, classname, pName, filename):
msg = u_("Class '%(classname)s' has no property '%(property)s'") \
- % {'classname': classname,
- 'property': pName}
- Error.__init__ (self, msg)
+ % {'classname': classname, 'property' : pName}
+ Error.__init__ (self, msg, [filename])
class ProcedureNotFoundError (Error):
- def __init__ (self, pName, classname):
+ def __init__ (self, classname, pName, filename):
msg = u_("Class '%(classname)s' has no procedure '%(procedure)s'") \
- % {'classname': classname,
- 'procedure': pName}
- Error.__init__ (self, msg)
+ % {'classname': classname, 'procedure': pName}
+ Error.__init__ (self, msg, [filename])
# =============================================================================
# This class reads a list of gld files and updates the class repository
# =============================================================================
-class gldReader (GClientApp.GClientApp):
+class gldClient (GClientApp.GClientApp):
NAME = "gnue-readgld"
VERSION = VERSION
@@ -87,6 +96,7 @@
# ---------------------------------------------------------------------------
def __init__ (self, connections = None):
+
self.addCommandOption ('connection', 'c', argument='connectionname',
default = "gnue",
help = _("Use the connection <connectionname> for creating the
schema"))
@@ -105,33 +115,35 @@
# ---------------------------------------------------------------------------
- # Verify the given commandline options
+ # Main program
# ---------------------------------------------------------------------------
- def __checkOptions (self):
+ def run (self):
"""
- This function checks wether the given command line arguments and options
- are usefull or not.
+ This is the main function of the whole process. It verifies the given
+ options, loads all layout definitions and then logs into the connection to
+ perform all actions requested.
"""
- self._args = [unicode (a, i18n.encoding) for a in self.ARGUMENTS]
- if not len (self._args):
- raise StartupError, u_("No input file specified.")
+ reader = gldReader (self.connections,
+ self.OPTIONS ['connection'],
+ [unicode (a, i18n.encoding) for a in self.ARGUMENTS])
- self._files = []
+ self._prepareConnection ()
- for filename in self._args:
- self._files.append (openResource (filename))
+ reader.run ()
- if not self.OPTIONS ['connection']:
- raise StartupError, u_("No connection specified.")
-
# ---------------------------------------------------------------------------
- # Open the needed datasources
+ # Prepare the connection
# ---------------------------------------------------------------------------
- def __openDatasources (self):
+ def _prepareConnection (self):
+ """
+ This function makes sure the connection will have the proper username and
+ password set.
+ """
+
connection = self.connections.getConnection (self.OPTIONS ['connection'])
if not connection.parameters.has_key ('username'):
@@ -145,414 +157,508 @@
if self.OPTIONS ['password'] is not None:
connection.parameters ['password'] = self.OPTIONS ['password']
- self.connections.loginToConnection (connection)
- self._dtsModules = self.__getDatasource ('gnue_module', ['gnue_id',
- 'gnue_name'])
- self._dtsClasses = self.__getDatasource ('gnue_class', ['gnue_id',
- 'gnue_name', 'gnue_module'])
+# =============================================================================
+# This class implements an integrator for GNUe Language Definition files
+# =============================================================================
- self._dtsProperty = self.__getDatasource ('gnue_property', ['gnue_id',
- 'gnue_name', 'gnue_module', 'gnue_class'])
+class gldReader:
- self._dtsProcedure = self.__getDatasource ('gnue_procedure', ['gnue_id',
- 'gnue_name', 'gnue_module', 'gnue_class', 'gnue_type'])
+ # ---------------------------------------------------------------------------
+ # Constructor
+ # ---------------------------------------------------------------------------
- self._dtsLabel = self.__getDatasource ('gnue_label', ['gnue_id',
- 'gnue_property', 'gnue_language', 'gnue_position', 'gnue_page',
- 'gnue_label', 'gnue_procedure', 'gnue_search', 'gnue_info'])
+ def __init__ (self, connections, database, files, modRS = None, clsRS = None,
+ prpRS = None, prcRS = None):
+ """
+ Create a new instance of a gcd reader
- self._dtsParameter = self.__getDatasource ('gnue_parameter', ['gnue_id'])
+ @param connections: GConnections instance to be used
+ @param database: name of the connection to use (in connection.conf)
+ @param files: sequence of filenames to integerate
+ @param modRS: resultSet instance for 'gnue_modules'
+ @param clsRS: resultSet instance for 'gnue_class'
+ @param prpRS: resultSet instance for 'gnue_property'
+ @param prcRS: resultSet instance for 'gnue_procedure'
+ """
+ self._connections = connections
+ self._database = database
+ self._filenames = files
+ self._files = []
- # ---------------------------------------------------------------------------
- # Create a new datasource for a given table
- # ---------------------------------------------------------------------------
+ self.__modRS = modRS
+ self.__clsRS = clsRS
+ self.__prpRS = prpRS
+ self.__prcRS = prcRS
- def __getDatasource (self, table, fieldList):
- return GDataSource.DataSourceWrapper ( \
- connections = self.connections,
- attributes = {'name' : "dts_%s" % table,
- 'database' : self.OPTIONS ['connection'],
- 'table' : table,
- 'primarykey': 'gnue_id'},
- fields = fieldList,
- unicodeMode = True)
+ if not len (self._filenames):
+ raise StartupError, u_("No input file specified.")
+ if not self._database:
+ raise StartupError, u_("No connection specified.")
+
+ try:
+ for filename in files:
+ self._files.append (openResource (filename))
+
+ except IOError, err:
+ for item in self._files:
+ item.close ()
+
+ raise StartupError, u_("Unable to open input file: %s") % \
+ errors.getException () [2]
+
+
# ---------------------------------------------------------------------------
- # Main program
+ # Process the given GLD-files
# ---------------------------------------------------------------------------
def run (self):
"""
- This is the main function of the whole process. It verifies the given
- options, loads all layout definitions and then logs into the connection to
- perform all actions requested.
+ This function loads all GLD files, builds the object dictionaries and
+ updates the repository using these dictionaries.
"""
- self.__checkOptions ()
- self.__openDatasources ()
-
- self.modules = {}
- self.classes = {}
+ self.labels = {}
self.messages = {}
- for item in range (len (self._files)):
- print o(u_("Loading gld file '%s' ...") % self._args [item])
+ print _("Loading GNUe language definitions")
+ for ix in xrange (len (self._files)):
+ gDebug (1, "Loading gld file '%s'" % self._filenames [ix])
+
try:
- schema = GLParser.loadFile (self._files [item])
+ self.__currentFile = self._filenames [ix]
+ self.__currentModule = None
+ self.__currentClass = None
+
+ # Load xml file and process all it's objects
+ schema = GLParser.loadFile (self._files [ix])
schema.walk (self.__iterateObjects)
finally:
- self._files [item].close ()
+ self._files [ix].close ()
- self.updateRepository ()
- self.updateMessages ()
+ # Replace all references to modules, classes, properties and procedures
+ self.__replaceReferences ()
+ # and finally update the repository
+ self.__updateLabels ()
+ self.__updateMessages ()
+
# ---------------------------------------------------------------------------
# Iterate over all top level elements
# ---------------------------------------------------------------------------
def __iterateObjects (self, sObject):
"""
- This function iterates over all objects of a GCD tree and processes the
- GCModule and GCClass instances.
+ This function processes all objects of a GLD tree.
@param sObject: current GLD object to be processed
"""
+
if sObject._type == 'GLModule':
- self.__translateModule (sObject)
+ self.__currentModule = sObject
elif sObject._type == 'GLClass':
- self.__translateClass (sObject)
+ self.__currentClass = sObject
+ elif sObject._type in ['GLProperty', 'GLProcedure']:
+ self.__translateItem (sObject)
+
elif sObject._type == 'GLMessage':
self.__translateMessage (sObject)
-
# ---------------------------------------------------------------------------
- # A module translates to a gnue_module data entry only
+ # Process a class item (property or procedure)
# ---------------------------------------------------------------------------
- def __translateModule (self, aModule):
+ def __translateItem (self, item):
"""
- This function adds a dictionary for the given module to the modules data
- block for later update of the class repository.
-
- @param aModule: GLD Layout object to be processed.
+ This function adds an item (label) to the labels dictionary. If the labels
+ is already listed, a DuplicateItemError will be raised.
"""
- mName = aModule.name
- self.modules [mName] = self.fetchTags (aModule, ['language'])
- self.modules [mName] ['gnue_id'] = self.__findModule (mName)
- self.__module = self.modules [mName]
+ # This key makes sure to have no clashes
+ key = "%s::%s.%s" % (self.__currentModule.language,
+ self.__currentClass.fullName, item.fullName)
+ if self.labels.has_key (key):
+ raise DuplicateItemError, \
+ (self.__currentModule.language,
+ "%s.%s" % (self.__currentClass.fullName, item.fullName),
+ [self.labels [key]['_file'], self.__currentFile])
+ self.labels [key] = { \
+ 'gnue_id' : None,
+ 'gnue_class' : self.__currentClass.fullName,
+ 'gnue_property' : item._type == 'GLProperty' and item.fullName or None,
+ 'gnue_procedure': item._type == 'GLProcedure' and item.fullName or
None,
+ 'gnue_language' : self.__currentModule.language,
+ '_file' : self.__currentFile}
+
+ # Add the optional attributes of the XML-tag
+ self.labels [key].update (self.__fetchTags (item,
+ ['page', 'label', 'position', 'search', 'info']))
+
+
# ---------------------------------------------------------------------------
- # A class translation needs a table creation/modification and a data entry
+ # Translate a message into a dictionary for later classrep update
# ---------------------------------------------------------------------------
- def __translateClass (self, aClass):
+ def __translateMessage (self, item):
"""
- This function creates an entry for schema creation of the given class, as
- well as a dictionary for the class repository update.
+ This function adds a message to the message-dictionary. If a message is
+ already listed, a DuplicateItemError will be raised.
+ """
- @param aClass: GLD Class object to be processed.
+ key = "%s::%s" % (item.language, item.fullName)
+ if self.messages.has_key (key):
+ raise DuplicateItemError, \
+ (self.__currentModule.language, item.fullName,
+ [self.messages [key]['_file'], self.__currentFile])
+
+ self.messages [key] = { \
+ 'gnue_language': item.language,
+ 'gnue_name' : item.name,
+ 'gnue_text' : item.getChildrenAsContent (),
+ 'gnue_module' : item.module,
+ '_file' : self.__currentFile}
+
+
+ # ---------------------------------------------------------------------------
+ # Get a dictionary with all keys listed in tags and values from sObject
+ # ---------------------------------------------------------------------------
+
+ def __fetchTags (self, sObject, tags):
"""
+ This function creates a dictionary with all attributes from sObject listed
+ in tags, where the keys are constructed by 'gnue_%s' % attributename.
- if not self.modules.has_key (aClass.module):
- moduleId = self.__findModule (aClass.module)
- self.modules [aClass.module] = {'gnue_id': moduleId}
+ @param sObject: Schema object to retriev attributes from
+ @param tags: list of all attributes to retrieve
- cDef = self.fetchTags (aClass, ['name', 'page', 'label'])
- cDef ['module'] = self.modules [aClass.module]['gnue_id']
- cDef ['gnue_id'] = self.__findClass (aClass.module, cDef ['name'])
- cDef ['properties'] = []
- cDef ['language'] = self.__module ['language']
+ @return: dictionary with the attribute names as keys and their values
+ """
- key = "%s::%s" % (self.__module ['language'], aClass.fullName)
- self.classes [key] = cDef
+ res = {}
+ for item in tags:
+ if hasattr (sObject, item):
+ res ["gnue_%s" % item] = getattr (sObject, item)
- # After processing the class definition, iterate over all it's items
- aClass.walk (self.__iterateClassObjects, defs = cDef)
+ return res
# ---------------------------------------------------------------------------
- # Iterate over all elements of a class definition
+ # Replace references to properties and procedures
# ---------------------------------------------------------------------------
- def __iterateClassObjects (self, sObject, defs):
+ def __replaceReferences (self):
"""
- This function processes all child objects of a GLD class instance.
-
- @param sObject: current GLD object to be processed
- @param defs: a class definition dictionary
+ This function replaces all references to modules, classes, properties and
+ procedures with the apropriate gnue_id's.
"""
- if sObject._type in ['GLProperty', 'GLProcedure']:
- pDef = self.fetchTags (sObject, ['name', 'pos', 'page', 'label', 'info',
- 'search'])
- pDef ['module'] = self.__findModule (sObject.module)
- pDef ['class'] = defs ['gnue_id']
+ # Build lookup dictionaries
+ modules = self.__getModuleLookup ()
+ classes = self.__getClassLookup (modules)
+ properties = self.__getPrLookup (self.__prpRS, 'gnue_property')
+ procedures = self.__getPrLookup (self.__prcRS, 'gnue_procedure')
- if sObject._type == 'GLProperty':
- try:
- pDef ['gnue_id'] = self.__findProperty (pDef, sObject._parent)
- pDef ['relation'] = 'gnue_property'
+ # Replace references for all labels
+ for label in self.labels.values ():
+ classname = label ['gnue_class']
+ classid = classes.get (classname)
+ if classid is None:
+ raise ClassNotFoundError, (classname, label ['_file'])
- except PropertyNotFoundError:
- pDef ['gnue_id'] = self.__findCalculated (pDef, sObject._parent)
- pDef ['relation'] = 'gnue_procedure'
+ del label ['gnue_class']
- else:
- pDef ['gnue_id'] = self.__findProcedure (pDef, sObject._parent)
- pDef ['relation'] = 'gnue_procedure'
-
- defs ['properties'].append (pDef)
+ if label ['gnue_property'] is not None:
+ (module, name) = Namespace.splitName (label ['gnue_property'])
+ key = "%s.%s_%s" % (classid, modules [module], name)
+ ckey = "%s.%s_get%s" % (classid, modules [module], name)
+ if properties.has_key (key):
+ label ['gnue_property'] = properties [key]
+ label ['gnue_procedure'] = None
+ elif procedures.has_key (ckey):
+ label ['gnue_procedure'] = procedures [ckey]
+ label ['gnue_property'] = None
- # ---------------------------------------------------------------------------
- # Translate a message into a dictionary for later classrep update
- # ---------------------------------------------------------------------------
+ else:
+ raise PropertyNotFoundError, \
+ (classname, label ['gnue_property'], label ['_file'])
- def __translateMessage (self, sObject):
- self.messages ["%s::%s" % (sObject.language, sObject.fullName)] = { \
- 'gnue_language': sObject.language,
- 'gnue_name' : sObject.name,
- 'gnue_text' : sObject.getChildrenAsContent (),
- 'gnue_module' : sObject.module}
+ elif label ['gnue_procedure'] is not None:
+ (module, name) = Namespace.splitName (label ['gnue_procedure'])
+ key = "%s.%s_%s" % (classid, modules [module], name)
+ ckey = "%s.%s_get%s" % (classid, modules [module], name)
+ ref = procedures.get (key) or procedures.get (ckey)
+ if ref is None:
+ raise ProcedureNotFoundError, \
+ (classname, label ['gnue_procedure'], label ['_file'])
+ label ['gnue_procedure'] = ref
- # ---------------------------------------------------------------------------
- # Find a class by name and module
- # ---------------------------------------------------------------------------
+ del label ['_file']
- def __findClass (self, module, classname):
- moduleId = self.modules [module]['gnue_id']
- mc = GConditions.buildConditionFromDict ({'gnue_name' : classname,
- 'gnue_module': moduleId})
- rs = self._dtsClasses.createResultSet (mc)
- if rs.firstRecord () is None:
- raise ClassNotFoundError, (module, classname)
- return rs.current.getField ('gnue_id')
+ # Replace references for all messages
+ for message in self.messages.values ():
+ message ['gnue_module'] = modules [message ['gnue_module']]
+ del message ['_file']
+
# ---------------------------------------------------------------------------
- # Find a module by name
+ # Create a lookup dictionary for modules
# ---------------------------------------------------------------------------
- def __findModule (self, module):
- if self.modules.has_key (module) and \
- self.modules [module].has_key ('gnue_id'):
- return self.modules [module]['gnue_id']
- else:
- mc = GConditions.buildConditionFromDict ({'gnue_name': module})
- rs = self._dtsModules.createResultSet (mc)
- if rs.firstRecord () is None:
- raise ModuleNotFoundError, (module)
+ def __getModuleLookup (self):
+ """
+ This function creates a lookup dictionary for modules. It's a mapping for
+ id to name and vice versa.
- return rs.current.getField ('gnue_id')
+ @result: dictionary with id-to-name and name-to-id maps for modules
+ """
+
+ result = {}
+ if self.__modRS is None:
+ src = self.__openSource ('gnue_module', ['gnue_id', 'gnue_name'])
+ self.__modRS = src.createResultSet ()
- # ---------------------------------------------------------------------------
- # Find a property by name, module- and class-id
- # ---------------------------------------------------------------------------
+ rec = self.__modRS.firstRecord ()
+ while rec is not None:
+ name = rec.getField ('gnue_name')
+ gid = rec.getField ('gnue_id')
- def __findProperty (self, pDef, parent):
- mc = GConditions.buildConditionFromDict ({'gnue_module': pDef ['module'],
- 'gnue_class': pDef ['class'],
- 'gnue_name': pDef ['name']})
- rs = self._dtsProperty.createResultSet (mc)
- if rs.firstRecord () is None:
- raise PropertyNotFoundError, (pDef ['name'], parent.fullName)
+ result [gid] = name
+ result [name] = gid
- return rs.current.getField ('gnue_id')
+ rec = self.__modRS.nextRecord ()
+ return result
- # ---------------------------------------------------------------------------
- # Find a procedure by name, module- and class-id
- # ---------------------------------------------------------------------------
- def __findProcedure (self, pDef, parent):
- mc = GConditions.buildConditionFromDict ({'gnue_module': pDef ['module'],
- 'gnue_class': pDef ['class'],
- 'gnue_name': pDef ['name']})
- rs = self._dtsProcedure.createResultSet (mc)
- if rs.firstRecord () is None:
- raise ProcedureNotFoundError, (pDef ['name'], parent.fullName)
- return rs.current.getField ('gnue_id')
-
-
# ---------------------------------------------------------------------------
- # Find a calculated property by name-, module- and class-id
+ # Create a lookup dictionary for classes
# ---------------------------------------------------------------------------
- def __findCalculated (self, pDef, parent):
- mc = GConditions.buildConditionFromDict (\
- {'gnue_module': pDef ['module'],
- 'gnue_class' : pDef ['class'],
- 'gnue_name' : "get%s" % pDef ['name']})
+ def __getClassLookup (self, modules):
+ """
+ This function creates a lookup dictionary for classes, where the full
+ qualified name is the key and the gnue_id is the value.
- rs = self._dtsProcedure.createResultSet (mc)
- if rs.firstRecord () is None:
- raise PropertyNotFoundError, (pDef ['name'], parent.fullName)
+ @param modules: lookup dictionary for modules
+ @return: dictionary with class mapping
+ """
- if rs.current.getField ('gnue_type') is None:
- raise PropertyNotFoundError, (pDef ['name'], parent.fullName)
+ result = {}
- rec = rs.current
+ if self.__clsRS is None:
+ src = self.__openSource ('gnue_class',
+ ['gnue_id', 'gnue_module', 'gnue_name'])
+ self.__clsRS = src.createResultSet ()
+ rec = self.__clsRS.firstRecord ()
while rec is not None:
- mc = GConditions.buildConditionFromDict ( \
- {'gnue_procedure': rec.getField ('gnue_id')})
- prs = self._dtsParameter.createResultSet (mc)
- if prs.firstRecord () is None:
- pDef ['name'] = "get%s" % pDef ['name']
- break
+ module = rec.getField ('gnue_module')
+ name = rec.getField ('gnue_name')
+ key = Namespace.createName (modules [module], name)
+ result [key] = rec.getField ('gnue_id')
- rec = rs.nextRecord ()
+ rec = self.__clsRS.nextRecord ()
- if rec is None:
- raise PropertyNotFoundError, (pDef ['name'], parent.fullName)
+ return result
- return rec.getField ('gnue_id')
# ---------------------------------------------------------------------------
- # Get a dictionary with all keys listed in tags and values from sObject
+ # Create a lookup dictionary for properties or procedures
# ---------------------------------------------------------------------------
- def fetchTags (self, sObject, tags):
+ def __getPrLookup (self, resultSet, table):
"""
- This function creates a dictionary with all attributes from sObject listed
- in tags, where the keys are constructed by 'gnue_%s' % attributename.
+ This function creates a lookup dictionary for properties or procedures.
- @param sObject: Schema object to retriev attributes from
- @param tags: list of all attributes to retrieve
- @return: dictionary with the attribute names as keys and their values
+ @param resultSet: a resultSet instance to use or None if a new resultSet
+ should be created.
+ @param table: if no resultSet is given this tablename will be used to
+ create a new one.
+ @return: dictionary with the property- or procedure-mapping
"""
- res = {}
- for item in tags:
- if hasattr (sObject, item):
- res [item] = getattr (sObject, item)
- return res
+ result = {}
+ if resultSet is None:
+ src = self.__openSource (table,
+ ['gnue_id', 'gnue_module', 'gnue_class', 'gnue_name'])
+ resultSet = src.createResultSet ()
+
+ rec = resultSet.firstRecord ()
+ while rec is not None:
+ key = "%s.%s_%s" % (rec ['gnue_class'], rec ['gnue_module'],
+ rec ['gnue_name'])
+ result [key] = rec ['gnue_id']
+
+ rec = resultSet.nextRecord ()
+
+ return result
+
+
# ---------------------------------------------------------------------------
- # update the class repository
+ # Create a new datasource for a given class
# ---------------------------------------------------------------------------
- def updateRepository (self):
- stat = [0, 0, 0] # inserted, updated, unchanged
+ def __openSource (self, classname, fieldList):
+ """
+ This function creates a new datasource for the given classname with the
+ given fieldlist. The primary key is always 'gnue_id'
- for cDef in self.classes.values ():
- for pDef in cDef ['properties']:
- cond = {pDef ['relation']: pDef ['gnue_id'],
- 'gnue_language' : cDef ['language']}
+ @param classname: name of the table to create a datasource for
+ @param fieldList: sequence of fieldnames to use
+ @return: datasource instance
+ """
- label = {pDef ['relation']: pDef ['gnue_id'],
- 'gnue_language' : cDef ['language'],
- 'gnue_position' : pDef ['pos'],
- 'gnue_page' : pDef ['page'],
- 'gnue_label' : pDef ['label']}
+ return GDataSource.DataSourceWrapper (self._connections,
+ fieldList,
+ {'name' : "dts_%s" % classname,
+ 'database' : self._database,
+ 'table' : classname,
+ 'primarykey': 'gnue_id'},
+ unicodeMode = True)
- if pDef.has_key ('search'):
- label ['gnue_search'] = pDef ['search']
- if pDef.has_key ('info'):
- label ['gnue_info'] = pDef ['info']
- resultSet = self._dtsLabel.createResultSet (cond)
+ # ---------------------------------------------------------------------------
+ # Update all labels in the repository
+ # ---------------------------------------------------------------------------
- if resultSet.firstRecord () is None:
- resultSet.insertRecord ()
- modifier = 0
- label ['gnue_id'] = self.__generateId ()
- else:
- label ['gnue_id'] = resultSet.current.getField ('gnue_id')
- modifier = 1
+ def __updateLabels (self):
+ """
+ Update the class repository with labels read from GLD files.
+ """
- if not self.doUpdate (resultSet, label):
- modifier += 1
+ src = self.__openSource ('gnue_label', ['gnue_id', 'gnue_property',
+ 'gnue_language', 'gnue_position', 'gnue_page', 'gnue_label',
+ 'gnue_procedure', 'gnue_search', 'gnue_info'])
- stat [modifier] += 1
+ # Load and update all labels. To identify a label without gnue_id, use
+ # 'gnue_language', 'gnue_property' and 'gnue_procedure'.
+ rSet = src.createResultSet ()
+ cond = ['gnue_language', 'gnue_property', 'gnue_procedure']
+ stat = self.__processResultSet (rSet, self.labels, cond)
if stat [0] + stat [1]:
- self.connections.commitAll ()
+ self._connections.commitAll ()
- print o(u_(" Labels : %(ins)3d inserted, %(upd)3d updated, %(kept)3d "
+ msg = u_(" Labels : %(ins)3d inserted, %(upd)3d updated, %(kept)3d "
"unchanged.") \
- % {'ins': stat [0], 'upd': stat [1], 'kept': stat [2]})
+ % {'ins': stat [0], 'upd': stat [1], 'kept': stat [2]}
+ gDebug (1, msg)
+ print o(msg)
+
# ---------------------------------------------------------------------------
- # Update/add messages to the class repository
+ # Update all messages in the repository
# ---------------------------------------------------------------------------
- def updateMessages (self):
- attributes = {'name' : "dts_gnueMessage",
- 'database': self.OPTIONS ['connection'],
- 'table' : 'gnue_message'}
+ def __updateMessages (self):
+ """
+ Update the class repository with all messages read from the GLD file.
+ """
- fieldList = ['gnue_id', 'gnue_module', 'gnue_language', 'gnue_name',
- 'gnue_text']
+ src = self.__openSource ('gnue_message',
+ ['gnue_id', 'gnue_module', 'gnue_language', 'gnue_name', 'gnue_text'])
- self._dtsMessage = GDataSource.DataSourceWrapper (
- connections = self.connections,
- attributes = attributes,
- fields = fieldList,
- unicodeMode = True)
+ # Load and update all messages. To identify a message without a gnue_id
+ # 'gnue_language', 'gnue_module' and 'gnue_name' will be used.
+ rSet = src.createResultSet ()
+ cond = ['gnue_language', 'gnue_module', 'gnue_name']
+ stat = self.__processResultSet (rSet, self.messages, cond)
- stat = [0, 0, 0] # inserted, updated, unchanged
+ if stat [0] + stat [1]:
+ self._connections.commitAll ()
- for message in self.messages.values ():
+ msg = u_(" Messages: %(ins)3d inserted, %(upd)3d updated, %(kept)3d "
+ "unchanged.") \
+ % {'ins': stat [0], 'upd': stat [1], 'kept': stat [2]}
- moduleId = self.__findModule (message ['gnue_module'])
+ gDebug (1, msg)
+ print o(msg)
- cond = GConditions.buildConditionFromDict ( \
- {'gnue_module' : moduleId,
- 'gnue_language': message ['gnue_language'],
- 'gnue_name' : message ['gnue_name']})
- resultSet = self._dtsMessage.createResultSet (cond)
- if resultSet.firstRecord () is None:
- resultSet.insertRecord ()
- modifier = 0
- message ['gnue_id'] = self.__generateId ()
- else:
- message ['gnue_id'] = resultSet.current.getField ('gnue_id')
- modifier = 1
+ # ---------------------------------------------------------------------------
+ # Update a given resultset using a dictionary of records
+ # ---------------------------------------------------------------------------
- message ['gnue_module'] = moduleId
+ def __processResultSet (self, resultSet, items, condition):
+ """
+ This function iterates over the given resultset and updates all records
+ listed in the given dictionary. All items of the dictionary not listed in
+ the resultset will be inserted as new records.
- if not self.doUpdate (resultSet, message):
- modifier += 1
+ @param resultSet: the resultset to update
+ @param items: dictionary of records to update the resultset with
+ @param condition: sequence of key-fields used for comparison.
+ @return: triple with number of (inserted, modified, unchanged) records
+ """
- stat [modifier] += 1
+ stat = [0, 0, 0]
+ needPost = False
- if stat [0] + stat [1]:
- self.connections.commitAll ()
+ # First we build a mapping using the condition as key for all items
+ mapping = {}
+ for item in items.values ():
+ key = tuple ([item [field] for field in condition])
+ mapping [key] = item
- print o(u_(" Messages: %(ins)3d inserted, %(upd)3d updated, %(kept)3d "
- "unchanged.") \
- % {'ins': stat [0], 'upd': stat [1], 'kept': stat [2]})
+ # Now run over all existing records and update the records if necessary
+ rec = resultSet.firstRecord ()
+ while rec is not None:
+ key = tuple ([rec.getField (field) for field in condition])
+ if mapping.has_key (key):
+ mapping [key]['gnue_id'] = rec.getField ('gnue_id')
+ post = self.doUpdate (resultSet, mapping [key], True)
+ needPost |= post
+ stat [1 + [1, 0][post]] += 1
+ del mapping [key]
+ rec = resultSet.nextRecord ()
+ # If there are keys left in the mapping, they must be new records
+ for item in mapping.values ():
+ item ['gnue_id'] = self.__generateId ()
+ resultSet.insertRecord ()
+ self.doUpdate (resultSet, item, True)
+ needPost = True
+ stat [0] += 1
+
+ if needPost:
+ resultSet.post ()
+
+ return stat
+
+
# ---------------------------------------------------------------------------
# Perform an update on the given resultset using a given data dictionary
# ---------------------------------------------------------------------------
- def doUpdate (self, resultSet, data):
+ def doUpdate (self, resultSet, data, deferPost = False):
"""
- This function sets all fields in the current record of the resultset base
+ This function sets all fields in the current record of the resultset based
on the key/values given by the data dictionary. It returns TRUE, if a field
value has been changed, otherwise FALSE. If a field was changed, the record
- gets posted.
+ gets posted unless 'deferPost' is set to True.
@param resultSet: resultset with the current record to be updated
@param data: dictionary with keys and values used for updates
+ @param deferPost: if True, this function does not post () the changes. The
+ caller will do this later.
@return: TRUE if a field has been changed, FALSE if no field has been
changed.
"""
@@ -563,7 +669,7 @@
resultSet.current.setField (key, data [key])
doPost = True
- if doPost:
+ if doPost and not deferPost:
resultSet.post ()
return doPost
@@ -589,4 +695,4 @@
# =============================================================================
if __name__ == '__main__':
- gldReader ().run ()
+ gldClient ().run ()
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [gnue] r6938 - in trunk/gnue-appserver: scripts src src/gcd src/gld,
johannes <=