/* 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)!!

/* 05/15/92 - PUBLIC
   Return the address range of module.
*/
Def addressRange(self)
{ ^addressRange }
!!

/* 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, extList | newStr idx found, parentWnd)
{
  /* Set the parent window of the dialog */
  if (TheSourcePresenter cand hWnd(TheSourcePresenter)) then
    parentWnd := TheSourcePresenter;
  else
    parentWnd := TheToolBarPresenter;
  endif;  
  /* run file dialog; returns path name selected. */
  if (newStr := getFileName(CLIULibraryClass$Inst,
                  parentWnd,
                  HE_DLGF_ADD_SRC_PATH,
                  IDS_SOURCE_FILTER,
                  nil /* no initial path */,
                  FILE_OPEN))
    if (size(newStr) > 0)
      newStr := asUpperCase(newStr);  /* convert to Upper case <Judy 7/2/96> */
      idx := 2;  /* start looking after 1st slash, e.g. "c:\" */
      loop
      while (idx < size(newStr)) cand
            (found := find(newStr, "\", idx+1))
      begin
        idx := found;
      endLoop;

      /* truncate string if a 2nd slash was found */
      newStr := subString(newStr, 0, idx+1);
      aliasPathRefIndex := addAliasPath(TheProjectInfoObj, newStr);
      ^newStr;  
    endif; 
  endif;
  
  /* User cancel defining a new alias path - so we can't do anything */
  ^nil;    
}
!!

/* 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, found, extList, i, numExt) {
  
  /* Build full file path for self - support null path */
  found := usedAlias := nil;
  pathStr := "";
  extList := nil;
  if (not(realFileExtension) cor size(realFileExtension) = 0) then
    if not(extList := getDefaultFileExtList(self)) then
       extList := tuple("C", "ASM");
    endif;   
    numExt  := size(extList);
    fileSpec := moduleName +"."+extList[0];   
  else   
    fileSpec := moduleName + "." + realFileExtension;
  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(found := exists(File, pathStr, 0)) cand extList) then
    tmpStr := pathParts(pathStr)[0];
    i := 0;
    loop
    while ((i < numExt) cand not(found))
    begin
      fileSpec := moduleName +"."+extList[i];
      pathStr := tmpStr+fileSpec;
      if not(found := exists(File, pathStr, 0)) then
        /* Let try the sourcePathCollection of the ProjectInfo */
        found := ((pathStr := searchFileSpec(TheProjectInfoObj, self, 
          fileSpec)) <> nil);
        if not(pathStr) pathStr := moduleName; endif;  
      endif;          
      i := i + 1;  
    endLoop;
  endif;               

  if not(found) then
    /* Let search the fileSpec in the SourcePathsCollection first */
    tmpStr := pathStr;
    if not(tmpStr) tmpStr := fileSpec; endif;
    /* 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);
      if not(pathStr := defineAliasPath(self, tmpStr, extList)) then
        ^nil;
      endif;          
    endif;       
  endif;
  /* reset the language of ModuleInfo object base on the new extension */
  setLanguageName(self, getLanguageName(self, pathStr));
  /* Save real file extension into instance variable (restored later) */
  realFileExtension := pathParts(pathStr)[2];
  ^pathStr;  
}!!

/* 7/22/1994 11:10 - PUBLIC
  Get the default source file extension list from the PWRVIEWS.INI file
  NOTES:
    return a collection of default file extensions or nil.
*/
Def getDefaultFileExtList(self | appName, iniItems)
{ 
  appName := "SourceInfo";
  if not(iniItems := getProfileEntry(TheProfileInfoObj, appName, 
      "DefaultModuleExtensions", "," )) then
    ^nil;
  endif;
  ^iniItems; 
}
!!

/* PRIVATE -- get language name from the module's reference */
Def getLanguageName(self, filePathStr | dotSuffix, partsColl)
{
  /* 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.  
  ** 06/12/95 - Fixed PV.DMP reported by GI. 
  ** Added check to make sure that the pathParts() return at least 3 parts.
  */
  if (filePathStr) cand (partsColl := pathParts(filePathStr)) cand
     (size(partsColl) = 3) then
    if (dotSuffix := partsColl[2]) cand (size(dotSuffix) > 0) cand
       (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;
  /* Assembly module is considered to be an unknown language */
  ^#Unknown
}
!!

/* 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 (realFileExtension cand (size(realFileExtension) > 1))
      /* Valid C++ extensions: .cpp, .cxx, .cc, .x, .i */
      if (asUpperCase(realFileExtension[0]) in asSet("CXI")) then
         ^#CPP;
      endif;
      if (asUpperCase(realFileExtension) = "ASM") then
         ^#Assembly;
      endif;
      ^#Unknown;
   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;
}
!!

/* 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));
    /* Save real file extension into instance variable (restored later) */
    realFileExtension := pathParts(srcPathRef)[2];
  else
    setLanguageName( self, #Unknown );
    /* Save real file extension into instance variable (restored later) */
    realFileExtension := nil;
  endif ;
  ^self
}
!!

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

/* 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;
}!!

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

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

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

/* 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;
}
!!

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

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

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

/* 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:21 - PUBLIC
  Set the mixedLocations Dictionary to hold the new virtMixedObject info.
*/
Def setMixedLocations(self, newDict)
{ 
 ^(mixedLocations := newDict);
}!!

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

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

/* 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:21 - PUBLIC
  Set the sourceLocations Dictionary to hold the new virtFileObject info.
*/
Def setSourceLocations(self, newDict)
{ 
 ^(sourceLocations := newDict);
}!!

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

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

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

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

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

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

/* 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 }
!!

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

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

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

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

