home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1999 mARCH / PCWK3A99.iso / Linux / DDD331 / DDD-3_1_.000 / DDD-3_1_ / ddd-3.1.1 / ddd / java.C < prev    next >
C/C++ Source or Header  |  1998-12-06  |  11KB  |  468 lines

  1. // $Id: java.C,v 1.5.4.2 1998/12/06 13:53:18 zeller Exp $ -*- C++ -*-
  2. // Java class lookup
  3.  
  4. // Copyright (C) 1998 Technische Universitaet Braunschweig, Germany.
  5. // Written by Andreas Zeller <zeller@ips.cs.tu-bs.de>.
  6. // 
  7. // This file is part of DDD.
  8. // 
  9. // DDD is free software; you can redistribute it and/or
  10. // modify it under the terms of the GNU General Public
  11. // License as published by the Free Software Foundation; either
  12. // version 2 of the License, or (at your option) any later version.
  13. // 
  14. // DDD is distributed in the hope that it will be useful,
  15. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  17. // See the GNU General Public License for more details.
  18. // 
  19. // You should have received a copy of the GNU General Public
  20. // License along with DDD -- see the file COPYING.
  21. // If not, write to the Free Software Foundation, Inc.,
  22. // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  23. // 
  24. // DDD is the data display debugger.
  25. // For details, see the DDD World-Wide-Web page, 
  26. // `http://www.cs.tu-bs.de/softech/ddd/',
  27. // or send a mail to the DDD developers <ddd@ips.cs.tu-bs.de>.
  28.  
  29. char java_rcsid[] = 
  30.     "$Id: java.C,v 1.5.4.2 1998/12/06 13:53:18 zeller Exp $";
  31.  
  32. #ifdef __GNUG__
  33. #pragma implementation
  34. #endif
  35.  
  36. #include "java.h"
  37.  
  38. #include "SmartC.h"
  39. #include "SourceView.h"
  40. #include "assert.h"
  41. #include "cook.h"
  42. #include "ddd.h"
  43. #include "filetype.h"
  44. #include "glob.h"
  45. #include "regexps.h"
  46. #include "status.h"
  47.  
  48.  
  49.  
  50. //-----------------------------------------------------------------------------
  51. // Constants
  52. //-----------------------------------------------------------------------------
  53.  
  54. // Suffixes for Java sources and classes
  55. #define JAVA_SRC_SUFFIX   ".java"
  56. #define JAVA_CLASS_SUFFIX ".class"
  57.  
  58.  
  59. //-----------------------------------------------------------------------------
  60. // Helpers
  61. //-----------------------------------------------------------------------------
  62.  
  63. // Don't rely on libiberty basename() because we don't want to depend
  64. // on libiberty include files
  65. static const char *file_basename(const char *name)
  66. {
  67.     const char *base = name;
  68.  
  69.     while (*name)
  70.     {
  71.     if (*name++ == '/')
  72.         base = name;
  73.     }
  74.  
  75.     return base;
  76. }
  77.  
  78. #define basename file_basename
  79.  
  80.  
  81. static bool is_archive(const string& loc)
  82. {
  83.     return loc.contains(".jar", -1) || 
  84.        loc.contains(".zip", -1) ||
  85.        loc.contains(".JAR", -1) ||
  86.        loc.contains(".ZIP", -1);
  87. }
  88.  
  89. static string concat_dir(const string& dir, const string& file)
  90. {
  91.     string mask;
  92.     if (dir == "")
  93.     mask = file;
  94.     else if (dir.contains('/', -1))
  95.     mask = dir + file;
  96.     else
  97.     mask = dir + "/" + file;
  98.  
  99.     return mask;
  100. }
  101.  
  102. // Store all classes in DIR in CLASSES_LIST.  BASE is the initial dir.  If
  103. // WITH_SOURCE_ONLY is set, consider only classes with loadable sources.
  104. static void get_java_classes(const string& dir, const string& base,
  105.                  StringArray& classes_list, bool with_source_only)
  106. {
  107.     StatusDelay delay("Scanning " + quote(dir) + " for classes");
  108.  
  109.     assert((base == "" && dir == "") || dir.contains(base, 0));
  110.  
  111.     string mask = concat_dir(dir, "*" JAVA_CLASS_SUFFIX);
  112.  
  113.     // Check for `.class' files in this directory.
  114.     char **files = glob_filename(mask);
  115.     if (files == (char **)0)
  116.     {
  117.     delay.outcome = mask + ": glob failed";
  118.     }
  119.     if (files == (char **)-1)
  120.     {
  121.     // No `*.class' in this directory
  122.     }
  123.     else
  124.     {
  125.     for (int i = 0; files[i] != 0; i++)
  126.     {
  127.         string file = files[i];
  128.  
  129.         // Build a class name from file name
  130.         string class_name;
  131.         if (base == "")
  132.         {
  133.         class_name = file;
  134.         }
  135.         else
  136.         {
  137.         class_name = file.after((int)base.length());
  138.         if (class_name.contains('/', 0))
  139.             class_name = class_name.after('/');
  140.         }
  141.  
  142.         strip_java_suffix(class_name);
  143.         class_name.gsub('/', '.');
  144.  
  145.         bool have_source = true;
  146.         if (with_source_only)
  147.         {
  148.         // Check whether we have some .java source for
  149.         // this class
  150.         string class_file = java_class_file(class_name);
  151.         have_source = (class_file != "");
  152.         }
  153.  
  154.         if (have_source)
  155.         {
  156.         // Okay - we have a class and a corresponding
  157.         // .java source file.  Go for it.
  158.         classes_list += class_name;
  159.         }
  160.  
  161.         free(files[i]);
  162.     }
  163.  
  164.     free((char *)files);
  165.     }
  166.  
  167.     // Check for `.class' files in subdirectories.
  168.     mask = concat_dir(dir, "*");
  169.     files = glob_filename(mask);
  170.     if (files == (char **)0)
  171.     {
  172.     delay.outcome = mask + ": glob failed\n";
  173.     }
  174.     else if (files == (char **)-1)
  175.     {
  176.     // No `*' in this directory
  177.     }
  178.     else
  179.     {
  180.     for (int i = 0; files[i] != 0; i++)
  181.     {
  182.         string file = basename(files[i]);
  183.         free(files[i]);
  184.  
  185.         if (file.matches(rxidentifier))
  186.         {
  187.         file = concat_dir(dir, file);
  188.         if (is_directory(file))
  189.             get_java_classes(file, base, classes_list, 
  190.                      with_source_only);
  191.         }
  192.     }
  193.  
  194.     free((char *)files);
  195.     }
  196. }
  197.  
  198.  
  199. //-----------------------------------------------------------------------------
  200. // Main functions
  201. //-----------------------------------------------------------------------------
  202.  
  203. // Store all classes in current use path in CLASSES_LIST.  If
  204. // WITH_SOURCE_ONLY is set, consider only classes with loadable sources.
  205. void get_java_classes(StringArray& classes_list, bool with_source_only)
  206. {
  207.     string use = source_view->class_path();
  208.     while (use != "")
  209.     {
  210.     // Determine current USE entry
  211.     string loc;
  212.     if (use.contains(':'))
  213.         loc = use.before(':');
  214.     else
  215.         loc = use;
  216.     use = use.after(':');
  217.  
  218.     if (is_archive(loc))
  219.     {
  220.         // Archive file.
  221.         // Should we search this for classes? (FIXME)
  222.         continue;
  223.     }
  224.  
  225.     if (!is_directory(loc))
  226.     {
  227.         // Not a directory.
  228.         continue;
  229.     }
  230.  
  231.     // Get all classes in this directory
  232.     get_java_classes(loc, loc, classes_list, with_source_only);
  233.     }
  234.  
  235.     smart_sort(classes_list);
  236. }
  237.  
  238. // Remove `.java' and `.class' suffix from S
  239. void strip_java_suffix(string& s)
  240. {
  241.     string s_down = downcase(s);
  242.  
  243.     if (s_down.contains(JAVA_SRC_SUFFIX, -1))
  244.     s = s.before(int(int(s.length()) - strlen(JAVA_SRC_SUFFIX)));
  245.     if (s_down.contains(JAVA_CLASS_SUFFIX, -1))
  246.     s = s.before(int(int(s.length()) - strlen(JAVA_CLASS_SUFFIX)));
  247. }
  248.  
  249. // Read FILE into S
  250. static void slurp_file(const string& file, string& s)
  251. {
  252.     s = "";
  253.  
  254.     FILE *fp = fopen(file, "r");
  255.     if (fp == NULL)
  256.     return;
  257.  
  258.     while (!feof(fp))
  259.     {
  260.     char buffer[BUFSIZ];
  261.     int nitems = fread(buffer, sizeof(char), BUFSIZ, fp);
  262.     s += string(buffer, nitems);
  263.     }
  264.  
  265.     fclose(fp);
  266. }
  267.  
  268. // Return the position of CLASS_NAME definition in TEXT; -1 if none
  269. int java_class_start(const string& text, const string& class_name, 
  270.              bool first_line)
  271. {
  272.     // We search for `class [whitespace] CLASS_NAME [whitespace]'.
  273.     int i = -1;
  274.     while ((i = text.index("class", i + 1)) >= 0)
  275.     {
  276.     while (i < int(text.length()) && !isspace(text[i]))
  277.         i++;
  278.     while (i < int(text.length()) && isspace(text[i]))
  279.         i++;
  280.  
  281.     if (text.contains(class_name, i))
  282.     {
  283.         int start = i;
  284.         i += class_name.length();
  285.         if (i < int(text.length()) && isspace(text[i]))
  286.         {
  287.         // Be sure the class name is followed by `{'
  288.         while (i < int(text.length()) && 
  289.                text[i] != ';' && text[i] != '{')
  290.             i++;
  291.         if (i < int(text.length()) && text[i] == '{')
  292.         {
  293.             // Okay, we got it.
  294.             if (first_line)
  295.             {
  296.             // Return first line after `{'
  297.             while (i < int(text.length()) && 
  298.                    text[i - 1] != '\n' && text[i - 1] != '}')
  299.                 i++;
  300.             return i;
  301.             }
  302.             else
  303.             {
  304.             // Return start of class definition.
  305.             return start;
  306.             }
  307.         }
  308.         }
  309.     }
  310.     }
  311.  
  312.     return -1;
  313. }
  314.  
  315. // Check if FILE_NAME has a definition of CLASS_NAME
  316. static bool has_class(const string& file_name, const string& class_name)
  317. {
  318.     string src_file;
  319.     slurp_file(file_name, src_file);
  320.     return java_class_start(src_file, class_name) > 0;
  321. }
  322.  
  323. // Return source file of CLASS_NAME; "" if none.
  324. static string _java_class_file(const string& class_name, bool search_classes)
  325. {
  326.     // We use 4 iterations:
  327.     // Trial 0.  Search for CLASS_NAME.java; make sure it defines CLASS_NAME.
  328.     // Trial 1.  Search for CLASS_NAME.class and its .java file, as in 0.
  329.     // Trial 2.  Search for CLASS_NAME.java, even if CLASS_NAME is not def'd.
  330.     // Trial 3.  Search for CLASS_NAME.class and its .java file, as in 2.
  331.     for (int trial = 0; trial < 4; trial++)
  332.     {
  333.     string base = class_name;
  334.     strip_java_suffix(base);
  335.     base.gsub(".", "/");
  336.  
  337.     switch (trial)
  338.     {
  339.     case 0:
  340.     case 2:
  341.         base += JAVA_SRC_SUFFIX;
  342.         break;
  343.  
  344.     case 1:
  345.     case 3:
  346.         if (!search_classes)
  347.         continue;
  348.         base += JAVA_CLASS_SUFFIX;
  349.         break;
  350.  
  351.     default:
  352.         return "";        // No such trial
  353.     }
  354.  
  355.     string use = source_view->class_path();
  356.     while (use != "")
  357.     {
  358.         string loc;
  359.         if (use.contains(':'))
  360.         loc = use.before(':');
  361.         else
  362.         loc = use;
  363.         use = use.after(':');
  364.  
  365.         if (is_archive(loc))
  366.         {
  367.         // Archive file.
  368.         // Should we search this for classes? (FIXME)
  369.         continue;
  370.         }
  371.  
  372.         if (!is_directory(loc))
  373.         {
  374.         // Not a directory.
  375.         continue;
  376.         }
  377.  
  378.         string file_name;
  379.         if (loc == "" || loc == ".")
  380.         {
  381.         file_name = base;
  382.         }
  383.         else
  384.         {
  385.         if (!loc.contains('/', -1))
  386.             loc += '/';
  387.         file_name = loc + base;
  388.         }
  389.  
  390.         switch (trial)
  391.         {
  392.         case 0:
  393.         case 2:
  394.         // Look for CLASS_NAME.java 
  395.         if (is_source_file(file_name) && 
  396.             (trial > 0 || has_class(file_name, class_name)))
  397.             return file_name;
  398.         break;
  399.  
  400.         case 1:
  401.         case 3:
  402.         {
  403.         // Look for CLASS_NAME.class and scan it for
  404.         // `SRC.java' occurrences.
  405.         if (is_regular_file(file_name))
  406.         {
  407.             string class_file;
  408.             slurp_file(file_name, class_file);
  409.  
  410.             // We begin searching at the end of the file,
  411.             // because that's where debugging information is
  412.             // usually placed.
  413.             int i = class_file.length();
  414.             while ((i = 
  415.                 class_file.index(JAVA_SRC_SUFFIX, 
  416.                          i - class_file.length() - 1)) > 0)
  417.             {
  418.             // The class file contains `.java' at I
  419.             string src_class;
  420.             string c;
  421.  
  422.             // Find start of name
  423.             int start = i;
  424.             while (start > 0 && isalnum(class_file[start - 1]))
  425.                 start--;
  426.             src_class = class_file(start, i - start);
  427.  
  428.             // Search for this class file instead.
  429.             c = java_class_file(src_class, false);
  430.             if (c != "")
  431.                 return c;
  432.  
  433.             // JDK javac places source file names as \001
  434.             // LENGTH NAME, where LENGTH is the length of
  435.             // NAME in 2-byte (lo/hi) format.  Try this.
  436.  
  437.             while (start >= 0 && class_file[start] != '\001')
  438.                 start--;
  439.             start += 3;    // Skip \001 LENGTH
  440.             src_class = class_file(start, i - start);
  441.                 
  442.             c = java_class_file(src_class, false);
  443.             if (c != "")
  444.                 return c;
  445.             }
  446.         }
  447.         }
  448.         }
  449.     }
  450.     }
  451.  
  452.     return "";            // Not found
  453. }
  454.  
  455. // Same, but with diagnostics
  456. string java_class_file(const string& class_name, bool search_classes)
  457. {
  458.     StatusDelay delay("Searching for " + quote(class_name) + " source");
  459.  
  460.     string c = _java_class_file(class_name, search_classes);
  461.     if (c == "")
  462.     delay.outcome = "failed";
  463.     else
  464.     delay.outcome = quote(c);
  465.  
  466.     return c;
  467. }
  468.