001    /* Copyright 2000, 2001, Compaq Computer Corporation */
002    
003    package javafe;
004    
005    import javafe.ast.PrettyPrint;
006    import javafe.ast.StandardPrettyPrint;
007    
008    import javafe.reader.StandardTypeReader;
009    import javafe.parser.PragmaParser;
010    
011    import javafe.tc.OutsideEnv;
012    import javafe.tc.TypeCheck;
013    
014    import javafe.util.*;
015    
016    import java.util.ArrayList;
017    
018    /**
019     * <code>FrontEndTool</code> is an abstract class for tools that use
020     * our Java front end.
021     *
022     * <p> It handles parsing the standard options for setting up the
023     * front end and initializing the front end using those options.  At
024     * the end of a run, it prints a count of how many cautions, warnings,
025     * and errors occurred (cf. <code>ErrorSet</code>).  It also handles
026     * catching <code>FatalError</code>s (see
027     * <code>ErrorSet.fatal</code>).  The remaining processing, if any, is
028     * front-end-tool specific. </p>
029     */
030    
031    public abstract class FrontEndTool extends Tool
032    {
033      /***************************************************
034       *                                                 *
035       * Standard front-end setup:                       *
036       *                                                 *
037       **************************************************/
038    
039      /**
040       * Setup: initialize the front end using the standard
041       * front-end-tool option variables ({@link Options#userPath}, 
042       * {@link Options#sysPath}).
043       *
044       * <p> This can be done only once.  The standard front-end-tool
045       * option variables have no effect after this point.  May exit
046       * with an error (via {@link ErrorSet#fatal(String)}). </p>
047       *
048       * <p> Ensures {@link OutsideEnv} has been properly initialized
049       * (except if an error occurs). </p>
050       *
051       * <p> Also initializes {@link PrettyPrint#inst} and {@link
052       * TypeCheck#inst} to their default front end values. </p>
053       */
054      protected String compositeSourcePath;
055      protected String compositeClassPath;
056    
057      public void setupPaths() {
058        String classPath = options.userPath;
059        if (classPath == null)
060          // The behavior of this code differs between 1.1 and 1.2:
061          classPath = javafe.filespace.ClassPath.current();
062    
063        String sourcePath = options.userSourcePath;
064    
065        String sys = options.sysPath;
066        if (sys == null) {
067          // This works only on Sun implementations of Java...
068          sys = System.getProperty("sun.boot.class.path", null);
069          //System.out.println("SYS-SUN " + sys);
070        }
071        Info.out("sysPath: " + sys);
072        if (sys == null) {
073          sys =
074            System.getProperty("java.home")
075            + java.io.File.separator
076            + "lib"
077            + java.io.File.separator
078            + "rt.jar";
079          //System.out.println("SYS-JH " + sys);
080        }
081    
082        // TODO: does this work if classPath is empty
083        Info.out("sysPath: " + sys);
084        if (sys != null && !sys.equals("")) {
085          if (!classPath.equals("")) {
086            classPath += java.io.File.pathSeparator;
087          }
088          classPath += sys;
089        }
090        compositeSourcePath = sourcePath;
091        compositeClassPath = classPath;
092      }
093    
094      public void setup() {
095        // javafe.util.Info.on = options.v;
096        setupPaths();
097        Info.out("[Full classpath is " + compositeClassPath + "]");
098        Info.out("[Full sourcepath is " + compositeSourcePath + "]");
099    
100        // It is ok if sourcePath is null; it then shares a database
101        // of the contents of the directory path with classpath,
102        // rather than creating a separate, identical database.
103        OutsideEnv.init(
104                        makeStandardTypeReader(
105                                               compositeClassPath,
106                                               compositeSourcePath,
107                                               makePragmaParser()));
108    
109        PrettyPrint.inst = makePrettyPrint();
110        TypeCheck.inst = makeTypeCheck();
111      }
112    
113      /**
114       * Called to clear any static initializations, so that the parser
115       * can be called multiple times within one process.  Called as
116       * part of construction of a new Main.
117       */
118      public void clear(boolean complete) {
119        ErrorSet.clear();
120        // FIXME LocationManagerCorrelatedReader.clear();
121        OutsideEnv.clear();
122      }
123    
124      /**
125       * Called to obtain the {@link StandardTypeReader} to be used for
126       * locating and reading in types.
127       */
128      //@ ensures \result != null;
129      public StandardTypeReader makeStandardTypeReader(
130                                                       String path,
131                                                       String sourcePath,
132                                                       PragmaParser P) {
133        return StandardTypeReader.make(path, sourcePath, P);
134      }
135    
136      /**
137       * Called to obtain the pragma parser to be used for parsing input
138       * files.  If <code>null</code> is returned, then no pragma
139       * parsing is done.  (By default, returns <code>null</code>).
140       */
141      public PragmaParser makePragmaParser() {
142        return null;
143      }
144    
145      /**
146       * Called to create a new {@link Options} object.
147       */
148      //@ ensures \result != null;
149      public Options makeOptions() {
150        return new Options();
151      }
152    
153      /** Processes the options into the current Options instance as
154       *  contained in the options field.
155       * @param args The command-line arguments to process
156       * @throws UsageError if the sequence of command-line arguments 
157       *                      is invalid
158       */
159      //@ requires args != null;   
160      public void processOptions(String[] args) throws UsageError {
161        options.processOptions(args);
162      }
163    
164      /**
165       * Called to obtain the pretty printer to set {@link
166       * PrettyPrint#inst} to.  May not return <code>null</code>.  By
167       * default, returns {@link javafe.ast.StandardPrettyPrint}.
168       */
169      //@ ensures \result != null;
170      public PrettyPrint makePrettyPrint() {
171        return new StandardPrettyPrint();
172      }
173    
174      /**
175       * Called to obtain an instance of the {@link javafe.tc.TypeCheck}
176       * class (or a subclass thereof) to be used for typechecking. May
177       * not return <code>null</code>.  By default, returns {@link
178       * javafe.tc.TypeCheck}.
179       */
180      //@ ensures \result != null;
181      public TypeCheck makeTypeCheck() {
182        return new TypeCheck();
183      }
184    
185      /***************************************************
186       *                                                 *
187       * Main processing code:                             *
188       *                                                 *
189       **************************************************/
190    
191      /**
192       * Start up an instance of this tool using command-line arguments
193       * <code>args</code>.
194       *
195       * <p> <strong>Note</strong>: this code needs to be copied
196       * verbatim to each subclass of {@link Tool} except with the name
197       * of the actual subclass inserted after the new operator and the
198       * comment characters (//) removed. </p>
199       *
200       * <p> (This needs to be done because static methods cannot be
201       * inherited.) </p>
202       */
203      //@ requires \nonnullelements(args);
204      public static void main(String[] args) {
205        // Tool t = new FrontEndTool();
206        // int result = t.run(args);
207        // if (result != 0) System.exit(result);
208      }
209    
210      /**
211       * Parses the options into a new instance of an {@link Options}
212       * subclass.  The new instance is assigned to the options field.
213       * If the argument is null, the tool is initialized with the
214       * existing options (the options field is unchanged).  The tool
215       * is then initialized (by calling setup) with the designated options.
216       * 
217       * @param args Either null or an array of arguments used to 
218       *             initialize the Options structure
219       * @return     Returns -1 if arguments were parsed satisfactorily,
220       *               otherwise returns the exit code with which to 
221       *               terminate the program
222       */
223    
224      //@ ensures args == null ==> \not_modified(options);
225      // FIXME //@ ensures args == null ==> \not_modified(options.* );
226      public int handleOptions(String[] args) {
227        if (args != null) {
228          try {
229            // Handle all tool options:
230            options = makeOptions();
231            processOptions(args);
232            if (options.issueUsage) {
233              usage();
234              return okExitCode;
235            }
236          } catch (UsageError e) {
237            badOptionUsage(e);
238            ErrorSet.errors++; // Just so that the JUnit tests detect that
239            // an error message was issued
240            return badUsageExitCode;
241          } catch (FatalError e) {
242            Info.out("[" + name() + " exiting due to a fatal error]");
243          }
244        }
245        // Setup the front end using the options:
246        setup();
247        return -1;
248      }
249    
250      /**
251       * A tool's main entry point, which should be overridden in derived classes
252       * to do the work of the tool.
253       * 
254       * @param args The command-line arguments the program was invoked with
255       * @return The exit code for the program, with 0 indicating success
256       * @see javafe.Tool#run(java.lang.String[])
257       */
258      /*@ also public normal_behavior
259        @  requires args != null;
260        @  modifies \everything;
261        @*/
262      public final int run(String[] args) {
263        int r = handleOptions(args);
264        if (r != -1)
265          return r;
266    
267        if (ErrorSet.errors == 0)
268          try {
269            // Do our front-end-tool-specific processing:
270            frontEndToolProcessing(options.inputEntries);
271          } catch (FatalError e) {
272            Info.out("[" + name() + " exiting due to a fatal error]");
273          }
274    
275        if (ErrorSet.cautions != 0)
276          System.out.println(
277                             ErrorSet.cautions
278                             + " caution"
279                             + (ErrorSet.cautions > 1 ? "s" : ""));
280        if (ErrorSet.warnings != 0)
281          System.out.println(
282                             ErrorSet.warnings
283                             + " warning"
284                             + (ErrorSet.warnings > 1 ? "s" : ""));
285        if (ErrorSet.errors != 0)
286          System.out.println(
287                             ErrorSet.errors + " error" + (ErrorSet.errors > 1 ? "s" : ""));
288    
289        // If we call exit here, we will break GUI-based clients.
290        // Return error status to caller:
291        if (ErrorSet.errors > 0)
292          return errorExitCode;
293        else {
294          return okExitCode;
295        }
296      }
297    
298      /**
299       * Perform any front-end-tool-specific processing.
300       *
301       * <p> The remaining arguments are <code>args[offset]</code>,
302       * <code>args[offset+1]</code>, ...</p>
303       */
304      // requires \nonnullelements(args);
305      // requires 0 <= offset && offset <= args.length;
306      public abstract void frontEndToolProcessing(ArrayList args);
307    }