/* CLASS: ModuleInfo
  This object is used for  internal module info of a LoadFileModule.  These
  Information are supplied by the Symbol Server, when a LoadFile is loaded.
  See SourcePresenter, VirtFileObject, VirtMixedObject and LoadFileModule
  for usage details.

  REQUIRE: LOADFILE.CLS
 */!!

inherit(Object, #ModuleInfo, #(moduleName /* key */
languageName /* a symbol; e.g. #C, #Cpp */
moduleDescriptor /* symbol descriptor */
timeStamp /* module timestamp */
addressRange /*  address descriptor */
parent /* a LoadFileModule */
reportOutOfSynch /* #true/nil */
srcPathRef /* compiled path reference */
aliasPathRefIndex /* Index of alias Path - The global PrjInfo object */
realFileExtension
sourceLocations /* VirtFileObject info */
sourceHighestChunk
totalSourceLinesExact
totalSourceLinesGuess
mixedLocations /* VirtMixedObject info */
mixedHighestChunk
totalMixedLinesExact
totalMixedLinesGuess
), 2, nil)!!

now(class(ModuleInfo))!!

/* 05/15/92 - PUBLIC
  Create a new ModuleInfo object and initialize it.
*/
Def new(self, moduleDesc, loadFileRoot | newInstance)
{
  newInstance := new(self:ancestor);
  ^initFromDescriptor(newInstance, moduleDesc, loadFileRoot);
}
!!

now(ModuleInfo)!!

/* 08/5/1993 - PUBLIC 
  Reset all browsing information about the module.
*/
Def resetBrowsedInfo(self)
{ 
  /* Source object */
  sourceHighestChunk     := nil;
  sourceLocations        := new(Dictionary, 2);
  totalSourceLinesGuess  := nil;
  totalSourceLinesExact  := nil;  
  /* Mixed object */
  mixedHighestChunk     := nil;
  mixedLocations        := new(Dictionary, 2);
  totalMixedLinesGuess  := nil;
  totalMixedLinesExact  := nil;
}
!!

/* 7/9/1993 16:35
PUBLIC
   Added so BrowseModules dialog will display correct user language name
   (e.g., C, C++, assembly), without affecting internal state variables
   used by the Source presenter (needs 'C' throughout for tokenization
   to work properly).
*/
Def getPrintLanguageName(self)
{
   if (size(realFileExtension) > 1)
      /* Valid C++ extensions: .cpp, .cxx, .cc, .x, .i */
      if (asUpperCase(realFileExtension[0]) in asSet("CXI")) then
         ^#CPP;
      else
         ^#unknown;
      endif;
   else
      if (languageName == #C) then
         ^#C;
      else
         /* Most toolchains don't include full filename for assembly modules.
            For no file extension, we assume assembly module. */
         ^#Assembly;
      endif;
   endif;
}
!!

/* 6/10/1993 15:49 - PRIVATE
  Define a new alias path for the specified startPath.  Also, add it
  to the sourcePathsCollection of the ProjectInfoObject
  Return the new aliasing path or nil.
*/
Def defineAliasPath(self, startPath | tmpDlg, result, tmpStr)
{   
  /* Create a new AliasPathToSrcDialog and run */
  tmpDlg := new(AliasPathToSrcDialog, startPath);
  if runModal(tmpDlg, DLG_PATH_ALIAS, ThePort) = IDOK then
    if (result := aliasPathToSource(tmpDlg)) <> nil cand
                              (size(result[0]) > 0) then
      /* Add the newStr to Collection and it list */
      if (result[1] = 1) then
        tmpStr := pathParts(result[0])[0];
        /* Strip off the last slash */
        if tmpStr cand tmpStr[size(tmpStr)-1] in asSet("\/") then
          tmpStr := subString(tmpStr, 0, size(tmpStr)-1);
        endif;
        /* addAliasPath() return the index that it added to list */
        aliasPathRefIndex := addAliasPath(TheProjectInfoObj, tmpStr);
      endif;
      ^result[0];  
    endif; 
  endif;
  /* User cancel defining a new alias path - so we can't do anything */
  ^nil;    
}
!!

/* 6/10/1993 15:25 - PUBLIC 
  Set the index of alias path reference.
*/
Def setAliasPathRef(self, index)
{ 
  aliasPathRefIndex := index;
}
!!

/* 6/8/1993 15:07 - PUBLIC 
  Return the compiled path reference
*/
Def srcPathRef(self)
{ 
  ^srcPathRef;
}
!!

/* 12/1/1992 14:28 - PUBLIC
  Return the total Mixed Lines guessing for self.
*/
Def totalMixedLinesGuess(self)
{ 
  ^totalMixedLinesGuess;
}
!!

/* 12/1/1992 14:28 - PUBLIC
  Return the total Mixed Lines guessing for self.
*/
Def totalMixedLinesExact(self)
{ 
  ^totalMixedLinesExact;
}
!!

/* 12/1/1992 14:27 - PUBLIC
  Set the highest chunk seen in VirtFileData Object of self.
*/
Def setSourceHighestChunk(self, newVal) {
  ^(sourceHighestChunk := newVal);
}
!!

/* 12/1/1992 14:27 - PUBLIC
  Return the highest chunk seen in VirtFileData Object of self.
*/
Def sourceHighestChunk(self) {
  ^sourceHighestChunk;
}
!!

/* 12/1/1992 14:28 - PUBLIC
  Set the totalMixed Lines for self.
*/
Def setTotalMixedLines(self, newValGuess, newValExact)
{ 
  totalMixedLinesGuess := newValGuess;
  totalMixedLinesExact := newValExact;
}
!!

/* 12/1/1992 14:27 - PUBLIC
  Return the highest chunk seen in VirtMixedData Object for self.
*/
Def setMixedHighestChunk(self, newVal)
{ 
  ^(mixedHighestChunk := newVal);
}
!!

/* 12/1/1992 14:27 - PUBLIC
  Return the highest chunk seen in VirtMixedData Object for self.
*/
Def mixedHighestChunk(self)
{ 
  ^mixedHighestChunk;
}
!!

/* 12/1/1992 14:21 - PUBLIC
  return the mixedLocations Dictionary contains virtMixedObject info.
*/
Def mixedLocations(self)
{ 
 ^mixedLocations;
} !!

/* 12/1/1992 14:21 - PUBLIC
  Set the mixedLocations Dictionary to hold the new virtMixedObject info.
*/
Def setMixedLocations(self, newDict)
{ 
 ^(mixedLocations := newDict);
} !!

/* 12/1/1992 14:21 - PUBLIC
  Set the sourceLocations Dictionary to hold the new virtFileObject info.
*/
Def setSourceLocations(self, newDict)
{ 
 ^(sourceLocations := newDict);
} !!

/* 12/1/1992 14:21 - PUBLIC
  Return the sourceLocations Dictionary contains a virtFileObject info.
*/
Def sourceLocations(self)
{ 
 ^sourceLocations;
} !!

/* 11/19/1992 14:53 - PUBLIC
  Return the reportOutOfSynch flag. If #true then do not report else report.
*/
Def setReportOutOfSynch(self, newFlag)
{ 
  ^(reportOutOfSynch := newFlag);
}
!!

/* 11/19/1992 14:53 - PUBLIC
  Return the reportOutOfSynch flag. If #true then do not report else report.
*/
Def reportOutOfSynch(self)
{ 
  ^reportOutOfSynch;
}
!!

/* PRIVATE -- get language name from the module's reference */
Def getLanguageName(self, filePathStr | dotSuffix)
{
  /* take name suffix from file name and map to symbol - .suffix->langName */
  /* C is the only mode the Source presenter knows about.  We'd like to
     handle *any* extension: .cc, .cpp, .cxx.  We'd also like to handle
     cfront preprocessor output: .x, .i.  */
  if (filePathStr) cand (dotSuffix := pathParts( filePathStr )[2]) then
    if (asUpperCase(dotSuffix[0]) in asSet("CXI")) then
      /* IMPORTANT NOTES: Fixed bug "failed to tokenize in Source" - Nghia
      ** Please do not change these symbols (e.g., #C and #Unknown).
      ** These symbols are used in selecting the correct language token parser. 
      */
      ^#C
    endif;
  endif;
  ^#Unknown
}
!!

/* 7/14/1992 15:03 - PUBLIC
  Set the totalSourceLines for self.
*/
Def setTotalSourceLines(self, numLinesGuess, numLinesExact)
{ 
  totalSourceLinesGuess := numLinesGuess;
  totalSourceLinesExact := numLinesExact;
}
!!

/* 7/14/1992 15:03 - PUBLIC
  Return the totalSourceLinesGuess.
*/
Def totalSourceLinesGuess(self)
{ 
  ^totalSourceLinesGuess;
}
!!

/* 7/14/1992 15:03 - PUBLIC
  Return the totalSourceLinesGuess.
*/
Def totalSourceLinesExact(self)
{ 
  ^totalSourceLinesExact;
}
!!
/* 05/15/92 - PUBLIC
   Return the address range of module.
*/
Def addressRange(self)
{ ^addressRange }
!!

/* 2/7/1992 15:02 - PUBLIC 
  Destroy all self information.
*/
Def deleteModuleInfo(self)
{ 
  parent            := nil; 
  moduleName        := nil;
  languageName      := nil;
  timeStamp         := nil;
  sourceLocations   := nil;
  mixedLocations    := nil;
  aliasPathRefIndex := nil;
  if addressRange then
    destroyAddress(AddressLibClass$Inst, addressRange);
    addressRange  := nil;
  endif;  
  ^moduleDescriptor;
}
!!

/* 2/6/1992 8:35 - PUBLIC 
  Return the full path name of a module, if path is null then 
  only the filename is return.
  If failed searching from the list of source paths then try the load path then
  open the AliasPathToSrcDialog to let user define.
  if all failed return nil. 
*/
Def filePath(self | pathStr, fileSpec, usedAlias, tmpStr, maName) {
  
  /* Build full file path for self - support null path */
  usedAlias := nil;
  pathStr := "";
  fileSpec := moduleName + "." + realFileExtension;
  
  if size(moduleName) > 8 then
    maName := getAliasModuleName(SymbolLibClass$Inst, fileSpec);
    fileSpec := maName;
  endif;
  
  /* Use the aliasPathRef if defined */
  if aliasPathRefIndex cand
      (pathStr := getAliasFilePath(TheProjectInfoObj, self, aliasPathRefIndex)) cand
      (size(pathStr) > 0) then
    usedAlias := #true;  
    pathStr := makePathString(pathStr, fileSpec);
  else
    /* No alias path - use the original module reference path */
    if size(srcPathRef) > 0 then
      pathStr := srcPathRef;
      /* UNIX source path reference - DO NOT try exists() on it */
      if unixFile?(pathStr) then
        ^pathStr;
      endif;  
    else  
      pathStr := fileSpec;
    endif;
  endif;  
  /* Verify that the file is existed */
  if not(exists(File, pathStr, 0)) then
    /* Let search the fileSpec in the SourcePathsCollection first */
    tmpStr := pathStr;
    /* searchFileSpec() will search in the list if fails it will try the LoadFile Path */
    if (usedAlias) cor not(pathStr := searchFileSpec(TheProjectInfoObj, self, fileSpec)) then       
      /* Now we really need a new path define */
      beep();
      displayFormattedError(ErrorTextLibClass$Inst,
        ER_CANT_OPEN_SOURCE_FILE, FORCE_POPUP, ": " + tmpStr, nil, nil);
      pathStr := defineAliasPath(self, tmpStr);        
    endif;       
  endif;
  ^pathStr;  
}!!

/* PRIVATE -- get all requisite info or return nil (fail) */
Def initFromDescriptor(self, moduleDesc, loadFileRoot | result)
{
  parent                := loadFileRoot;
  moduleDescriptor      := moduleDesc;
  reportOutOfSynch      := nil;
  aliasPathRefIndex     := nil; /* A number or nil */
  /* Init VirtData object info */
  resetBrowsedInfo(self);

  /* Get module info from the symbol table */
  if not(result := getModuleInfo(SymbolLibClass$Inst, moduleDesc )) then
    ^nil
  endif ;
  /* NOTES: result = #(moduleNameStr, moduleAddrRangeDesc) */
  moduleName := result[0]; /* a module name string */
  addressRange := result[1];
  /* Get module timestamp */
  if not(timeStamp := getModuleTimestamp( SymbolLibClass$Inst, moduleDesc )) then
    ^nil
  endif ;
  
  /* Get module's compiled reference path */
  if not(srcPathRef := getModuleSrcPath( SymbolLibClass$Inst, moduleDesc )) then
    ^nil
  endif ;

  if (size(srcPathRef) > 0) then
    setLanguageName(self, getLanguageName(self, srcPathRef));
  else
    setLanguageName( self, #Unknown );
  endif ;
  
  /* Save real file extension into instance variable (restored later) */
  realFileExtension := pathParts(srcPathRef)[2];

  ^self
}
!!

/* 05/15/92 - PUBLIC */
Def languageName(self)
{
  if languageName then 
    ^languageName
  endif;
  ^#unknown
}
!!

/*  05/15/92 - PUBLIC
  Return Module Descriptor of the moduleInfo.
*/
Def moduleDescriptor(self)
{ ^moduleDescriptor }
!!

/* 05/15/92 - PUBLIC */
Def moduleName(self)
{ ^moduleName }
!!

/* 05/15/92 - PUBLIC */
Def setAddressRange(self, newAddrDesc)
{ ^(addressRange := newAddrDesc) }
!!

/* 05/15/92 - PUBLIC */
Def setLanguageName(self, newNameSymbol)
{ ^(languageName := newNameSymbol) }
!!

/* 05/15/92 - PUBLIC */
Def setModuleName(self, newNameSymbol)
{ ^(moduleName := newNameSymbol) }
!!

/* 05/15/92 - PUBLIC */
Def setTimeStamp(self, newTimeStamp)
{ ^(timeStamp := newTimeStamp) }
!!

/* 2/13/1992 15:09 - PUBLIC
  Return module time stamp in string format */
Def stringTimeStamp(self | timeStr, tmp)
{ 
  timeStr := "";
  if timeStamp
  then
    timeStr := timeStr + /* MM/DD/YY */
      asString(atLSB(timeStamp, 2)) + "/" +
      asString(atMSB(timeStamp, 2)) + "/" +    
      asString(wordAt(timeStamp, 0)) + " - " +
      /* HH:MM:SS */ 
      right(asString(atLSB(timeStamp, 4)), 2, "0") + ":" +    
      right(asString(atMSB(timeStamp, 4)), 2, "0") + ":" +
      right(asString(atLSB(timeStamp, 6)), 2, "0");
  endif;
  ^timeStr;   
}
!!

/* 05/15/92 - PUBLIC */
Def timeStamp(self)
{ ^timeStamp }
!!
