 /*------------------------------------------------------------------------
    File        : BuildBlocksOne
    Purpose     : Build objects for procedures, classes, IPs, methods, functions, etc. for one compile unit
    Syntax      : 
    Description : 
    Author(s)   : Thomas Mercer-Hursh
    Created     : Tue Apr 15 14:51:48 CDT 2014
    Notes       : 
  ----------------------------------------------------------------------*/

using Progress.Lang.*.
using com.cintegrity.ABL2DB.SchemaUtilities.
using com.cintegrity.FileSystem.PathUtilities.
using com.cintegrity.LoggingUtils.FlyWeightLogger.
using com.cintegrity.Proparse.SetEnvironment.
using com.cintegrity.Proparse.SetSchemaFromFile.
using com.joanju.proparse.NodeTypes from assembly.
using com.joanju.proparse.ProParserTokenTypes from assembly.
using org.prorefactor.core.JPNode from assembly.
using org.prorefactor.core.TreeUtils from assembly.
using org.prorefactor.treeparser.ParseUnit from assembly.
using org.prorefactor.treeparser.Symbol from assembly.
using org.prorefactor.treeparser.SymbolScope from assembly.
using org.prorefactor.treeparser.TableBuffer from assembly.

block-level on error undo, throw.

class com.cintegrity.ABL2DB.BuildBlocksOne: 
  define public property chLogicalDataBaseList as character no-undo get. set.
  define public property chAliasList as character no-undo get. set.
  define public property chSchemaDirectory as character no-undo get. set.
  define public property inTotalFiles as integer no-undo get. private set.
  define public property inTotalExceptions as integer no-undo get. private set.
  define public property inTotalCompiled as integer no-undo get. set.
  define public property inTotalBlocks as integer no-undo get. set.
    
  define private variable chCurrentBlockID as character no-undo.
  define private variable chCurrentCompileUnitName as character.
  define private variable chFullPath as character no-undo.
  define private variable inCurrentLine as integer no-undo.
  define private variable lgInBuffers as logical no-undo.
  define private variable lgInFrames as logical no-undo.
  define private variable obSetEnvironment as class SetEnvironment no-undo.
  define private variable obSetSchemaFromFile as class SetSchemaFromFile no-undo.
  define private variable obJavaFile as class java.io.File.
  define private variable obParseUnit as class ParseUnit.
  define private variable obSchemaUtilities as class SchemaUtilities no-undo.
  define private variable obReport as class FlyWeightLogger no-undo. 
  define private variable obPathUtilities as class PathUtilities no-undo.

       /* Note: originally, this table included line number, but we are currently not
          reporting on duplicates, so this has been eliminated pending conversion to
          tracking them by scope. */  
  define private temp-table ttBufferMatch
      field chBufferName as character
      field chDataBaseName as character
      field chDataBaseID as character
      field chTableName as character
      field chTableID as character
      field lgIsDBTable as logical
      field chFileName as character
      index ixByBuffer as unique primary chBufferName
      .
    
 	constructor public BuildBlocksOne ( 
      ipobReport as class FlyWeightLogger
      ): 
    obReport = ipobReport.
	end constructor.

	destructor public BuildBlocksOne ( ):
    empty temp-table ttBufferMatch.
    if valid-object(obSchemaUtilities) then delete object obSchemaUtilities.
    if valid-object(obSetEnvironment) then delete object obSetEnvironment.
    if valid-object(obSetSchemaFromFile) then delete object obSetSchemaFromFile.
	end destructor.

  method public void Initialize():
                                                          /* Initialize for Proparse */
    obSetEnvironment = new SetEnvironment().
    obSetEnvironment:Initialize().

    obSetSchemaFromFile = new SetSchemaFromFile().
    obSetSchemaFromFile:chSchemaDirectory = chSchemaDirectory.
    obSetSchemaFromFile:chLogicalDataBaseList = chLogicalDataBaseList.           
    obSetSchemaFromFile:chAliasList = chAliasList.
    obSetSchemaFromFile:lgDeletePPSchemaFile = false.
    obSetSchemaFromFile:Initialize().

    obPathUtilities = new PathUtilities( ).
    obPathUtilities:Initialize().
    
    obSchemaUtilities = new SchemaUtilities ().
    
    catch eobAppError as Progress.Lang.AppError:
      log-manager:write-message ( eobAppError:ReturnValue ).  
      delete object eobAppError.
    end catch.
  end method.
  
  method public void ProcessOne (
      ipchName as character,
      ipchPackage as character,
      ipchSourceDirectory as character,
      ipchFullPath as character,
      ipchExtension as character
      ):
    define variable mchListFile as character no-undo.
    define variable mchCompileUnitID as character no-undo.
    define variable mchCurrentFullPath as character no-undo.
    define variable mchMangledName as character no-undo.
    
    define buffer bfDiskFile for DiskFile.
    define buffer bfCompileUnit for CompileUnit.
    
    inTotalFiles = inTotalFiles + 1.

    do for bfDiskFile:
      find bfDiskFile no-lock
          where bfDiskFile.chBaseDirectory = ipchSourceDirectory
          and   bfDiskFile.chPackage = ipchPackage
          and   bfDiskFile.chName = ipchName
          no-error.
      if not available ( bfDiskFile )
      then do:
        obReport:WriteMessage ( substitute("BuildBlocksOne: DiskFile at &1 not found in ScanOne", ipchFullPath )).
        inTotalExceptions = inTotalExceptions + 1.
        return.  
      end.     
      else do for bfCompileUnit:
        if bfDiskFile.lgCompilable = no then return.      
        find bfCompileUnit no-lock where bfCompileUnit.chDiskFileID = bfDiskFile.chID no-error.
        if available ( bfCompileUnit )
        then do:
          assign
              inTotalCompiled = inTotalCompiled + 1
              chFullPath = ipchFullPath
              mchListFile = obPathUtilities:ListFile( bfCompileUnit.chListBaseDir, ipchPackage, ipchName ).
              chCurrentCompileUnitName = obPathUtilities:FullPathFromComponents( bfDiskFile.chBaseDirectory, bfDiskFile.chPackage, bfDiskFile.chName ).
              mchMangledName = obPathUtilities:MangledNameFromPath ( chCurrentCompileUnitName ).
              .
            ProcessBuffers( bfDiskFile.chID ).
            ProcessBlocks ( bfCompileUnit.chID, mchListFile, mchMangledName ).
        end. /* available bfCompileUnit */
      end. /* else do */
    end. /* for bfDiskFile */
  end method.

  method private void ProcessBuffers(
      ipchCUID as character
      ):
    define variable minWhich as integer no-undo.
    
    do on stop undo, retry:
      if retry
      then do:
        log-manager:write-message ("STOP condition encountered in BuildBlocksOne", "SYSERROR").
        return.
      end.
    
      do on error undo, leave:    
        obJavaFile = new java.io.File(chFullPath).
        obParseUnit = new ParseUnit(obJavaFile).
        obParseUnit:treeParser01().       
        
        CollectBuffers( obParseUnit ).
    
        catch eobAppError as Progress.Lang.AppError:
          log-manager:write-message ( eobAppError:ReturnValue ).  
          delete object eobAppError.
        end catch.
      
        catch eobSysError as Progress.Lang.SysError :
          define variable inWhich as integer no-undo.
          log-manager:write-message(substitute("Unexpected Exception: &1", eobSysError:getMessage(1)), "SYSERROR").
          if eobSysError:NumMessages > 1
          then do inWhich = 2 to eobSysError:NumMessages:
            log-manager:write-message(substitute("Unexpected Exception (cont.): &1", eobSysError:getMessage(inWhich)), "SYSERROR").
          end.
          do inWhich = 1 to num-entries(eobSysError:CallStack,"~n"):
            log-manager:write-message(entry(inWhich,eobSysError:CallStack,"~n"),"CALLSTACK").
          end.
          delete object eobSysError no-error.
        end catch.
        
        catch eobOtherError as Progress.Lang.Error :
          log-manager:write-message(substitute("Other error in &1 line &2: &3", chCurrentCompileUnitName, inCurrentLine, eobOtherError:getMessage(1))).
          if eobOtherError:NumMessages > 1
          then do minWhich = 2 to eobOtherError:NumMessages:
            log-manager:write-message(substitute("Other error (cont.): &1", eobOtherError:getMessage(minWhich)), "OtherERROR").
          end.
          do minWhich = 1 to num-entries(eobOtherError:CallStack,"~n"):
            log-manager:write-message(entry(minWhich,eobOtherError:CallStack,"~n"),"CALLSTACK").
          end.
          delete object eobOtherError no-error. 
        end catch.
      end. /* do on error */
    end. /* do on stop */

  end method.

  method private void CollectBuffers(
      ipobParseUnit as class ParseUnit
      ): 
    define variable molBufferList as class java.util.ArrayList no-undo.
    define variable mobIterator as class java.util.Iterator no-undo.
    define variable mobSymbol as class Symbol no-undo.
    define variable mobTableBuffer as class TableBuffer no-undo.
    define variable mobTable as class org.prorefactor.core.schema.Table no-undo.
    define variable minWhich as integer no-undo.
    define variable mlgDefined as logical no-undo.
    define variable mlgIndirect as logical no-undo.
    define variable mobToken as JPNode no-undo.
    define variable mobChildToken as JPNode no-undo.
    define variable mchTokenType as character no-undo.
    define variable mlgIsNew as logical no-undo initial false.
    define variable mlgIsGlobal as logical no-undo initial false.
    define variable mchDataType as character no-undo.
    define variable mchName as character no-undo.
    define variable mchObjectType as character no-undo.
    define variable mlgIsDBTable as logical no-undo initial false.
    define variable mchBufferName as character no-undo.
    define variable mchRecordName as character no-undo.
    define variable mchDataBaseName as character no-undo.
    define variable mchTableName as character no-undo.
    define variable mchDatabaseID as character no-undo.
    define variable mchTableID as character no-undo.
    define variable mchFilename as character no-undo.
    define variable minStoreType as integer no-undo.
    define variable mchStoreType as character no-undo.
        
    if (ipobParseUnit eq ?) then return.  
    
    molBufferList = ipobParseUnit:getRootScope():getAllSymbolsDeep( ).
    mobIterator = molBufferList:iterator().
    
    do while mobIterator:hasNext():
      mobSymbol = cast( mobIterator:next(), Symbol ).
      
      if NodeTypes:getTypeName(mobSymbol:getProgressType()) <> "BUFFER" then next.
      
      mobTableBuffer = cast( mobSymbol, TableBuffer ).
      mobTable = mobTableBuffer:getTable().
      mchTableName = mobTable:getName().
      minStoreType = mobTable:getStoretype().
      case minStoreType:
        when 1102 then mchStoreType = "DBTable".
        when 1103 then mchStoreType = "TempTable".
        when 1104 then mchStoreType = "WorkTable".
      end case.
      if mobSymbol:getDefineNode() <> ? then mlgDefined = yes. else mlgDefined = no.
      if mobSymbol:getIndirectDefineIdNode() <> ? then mlgIndirect = yes. else mlgIndirect = no.

      if mlgDefined
      then do:
        mlgIsNew = false.
        mlgIsGlobal = false.
        mobToken = mobSymbol:getDefineNode():firstChild().
        mchTokenType = mobToken:getText().
        if  mchTokenType = "NEW"
        then do:
          mlgIsNew = true.
          mobChildToken = mobToken:firstChild().
          if NodeTypes:getTypeName(mobChildToken:GetType()) = "GLOBAL"
          then do:
            mlgIsGlobal = true.
            mobChildToken = mobChildToken:nextSibling().
            mobToken = mobToken:nextSibling().
            mchTokenType = NodeTypes:getTypeName(mobToken:GetType()).      
          end. /* if global */
          else do:
            if NodeTypes:getTypeName(mobChildToken:GetType()) = "SHARED"
            then do:
              mobToken = mobToken:nextSibling().
              mchTokenType = NodeTypes:getTypeName(mobToken:GetType()).      
            end.
          end. /* not global */
        end. /* new */
        else do:
          if mchTokenType = "SHARED" 
          then do:
            mobToken = mobToken:nextSibling().
            mchTokenType = NodeTypes:getTypeName(mobToken:GetType()).
          end.
        end.         
        mchObjectType = NodeTypes:getTypeName(mobToken:GetType()).        
      end.  /* Defined */
      /*
      if mchObjectType = "TEMPTABLE" or mchObjectType = "WORKTABLE" then next.
      */
      if mlgDefined or mlgIndirect
      then do:
        mchRecordName = mobSymbol:fullName().
        AddEntry( mchRecordName, mchTableName, mchFilename ). 
      end. /* Defined or Indirect */  
    end. /* hasNext */
    
    catch eobOtherError as Progress.Lang.Error :
      log-manager:write-message(substitute("Error: &1", eobOtherError:getMessage(1))).  
      if eobOtherError:NumMessages > 1
      then do minWhich = 2 to eobOtherError:NumMessages:
        log-manager:write-message(substitute("Error (cont.): &1", eobOtherError:getMessage(minWhich)) ).
      end.
      do minWhich = 1 to num-entries(eobOtherError:CallStack,"~n"):
        log-manager:write-message(entry(minWhich,eobOtherError:CallStack,"~n")).
      end.
      delete object eobOtherError no-error.
    end catch.
     
  end method. /* CollectBuffers */  
  
  method private void AddEntry(
      ipchRecordName as character,
      ipchTableName as character,
      ipchFilename as character
      ): 
    define variable mlgIsDBTable as logical no-undo initial false.
    define variable mchBufferName as character no-undo.
    define variable mchDataBaseName as character no-undo.
    define variable mchDatabaseID as character no-undo.
    define variable mchTableID as character no-undo.

    define buffer mbfttBufferMatch for ttBufferMatch.
    define buffer mbfDB_DB for DB_DB.
    define buffer mbfDB_Table for DB_Table.
    
    if num-entries( ipchRecordName, "." ) = 2
    then do:
      mlgIsDBTable = true.
      mchDatabaseName = entry( 1, ipchRecordName, "." ).
      mchBufferName = entry( 2, ipchRecordName, "." ).
      find mbfDB_DB no-lock where mbfDB_DB.chDB_Name = mchDatabaseName no-error.
      if available( mbfDB_DB ) 
      then do:
        mchDatabaseID = mbfDB_DB.chID.
        find mbfDB_Table 
            where mbfDB_Table.chDBID = mchDatabaseID 
            and   mbfDB_Table.chTableName = ipchTableName
            and   mbfDB_Table.chOwner = "PUB":u 
            no-lock no-error.
        if available( mbfDB_Table ) then mchTableID = mbfDB_Table.chID.
      end.          
    end.  /* num-entries = 2 */
    else do:
      mlgIsDBTable = false.
      mchBufferName = ipchRecordName.
    end.

    do for mbfttBufferMatch:
      find first mbfttBufferMatch 
          where mbfttBufferMatch.chBufferName = mchBufferName 
          no-error.
      if not available( mbfttBufferMatch )
      then do:
      create mbfttBufferMatch.
        assign                                                         
            mbfttBufferMatch.chBufferName = mchBufferName               
            mbfttBufferMatch.chTableName = ipchTableName      
            mbfttBufferMatch.chTableID = mchTableID           
            mbfttBufferMatch.chDataBaseName = mchDataBaseName
            mbfttBufferMatch.chDataBaseID = mchDataBaseID          
            mbfttBufferMatch.lgIsDBTable = mlgIsDBTable     
            mbfttBufferMatch.chFilename = ipchFilename            
            .
      end.
      else do:
        /*  No report for now.
        obReport:WriteMessage ( substitute("BufferSet: Duplicate buffer names in file &1 for buffer &2 at line &3 and line &4", chFullPath, mchBufferName, mbfttBufferMatch.inLineNo, inCurrentLine )).
        */        
      end.
    end. /* for mbfttBufferMatch */        
  end. /* AddEntry */

  method private void ProcessBlocks (
      ipchCUID as character,
      ipchListFile as character,
      ipchMangledName as character
      ):
    define variable mchPackage as character no-undo.
    define variable mchName as character no-undo.
    define variable minLine as integer no-undo.
    define variable mchALine as character no-undo.
    define variable mlcListFile as longchar no-undo.
    define variable minTotalLines as integer no-undo.
    define variable mlgInSummary as logical no-undo.
    define variable minStart as integer no-undo initial 1.
    define variable minEnd as integer no-undo initial 1.
    
    define buffer bfCU_Block for CU_Block.
    define buffer bfCU_Buffer for CU_Buffer.
    define buffer bfCU_Frame for CU_Frame.
    define buffer bfCompileUnit for CompileUnit.
    
    copy-lob from file ipchListFile to mlcListFile.
    minTotalLines = num-entries( mlcListFile, "~n" ).

    do while minStart > 0:
      minEnd = index( mlcListFile, "~n", minStart ).

      if minEnd > 0
      then 
        assign
            mchALine = substring( mlcListFile, minStart, minEnd - minStart )
            minStart = minEnd + 1
            .
      else
        assign
            mchALine = substring( mlcListFile, minStart )
            minStart = 0
            .
      if mlgInSummary 
      then do:
        if substring(mchAline, 1, 3) = "..."
        then do:
          ProcessBlockLine( mchALine, ipchCUID, ipchMangledName ).
          lgInBuffers = false.
          lgInFrames = false.
        end.
        else if substring(mchAline, 1, 12) = "    Buffers:"
        then do:
          ProcessBufferLine( mchALine ).
          lgInBuffers = true.
          lgInFrames = false.
        end.
        else if substring(mchAline, 1, 12) = "    Frames: "
        then do:
          ProcessFrameLine( mchALine ).
          lgInBuffers = false.
          lgInFrames = true.
        end.
        else if substring(mchAline, 1, 12) = "            " and lgInBuffers
        then ProcessBufferLine( mchALine ).
        else if substring(mchAline, 1, 12) = "            " and lgInFrames 
        then ProcessFrameLine( mchALine ).
        /* Other possible lines are page headers, blank lines, and formfeeds */
      end.
      else do:
        mlgInSummary = mchALine begins "-------------------- ---- ----------- ---- --------------------------------".
      end. 
    end.

  end method.

  method private void ProcessBlockLine(
      ipchALine as character,
      ipchCUID as character,
      ipchMangledName as character
      ):
    define variable mchFileName as character no-undo.
    define variable mchLineNo as character no-undo.
    define variable minLineNo as integer no-undo.
    define variable mchBlockType as character no-undo.
    define variable mchTrans as character no-undo.
    define variable mlgTrans as logical no-undo.
    define variable mchBlockLabel as character no-undo.
    define variable mchMangledIncludeName as character no-undo.
    define variable mchIncludeFullPath as character no-undo.
    define variable mlgIncludeFound as logical no-undo.
    define variable mlgMainBlockFound as logical no-undo.
    
    define buffer bfDiskFile for DiskFile.
    define buffer bfCU_Block for CU_Block.
    define buffer bfIncludeUnit for IncludeUnit.   
    define buffer bfIncludeReference for IncludeReference. 
        
    assign
        mchFileName   = trim(substring( ipchALine,  1, 20 ))
        mchLineNo     = trim(substring( ipchALine, 22,  4 ))
        mlgMainBlockFound = false
        mlgIncludeFound = false
        .
    if mchLineNo = "-(":U  
    then do:
      assign
          minLineNo = 32768
          mchBlockType  = trim(substring( ipchALine, 27, 11 ))
          mchTrans      = trim(substring( ipchALine, 39,  4 ))
          mchBlockLabel = trim(substring( ipchALine, 44, 32 ))
          .
      if trim(mchTrans) = "Yes" then mlgTrans = true.    
    end.
    else do:
      if mchLineNo = trim(substring( ipchALine, 22,  5 ))  /* Line # is 4 digits 0 to 9999 or -1 to -999 */
      then do:
        minLineNo = integer (mchLineNo).
        if minLineNo < 0                                   /* -999 to -1 = 64537 to 65535 */
        then minLineNo = 65536 - minLineNo.
        assign
            mchBlockType  = substring( ipchALine, 27, 11 )
            mchTrans      = substring( ipchALine, 39,  4 )
            mchBlockLabel = substring( ipchALine, 44, 32 )
            .
        if trim(mchTrans) = "Yes" then mlgTrans = true.      
      end.
      else do:
        mchLineNo = trim(substring( ipchALine, 22,  5 )).
        if mchLineNo = trim(substring( ipchALine, 22,  6 ))  /* Line # is 5 digits 10000 to 32767 or -1000 to -9999 */
        then do:
          minLineNo = integer (mchLineNo).
          if minLineNo < 0                                   /* -9999 to -1000 = 55537 to 64536 */
          then minLineNo = 65536 - minLineNo.
          assign
              mchBlockType  = substring( ipchALine, 28, 11 )
              mchTrans      = substring( ipchALine, 40,  4 )
              mchBlockLabel = substring( ipchALine, 45, 32 )
              .
          if trim(mchTrans) = "Yes" then mlgTrans = true.                 
        end.
        else do:
          mchLineNo = trim(substring( ipchALine, 22,  6 )). /* Line # is 6 digits -10000 to -32767 */
          if minLineNo < 0                                  /* -10000 to -32767 = 32769 to 55536 */
          then minLineNo = 65536 - minLineNo.               /* Must be negative to be 6 digits */
          assign
              mchBlockType  = substring( ipchALine, 29, 11 )
              mchTrans      = substring( ipchALine, 41,  4 )
              mchBlockLabel = substring( ipchALine, 46, 32 )
              .
          if trim(mchTrans) = "Yes" then mlgTrans = true.             
        end.
      end.
    end.

    if minLineNo = 0 then mchBlockType = "WholeCU". 
    
    if mchFileName = ipchMangledName            /* Block is in main program */
    then do for bfCU_Block:
      create bfCU_Block.
      assign
          bfCU_Block.chID = guid
          bfCU_Block.chCompileUnitID = ipchCUID
          bfCU_Block.chBlockType = mchBlockType
          bfCU_Block.inLineStart = minLineNo
          bfCU_Block.lgIsTransaction = mlgTrans
          bfCU_Block.chBlockLabel = mchBlockLabel
          chCurrentBlockID = bfCU_Block.chID
          mlgMainBlockFound = true
          inTotalBlocks = inTotalBlocks + 1
          .
    end.
    else do for bfIncludeUnit, bfDiskFile:    /* Block is in some include file */
      mlgIncludeFound = false.
      FIND-INCLUDE:
      for each bfIncludeReference no-lock where bfIncludeReference.chCompileUnitID = ipchCUID:
        find bfIncludeUnit no-lock where bfIncludeUnit.chID = bfIncludeReference.chIncludeUnitID.
        find bfDiskFile no-lock where bfDiskFile.chID = bfIncludeUnit.chDiskFileID.
        mchIncludeFullPath = obPathUtilities:FullPathFromComponents( bfDiskFile.chBaseDirectory, bfDiskFile.chPackage, bfDiskFile.chName ).
        mchMangledIncludeName = obPathUtilities:MangledNameFromPath ( mchIncludeFullPath ).
        if mchFileName = mchMangledIncludeName    
        then do for bfCU_Block:
          create bfCU_Block.
          assign
              bfCU_Block.chID = guid
              bfCU_Block.chCompileUnitID = ipchCUID
              bfCU_Block.chBlockType = mchBlockType
              bfCU_Block.inLineStart = minLineNo
              bfCU_Block.lgIsTransaction = mlgTrans
              bfCU_Block.chBlockLabel = mchBlockLabel
              chCurrentBlockID = bfCU_Block.chID
              inTotalBlocks = inTotalBlocks + 1
              mlgIncludeFound = true.
              .
          leave FIND-INCLUDE.
        end. /* of bfCU_Block */
      end. /* of for bIncludeReference */
    end. /* of else */
    if (mlgIncludeFound = false) and (mlgMainBlockFound = false)
    then do:
      obReport:WriteMessage ( substitute("File '&1' not found in compile unit &2 ", mchFileName, chCurrentCompileUnitName ) ).
      inTotalExceptions = inTotalExceptions + 1.
    end.
  end method.

  method private void ProcessBufferLine(
      ipchALine as character
      ):
    define variable mchFullBufferName as character no-undo.
    define variable mchBufferName as character no-undo.
    define variable mchTableName as character no-undo.   
    define variable mchTableID as character no-undo.
    define variable mchDatabaseName as character no-undo.
    define variable mchDatabaseID as character no-undo.    
    define variable mlgIsDBTable as logical no-undo initial false.
    
    define buffer mbfDB_DB for DB_DB.
    define buffer mbfDB_Table for DB_Table.
    define buffer mbfCU_Buffer for CU_Buffer.
    define buffer mbfttBufferMatch for ttBufferMatch.

    mchFullBufferName = trim(substring(ipchALine, 14)).
    
    if num-entries(mchFullBufferName,".") > 1    /* Should be qualified name for DB buffer */
    then do for mbfttBufferMatch:
      mchDataBaseName = entry(1, mchFullBufferName, ".").
      mchBufferName = entry(2, mchFullBufferName, ".").
      find mbfttBufferMatch
          where mbfttBufferMatch.chBufferName = mchBufferName
          no-error.
      if available( mbfttBufferMatch )
      then do:
        if mbfttBufferMatch.lgIsDBTable = true  /* Should be true if buffer name qualified */
        then do:
          mchDataBaseName = mbfttBufferMatch.chDatabaseName.
          mchTableName = mbfttBufferMatch.chTableName.
          mchDatabaseID = mbfttBufferMatch.chDatabaseID.
          mchTableID = mbfttBufferMatch.chTableID.
        end.
        if substring(mchTableName, 1, 1) = "_"    /* Is it a metaschema table? */
        then mchDatabaseName = obSchemaUtilities:MassageMetaDBName ( mchDatabaseName ).
      end.
      else do:  /* Might be direct table reference */
        mchTableName = mchBufferName.
        if substring(mchTableName, 1, 1) = "_"    /* Is it a metaschema table? */
        then mchDatabaseName = obSchemaUtilities:MassageMetaDBName ( mchDatabaseName ).
        find mbfDB_DB no-lock where mbfDB_DB.chDB_Name = mchDataBaseName no-error.
        if available( mbfDB_DB )
        then do:        
          mchDataBaseID = mbfDB_DB.chID.
          find first mbfDB_Table no-lock 
            where mbfDB_Table.chDBID = mchDataBaseID
            and   mbfDB_Table.chTableName = mchTableName 
            no-error.
          if available( mbfDB_Table ) then mchTableID = mbfDB_Table.chID.          
        end.
        else do:
          obReport:WriteMessage ( substitute("BufferSet: Apparent database buffer &1 cannot be matched to buffer or table in compile unit &2", mchFullBufferName, chCurrentCompileUnitName )).          
        end.
      end.

      if mchTableName = "" or mchDatabaseName = ""
      then obReport:WriteMessage ( substitute("BufferSet: Blank database (&1) or table (&2) name for buffer &3 in compile unit &4", mchDatabaseName, mchTableName, mchFullBufferName, chCurrentCompileUnitName )).
       
      find mbfDB_DB no-lock where mbfDB_DB.chID = mchDatabaseID no-error.
      if not available (mbfDB_DB)
      then do:
        obReport:WriteMessage ( substitute("BufferSet: Database &1 not found for buffer &2 ( &3 ) in compile unit &4", mchDatabaseName, mchTableName, mchFullBufferName, chCurrentCompileUnitName ) ).
        inTotalExceptions = inTotalExceptions + 1.
        return.
      end.
      else do on error undo, leave:
        find mbfDB_Table no-lock where mbfDB_Table.chDBID = mbfDB_DB.chID and mbfDB_Table.chID = mchTableID no-error.
        if not available( mbfDB_Table)
        then do: 
          obReport:WriteMessage ( substitute("BufferSet: Table &1 in Database &2 not found for buffer &3 in compile unit &4", mchTableName, mchDatabaseName, mchFullBufferName, chCurrentCompileUnitName ) ).
          inTotalExceptions = inTotalExceptions + 1.
          return.
        end.
      end.
    end.  /* Entries more than 1 */
    else do: /* Entry not qualified with DB name so buffer presumed to be for TT */
      mchBufferName = mchFullBufferName.
      mlgIsDBTable = false.
    end.
        
    do for mbfCU_Buffer:
      create mbfCU_Buffer.
      assign
          mbfCU_Buffer.chID = guid
          mbfCU_Buffer.chBlockID = chCurrentBlockID
          mbfCU_Buffer.lgIsDBTable = mlgIsDBTable
          mbfCU_Buffer.chBufferName = mchBufferName
          mbfCU_Buffer.chTableID = mchTableID
          .
    end.
  end.
  
  method private void ProcessFrameLine(
      ipchALine as character
      ):
    define variable mchFrameName as character no-undo.       

    define buffer bfCU_Frame for CU_Frame.
    
    mchFrameName = trim(substring(ipchALine, 14)).
        
    do for bfCU_Frame:
      create bfCU_Frame.
      assign
          bfCU_Frame.chID = guid
          bfCU_Frame.chBlockID = chCurrentBlockID
          bfCU_Frame.chFrameName = mchFrameName
          .
    end.   
  end.
  
end class.