commit-gnue
[Top][All Lists]
Advanced

[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 ()





reply via email to

[Prev in Thread] Current Thread [Next in Thread]