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        if (sys == null) {
072          sys =
073            System.getProperty("java.home")
074            + java.io.File.separator
075            + "lib"
076            + java.io.File.separator
077            + "rt.jar";
078          //System.out.println("SYS-JH " + sys);
079        }
080    
081        if (sys != null && !sys.equals("")) {
082          if (!classPath.equals("")) {
083            classPath += System.getProperty("path.separator", ":");
084          }
085          classPath += sys;
086        }
087        compositeSourcePath = sourcePath;
088        compositeClassPath = classPath;
089      }
090    
091      public void setup() {
092        // javafe.util.Info.on = options.v;
093        setupPaths();
094        Info.out("[Full classpath is " + compositeClassPath + "]");
095        Info.out("[Full sourcepath is " + compositeSourcePath + "]");
096    
097        // It is ok if sourcePath is null; it then shares a database
098        // of the contents of the directory path with classpath,
099        // rather than creating a separate, identical database.
100        OutsideEnv.init(
101                        makeStandardTypeReader(
102                                               compositeClassPath,
103                                               compositeSourcePath,
104                                               makePragmaParser()));
105    
106        PrettyPrint.inst = makePrettyPrint();
107        TypeCheck.inst = makeTypeCheck();
108      }
109    
110      /**
111       * Called to clear any static initializations, so that the parser
112       * can be called multiple times within one process.  Called as
113       * part of construction of a new Main.
114       */
115      public void clear(boolean complete) {
116        ErrorSet.clear();
117        // FIXME LocationManagerCorrelatedReader.clear();
118        OutsideEnv.clear();
119      }
120    
121      /**
122       * Called to obtain the {@link StandardTypeReader} to be used for
123       * locating and reading in types.
124       */
125      //@ ensures \result != null;
126      public StandardTypeReader makeStandardTypeReader(
127                                                       String path,
128                                                       String sourcePath,
129                                                       PragmaParser P) {
130        return StandardTypeReader.make(path, sourcePath, P);
131      }
132    
133      /**
134       * Called to obtain the pragma parser to be used for parsing input
135       * files.  If <code>null</code> is returned, then no pragma
136       * parsing is done.  (By default, returns <code>null</code>).
137       */
138      public PragmaParser makePragmaParser() {
139        return null;
140      }
141    
142      /**
143       * Called to create a new {@link Options} object.
144       */
145      //@ ensures \result != null;
146      public Options makeOptions() {
147        return new Options();
148      }
149    
150      /** Processes the options into the current Options instance as
151       *  contained in the options field.
152       * @param args The command-line arguments to process
153       * @throws UsageError if the sequence of command-line arguments 
154       *                      is invalid
155       */
156      //@ requires args != null;   
157      public void processOptions(String[] args) throws UsageError {
158        options.processOptions(args);
159      }
160    
161      /**
162       * Called to obtain the pretty printer to set {@link
163       * PrettyPrint#inst} to.  May not return <code>null</code>.  By
164       * default, returns {@link javafe.ast.StandardPrettyPrint}.
165       */
166      //@ ensures \result != null;
167      public PrettyPrint makePrettyPrint() {
168        return new StandardPrettyPrint();
169      }
170    
171      /**
172       * Called to obtain an instance of the {@link javafe.tc.TypeCheck}
173       * class (or a subclass thereof) to be used for typechecking. May
174       * not return <code>null</code>.  By default, returns {@link
175       * javafe.tc.TypeCheck}.
176       */
177      //@ ensures \result != null;
178      public TypeCheck makeTypeCheck() {
179        return new TypeCheck();
180      }
181    
182      /***************************************************
183       *                                                 *
184       * Main processing code:                             *
185       *                                                 *
186       **************************************************/
187    
188      /**
189       * Start up an instance of this tool using command-line arguments
190       * <code>args</code>.
191       *
192       * <p> <strong>Note</strong>: this code needs to be copied
193       * verbatim to each subclass of {@link Tool} except with the name
194       * of the actual subclass inserted after the new operator and the
195       * comment characters (//) removed. </p>
196       *
197       * <p> (This needs to be done because static methods cannot be
198       * inherited.) </p>
199       */
200      //@ requires \nonnullelements(args);
201      public static void main(String[] args) {
202        // Tool t = new FrontEndTool();
203        // int result = t.run(args);
204        // if (result != 0) System.exit(result);
205      }
206    
207      /**
208       * Parses the options into a new instance of an {@link Options}
209       * subclass.  The new instance is assigned to the options field.
210       * If the argument is null, the tool is initialized with the
211       * existing options (the options field is unchanged).  The tool
212       * is then initialized (by calling setup) with the designated options.
213       * 
214       * @param args Either null or an array of arguments used to 
215       *             initialize the Options structure
216       * @return     Returns -1 if arguments were parsed satisfactorily,
217       *               otherwise returns the exit code with which to 
218       *               terminate the program
219       */
220    
221      //@ ensures args == null ==> \not_modified(options);
222      // FIXME //@ ensures args == null ==> \not_modified(options.* );
223      public int handleOptions(String[] args) {
224        if (args != null) {
225          try {
226            // Handle all tool options:
227            options = makeOptions();
228            processOptions(args);
229            if (options.issueUsage) {
230              usage();
231              return okExitCode;
232            }
233          } catch (UsageError e) {
234            badOptionUsage(e);
235            ErrorSet.errors++; // Just so that the JUnit tests detect that
236            // an error message was issued
237            return badUsageExitCode;
238          } catch (FatalError e) {
239            Info.out("[" + name() + " exiting due to a fatal error]");
240          }
241        }
242        // Setup the front end using the options:
243        setup();
244        return -1;
245      }
246    
247      /**
248       * A tool's main entry point, which should be overridden in derived classes
249       * to do the work of the tool.
250       * 
251       * @param args The command-line arguments the program was invoked with
252       * @return The exit code for the program, with 0 indicating success
253       * @see javafe.Tool#run(java.lang.String[])
254       */
255      /*@ also public normal_behavior
256        @  requires args != null;
257        @  modifies \everything;
258        @*/
259      public final int run(String[] args) {
260        int r = handleOptions(args);
261        if (r != -1)
262          return r;
263    
264        if (ErrorSet.errors == 0)
265          try {
266            // Do our front-end-tool-specific processing:
267            frontEndToolProcessing(options.inputEntries);
268          } catch (FatalError e) {
269            Info.out("[" + name() + " exiting due to a fatal error]");
270          }
271    
272        if (ErrorSet.cautions != 0)
273          System.out.println(
274                             ErrorSet.cautions
275                             + " caution"
276                             + (ErrorSet.cautions > 1 ? "s" : ""));
277        if (ErrorSet.warnings != 0)
278          System.out.println(
279                             ErrorSet.warnings
280                             + " warning"
281                             + (ErrorSet.warnings > 1 ? "s" : ""));
282        if (ErrorSet.errors != 0)
283          System.out.println(
284                             ErrorSet.errors + " error" + (ErrorSet.errors > 1 ? "s" : ""));
285    
286        // If we call exit here, we will break GUI-based clients.
287        // Return error status to caller:
288        if (ErrorSet.errors > 0)
289          return errorExitCode;
290        else {
291          return okExitCode;
292        }
293      }
294    
295      /**
296       * Perform any front-end-tool-specific processing.
297       *
298       * <p> The remaining arguments are <code>args[offset]</code>,
299       * <code>args[offset+1]</code>, ...</p>
300       */
301      // requires \nonnullelements(args);
302      // requires 0 <= offset && offset <= args.length;
303      public abstract void frontEndToolProcessing(ArrayList args);
304    }