KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > google > gwt > dev > jdt > CacheManager


1 /*
2  * Copyright 2007 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */

16 package com.google.gwt.dev.jdt;
17
18 import com.google.gwt.core.ext.TreeLogger;
19 import com.google.gwt.core.ext.UnableToCompleteException;
20 import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider;
21 import com.google.gwt.core.ext.typeinfo.HasMetaData;
22 import com.google.gwt.core.ext.typeinfo.JClassType;
23 import com.google.gwt.core.ext.typeinfo.JType;
24 import com.google.gwt.core.ext.typeinfo.TypeOracle;
25 import com.google.gwt.dev.shell.JavaScriptHost;
26 import com.google.gwt.dev.shell.ShellGWT;
27 import com.google.gwt.dev.shell.ShellJavaScriptHost;
28 import com.google.gwt.dev.util.Util;
29
30 import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
31 import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
32 import org.eclipse.jdt.internal.compiler.ast.Javadoc;
33 import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
34 import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
35 import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
36 import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
37 import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
38 import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
39
40 import java.io.File JavaDoc;
41 import java.io.FileInputStream JavaDoc;
42 import java.io.FileOutputStream JavaDoc;
43 import java.io.IOException JavaDoc;
44 import java.io.ObjectInputStream JavaDoc;
45 import java.io.ObjectOutputStream JavaDoc;
46 import java.util.AbstractMap JavaDoc;
47 import java.util.HashMap JavaDoc;
48 import java.util.HashSet JavaDoc;
49 import java.util.IdentityHashMap JavaDoc;
50 import java.util.Iterator JavaDoc;
51 import java.util.Map JavaDoc;
52 import java.util.Set JavaDoc;
53 import java.util.TreeSet JavaDoc;
54
55 /**
56  * CacheManager manages all the caching used to speed up hosted mode startup and
57  * refresh, and manages the invalidations required to ensure that changes are
58  * reflected correctly on reload.
59  */

60 public class CacheManager {
61
62   /**
63    * Maps SourceTypeBindings to their associated types.
64    */

65   static class Mapper {
66     private final Map JavaDoc map = new IdentityHashMap JavaDoc();
67
68     public JClassType get(SourceTypeBinding binding) {
69       JClassType type = (JClassType) map.get(binding);
70       return type;
71     }
72
73     public void put(SourceTypeBinding binding, JClassType type) {
74       boolean firstPut = (null == map.put(binding, type));
75       assert (firstPut);
76     }
77
78     public void reset() {
79       map.clear();
80     }
81   }
82
83   /**
84    * This class is a very simple multi-valued map.
85    */

86   private static class Dependencies {
87     private Map JavaDoc map = new HashMap JavaDoc();
88
89     /**
90      * This method adds <code>item</code> to the list stored under
91      * <code>key</code>.
92      *
93      * @param key the key used to access the list
94      * @param item the item to be added to the list
95      */

96     private void add(String JavaDoc dependerFilename, String JavaDoc dependeeFilename) {
97       if (!map.containsKey(dependeeFilename)) {
98         map.put(dependeeFilename, new HashSet JavaDoc());
99       }
100
101       get(dependeeFilename).add(dependerFilename);
102     }
103
104     /**
105      * This method gets the list stored under <code>key</code>.
106      *
107      * @param key the key used to access the list.
108      * @return the list stored under <code>key</code>
109      */

110     private Set JavaDoc get(String JavaDoc filename) {
111       return (Set JavaDoc) map.get(filename);
112     }
113
114     private void remove(String JavaDoc filename) {
115       map.remove(filename);
116     }
117
118     private Set JavaDoc transitiveClosure(final String JavaDoc filename) {
119       String JavaDoc current = filename;
120       TreeSet JavaDoc queue = new TreeSet JavaDoc();
121       Set JavaDoc finished = new HashSet JavaDoc();
122       queue.add(filename);
123       while (true) {
124         finished.add(current);
125         Set JavaDoc children = get(current);
126         if (children != null) {
127           for (Iterator JavaDoc iter = children.iterator(); iter.hasNext();) {
128             String JavaDoc child = (String JavaDoc) iter.next();
129             if (!finished.contains(child)) {
130               queue.add(child);
131             }
132           }
133         }
134         if (queue.size() == 0) {
135           return finished;
136         } else {
137           current = (String JavaDoc) queue.first();
138           queue.remove(current);
139         }
140       }
141     }
142   }
143
144   /**
145    * Visit all of the CUDs and extract dependencies. This visitor handles
146    * explicit TypeRefs via the onTypeRef method AND it also deals with the
147    * gwt.typeArgs annotation.
148    *
149    * <ol>
150    * <li>Extract the list of type names from the gwt.typeArgs annotation</li>
151    * <li>For each type name, locate the CUD that defines it</li>
152    * <li>Add the referenced CUD as a dependency</li>
153    * </ol>
154    */

155   private final class DependencyVisitor extends TypeRefVisitor {
156     private final Dependencies dependencies;
157
158     private DependencyVisitor(Dependencies dependencies) {
159       this.dependencies = dependencies;
160     }
161
162     public void endVisit(FieldDeclaration fieldDeclaration,
163         final MethodScope scope) {
164       extractDependenciesFromTypeArgs(fieldDeclaration.javadoc,
165           scope.referenceContext(), true);
166     }
167
168     public void endVisit(MethodDeclaration methodDeclaration, ClassScope scope) {
169       extractDependenciesFromTypeArgs(methodDeclaration.javadoc,
170           scope.referenceContext(), false);
171     }
172
173     protected void onTypeRef(SourceTypeBinding referencedType,
174         CompilationUnitDeclaration unitOfReferrer) {
175       // If the referenced type belongs to a compilation unit that
176
// was changed, then the unit in which it
177
// is referenced must also be treated as changed.
178
//
179
String JavaDoc dependeeFilename = String.valueOf(referencedType.getFileName());
180       String JavaDoc dependerFilename = String.valueOf(unitOfReferrer.getFileName());
181
182       dependencies.add(dependerFilename, dependeeFilename);
183     }
184
185     private String JavaDoc combine(String JavaDoc[] strings, int startIndex) {
186       StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
187       for (int i = startIndex; i < strings.length; i++) {
188         String JavaDoc s = strings[i];
189         sb.append(s);
190       }
191       return sb.toString();
192     }
193
194     /**
195      * Extracts additional dependencies based on the gwt.typeArgs annotation.
196      * This is not detected by JDT so we need to do it here. We do not perform
197      * as strict a parse as the TypeOracle would do.
198      *
199      * @param javadoc javadoc text
200      * @param scope scope that contains the definition
201      * @param isField true if the javadoc is associated with a field
202      */

203     private void extractDependenciesFromTypeArgs(Javadoc javadoc,
204         final ReferenceContext scope, final boolean isField) {
205       if (javadoc == null) {
206         return;
207       }
208       final char[] source = scope.compilationResult().compilationUnit.getContents();
209
210       TypeOracleBuilder.parseMetaDataTags(source, new HasMetaData() {
211         public void addMetaData(String JavaDoc tagName, String JavaDoc[] values) {
212           assert (values != null);
213
214           if (!TypeOracle.TAG_TYPEARGS.equals(tagName)) {
215             // don't care about non gwt.typeArgs
216
return;
217           }
218
219           if (values.length == 0) {
220             return;
221           }
222
223           Set JavaDoc typeNames = new HashSet JavaDoc();
224
225           /*
226            * if the first element starts with a "<" then we assume that no
227            * parameter name was specified
228            */

229           int startIndex = 1;
230           if (values[0].trim().startsWith("<")) {
231             startIndex = 0;
232           }
233
234           extractTypeNamesFromTypeArg(combine(values, startIndex), typeNames);
235
236           Iterator JavaDoc it = typeNames.iterator();
237           while (it.hasNext()) {
238             String JavaDoc typeName = (String JavaDoc) it.next();
239
240             try {
241               ICompilationUnit compilationUnit = astCompiler.getCompilationUnitForType(
242                   TreeLogger.NULL, typeName);
243
244               String JavaDoc dependeeFilename = String.valueOf(compilationUnit.getFileName());
245               String JavaDoc dependerFilename = String.valueOf(scope.compilationResult().compilationUnit.getFileName());
246
247               dependencies.add(dependerFilename, dependeeFilename);
248
249             } catch (UnableToCompleteException e) {
250               // Purposely ignored
251
}
252           }
253         }
254
255         public String JavaDoc[][] getMetaData(String JavaDoc tagName) {
256           return null;
257         }
258
259         public String JavaDoc[] getMetaDataTags() {
260           return null;
261         }
262       }, javadoc);
263     }
264
265     /**
266      * Extracts the type names referenced from a gwt.typeArgs annotation and
267      * adds them to the set of type names.
268      *
269      * @param typeArg a string containing the type args as the user entered them
270      * @param typeNames the set of type names referenced in the typeArgs string
271      */

272     private void extractTypeNamesFromTypeArg(String JavaDoc typeArg, Set JavaDoc typeNames) {
273       // Remove all whitespace
274
typeArg.replaceAll("\\\\s", "");
275
276       // Remove anything that is not a raw type name
277
String JavaDoc[] typeArgs = typeArg.split("[\\[\\]<>,]");
278
279       for (int i = 0; i < typeArgs.length; ++i) {
280         if (typeArgs[i].length() > 0) {
281           typeNames.add(typeArgs[i]);
282         }
283       }
284     }
285   }
286
287   /**
288    * Caches information using a directory, with an in memory cache to prevent
289    * unneeded disk access.
290    */

291   private static class DiskCache extends AbstractMap JavaDoc {
292
293     private class FileEntry implements Map.Entry JavaDoc {
294
295       private File JavaDoc file;
296
297       private FileEntry(File JavaDoc file) {
298         this.file = file;
299       }
300
301       private FileEntry(Object JavaDoc name) {
302         this(new File JavaDoc(directory, possiblyAddTmpExtension(name)));
303       }
304
305       private FileEntry(Object JavaDoc name, Object JavaDoc o) {
306         this(new File JavaDoc(directory, possiblyAddTmpExtension(name)));
307         setValue(o);
308       }
309
310       public Object JavaDoc getKey() {
311         return possiblyRemoveTmpExtension(file.getName());
312       }
313
314       public Object JavaDoc getValue() {
315         if (!file.exists()) {
316           return null;
317         }
318         try {
319           FileInputStream JavaDoc fis = new FileInputStream JavaDoc(file);
320           ObjectInputStream JavaDoc ois = new ObjectInputStream JavaDoc(fis);
321           Object JavaDoc out = ois.readObject();
322           ois.close();
323           fis.close();
324           return out;
325         } catch (IOException JavaDoc e) {
326           return null;
327           // If we can't read the file, we can't get the item from the cache.
328
} catch (ClassNotFoundException JavaDoc e) {
329           return null;
330           // The class does not match because the serialUID is not correct
331
// so we don't want this item anyway.
332
}
333       }
334
335       public void remove() {
336         file.delete();
337       }
338
339       public Object JavaDoc setValue(Object JavaDoc value) {
340         Object JavaDoc o = getValue();
341         FileOutputStream JavaDoc fos;
342         try {
343           fos = new FileOutputStream JavaDoc(file);
344           ObjectOutputStream JavaDoc oos = new ObjectOutputStream JavaDoc(fos);
345           oos.writeObject(value);
346           oos.close();
347           fos.close();
348         } catch (IOException JavaDoc e) {
349           markCacheDirectoryUnusable();
350         }
351         return o;
352       }
353
354       private long lastModified() {
355         return file.lastModified();
356       }
357     }
358
359     private final Map JavaDoc cache = new HashMap JavaDoc();
360
361     // May be set to null after the fact if the cache directory becomes
362
// unusable.
363
private File JavaDoc directory;
364
365     public DiskCache(File JavaDoc dirName) {
366       if (dirName != null) {
367         directory = dirName;
368         possiblyCreateCacheDirectory();
369       } else {
370         directory = null;
371       }
372     }
373
374     public void clear() {
375       cache.clear();
376       if (directory != null) {
377         for (Iterator JavaDoc iter = keySet().iterator(); iter.hasNext();) {
378           iter.remove();
379         }
380       }
381     }
382
383     public Set JavaDoc entrySet() {
384       Set JavaDoc out = new HashSet JavaDoc() {
385         public boolean remove(Object JavaDoc o) {
386           boolean removed = (DiskCache.this.remove(((Entry) o).getKey())) != null;
387           super.remove(o);
388           return removed;
389         }
390       };
391       out.addAll(cache.entrySet());
392       // No directory means no persistance.
393
if (directory != null) {
394         possiblyCreateCacheDirectory();
395         // Add files not yet loaded into this cache.
396
File JavaDoc[] entries = directory.listFiles();
397         for (int i = 0; i < entries.length; i++) {
398           if (!cache.containsKey(new FileEntry(entries[i]).getKey())) {
399             out.add(new FileEntry(entries[i]));
400           }
401         }
402       }
403       return out;
404     }
405
406     public Object JavaDoc get(Object JavaDoc key) {
407       if (cache.containsKey(key)) {
408         return cache.get(key);
409       }
410       Object JavaDoc value = null;
411       if (directory != null) {
412         value = new FileEntry(key).getValue();
413         cache.put(key, value);
414       }
415       return value;
416     }
417
418     public Set JavaDoc keySet() {
419       Set JavaDoc out = new HashSet JavaDoc() {
420         public boolean remove(Object JavaDoc o) {
421           boolean removed = (DiskCache.this.remove(o)) != null;
422           super.remove(o);
423           return removed;
424         }
425       };
426       out.addAll(cache.keySet());
427       // No directory means no persistance.
428
if (directory != null) {
429         possiblyCreateCacheDirectory();
430         // Add files not yet loaded into this cache.
431
File JavaDoc[] entries = directory.listFiles();
432         for (int i = 0; i < entries.length; i++) {
433           out.add(new FileEntry(entries[i].getName()).getKey());
434         }
435       }
436       return out;
437     }
438
439     public Object JavaDoc put(Object JavaDoc key, Object JavaDoc value) {
440       return put(key, value, true);
441     }
442
443     public Object JavaDoc remove(Object JavaDoc key) {
444       Object JavaDoc out = get(key);
445       // No directory means no persistance.
446
if (directory != null) {
447         possiblyCreateCacheDirectory();
448         FileEntry e = new FileEntry(key);
449         e.remove();
450       }
451       cache.remove(key);
452       return out;
453     }
454
455     private long lastModified(Object JavaDoc key) {
456       if (directory == null) {
457         // we have no file on disk to refer to, so should return the same result
458
// as if the file did not exist -- namely 0.
459
return 0;
460       }
461       return new FileEntry(key).lastModified();
462     }
463
464     /**
465      * This method marks the cache directory as being invalid, so we do not try
466      * to use it.
467      */

468     private void markCacheDirectoryUnusable() {
469       System.err.println("The directory " + directory.getAbsolutePath()
470           + " is not usable as a cache directory");
471       directory = null;
472     }
473
474     /**
475      * This is used to ensure that if something wicked happens to the cache
476      * directory while we are running, we do not crash.
477      */

478     private void possiblyCreateCacheDirectory() {
479       directory.mkdirs();
480       if (!(directory.exists() && directory.canWrite())) {
481         markCacheDirectoryUnusable();
482       }
483     }
484
485     private Object JavaDoc put(Object JavaDoc key, Object JavaDoc value, boolean persist) {
486       Object JavaDoc out = get(key);
487
488       // We use toString to match the string value in FileEntry.
489
cache.remove(key.toString());
490
491       // Writes the file.
492
if (persist && directory != null) {
493         // This writes the file to the disk and is all that is needed.
494
new FileEntry(key, value);
495       }
496       cache.put(key, value);
497       return out;
498     }
499   }
500
501   /**
502    * The set of all classes whose bytecode needs to exist as bootstrap bytecode
503    * to be taken as given by the bytecode compiler.
504    */

505   public static final Class JavaDoc[] BOOTSTRAP_CLASSES = new Class JavaDoc[] {
506       JavaScriptHost.class, ShellJavaScriptHost.class, ShellGWT.class};
507
508   /**
509    * The set of bootstrap classes, which are marked transient, but are
510    * nevertheless not recompiled each time, as they are bootstrap classes.
511    */

512   private static final Set JavaDoc TRANSIENT_CLASS_NAMES;
513
514   static {
515     TRANSIENT_CLASS_NAMES = new HashSet JavaDoc(BOOTSTRAP_CLASSES.length + 3);
516     for (int i = 0; i < BOOTSTRAP_CLASSES.length; i++) {
517       TRANSIENT_CLASS_NAMES.add(BOOTSTRAP_CLASSES[i].getName());
518     }
519   }
520
521   // This method must be outside of DiskCache because of the restriction against
522
// defining static methods in inner classes.
523
private static String JavaDoc possiblyAddTmpExtension(Object JavaDoc className) {
524     String JavaDoc fileName = className.toString();
525     if (fileName.indexOf("-") == -1) {
526       int hashCode = fileName.hashCode();
527       String JavaDoc hashCodeStr = Integer.toHexString(hashCode);
528       while (hashCodeStr.length() < 8) {
529         hashCodeStr = '0' + hashCodeStr;
530       }
531       fileName = fileName + "-" + hashCodeStr + ".tmp";
532     }
533     return fileName;
534   }
535
536   // This method must be outside of DiskCache because of the restriction against
537
// defining static methods in inner classes.
538
private static String JavaDoc possiblyRemoveTmpExtension(Object JavaDoc fileName) {
539     String JavaDoc className = fileName.toString();
540     if (className.indexOf("-") != -1) {
541       className = className.split("-")[0];
542     }
543     return className;
544   }
545
546   private final Set JavaDoc addedCups = new HashSet JavaDoc();
547
548   private final AstCompiler astCompiler;
549
550   private final DiskCache byteCodeCache;
551
552   private final File JavaDoc cacheDir;
553
554   private final Set JavaDoc changedFiles;
555
556   private final Map JavaDoc cudsByFileName;
557
558   private final Map JavaDoc cupsByLocation = new HashMap JavaDoc();
559
560   private boolean firstTime = true;
561
562   /**
563    * Set of {@link CompilationUnitProvider} locations for all of the compilation
564    * units generated by {@link com.google.gwt.core.ext.Generator Generator}s.
565    *
566    * TODO: This seems like it should be a Set of CUPs rather than a set of CUP
567    * locations.
568    */

569   private final Set JavaDoc generatedCupLocations = new HashSet JavaDoc();
570
571   private final Mapper identityMapper = new Mapper();
572
573   private final Set JavaDoc invalidatedTypes = new HashSet JavaDoc();
574
575   private final TypeOracle oracle;
576
577   private final Map JavaDoc timesByLocation = new HashMap JavaDoc();
578
579   private boolean typeOracleBuilderFirstTime = true;
580
581   private final Map JavaDoc unitsByCup = new HashMap JavaDoc();
582
583   /**
584    * Creates a new <code>CacheManager</code>, creating a new
585    * <code>TypeOracle</code>. This constructor does not specify a cache
586    * directory, and therefore is to be used in unit tests and executables that
587    * do not need caching.
588    */

589   public CacheManager() {
590     this(null, null);
591   }
592
593   /**
594    * Creates a new <code>CacheManager</code>, creating a new
595    * <code>TypeOracle</code>. This constructor uses the specified cacheDir,
596    * and does cache information across reloads. If the specified cacheDir is
597    * null, caching across reloads will be disabled.
598    */

599   public CacheManager(String JavaDoc cacheDir, TypeOracle oracle) {
600     if (oracle == null) {
601       this.oracle = new TypeOracle();
602     } else {
603       this.oracle = oracle;
604     }
605     changedFiles = new HashSet JavaDoc();
606     cudsByFileName = new HashMap JavaDoc();
607     if (cacheDir != null) {
608       this.cacheDir = new File JavaDoc(cacheDir);
609       this.cacheDir.mkdirs();
610       byteCodeCache = new DiskCache(new File JavaDoc(cacheDir, "bytecode"));
611     } else {
612       this.cacheDir = null;
613       byteCodeCache = new DiskCache(null);
614     }
615     SourceOracleOnTypeOracle sooto = new SourceOracleOnTypeOracle(this.oracle);
616     astCompiler = new AstCompiler(sooto);
617   }
618
619   /**
620    * Creates a new <code>CacheManager</code>, using the supplied
621    * <code>TypeOracle</code>. This constructor does not specify a cache
622    * directory, and therefore is to be used in unit tests and executables that
623    * do not need caching.
624    */

625   public CacheManager(TypeOracle typeOracle) {
626     this(null, typeOracle);
627   }
628
629   /**
630    * Adds the specified {@link CompilationUnitProvider} to the set of CUPs
631    * generated by {@link com.google.gwt.core.ext.Generator Generator}s. Generated
632    * <code>CompilationUnitProviders</code> are not cached across reloads.
633    */

634   public void addGeneratedCup(CompilationUnitProvider generatedCup) {
635     assert (generatedCup != null);
636
637     generatedCupLocations.add(generatedCup.getLocation());
638   }
639
640   /**
641    * This method returns the <code>TypeOracle</code> associated with this
642    * <code>CacheManager</code>.
643    */

644   public TypeOracle getTypeOracle() {
645     return oracle;
646   }
647
648   /**
649    * Ensures that all compilation units generated via generators are removed
650    * from the system so that they will be generated again, and thereby take into
651    * account input that may have changed since the last reload.
652    */

653   public void invalidateVolatileFiles() {
654     for (Iterator JavaDoc iter = addedCups.iterator(); iter.hasNext();) {
655       CompilationUnitProvider cup = (CompilationUnitProvider) iter.next();
656       if (isGeneratedCup(cup)) {
657         iter.remove();
658       }
659     }
660   }
661
662   /**
663    * This method adds byte.
664    *
665    * @param logger
666    * @param binaryTypeName
667    * @param byteCode
668    * @return
669    */

670   boolean acceptIntoCache(TreeLogger logger, String JavaDoc binaryTypeName,
671       ByteCode byteCode) {
672     synchronized (byteCodeCache) {
673       if (getByteCode(logger, binaryTypeName) == null) {
674         byteCodeCache.put(binaryTypeName, byteCode, (!byteCode.isTransient()));
675         logger.log(TreeLogger.SPAM, "Cached bytecode for " + binaryTypeName,
676             null);
677         return true;
678       } else {
679         logger.log(TreeLogger.SPAM, "Bytecode not re-cached for "
680             + binaryTypeName, null);
681         return false;
682       }
683     }
684   }
685
686   /**
687    * Adds this compilation unit if it is not present, or is older. Otherwise
688    * does nothing.
689    *
690    * @throws UnableToCompleteException thrown if we cannot figure out when this
691    * cup was modified
692    */

693   void addCompilationUnit(CompilationUnitProvider cup)
694       throws UnableToCompleteException {
695     Long JavaDoc lastModified = new Long JavaDoc(cup.getLastModified());
696     if (isCupUnchanged(cup, lastModified)) {
697       return;
698     }
699     CompilationUnitProvider oldCup = getCup(cup);
700     if (oldCup != null) {
701       addedCups.remove(oldCup);
702       markCupChanged(cup);
703     }
704     timesByLocation.put(cup.getLocation(), lastModified);
705     cupsByLocation.put(cup.getLocation(), cup);
706     addedCups.add(cup);
707   }
708
709   /**
710    * This method modifies the field <code>changedFiles</code> to contain all
711    * of the additional files that are capable of reaching any of the files
712    * currently contained within <code>changedFiles</code>.
713    */

714   void addDependentsToChangedFiles() {
715     final Dependencies dependencies = new Dependencies();
716
717     DependencyVisitor trv = new DependencyVisitor(dependencies);
718
719     // Find references to type in units that aren't any longer valid.
720
//
721
for (Iterator JavaDoc iter = cudsByFileName.values().iterator(); iter.hasNext();) {
722       CompilationUnitDeclaration cud = (CompilationUnitDeclaration) iter.next();
723       cud.traverse(trv, cud.scope);
724     }
725
726     Set JavaDoc toTraverse = new HashSet JavaDoc(changedFiles);
727     for (Iterator JavaDoc iter = toTraverse.iterator(); iter.hasNext();) {
728       String JavaDoc fileName = (String JavaDoc) iter.next();
729       changedFiles.addAll(dependencies.transitiveClosure(fileName));
730     }
731   }
732
733   ICompilationUnit findUnitForCup(CompilationUnitProvider cup) {
734     if (!unitsByCup.containsKey(cup.getLocation())) {
735       unitsByCup.put(cup.getLocation(), new ICompilationUnitAdapter(cup));
736     }
737     return (ICompilationUnit) unitsByCup.get(cup.getLocation());
738   }
739
740   Set JavaDoc getAddedCups() {
741     return addedCups;
742   }
743
744   AstCompiler getAstCompiler() {
745     return astCompiler;
746   }
747
748   /**
749    * Gets the bytecode from the cache, rejecting it if an incompatible change
750    * occured since it was cached.
751    */

752   ByteCode getByteCode(TreeLogger logger, String JavaDoc binaryTypeName) {
753     synchronized (byteCodeCache) {
754       ByteCode byteCode = (ByteCode) byteCodeCache.get(binaryTypeName);
755       // we do not want bytecode created with a different classpath or os or
756
// version of GWT.
757
if ((byteCode != null)
758           && byteCode.getSystemIdentifier() != null
759           && (!(byteCode.getSystemIdentifier().equals(ByteCode.getCurrentSystemIdentifier())))) {
760         byteCodeCache.remove(binaryTypeName);
761         byteCode = null;
762       }
763       if (byteCode != null) {
764         // Found it.
765
//
766
return byteCode;
767       } else {
768         // This type has not been compiled before, or we tried but failed.
769
//
770
return null;
771       }
772     }
773   }
774
775   Set JavaDoc getChangedFiles() {
776     return changedFiles;
777   }
778
779   Map JavaDoc getCudsByFileName() {
780     return cudsByFileName;
781   }
782
783   CompilationUnitProvider getCup(CompilationUnitProvider cup) {
784     return (CompilationUnitProvider) getCupsByLocation().get(cup.getLocation());
785   }
786
787   Object JavaDoc getCupLastUpdateTime(CompilationUnitProvider cup) {
788     return getTimesByLocation().get(cup.getLocation());
789   }
790
791   Map JavaDoc getCupsByLocation() {
792     return cupsByLocation;
793   }
794
795   Mapper getIdentityMapper() {
796     return identityMapper;
797   }
798
799   Map JavaDoc getTimesByLocation() {
800     return timesByLocation;
801   }
802
803   JType getTypeForBinding(SourceTypeBinding sourceTypeBinding) {
804     return identityMapper.get(sourceTypeBinding);
805   }
806
807   /**
808    * This removes all state changed since the last time the typeOracle was run.
809    * Since the typeOracle information is not cached on disk, this is not needed
810    * the first time.
811    *
812    * @param typeOracle
813    */

814   void invalidateOnRefresh(TypeOracle typeOracle) {
815     // If a class is changed, the set of classes in the transitive closure
816
// of "refers to" must be marked changed as well.
817
// The initial change set is computed in addCompilationUnit.
818
// For the first time we do not do this because the compiler
819
// has no cached info.
820
if (!isTypeOracleBuilderFirstTime()) {
821       changedFiles.addAll(generatedCupLocations);
822       addDependentsToChangedFiles();
823
824       for (Iterator JavaDoc iter = changedFiles.iterator(); iter.hasNext();) {
825         String JavaDoc location = (String JavaDoc) iter.next();
826         CompilationUnitProvider cup = (CompilationUnitProvider) getCupsByLocation().get(
827             location);
828         unitsByCup.remove(location);
829         Util.invokeInaccessableMethod(TypeOracle.class,
830             "invalidateTypesInCompilationUnit",
831             new Class JavaDoc[] {CompilationUnitProvider.class}, typeOracle,
832             new Object JavaDoc[] {cup});
833       }
834       astCompiler.invalidateChangedFiles(changedFiles, invalidatedTypes);
835     } else {
836       becomeTypeOracleNotFirstTime();
837     }
838   }
839
840   /**
841    * Was this cup, last modified at time lastModified modified since it was last
842    * processed by the system?
843    */

844   boolean isCupUnchanged(CompilationUnitProvider cup, Long JavaDoc lastModified) {
845     Long JavaDoc oldTime = (Long JavaDoc) getCupLastUpdateTime(cup);
846     if (oldTime != null) {
847       if (oldTime.longValue() >= lastModified.longValue()
848           && (!cup.isTransient())) {
849         return true;
850       }
851     }
852     return false;
853   }
854
855   /**
856    * This method is called when a cup is known to have changed. This will ensure
857    * that all the types defined in this cup are invalidated.
858    *
859    * @param cup the cup modified
860    */

861   void markCupChanged(CompilationUnitProvider cup) {
862     changedFiles.add(String.valueOf(cup.getLocation()));
863   }
864
865   boolean removeFromCache(TreeLogger logger, String JavaDoc binaryTypeName) {
866     synchronized (byteCodeCache) {
867       if (getByteCode(logger, binaryTypeName) == null) {
868         logger.log(TreeLogger.SPAM, "Bytecode for " + binaryTypeName
869             + " was not cached, so not removing", null);
870         return false;
871       } else {
872         byteCodeCache.remove(binaryTypeName);
873         logger.log(TreeLogger.SPAM, "Bytecode not re-cached for "
874             + binaryTypeName, null);
875         return false;
876       }
877     }
878   }
879
880   /**
881    * This method removes all of the bytecode which is out of date from the
882    * bytecode cache. The set of files needing to be changed are going to be the
883    * set already known to be changed plus those that are out of date in the
884    * bytecode cache.
885    */

886   void removeStaleByteCode(TreeLogger logger, AbstractCompiler compiler) {
887     if (cacheDir == null) {
888       byteCodeCache.clear();
889       return;
890     }
891     if (isFirstTime()) {
892       Set JavaDoc classNames = byteCodeCache.keySet();
893       for (Iterator JavaDoc iter = classNames.iterator(); iter.hasNext();) {
894         Object JavaDoc className = iter.next();
895         ByteCode byteCode = ((ByteCode) (byteCodeCache.get(className)));
896         if (byteCode == null) {
897           iter.remove();
898           continue;
899         }
900         String JavaDoc qname = byteCode.getBinaryTypeName();
901         if (TRANSIENT_CLASS_NAMES.contains(qname)) {
902           continue;
903         }
904         if (byteCode != null) {
905           String JavaDoc location = byteCode.getLocation();
906           if (byteCode.isTransient()) {
907             // GWT transient classes; no need to test.
908
// Either standardGeneratorContext created it
909
// in which case we already know it is invalid
910
// or its something like GWT and it lives.
911
continue;
912           }
913           String JavaDoc fileName = Util.findFileName(location);
914           CompilationUnitDeclaration compilationUnitDeclaration = ((CompilationUnitDeclaration) cudsByFileName.get(location));
915           if (compilationUnitDeclaration == null) {
916             changedFiles.add(location);
917             continue;
918           }
919           long srcLastModified = Long.MAX_VALUE;
920           File JavaDoc srcLocation = new File JavaDoc(fileName);
921           if (srcLocation.exists()) {
922             srcLastModified = srcLocation.lastModified();
923           }
924           long byteCodeLastModified = byteCodeCache.lastModified(className);
925           if (srcLastModified >= byteCodeLastModified) {
926             changedFiles.add(location);
927           }
928         }
929       }
930       addDependentsToChangedFiles();
931     }
932     becomeNotFirstTime();
933     invalidateChangedFiles(logger, compiler);
934   }
935
936   void setTypeForBinding(SourceTypeBinding binding, JClassType type) {
937     identityMapper.put(binding, type);
938   }
939
940   private void becomeNotFirstTime() {
941     firstTime = false;
942   }
943
944   private void becomeTypeOracleNotFirstTime() {
945     typeOracleBuilderFirstTime = false;
946   }
947
948   /**
949    * Actually performs the work of removing the invalidated data from the
950    * system. At this point, changedFiles should be complete. After this method
951    * is called, changedFiles should now be empty, since all invalidation that is
952    * needed to be done.
953    *
954    * @param logger logs the process
955    * @param compiler the compiler caches data, so must be invalidated
956    */

957   private void invalidateChangedFiles(TreeLogger logger,
958       AbstractCompiler compiler) {
959     Set JavaDoc invalidTypes = new HashSet JavaDoc();
960     if (logger.isLoggable(TreeLogger.TRACE)) {
961       TreeLogger branch = logger.branch(TreeLogger.TRACE,
962           "The following compilation units have changed since "
963               + "the last compilation to bytecode", null);
964       for (Iterator JavaDoc iter = changedFiles.iterator(); iter.hasNext();) {
965         String JavaDoc filename = (String JavaDoc) iter.next();
966         branch.log(TreeLogger.TRACE, filename, null);
967       }
968     }
969     for (Iterator JavaDoc iter = byteCodeCache.keySet().iterator(); iter.hasNext();) {
970       Object JavaDoc key = iter.next();
971       ByteCode byteCode = ((ByteCode) (byteCodeCache.get(key)));
972       if (byteCode != null) {
973         String JavaDoc location = byteCode.getLocation();
974         if (changedFiles.contains(location)) {
975           String JavaDoc binaryTypeName = byteCode.getBinaryTypeName();
976           invalidTypes.add(binaryTypeName);
977           removeFromCache(logger, binaryTypeName);
978         }
979       }
980     }
981     compiler.invalidateUnitsInFiles(changedFiles, invalidTypes);
982     changedFiles.clear();
983   }
984
985   private boolean isFirstTime() {
986     return firstTime;
987   }
988
989   private boolean isGeneratedCup(CompilationUnitProvider cup) {
990     return generatedCupLocations.contains(cup.getLocation());
991   }
992
993   private boolean isTypeOracleBuilderFirstTime() {
994     return typeOracleBuilderFirstTime;
995   }
996 }
997
Popular Tags