001    /* Copyright 2000, 2001, Compaq Computer Corporation */
002    
003    package javafe;
004    
005    
006    import java.util.Vector;
007    
008    import javafe.ast.*;
009    import javafe.tc.*;
010    import javafe.util.*;
011    
012    import java.io.BufferedReader;
013    import java.io.FileReader;
014    import java.io.*;
015    
016    import java.util.StringTokenizer;
017    import java.util.ArrayList;
018    import java.util.Iterator;
019    
020    public class CopyLoaded extends FrontEndTool implements Listener {
021    
022        public String name() { return "CopyLoaded"; }
023    
024        private PrintWriter libIndirectWriter;
025        private PrintWriter progIndirectWriter;
026    
027        /* 
028         * This maps files from original progIndirect file names to their names 
029         * relative to the outDir
030         */
031        public final Vector progIndirectFiles = new Vector();
032        //+@ invariant progIndirectFiles.elementType == \type(String);
033        //@  invariant progIndirectFiles.owner == this;
034        //+@ invariant !progIndirectFiles.containsNull;
035    
036        public final Vector argumentFileNames = new Vector();
037        //+@ invariant argumentFileNames.elementType == \type(String);
038        //+@ invariant !argumentFileNames.containsNull;
039        //@  invariant argumentFileNames.owner == this;
040    
041    
042    
043        /***************************************************
044         *                                                 *
045         * Keeping track of loaded CompilationUnits:       *
046         *                                                 *
047         **************************************************/
048    
049    
050        //@  invariant loaded != null;
051        //+@ invariant loaded.elementType == \type(CompilationUnit);
052        //+@ invariant !loaded.containsNull;
053        //@  invariant loaded.owner == this;
054        public Vector loaded = new Vector();
055    
056        //@ invariant loaded != argumentFileNames;
057     
058        public CopyLoaded() {
059            //+@ set argumentFileNames.elementType = \type(String);
060            //+@ set argumentFileNames.containsNull = false;
061            //@  set argumentFileNames.owner = this;
062        
063            //+@ set loaded.elementType = \type(CompilationUnit);
064            //+@ set loaded.containsNull = false;
065            //@  set loaded.owner = this;
066        
067            //+@ set progIndirectFiles.containsNull = false;
068            //+@ set progIndirectFiles.elementType = \type(String);
069            //@  set progIndirectFiles.owner = this;
070        }
071    
072    
073        public void setup() { 
074            super.setup();
075        }
076    
077    
078        private String packageDirForFile(/*@ non_null */ CompilationUnit cu) {
079            Name pkg = cu.pkgName;
080            String s = pkg == null ? "" : pkg.printName() + ".";
081            s = s.replace('.', '/'); 
082            return s;
083        }
084    
085        public void notify(CompilationUnit justLoaded) {
086            loaded.addElement(justLoaded);
087         
088            String fileName = Location.toFileName(justLoaded.loc);
089            /* if a Java file, then copy the file over into outDir */
090            if (fileName.endsWith(".java")) {
091                copySourceFile(fileName, 
092                       packageDirForFile(justLoaded) + fileNameName(fileName));
093            } else {
094                TypeDeclVec elems = justLoaded.elems;
095                /* generate a spec file for each type decl in compilation unit */
096                for (int i=0; i<elems.size(); i++) {
097                    TypeDecl d = elems.elementAt(i);
098                    TypeSig sig = TypeCheck.inst.getSig(d);
099                    if (d.specOnly || d.isBinary()) {
100                        printSpec(sig.toString());
101                    }
102                }
103            }
104        }
105    
106        /***************************************************
107         *                                                 *
108         * Main processing code:                   *
109         *                                                 *
110         **************************************************/
111    
112    
113    
114        //@ requires \nonnullelements(args);
115        public static void main(String[] args) {
116            Tool t = new CopyLoaded();
117            int result = t.run(args);
118            if (result != 0) System.exit(result);
119        }
120        
121        public Options makeOptions() {
122            return new CopyLoadedOptions();
123        }
124        
125        public CopyLoadedOptions options() {
126            return (CopyLoadedOptions)options;
127        }
128    
129    
130    
131        //@ ensures \nonnullelements(\result);
132        //@ ensures \result != null;
133        public String[] FQNpackage(/*@ non_null */ String s) {
134            StringTokenizer st = new StringTokenizer(s, ".", false);
135            int len = st.countTokens();
136            return fillArray(st, len-1);
137        } 
138    
139        //@ ensures \result != null;
140        public String FQNname(/*@ non_null */ String s) {
141             return s.substring(s.lastIndexOf(".") + 1);
142        } 
143     
144        //@ ensures \nonnullelements(\result);
145        //@ ensures \result != null;
146        public String[] fileNamePackage(/*@ non_null */ String file) {
147            StringTokenizer st = new StringTokenizer(file, "/", false);
148            int len = st.countTokens();
149            return fillArray(st, len-1);
150        } 
151    
152    
153        //@ ensures \result != null;
154        public String fileNameName(/*@ non_null */ String s) {
155             return s.substring(s.lastIndexOf("/") + 1);
156        }    
157    
158        //@ ensures \nonnullelements(\result);
159        //@ ensures \result != null;
160        public String[] directoryPackage(/*@ non_null */ String dir) {
161            StringTokenizer st = new StringTokenizer(dir, "/", false);
162            int len = st.countTokens();
163            return fillArray(st, len);
164        } 
165    
166        //@ ensures \nonnullelements(\result);
167        //@ ensures \result != null;
168        private String[] fillArray(/*@ non_null */ StringTokenizer st, int len) {
169            if (len < 0) {
170                return new String[0];
171            }
172            String array[] = new String[len];
173            for (int i = 0; i < len; i++) {
174                array[i] = st.nextToken();
175            }
176            return array;
177        }    
178    
179        //@ requires \nonnullelements(P);
180        private String makeDirTree(/*@ non_null */ String root, 
181                                   /*@ non_null */ String P[]) {
182            String s = root;
183            for (int i = 0; i < P.length; i++) {
184                s = s + "/" + P[i];
185                File f = new File(s);
186                if (!f.exists()) {
187                    System.out.println("[making " + s + "]");
188                    f.mkdir();            
189                } else {
190                }
191            }
192            return s;
193        }
194    
195        //@ requires \nonnullelements(P);
196        //@ ensures \result != null;
197        private String makeDirPath(/*@ non_null */ String P[]) {
198            String s = "";
199            for (int i = 0; i < P.length; i++) {
200                s = s + P[i] +"/";
201            }
202            return s;
203        }
204    
205        /**
206         * Prints the spec file for the FQN s.  The file is written
207         * relative to the outDir.
208         */  
209        public void printSpec(/*@ non_null */ String s) {
210            String P[] = FQNpackage(s);
211            String T = FQNname(s);
212            TypeSig sig = OutsideEnv.lookup(P, T);
213            Assert.notFalse(sig != null); //@ nowarn Pre;
214        
215            String path = makeDirTree(options().outDir, P);
216            String outFile = T + ".spec";
217            String filename = path + "/" +  outFile;
218        
219            System.out.println("[generating spec file for " + s + 
220                               " as " + filename + "]");
221        
222            //@ assume libIndirectWriter != null;
223            libIndirectWriter.println("./" + makeDirPath(P) + outFile);
224        
225            try {
226                FileOutputStream fos = null;
227                try {
228                    fos = new FileOutputStream(filename);
229                    PrettyPrint.inst.print(fos, sig.getCompilationUnit());
230                } finally {
231                    if (fos != null) fos.close();
232                }
233            } catch (Exception e) {
234                ErrorSet.fatal(e.getMessage());
235            }
236        }
237    
238        public final void frontEndToolProcessing(ArrayList args) {
239            /*
240             * At this point, all options have already been processed and
241             * the front end has been initialized.
242             */
243        
244            String outDir = options().outDir;
245            String outProgIndirect = options().outProgIndirect;
246            String outLibIndirect = options().outLibIndirect;
247            
248            System.out.println("[outdir is " + outDir + "]");
249        
250            // Set up to receive CompilationUnit-loading notification events:
251            OutsideEnv.setListener(this);
252        
253            // create the outDir
254            if (outDir.startsWith(".")) {
255                makeDirTree(".", directoryPackage(outDir));
256            } else {
257                makeDirTree("", directoryPackage(outDir));
258            } 
259            
260            // set up the indirection files.
261            try {
262                progIndirectWriter =
263                  new PrintWriter(new FileWriter(new File(outProgIndirect)));
264                libIndirectWriter = 
265                  new PrintWriter(new FileWriter(new File(outLibIndirect)));
266            } catch (IOException e) {
267                ErrorSet.fatal(e.getMessage());
268            }
269        
270            /*
271             * Load in each source file:
272             */
273            Iterator i = args.iterator();
274            while (i.hasNext()) OutsideEnv.addSource((String)i.next());
275        
276             /* load in source files from supplied file name */
277            i = argumentFileNames.iterator();
278            while (i.hasNext()) {
279                String argumentFileName = (String)i.next();
280                try {
281                        BufferedReader in = null;
282                    try {
283                        in = new BufferedReader(
284                                new FileReader(argumentFileName));
285                        String s;
286                        while ((s = in.readLine()) != null) {
287                            // allow blank lines in files list
288                            if (!s.equals("")) {
289                                progIndirectFiles.addElement(s);
290                                OutsideEnv.addSource(s); 
291                            }
292                        }
293                    } finally {
294                        if (in != null) in.close();
295                    }
296                } catch (IOException e) {
297                    ErrorSet.fatal(e.getMessage());
298                }
299            }
300        
301            i = loaded.iterator();
302            while (i.hasNext()) {
303                handleCU((CompilationUnit)i.next());
304            }
305        
306            progIndirectWriter.close();
307            libIndirectWriter.close();
308        }
309    
310    
311        /**
312         * Copy the source file original into the file newName.  newName
313         * is appended to the outDir to construct the full file location
314         * of the new file.  This method also puts the newName into the
315         * correct indirection file.
316         */
317        private void copySourceFile(/*@ non_null */ String original, 
318                    /*@ non_null */ String newName) {
319            try {
320                String path = makeDirTree(options().outDir, fileNamePackage(newName));
321                String newFileName = path + "/" + fileNameName(newName);
322        
323                System.out.println("[copying source file " + original + 
324                           " to " + newFileName + "]");
325        
326                //@ assume libIndirectWriter != null;
327                //@ assume progIndirectWriter != null;
328                if (progIndirectFiles.contains(original)) {
329                    progIndirectWriter.println("./" + newName);
330                } else {
331                    libIndirectWriter.println("./" + newName);
332                }
333                
334                File f = new File(newFileName);
335                BufferedReader reader = null;
336                PrintWriter writer = null;
337                try {
338                    reader = new BufferedReader(new FileReader(original));
339                    try {
340                        writer = new PrintWriter(new FileWriter(f));
341                        String s;
342                        while ((s = reader.readLine()) != null) {
343                            writer.println(s);
344                        }
345                    } finally {
346                        if (writer != null) writer.close();
347                    }
348                } finally {
349                    if (reader != null) reader.close();
350                }
351            } catch (IOException e) {
352                ErrorSet.fatal(e.getMessage());
353            }
354        }
355    
356    
357        /**
358         * Process each CU's type decls.
359         */
360        public void handleCU(/*@ non_null */ CompilationUnit cu) {
361            // Iterate over all the TypeDecls representing outside types in cu:
362            TypeDeclVec elems = cu.elems;
363            for (int i=0; i<elems.size(); i++) {
364                TypeDecl d = elems.elementAt(i);
365                handleTD(d);
366            }
367        }
368    
369        /**
370         * Called from handleCU on each TypeDecl from the CU's loaded from the
371         * program files.  In addition, it calls itself recursively to handle types
372         * nested within outside types.<p>
373         */
374        public void handleTD(/*@ non_null */ TypeDecl td) {
375            TypeSig sig = TypeCheck.inst.getSig(td);
376            if (sig.getTypeDecl().specOnly)    // do not process specs
377                return;
378            
379            System.out.println("\n" + sig.toString() + " ...");
380            
381                        // Do actual work:
382            boolean aborted = processTD(td);
383            
384            TypeDecl decl = sig.getTypeDecl();
385            for (int i=0; i<decl.elems.size(); i++) {
386                if (decl.elems.elementAt(i) instanceof TypeDecl)
387                handleTD((TypeDecl)decl.elems.elementAt(i)); //@ nowarn Cast;
388            }
389        }
390        
391        
392        /**
393         * Typecheck a TypeDecl;
394         * return true if we had to abort. <p>
395         *
396         * Precondition: td is not from a binary file.<p>
397         */
398        private boolean processTD(/*@ non_null */ TypeDecl td) {
399            int errorCount = ErrorSet.errors;
400            TypeSig sig = TypeCheck.inst.getSig(td);
401            sig.typecheck();
402        
403            return false;
404        }
405        
406    
407    }
408    
409    class CopyLoadedOptions extends SrcToolOptions {
410    
411        /*@ non_null */ public String outDir = "./outdir/src-annotated";
412        /*@ non_null */ public String outProgIndirect = "./outProgIndirect";
413        /*@ non_null */ public String outLibIndirect = "./outLibIndirect";
414    
415       
416        public String showNonOptions() {
417            return ("<program indirection file>");
418        }
419    
420        public String showOptions(boolean all) {
421            StringBuffer sb = new StringBuffer(super.showOptions(all));
422            sb.append("  -outdir <root of output files>"); sb.append(eol);
423            sb.append("  -outProgIndirect <new prog Indirect file>");sb.append(eol);
424            sb.append("  -outLibIndirect <new lib Indirect file>"); sb.append(eol);
425            sb.append("  -f <file containing source file names>"); sb.append(eol);
426            return sb.toString();
427        }
428    
429    
430        /***************************************************
431         *                                                 *
432         * Option processing:                   *
433         *                                                 *
434         **************************************************/
435    
436        //private final String name = "CopyLoaded";
437        
438        public int processOption(String option, String[] args, int offset) 
439                                            throws UsageError {
440            if (option.equals("-outProgIndirect")) {
441                if (offset>=args.length) {
442                    throw new UsageError("Option " + option + 
443                                             " requires one argument");
444                }
445                outProgIndirect = args[offset];
446                return offset + 1;
447            } else if (option.equals("-outLibIndirect")) {
448                if (offset>=args.length) {
449                    throw new UsageError("Option " + option + 
450                                             " requires one argument");
451                }
452                outLibIndirect = args[offset];
453                return offset + 1;
454            } else if (option.equals("-outdir")) {
455                if (offset>=args.length) {
456                    throw new UsageError("Option " + option + 
457                                             " requires one argument");
458                }
459                outDir = args[offset];
460                return offset + 1;
461            } else {
462                // Pass on unrecognized options:
463                return super.processOption(option, args, offset);
464            }
465        }
466    }