IMPLEMENTATION MODULE ModuleTable;

FROM Conversions IMPORT IntConversions;
FROM YaflClasses IMPORT ClassDeclaration;
FROM YaflCreator IMPORT Creators;
FROM YaflModules IMPORT CompilationUnit;
FROM YaflCfg IMPORT YaflCfg;
FROM YaflError IMPORT MainErrorHandler;
FROM YaflWorlds IMPORT WorldList, DefaultWorld;
IMPORT StreamLookAhead;
FROM Streams IMPORT Stream, InputStream, StdOut, StdErr;
IMPORT String;
FROM StringSpace IMPORT StringElement, StringHashSpace;
IMPORT SYSTEM;
IMPORT Space;
                                     
FROM YaflController IMPORT Controller;

  CLASS Module;
    INHERITS StringElement;
    
    VAR
      TheDefModule: DefinitionModule;
      TheImplModule: ImplementationModule;
      TheDefError,
      TheImplError: BOOLEAN;
      TheDefLines,
      TheImplLines: INTEGER;
  
    METHOD ModuleName: ARRAY OF CHAR;
      BEGIN
      RESULT := Text;
      END ModuleName;

    METHOD Lines: INTEGER;
      BEGIN     
      RESULT := TheDefLines + TheImplLines;
      END Lines;
      
    METHOD LocateCompilationUnit (CompUnit: CompilationUnit;
                                  Extension: ARRAY OF CHAR): InputStream;
      VAR
        Input: InputStream;                                
        FName: ARRAY OF CHAR;
      BEGIN  
      MainErrorHandler.SetSourceFileName (VOID);
      ASSERT CompUnit.GetWorld = VOID;              
      Input.CREATE;                
      FOR j := 0 TO WorldList.Size - 1 WHILE RESULT = VOID DO
        FName := WorldList.Get(j).BuildSourceFName (Text, Extension);
        Input.Open (FName, Input.ReadAccess);
        IF Input.ErrorCode = Input.NoError THEN
          MainErrorHandler.SetSourceFileName (FName);
          RESULT := Input;
          CompUnit.SetWorld (WorldList.Get(j));
          END;
        DEBUG
          IF YaflCfg.VerboseLevel > 2 THEN 
            StdOut.WriteString  ("Attempting to open: " + FName);
            IF RESULT = VOID THEN
              StdOut.WriteLine ("  Failure");
             ELSE
              StdOut.WriteLine ("  Success");
              END;
            END;
          END;
        END;
      ASSERT (RESULT <> VOID) IMPLIES (CompUnit.GetWorld <> VOID);
      END LocateCompilationUnit;

    METHOD DoGetDefinitionModule (TheWorld: World);
      VAR
        Input: InputStream;
        StreamLkh: StreamLookAhead;
        OldRef: CompilationUnit;
        FName: ARRAY OF CHAR;
      BEGIN       
      ASSERT TheDefModule = VOID;
      TheDefModule := Creators.Object.CreateDefinitionModule;
      TheDefModule.SetWorld (TheWorld);
      IF TheWorld = VOID THEN
        Input := LocateCompilationUnit (TheDefModule, YaflCfg.DefExt);
       ELSE
        Input.CREATE;
        FName := TheWorld.BuildSourceFName (ModuleName, YaflCfg.DefExt);
        MainErrorHandler.SetSourceFileName (FName);
        Input.Open (FName,
                    Input.ReadAccess);
        END; 
      IF (Input <> VOID) AND (Input.ErrorCode = Input.NoError) THEN

        StreamLkh := Creators.Stream.CreateStreamLookAhead (Input,
                                     YaflCfg.PleaseGenerateMetrics);
        OldRef := MainErrorHandler.Ref;
        MainErrorHandler.SetRef (TheDefModule);
        TheDefModule.Parse (StreamLkh);
        TheDefModule.CheckModuleName (ModuleName);
        TheDefLines := StreamLkh.LineNr;
--------------------------------          
--      The CheckHierarchy method verifies that
--      father-son-grandpa relationship are consistent
--      with the SubTree method. It was used in early 
--      development phases, but should not be required
--      anymore.
--------------------------------          
--        DEBUG
--          TheDefModule.CheckHierarchy;
--          END;
--------------------------------          
        -----------------------------------
        -- Restore the current module which 
        -- has been stored earlier.
        -----------------------------------  
        MainErrorHandler.SetRef (OldRef);
        Input.Close;
        TheDefError := TheDefModule.ErrorFound;
       ELSE
        TheDefModule := VOID;
        TheDefError := TRUE;
        MainErrorHandler.SetError (0, 0,
                     "Cannot open definition module " + ModuleName);
        END;
      END DoGetDefinitionModule;
      
    METHOD GetDefinitionModule (TheWorld: World): DefinitionModule;
      BEGIN
      IF (TheDefModule = VOID) AND NOT TheDefError THEN
        IF YaflCfg.VerboseLevel > 0 THEN
          StdOut.WriteLine ("Reading definition module: " + ModuleName);
          END; -- IF
        DoGetDefinitionModule (TheWorld);
        END;
      RESULT := TheDefModule;
--      ASSERT RESULT <> VOID;
      END GetDefinitionModule;
                
    METHOD LocateDefinitionModule: DefinitionModule;
      BEGIN
      RESULT := GetDefinitionModule (VOID);
      END LocateDefinitionModule;
                              
    METHOD DoGetImplementationModule (TheWorld: World);
      VAR
        Input: InputStream;
        StreamLkh: StreamLookAhead;
        FName: ARRAY OF CHAR;
      BEGIN  
      ASSERT TheImplModule = VOID;
      TheImplModule := Creators.Object.CreateImplementationModule;
      TheImplModule.SetWorld (TheWorld);
      IF TheWorld = VOID THEN
        Input := LocateCompilationUnit (TheImplModule, YaflCfg.ImpExt);
       ELSE
        Input.CREATE;
        FName := TheWorld.BuildSourceFName (Text, YaflCfg.ImpExt);
        MainErrorHandler.SetSourceFileName (FName);
        Input.Open (FName, Input.ReadAccess);
        END;
      IF (Input <> VOID) AND (Input.ErrorCode = Stream.NoError) THEN

        StreamLkh := Creators.Stream.CreateStreamLookAhead (Input,
                                       YaflCfg.PleaseGenerateMetrics);
        TheImplModule.Parse (StreamLkh);
        TheImplModule.CheckModuleName (Text);
        TheImplLines := StreamLkh.LineNr;
        Input.Close;
       ELSE
        TheImplModule := VOID;
        TheImplError := TRUE;
        END;
      END DoGetImplementationModule;
      
    METHOD GetImplementationModule (TheWorld: World): ImplementationModule;
      BEGIN     
      IF (TheImplModule = VOID) AND NOT TheImplError THEN
        DoGetImplementationModule (TheWorld);
        END;
      RESULT := TheImplModule;
      IF NOT ModuleTable.PleaseKeepImplementationModules THEN
        TheImplModule := VOID;
        END;
      END GetImplementationModule;
      
    METHOD LocateImplementationModule: ImplementationModule;     
      BEGIN
      RESULT := GetImplementationModule (VOID);
      END LocateImplementationModule;
                                                
    METHOD GetImplementationModuleImportList (TheWorld: World): ImportList;
      VAR
        Input: InputStream;
        StreamLkh: StreamLookAhead;
        w: World;
      BEGIN
      w := TheWorld;
      IF w = VOID THEN
        w := DefaultWorld;
        END;
      Input.CREATE;
      Input.Open (w.BuildSourceFName (ModuleName, 
                                      ModuleTable.ImpExt), Stream.ReadAccess);
      IF Input.ErrorCode = Stream.NoError THEN
        StreamLkh := Creators.Stream.CreateStreamLookAhead (Input,
                                       YaflCfg.PleaseGenerateMetrics);
        RESULT := StreamLkh.AcceptImplementationModuleImportList;
        Input.Close;
        END;
      END GetImplementationModuleImportList;
                                                      
  END Module;
----------------------------------------------------------
  ONCE CLASS ModuleTable;
    VAR
      TheRandomCollectionClass: ClassDefinition;
      Modules: StringHashSpace(Module);
      
    CONST
      DefExt = YaflCfg.DefExt;
      ImpExt = YaflCfg.ImpExt;

    METHOD Lines: INTEGER;
      VAR
        p: ARRAY OF Module;
      BEGIN
      p := Modules.Row;
      FOR i := 0 TO p.SIZE - 1 DO
        RESULT := RESULT + p[i].Lines;
        END;      
      END Lines;

    REDEFINE METHOD CREATE;
      BEGIN
      Modules.CREATE(512);
      END CREATE;

    METHOD DoGetDefinitionModule (TheWorld: World;
                                  ModuleName: ARRAY OF CHAR): 
                                                  DefinitionModule;
      BEGIN       

      YaflCfg.GetController.SetMessage (ModuleName);
      YaflCfg.GetController.Capture (Controller.ReadingDef);


      IF NOT YaflCfg.Interrupted THEN
        RESULT := FindModule(ModuleName).GetDefinitionModule (TheWorld);
        ASSERT RESULT <> VOID IMPLIES RESULT.IsParsed;
        END; -- IF
      END DoGetDefinitionModule;
      
    METHOD GetDefinitionModule (TheWorld: World;
                                ModuleName: ARRAY OF CHAR):
                                                DefinitionModule;
      BEGIN
      ASSERT TheWorld <> VOID;
      
      RESULT := DoGetDefinitionModule (TheWorld, ModuleName);
      END GetDefinitionModule;
                
    METHOD LocateDefinitionModule (ModuleName: ARRAY OF CHAR):
                                                DefinitionModule;
      BEGIN
      RESULT := DoGetDefinitionModule (VOID, ModuleName);
      END LocateDefinitionModule;
                
    METHOD DoGetImplementationModule (TheWorld: World;
                                      ModuleName: ARRAY OF CHAR): ImplementationModule;
      BEGIN
      YaflCfg.GetController.SetMessage (ModuleName);
      YaflCfg.GetController.Capture (Controller.ReadingImp);

      IF YaflCfg.VerboseLevel > 0 THEN
        StdOut.WriteLine ("Reading implementation module: " + ModuleName);
        END; -- IF

      IF NOT YaflCfg.Interrupted THEN
        RESULT := FindModule (ModuleName).GetImplementationModule (TheWorld);
        ASSERT RESULT <> VOID IMPLIES RESULT.IsParsed;
        END; -- IF
      END DoGetImplementationModule;
      
    METHOD GetImplementationModule (TheWorld: World;
                                    ModuleName: ARRAY OF CHAR):
                                                ImplementationModule;
      BEGIN
      ASSERT TheWorld <> VOID;
      
      RESULT := DoGetImplementationModule (TheWorld, ModuleName);
      END GetImplementationModule;
      
    METHOD LocateImplementationModule (ModuleName: ARRAY OF CHAR):
                                                ImplementationModule;    
      BEGIN
      RESULT := DoGetImplementationModule (VOID, ModuleName);
      END LocateImplementationModule;
                                                
    METHOD GetImplementationModuleImportList (TheWorld: World;
                                              ModuleName: ARRAY OF CHAR):
                                                ImportList;
      BEGIN
      RESULT := FindModule(ModuleName).
                GetImplementationModuleImportList(TheWorld);
      END GetImplementationModuleImportList;
                                                      
    METHOD Zap;
      BEGIN
      Modules.Purge;
      TheRandomCollectionClass := VOID;
      END Zap;
      
    METHOD FindModule (ModuleName: ARRAY OF CHAR): Module;
      BEGIN          
      RESULT := Modules.FindStringElement(ModuleName);
      IF RESULT = VOID THEN
        RESULT.CREATE (ModuleName);
        VOID := Modules.Store (RESULT);
        END;
      END FindModule;

    VAR
      KeepImplementationsFlag: BOOLEAN;

    METHOD PleaseKeepImplementationModules: BOOLEAN;
      BEGIN  
      RESULT := KeepImplementationsFlag;
      END PleaseKeepImplementationModules;
      
    METHOD SetKeepImplementationModules(Value: BOOLEAN);
      BEGIN
      KeepImplementationsFlag := Value;
      END SetKeepImplementationModules;
      
    METHOD RandomCollectionClass: ClassDefinition;
      VAR
        CollModule: DefinitionModule;
        Cl: ClassDeclaration;
      BEGIN
      IF TheRandomCollectionClass = VOID THEN
        CollModule := LocateDefinitionModule (Space.StoreString ("Collection"));
        Cl := CollModule.GetClass (Space.StoreString("RandomCollection"));                      
        WHAT Cl OF
          IN ClassDefinition:
            TheRandomCollectionClass := TAG;
            END;
          END;
        END;
      RESULT := TheRandomCollectionClass;
      END RandomCollectionClass;
      
    END ModuleTable;

END ModuleTable;
