/********************************************************************
                         JavaImport

  Description: View Java imported package files
  Syntax:      JavaImport [-config]

  This macro scans for import statements in the Java source file
  and displays all imported packages in the list box from where
  you can open it.
  When this script is started for the first time it will ask you
  to specify directories where Java package source files are. You
  can specify more than one directory separated with semicolon ";"
  (e.g. c:\jdk1.1.1\src;c:\mypackages). If you want to modify
  directories later, start this script with the -config option
  (e.g. JavaImport -config).

  To map JavaImport command to an shortcut key:

  1. Save this script in the GwdTextEditor\script directory.
  2. From the Macro menu, choose Macro command.
  3. Choose the New button.
  4. In the New Macro Name input box, type a name for the macro
     (e.g. Script_JavaImport). In this dialog you can also define a
     shortcut key for macro.
  5. Choose the OK button.
  6. In the Edit Macro dialog editor, enter the following two
     lines:
        Param1 "JavaImport"
        ExecScript
  7. Choose the Close button.
  8. You can now define shortcut key for Script_JavaClose macro
     in the Key Mapping dialog box.


  If you modify this macro, save it under different name because
  new version of the GWD Text Editor may overwrite it.

  THIS SOURCE CODE IS UNDER COPYRIGHT AND IT IS PART OF THE
  GWD TEXT EDITOR. YOU CAN MODIFY IT, BUT ONLY IF YOU INTEND TO
  USE IT WITH THE GWD TEXT EDITOR.
  YOU SHOULD NOT USE THIS SOURCE FILE AND ANY PART OF IT, WITH ANY
  OTHER PRODUCT IN ANY WAY.

  Copyright (c) 1998 Vedran Gaco. All right reserved.
********************************************************************/

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>
#include <dir.h>
#include <editor/doc.h>
#include <editor/dlg.h>
#include <editor/macros.h>

// function prototypes
void ScriptMain(HDOC);
int GetJavaImportFiles(HDOC, char *, unsigned);
int GetJavaPackageFiles(const char *, char *, unsigned);
void OpenJavaImportFile(const char *);
void ConfigClassPath(void);

char g_szClassPath[512];

/*-------------------------------------------------------------------
                    main function
-------------------------------------------------------------------*/

int main(int argc, char *argv[])
{
   HDOC hDoc;

   // load settings from ini file
   GetPrivateProfileString("Script", "JavaImport_ClassPath", "", g_szClassPath,
                           sizeof(g_szClassPath), "gte.ini");

   // check command line options
   if(argc > 2 && strcmp(argv[1], "-config") == 0)
   {
      ConfigClassPath();
      return 0;
   }

   // if the class path is not set ask user to enter it
   if(g_szClassPath[0] == 0)
   {
      ConfigClassPath();
   }
   if(g_szClassPath[0] == 0)
      return 1;

   hDoc = Gte_GetActiveDocument();
   if(hDoc == NULL) {
      MsgBox("Cannot get handle of the active document!", MB_OK);
      return 1;
   }
   // check file extension
   {
      char szFileExt[8];

      Gte_GetFileExtension(hDoc, szFileExt, sizeof(szFileExt));
      if(stricmp(szFileExt, "java") != 0)
         if(MsgBox("This is not Java source file. Continue?", MB_YESNO) != IDYES)
            return 2;
   }

   ScriptMain(hDoc);
   return 0;
}


/*-------------------------------------------------------------------
                         ScriptMain

  Main script function.

  PARAMETERS

     hDoc - Handle of the active document.
-------------------------------------------------------------------*/

void ScriptMain(HDOC hDoc)
{
   char *pszInclFiles;
   int  nInclFiles;

   // reserve memory for import files
   if((pszInclFiles = (char *)malloc(4096)) == NULL)
   {
      MsgBox("Not enought memory", MB_OK);
      return;
   }
   // extract import files from document
   if((nInclFiles = GetJavaImportFiles(hDoc, pszInclFiles, 4096)) > 0)
   {
      // GetJavaImportFiles function returs file names in the
      // bufer terminated with two null terminators (TIncludeFilesDlg
      // dialog box format). For script we must convert this in the
      // Gte_ListBoxDlg dialog box format.
      char **ppszListBoxItems;

      ppszListBoxItems = (char **)malloc((nInclFiles + 1) * sizeof(char *));
      if(ppszListBoxItems)
      {
         char *psz;
         int i;

         psz = pszInclFiles;
         i = 0;
         while(*psz)
         {
            ppszListBoxItems[i] = psz;
            psz += strlen(psz) + 1;
            i++;
         }
         ppszListBoxItems[nInclFiles] = NULL;

         if((i = Gte_ListBoxDlg("Imported Files", ppszListBoxItems)) > 0)
         {
            if(strchr(ppszListBoxItems[i-1], '*') == NULL)
            {
               OpenJavaImportFile(ppszListBoxItems[i-1]);
            }
            else
            {
               // user imports all files in the directory so we will need
               // to read all files from this directory and display dialog
               // box again
               char *pszPackageFiles;

               pszPackageFiles = (char *)malloc(8096);
               if(pszPackageFiles)
               {
                  nInclFiles = GetJavaPackageFiles(ppszListBoxItems[i-1], pszPackageFiles, 8096);
                  if(nInclFiles > 0)
                  {
                     // GetJavaPackageFiles function returs file names in the
                     // bufer terminated with two null terminators (TIncludeFilesDlg
                     // dialog box format). For script we must convert this in the
                     // Gte_ListBoxDlg dialog box format.
                     char **ppszListBoxItems2;

                     ppszListBoxItems2 = (char **)malloc((nInclFiles + 1) * sizeof(char *));
                     if(ppszListBoxItems2)
                     {
                        char *psz = pszPackageFiles;
                        int j = 0;

                        while(*psz)
                        {
                           ppszListBoxItems2[j] = psz;
                           psz += strlen(psz) + 1;
                           j++;
                        }
                        ppszListBoxItems2[nInclFiles] = NULL;

                        if((j = Gte_ListBoxDlg("Imported Files", ppszListBoxItems2)) > 0)
                        {
                           char szJavaClassFile[FILENAME_MAX];

                           // in the ppszListBoxItems[i-1] we have Java package
                           // (e.g. "java.awt.*" and in the ppszListBoxItems2[j-1]
                           // Java class file name without extension (e.g. Button)
                           // we now must make "java.awt.Button" string)

                           strcpy(szJavaClassFile, ppszListBoxItems[i-1]);
                           if(szJavaClassFile[strlen(szJavaClassFile)-1] == '*')
                              szJavaClassFile[strlen(szJavaClassFile)-1] = 0;
                           strcat(szJavaClassFile, ppszListBoxItems2[j-1]);
                           OpenJavaImportFile(szJavaClassFile);
                        }
                        free((void *)ppszListBoxItems2);
                     }
                  }
                  free(pszPackageFiles);
               }
            }
         }
         free((char *)ppszListBoxItems);
      }
   }
   else
   {
      MsgBox("You didn't import any package in this file", MB_OK | MB_ICONINFORMATION);
   }
   free(pszInclFiles);
}


/*-------------------------------------------------------------------
                         GetJavaImportFiles

   Scans for "import dir.dir.file;" lines in the document and
   put file names into the pszInclFiles buffer which is string
   array termined with two null terminators.

   PARAMETERS

      hDoc            Handle of document.
      pszInclFiles    Point to buffer which will receive Java import
                      files. Last in the list will be terminated with
                      two null terminator characters.
      nInclFilesSize  Size of pszInclBuffer buffer. It should be at
                      least 2048 characters long.

   RETURN VALUE

      Returns number of files in the array.
-------------------------------------------------------------------*/

int GetJavaImportFiles(HDOC hDoc, char *pszInclFiles, unsigned nInclFilesSize)
{
   long lLineNum;
   int  nInclFilesBufSizeLeft;
   int  nFilesCount;
   long lScrlPosX, lScrlPosY;

   if(pszInclFiles == NULL || nInclFilesSize < 2)
   {
      //ASSERT(FALSE);
      return 0;
   }

   pszInclFiles[0] = 0;
   pszInclFiles[1] = 0;
   nInclFilesBufSizeLeft = nInclFilesSize;
   nFilesCount = 0;
   lLineNum    = 1;

   Gte_LockUpdate(hDoc);
   GM_SaveCursorPos(hDoc);
   Gte_GetWndScrollPos(hDoc, &lScrlPosX, &lScrlPosY);

   while(Gte_Find(hDoc, "^ *import ", 1, lLineNum, FALSE, TRUE, TRUE, TRUE) == 0)
   {
      char szLine[256];
      char szFileName[256];

      // Get line number
      Gte_GetSelBeginPos(hDoc, NULL, &lLineNum);
      // Get line text (first 255 characters)
      Gte_GetLine(hDoc, lLineNum, szLine, sizeof(szLine));

      // scan for file name
      if(sscanf(szLine, " import %[^;];", szFileName) == 1)
      {
         // now we have the name of include file in szFileName
         // lets add it in buffer
         int nFileLen = strlen(szFileName);

         if(nFileLen > 0 && nFileLen < nInclFilesBufSizeLeft - 4)
         {
            strcpy(pszInclFiles, szFileName);
            pszInclFiles += nFileLen + 1;
            *pszInclFiles = 0; // list terminator
            nInclFilesBufSizeLeft -= nFileLen + 1;
            nFilesCount++;
         }
      }
      lLineNum++;
   }
   GM_RestoreCursorPos(hDoc);
   Gte_SetWndScrollPos(hDoc, lScrlPosX, lScrlPosY);
   Gte_UnlockUpdate(hDoc);

   return nFilesCount;
}

/*-------------------------------------------------------------------
                         GetJavaPackageFiles

   Returns all files from the package directory. This function
   returns only file names (without) ".java" extension.

   PARAMETERS

      pszPackage      Points to string which specifies package in
                      the Java import statement (eg. java.awt.*).
      pszClassFiles   Point to buffer which will receive Java class
                      files. Last filename in the list will be
                      terminated with two null terminator characters.
      nClassFilesSize Size of pszClassBuffer buffer. It should be at
                      least 4096 characters long.

   RETURN VALUE

      Returns number of files names in the array.
-------------------------------------------------------------------*/

int GetJavaPackageFiles(const char *pszPackage, char *pszClassFiles, unsigned nClassFilesSize)
{
   int  nClassFilesBufSizeLeft;
   int  nFilesCount;
   char szJavaPackage[FILENAME_MAX];
   char *psz;

   if(szJavaPackage == NULL || szJavaPackage[0] == 0 ||
      pszClassFiles == NULL || nClassFilesSize < 2)
   {
      //ASSERT(FALSE);
      return 0;
   }

   // remove * from the end of package name and convert . to \ dir separator
   strcpy(szJavaPackage, pszPackage);
   szJavaPackage[strlen(szJavaPackage)-1] = 0;
   psz = szJavaPackage;
   while(*psz)
   {
      if(*psz == '.')
         *psz = '\\';
      psz++;
   }

   pszClassFiles[0] = 0;
   pszClassFiles[1] = 0;
   nFilesCount = 0;
   nClassFilesBufSizeLeft = nClassFilesSize;

   if(strlen(g_szClassPath) != 0)
   {
      char *pszDirs = (char *)malloc(strlen(g_szClassPath)+2);

      if(pszDirs)
      {
         char szOldDir[FILENAME_MAX];
         char szPackageDir[FILENAME_MAX];

         getcwd(szOldDir, sizeof(szOldDir));

         strcpy(pszDirs, g_szClassPath);
         pszDirs[strlen(pszDirs)+1] = 0; // double null terminator

         // converting ; into null terminator
         psz = pszDirs;
         while(*psz) {
            if(*psz == ';')
               *psz = 0;
            psz++;
         }
         // now we have array of strings terminated with two null terminators
         psz = pszDirs;
         while(*psz)
         {
            szPackageDir[0] = 0;
            strncat(szPackageDir, psz, sizeof(szPackageDir) - 4);
            strcat(szPackageDir, "\\");
            strncat(szPackageDir, szJavaPackage, sizeof(szPackageDir)-strlen(szPackageDir)-1);
            if(szPackageDir[strlen(szPackageDir)-1] == '\\')
               szPackageDir[strlen(szPackageDir)-1] = 0;

            if(chdir(szPackageDir) == 0)
            {
               // we found package dir
               // get all file names from a dir
               struct ffblk ff;
               int    nDone;

               nDone = findfirst("*.java",&ff,0);
               while(nDone == 0)
               {
                  int nFileLen = strlen(ff.ff_name);

                  if(nFileLen > 0 && nFileLen < nClassFilesBufSizeLeft - 4)
                  {
                     char *pszExt;

                     strcpy(pszClassFiles, ff.ff_name);
                     if((pszExt = strstr(pszClassFiles, ".java")) != NULL)
                        *pszExt = 0;

                     nClassFilesBufSizeLeft -= strlen(pszClassFiles) + 1;
                     pszClassFiles += strlen(pszClassFiles) + 1;
                     *pszClassFiles = 0; // list terminator
                     nFilesCount++;
                  }
                  nDone = findnext(&ff);
               }
               break;
            }
            psz += strlen(psz) + 1;
         }
         chdir(szOldDir);

         free(pszDirs);
      }
   }
   if(nFilesCount == 0)
   {
      MsgBox("Cannot find package source files. Start this script with -config "
             "option and check ClassPath", MB_OK);
   }
   return nFilesCount;
}


/*-------------------------------------------------------------------
                         OpenJavaImportFile

 Opens java package class file

 PARAMETERS

   pszFile   Pointer to java package class specified in the
             company.package.class format (like in the Java
             import statement).
-------------------------------------------------------------------*/

void OpenJavaImportFile(const char *pszFile)
{
   char szOpenFileName[FILENAME_MAX];
   char szJavaClassFile[FILENAME_MAX];
   char *psz;

   // java package is in the "company.package.class" format
   // we need to convert this in the "company\package\class.java"
   strcpy(szJavaClassFile, pszFile);
   psz = szJavaClassFile;
   while(*psz)
   {
      if(*psz == '.')
         *psz = '\\';
      psz++;
   }
   strcat(szJavaClassFile, ".java");

   if(strlen(g_szClassPath) != 0)
   {
      char *pszDirs = (char *)malloc(strlen(g_szClassPath)+2);

      if(pszDirs)
      {
         strcpy(pszDirs, g_szClassPath);
         pszDirs[strlen(pszDirs)+1] = 0; // double null terminator

         // converting ; into null terminator
         psz = pszDirs;
         while(*psz) {
            if(*psz == ';')
               *psz = 0;
            psz++;
         }
         // now we have array of strings terminated with two null terminators
         psz = pszDirs;
         while(*psz) {
            szOpenFileName[0] = 0;
            strncat(szOpenFileName, psz, sizeof(szOpenFileName) - 4);
            strcat(szOpenFileName, "\\");
            strncat(szOpenFileName, szJavaClassFile, sizeof(szOpenFileName) -
                    strlen(szOpenFileName) - 1);

            if(access(szOpenFileName, 0) == 0)
            {
               // File has been found. Lets open it (file name stays in the
               // szOpenFileName) and lets send message to main window to
               // open that file
               free(pszDirs);
               Gte_OpenDocument(szOpenFileName);
               return;
            }
            psz += strlen(psz) + 1;
         }
         free(pszDirs);
      }
   }
   MsgBox("Cannot find package source file. Start this script with -config "
          "option and check ClassPath", MB_OK);
}

/*-------------------------------------------------------------------
                      ConfigClassPath

  Configures java class path string. Directories where JavaImport will
  search for java packages
-------------------------------------------------------------------*/

void ConfigClassPath(void)
{
   MsgBox("Enter directories where this script will search for imported "
          "Java package files. You can specify more than one directory "
          "separated with semicolon \";\".", MB_OK | MB_ICONINFORMATION);

   if(Gte_GetStringDlg("JavaImport", "&Class path:", g_szClassPath, sizeof(g_szClassPath)))
      WritePrivateProfileString("Script", "JavaImport_ClassPath", g_szClassPath, "gte.ini");
}