Source Code Cross Referenced for CacheManager.java in  » Ajax » GWT » com » google » gwt » dev » jdt » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Ajax » GWT » com.google.gwt.dev.jdt 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Copyright 2007 Google Inc.
0003:         * 
0004:         * Licensed under the Apache License, Version 2.0 (the "License"); you may not
0005:         * use this file except in compliance with the License. You may obtain a copy of
0006:         * the License at
0007:         * 
0008:         * http://www.apache.org/licenses/LICENSE-2.0
0009:         * 
0010:         * Unless required by applicable law or agreed to in writing, software
0011:         * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
0012:         * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
0013:         * License for the specific language governing permissions and limitations under
0014:         * the License.
0015:         */
0016:        package com.google.gwt.dev.jdt;
0017:
0018:        import com.google.gwt.core.ext.TreeLogger;
0019:        import com.google.gwt.core.ext.UnableToCompleteException;
0020:        import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider;
0021:        import com.google.gwt.core.ext.typeinfo.HasMetaData;
0022:        import com.google.gwt.core.ext.typeinfo.JClassType;
0023:        import com.google.gwt.core.ext.typeinfo.JType;
0024:        import com.google.gwt.core.ext.typeinfo.TypeOracle;
0025:        import com.google.gwt.dev.shell.JavaScriptHost;
0026:        import com.google.gwt.dev.shell.ShellGWT;
0027:        import com.google.gwt.dev.shell.ShellJavaScriptHost;
0028:        import com.google.gwt.dev.util.Util;
0029:
0030:        import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
0031:        import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
0032:        import org.eclipse.jdt.internal.compiler.ast.Javadoc;
0033:        import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
0034:        import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
0035:        import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
0036:        import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
0037:        import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
0038:        import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
0039:        import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
0040:
0041:        import java.io.File;
0042:        import java.io.FileInputStream;
0043:        import java.io.FileOutputStream;
0044:        import java.io.IOException;
0045:        import java.io.ObjectInputStream;
0046:        import java.io.ObjectOutputStream;
0047:        import java.util.AbstractMap;
0048:        import java.util.HashMap;
0049:        import java.util.HashSet;
0050:        import java.util.IdentityHashMap;
0051:        import java.util.Iterator;
0052:        import java.util.Map;
0053:        import java.util.Set;
0054:        import java.util.TreeSet;
0055:
0056:        /**
0057:         * CacheManager manages all the caching used to speed up hosted mode startup and
0058:         * refresh, and manages the invalidations required to ensure that changes are
0059:         * reflected correctly on reload.
0060:         */
0061:        public class CacheManager {
0062:
0063:            /**
0064:             * Maps SourceTypeBindings to their associated types.
0065:             */
0066:            static class Mapper {
0067:                private final Map<ReferenceBinding, JClassType> map = new IdentityHashMap<ReferenceBinding, JClassType>();
0068:
0069:                public JClassType get(ReferenceBinding referenceBinding) {
0070:                    JClassType type = map.get(referenceBinding);
0071:                    return type;
0072:                }
0073:
0074:                public void put(ReferenceBinding binding, JClassType type) {
0075:                    boolean firstPut = (null == map.put(binding, type));
0076:                    assert (firstPut);
0077:                }
0078:
0079:                public void reset() {
0080:                    map.clear();
0081:                }
0082:            }
0083:
0084:            /**
0085:             * This class is a very simple multi-valued map.
0086:             */
0087:            private static class Dependencies {
0088:                private Map<String, HashSet<String>> map = new HashMap<String, HashSet<String>>();
0089:
0090:                /**
0091:                 * This method adds <code>item</code> to the list stored under
0092:                 * <code>key</code>.
0093:                 * 
0094:                 * @param key the key used to access the list
0095:                 * @param item the item to be added to the list
0096:                 */
0097:                private void add(String dependerFilename,
0098:                        String dependeeFilename) {
0099:                    if (!map.containsKey(dependeeFilename)) {
0100:                        map.put(dependeeFilename, new HashSet<String>());
0101:                    }
0102:
0103:                    get(dependeeFilename).add(dependerFilename);
0104:                }
0105:
0106:                /**
0107:                 * This method gets the list stored under <code>key</code>.
0108:                 * 
0109:                 * @param key the key used to access the list.
0110:                 * @return the list stored under <code>key</code>
0111:                 */
0112:                private Set<String> get(String filename) {
0113:                    return map.get(filename);
0114:                }
0115:
0116:                private Set<String> transitiveClosure(final String filename) {
0117:                    String current = filename;
0118:                    TreeSet<String> queue = new TreeSet<String>();
0119:                    Set<String> finished = new HashSet<String>();
0120:                    queue.add(filename);
0121:                    while (true) {
0122:                        finished.add(current);
0123:                        Set<String> children = get(current);
0124:                        if (children != null) {
0125:                            for (Iterator<String> iter = children.iterator(); iter
0126:                                    .hasNext();) {
0127:                                String child = iter.next();
0128:                                if (!finished.contains(child)) {
0129:                                    queue.add(child);
0130:                                }
0131:                            }
0132:                        }
0133:                        if (queue.size() == 0) {
0134:                            return finished;
0135:                        } else {
0136:                            current = queue.first();
0137:                            queue.remove(current);
0138:                        }
0139:                    }
0140:                }
0141:            }
0142:
0143:            /**
0144:             * Visit all of the CUDs and extract dependencies. This visitor handles
0145:             * explicit TypeRefs via the onTypeRef method AND it also deals with the
0146:             * gwt.typeArgs annotation.
0147:             * 
0148:             * <ol>
0149:             * <li>Extract the list of type names from the gwt.typeArgs annotation</li>
0150:             * <li>For each type name, locate the CUD that defines it</li>
0151:             * <li>Add the referenced CUD as a dependency</li>
0152:             * </ol>
0153:             */
0154:            private final class DependencyVisitor extends TypeRefVisitor {
0155:                private final Dependencies dependencies;
0156:
0157:                private DependencyVisitor(Dependencies dependencies) {
0158:                    this .dependencies = dependencies;
0159:                }
0160:
0161:                @Override
0162:                public void endVisit(FieldDeclaration fieldDeclaration,
0163:                        final MethodScope scope) {
0164:                    extractDependenciesFromTypeArgs(fieldDeclaration.javadoc,
0165:                            scope.referenceContext());
0166:                }
0167:
0168:                @Override
0169:                public void endVisit(MethodDeclaration methodDeclaration,
0170:                        ClassScope scope) {
0171:                    extractDependenciesFromTypeArgs(methodDeclaration.javadoc,
0172:                            scope.referenceContext());
0173:                }
0174:
0175:                @Override
0176:                protected void onTypeRef(SourceTypeBinding referencedType,
0177:                        CompilationUnitDeclaration unitOfReferrer) {
0178:                    // If the referenced type belongs to a compilation unit that
0179:                    // was changed, then the unit in which it
0180:                    // is referenced must also be treated as changed.
0181:                    //
0182:                    String dependeeFilename = String.valueOf(referencedType
0183:                            .getFileName());
0184:                    String dependerFilename = String.valueOf(unitOfReferrer
0185:                            .getFileName());
0186:
0187:                    dependencies.add(dependerFilename, dependeeFilename);
0188:                }
0189:
0190:                private String combine(String[] strings, int startIndex) {
0191:                    StringBuffer sb = new StringBuffer();
0192:                    for (int i = startIndex; i < strings.length; i++) {
0193:                        String s = strings[i];
0194:                        sb.append(s);
0195:                    }
0196:                    return sb.toString();
0197:                }
0198:
0199:                /**
0200:                 * Extracts additional dependencies based on the gwt.typeArgs annotation.
0201:                 * This is not detected by JDT so we need to do it here. We do not perform
0202:                 * as strict a parse as the TypeOracle would do.
0203:                 * 
0204:                 * @param javadoc javadoc text
0205:                 * @param scope scope that contains the definition
0206:                 * @param isField true if the javadoc is associated with a field
0207:                 */
0208:                private void extractDependenciesFromTypeArgs(Javadoc javadoc,
0209:                        final ReferenceContext scope) {
0210:                    if (javadoc == null) {
0211:                        return;
0212:                    }
0213:                    final char[] source = scope.compilationResult().compilationUnit
0214:                            .getContents();
0215:
0216:                    TypeOracleBuilder.parseMetaDataTags(source,
0217:                            new HasMetaData() {
0218:                                public void addMetaData(String tagName,
0219:                                        String[] values) {
0220:                                    assert (values != null);
0221:
0222:                                    if (!TypeOracle.TAG_TYPEARGS
0223:                                            .equals(tagName)) {
0224:                                        // don't care about non gwt.typeArgs
0225:                                        return;
0226:                                    }
0227:
0228:                                    if (values.length == 0) {
0229:                                        return;
0230:                                    }
0231:
0232:                                    Set<String> typeNames = new HashSet<String>();
0233:
0234:                                    /*
0235:                                     * if the first element starts with a "<" then we assume that no
0236:                                     * parameter name was specified
0237:                                     */
0238:                                    int startIndex = 1;
0239:                                    if (values[0].trim().startsWith("<")) {
0240:                                        startIndex = 0;
0241:                                    }
0242:
0243:                                    extractTypeNamesFromTypeArg(combine(values,
0244:                                            startIndex), typeNames);
0245:
0246:                                    Iterator<String> it = typeNames.iterator();
0247:                                    while (it.hasNext()) {
0248:                                        String typeName = it.next();
0249:
0250:                                        try {
0251:                                            ICompilationUnit compilationUnit = astCompiler
0252:                                                    .getCompilationUnitForType(
0253:                                                            TreeLogger.NULL,
0254:                                                            typeName);
0255:
0256:                                            String dependeeFilename = String
0257:                                                    .valueOf(compilationUnit
0258:                                                            .getFileName());
0259:                                            String dependerFilename = String
0260:                                                    .valueOf(scope
0261:                                                            .compilationResult().compilationUnit
0262:                                                            .getFileName());
0263:
0264:                                            dependencies.add(dependerFilename,
0265:                                                    dependeeFilename);
0266:
0267:                                        } catch (UnableToCompleteException e) {
0268:                                            // Purposely ignored
0269:                                        }
0270:                                    }
0271:                                }
0272:
0273:                                public String[][] getMetaData(String tagName) {
0274:                                    return null;
0275:                                }
0276:
0277:                                public String[] getMetaDataTags() {
0278:                                    return null;
0279:                                }
0280:                            }, javadoc);
0281:                }
0282:
0283:                /**
0284:                 * Extracts the type names referenced from a gwt.typeArgs annotation and
0285:                 * adds them to the set of type names.
0286:                 * 
0287:                 * @param typeArg a string containing the type args as the user entered them
0288:                 * @param typeNames the set of type names referenced in the typeArgs string
0289:                 */
0290:                private void extractTypeNamesFromTypeArg(String typeArg,
0291:                        Set<String> typeNames) {
0292:                    // Remove all whitespace
0293:                    typeArg = typeArg.replaceAll("\\\\s", "");
0294:
0295:                    // Remove anything that is not a raw type name
0296:                    String[] typeArgs = typeArg.split("[\\[\\]<>,]");
0297:
0298:                    for (int i = 0; i < typeArgs.length; ++i) {
0299:                        if (typeArgs[i].length() > 0) {
0300:                            typeNames.add(typeArgs[i]);
0301:                        }
0302:                    }
0303:                }
0304:            }
0305:
0306:            /**
0307:             * Caches information using a directory, with an in memory cache to prevent
0308:             * unneeded disk access.
0309:             */
0310:            private static class DiskCache extends AbstractMap<String, Object> {
0311:
0312:                private class FileEntry implements  Map.Entry<String, Object> {
0313:
0314:                    private File file;
0315:
0316:                    private FileEntry(File file) {
0317:                        this .file = file;
0318:                    }
0319:
0320:                    private FileEntry(String className) {
0321:                        this (new File(directory,
0322:                                possiblyAddTmpExtension(className)));
0323:                    }
0324:
0325:                    private FileEntry(String className, Object o) {
0326:                        this (new File(directory,
0327:                                possiblyAddTmpExtension(className)));
0328:                        setValue(o);
0329:                    }
0330:
0331:                    public String getKey() {
0332:                        return possiblyRemoveTmpExtension(file.getName());
0333:                    }
0334:
0335:                    public Object getValue() {
0336:                        if (!file.exists()) {
0337:                            return null;
0338:                        }
0339:                        try {
0340:                            FileInputStream fis = new FileInputStream(file);
0341:                            ObjectInputStream ois = new ObjectInputStream(fis);
0342:                            Object out = ois.readObject();
0343:                            ois.close();
0344:                            fis.close();
0345:                            return out;
0346:                        } catch (IOException e) {
0347:                            return null;
0348:                            // If we can't read the file, we can't get the item from the cache.
0349:                        } catch (ClassNotFoundException e) {
0350:                            return null;
0351:                            // The class does not match because the serialUID is not correct
0352:                            // so we don't want this item anyway.
0353:                        }
0354:                    }
0355:
0356:                    public void remove() {
0357:                        file.delete();
0358:                    }
0359:
0360:                    public Object setValue(Object value) {
0361:                        Object o = getValue();
0362:                        FileOutputStream fos;
0363:                        try {
0364:                            fos = new FileOutputStream(file);
0365:                            ObjectOutputStream oos = new ObjectOutputStream(fos);
0366:                            oos.writeObject(value);
0367:                            oos.close();
0368:                            fos.close();
0369:                        } catch (IOException e) {
0370:                            markCacheDirectoryUnusable();
0371:                        }
0372:                        return o;
0373:                    }
0374:
0375:                    private long lastModified() {
0376:                        return file.lastModified();
0377:                    }
0378:                }
0379:
0380:                private final Map<String, Object> cache = new HashMap<String, Object>();
0381:
0382:                // May be set to null after the fact if the cache directory becomes
0383:                // unusable.
0384:                private File directory;
0385:
0386:                public DiskCache(File dirName) {
0387:                    if (dirName != null) {
0388:                        directory = dirName;
0389:                        possiblyCreateCacheDirectory();
0390:                    } else {
0391:                        directory = null;
0392:                    }
0393:                }
0394:
0395:                @Override
0396:                public void clear() {
0397:                    cache.clear();
0398:                    if (directory != null) {
0399:                        for (Iterator<String> iter = keySet().iterator(); iter
0400:                                .hasNext();) {
0401:                            iter.remove();
0402:                        }
0403:                    }
0404:                }
0405:
0406:                @Override
0407:                public Set<Entry<String, Object>> entrySet() {
0408:                    Set<Entry<String, Object>> out = new HashSet<Entry<String, Object>>() {
0409:                        @Override
0410:                        public boolean remove(Object o) {
0411:                            Entry<String, Object> entry = (Entry<String, Object>) o;
0412:                            boolean removed = (DiskCache.this .remove(entry
0413:                                    .getKey())) != null;
0414:                            super .remove(o);
0415:                            return removed;
0416:                        }
0417:                    };
0418:                    out.addAll(cache.entrySet());
0419:                    // No directory means no persistence.
0420:                    if (directory != null) {
0421:                        possiblyCreateCacheDirectory();
0422:                        // Add files not yet loaded into this cache.
0423:                        File[] entries = directory.listFiles();
0424:                        for (int i = 0; i < entries.length; i++) {
0425:                            if (!cache.containsKey(new FileEntry(entries[i])
0426:                                    .getKey())) {
0427:                                out.add(new FileEntry(entries[i]));
0428:                            }
0429:                        }
0430:                    }
0431:                    return out;
0432:                }
0433:
0434:                public Object get(String key) {
0435:                    if (cache.containsKey(key)) {
0436:                        return cache.get(key);
0437:                    }
0438:                    Object value = null;
0439:                    if (directory != null) {
0440:                        value = new FileEntry(key).getValue();
0441:                        cache.put(key, value);
0442:                    }
0443:                    return value;
0444:                }
0445:
0446:                @Override
0447:                public Set<String> keySet() {
0448:                    Set<String> out = new HashSet<String>() {
0449:                        @Override
0450:                        public boolean remove(Object o) {
0451:                            boolean removed = (DiskCache.this .remove(o)) != null;
0452:                            super .remove(o);
0453:                            return removed;
0454:                        }
0455:                    };
0456:                    out.addAll(cache.keySet());
0457:                    // No directory means no persistence.
0458:                    if (directory != null) {
0459:                        possiblyCreateCacheDirectory();
0460:                        // Add files not yet loaded into this cache.
0461:                        File[] entries = directory.listFiles();
0462:                        for (int i = 0; i < entries.length; i++) {
0463:                            out.add(new FileEntry(entries[i].getName())
0464:                                    .getKey());
0465:                        }
0466:                    }
0467:                    return out;
0468:                }
0469:
0470:                @Override
0471:                public Object put(String key, Object value) {
0472:                    return put(key, value, true);
0473:                }
0474:
0475:                @Override
0476:                public Object remove(Object key) {
0477:                    String fileName = (String) key;
0478:                    Object out = get(fileName);
0479:                    // No directory means no persistence.
0480:                    if (directory != null) {
0481:                        possiblyCreateCacheDirectory();
0482:                        FileEntry e = new FileEntry(fileName);
0483:                        e.remove();
0484:                    }
0485:                    cache.remove(key);
0486:                    return out;
0487:                }
0488:
0489:                private long lastModified(String className) {
0490:                    if (directory == null) {
0491:                        // we have no file on disk to refer to, so should return the same result
0492:                        // as if the file did not exist -- namely 0.
0493:                        return 0;
0494:                    }
0495:                    return new FileEntry(className).lastModified();
0496:                }
0497:
0498:                /**
0499:                 * This method marks the cache directory as being invalid, so we do not try
0500:                 * to use it.
0501:                 */
0502:                private void markCacheDirectoryUnusable() {
0503:                    System.err.println("The directory "
0504:                            + directory.getAbsolutePath()
0505:                            + " is not usable as a cache directory");
0506:                    directory = null;
0507:                }
0508:
0509:                /**
0510:                 * This is used to ensure that if something wicked happens to the cache
0511:                 * directory while we are running, we do not crash.
0512:                 */
0513:                private void possiblyCreateCacheDirectory() {
0514:                    directory.mkdirs();
0515:                    if (!(directory.exists() && directory.canWrite())) {
0516:                        markCacheDirectoryUnusable();
0517:                    }
0518:                }
0519:
0520:                private Object put(String key, Object value, boolean persist) {
0521:                    Object out = get(key);
0522:
0523:                    // We use toString to match the string value in FileEntry.
0524:                    cache.remove(key.toString());
0525:
0526:                    // Writes the file.
0527:                    if (persist && directory != null) {
0528:                        // This writes the file to the disk and is all that is needed.
0529:                        new FileEntry(key, value);
0530:                    }
0531:                    cache.put(key, value);
0532:                    return out;
0533:                }
0534:            }
0535:
0536:            /**
0537:             * The set of all classes whose bytecode needs to exist as bootstrap bytecode
0538:             * to be taken as given by the bytecode compiler.
0539:             */
0540:            public static final Class<?>[] BOOTSTRAP_CLASSES = new Class<?>[] {
0541:                    JavaScriptHost.class, ShellJavaScriptHost.class,
0542:                    ShellGWT.class };
0543:
0544:            /**
0545:             * The set of bootstrap classes, which are marked transient, but are
0546:             * nevertheless not recompiled each time, as they are bootstrap classes.
0547:             */
0548:            private static final Set<String> TRANSIENT_CLASS_NAMES;
0549:
0550:            static {
0551:                TRANSIENT_CLASS_NAMES = new HashSet<String>(
0552:                        BOOTSTRAP_CLASSES.length + 3);
0553:                for (int i = 0; i < BOOTSTRAP_CLASSES.length; i++) {
0554:                    TRANSIENT_CLASS_NAMES.add(BOOTSTRAP_CLASSES[i].getName());
0555:                }
0556:            }
0557:
0558:            // This method must be outside of DiskCache because of the restriction against
0559:            // defining static methods in inner classes.
0560:            private static String possiblyAddTmpExtension(Object className) {
0561:                String fileName = className.toString();
0562:                if (fileName.indexOf("-") == -1) {
0563:                    int hashCode = fileName.hashCode();
0564:                    String hashCodeStr = Integer.toHexString(hashCode);
0565:                    while (hashCodeStr.length() < 8) {
0566:                        hashCodeStr = '0' + hashCodeStr;
0567:                    }
0568:                    fileName = fileName + "-" + hashCodeStr + ".tmp";
0569:                }
0570:                return fileName;
0571:            }
0572:
0573:            // This method must be outside of DiskCache because of the restriction against
0574:            // defining static methods in inner classes.
0575:            private static String possiblyRemoveTmpExtension(Object fileName) {
0576:                String className = fileName.toString();
0577:                if (className.indexOf("-") != -1) {
0578:                    className = className.split("-")[0];
0579:                }
0580:                return className;
0581:            }
0582:
0583:            private final Set<CompilationUnitProvider> addedCups = new HashSet<CompilationUnitProvider>();
0584:
0585:            private final AstCompiler astCompiler;
0586:
0587:            private final DiskCache byteCodeCache;
0588:
0589:            private final File cacheDir;
0590:
0591:            private final Set<String> changedFiles;
0592:
0593:            private final Map<String, CompilationUnitDeclaration> cudsByFileName;
0594:
0595:            private final Map<String, CompilationUnitProvider> cupsByLocation = new HashMap<String, CompilationUnitProvider>();
0596:
0597:            private boolean firstTime = true;
0598:
0599:            /**
0600:             * Set of {@link CompilationUnitProvider} locations for all of the compilation
0601:             * units generated by {@link com.google.gwt.core.ext.Generator Generator}s.
0602:             * 
0603:             * TODO: This seems like it should be a Set of CUPs rather than a set of CUP
0604:             * locations.
0605:             */
0606:            private final Set<String> generatedCupLocations = new HashSet<String>();
0607:
0608:            private final Set<String> generatedResources = new HashSet<String>();
0609:
0610:            private final Mapper identityMapper = new Mapper();
0611:
0612:            private final Set<String> invalidatedTypes = new HashSet<String>();
0613:
0614:            private final TypeOracle oracle;
0615:
0616:            private final Map<String, Long> timesByLocation = new HashMap<String, Long>();
0617:
0618:            private boolean typeOracleBuilderFirstTime = true;
0619:
0620:            private final Map<String, ICompilationUnitAdapter> unitsByCup = new HashMap<String, ICompilationUnitAdapter>();
0621:
0622:            /**
0623:             * Creates a new <code>CacheManager</code>, creating a new
0624:             * <code>TypeOracle</code>. This constructor does not specify a cache
0625:             * directory, and therefore is to be used in unit tests and executables that
0626:             * do not need caching.
0627:             */
0628:            public CacheManager() {
0629:                this (null, null);
0630:            }
0631:
0632:            /**
0633:             * Creates a new <code>CacheManager</code>, creating a new
0634:             * <code>TypeOracle</code>. This constructor uses the specified cacheDir,
0635:             * and does cache information across reloads. If the specified cacheDir is
0636:             * null, caching across reloads will be disabled.
0637:             */
0638:            public CacheManager(String cacheDir, TypeOracle oracle) {
0639:                if (oracle == null) {
0640:                    this .oracle = new TypeOracle();
0641:                } else {
0642:                    this .oracle = oracle;
0643:                }
0644:                changedFiles = new HashSet<String>();
0645:                cudsByFileName = new HashMap<String, CompilationUnitDeclaration>();
0646:                if (cacheDir != null) {
0647:                    this .cacheDir = new File(cacheDir);
0648:                    this .cacheDir.mkdirs();
0649:                    byteCodeCache = new DiskCache(
0650:                            new File(cacheDir, "bytecode"));
0651:                } else {
0652:                    this .cacheDir = null;
0653:                    byteCodeCache = new DiskCache(null);
0654:                }
0655:                SourceOracleOnTypeOracle sooto = new SourceOracleOnTypeOracle(
0656:                        this .oracle);
0657:                astCompiler = new AstCompiler(sooto);
0658:            }
0659:
0660:            /**
0661:             * Creates a new <code>CacheManager</code>, using the supplied
0662:             * <code>TypeOracle</code>. This constructor does not specify a cache
0663:             * directory, and therefore is to be used in unit tests and executables that
0664:             * do not need caching.
0665:             */
0666:            public CacheManager(TypeOracle typeOracle) {
0667:                this (null, typeOracle);
0668:            }
0669:
0670:            /**
0671:             * Adds the specified {@link CompilationUnitProvider} to the set of CUPs
0672:             * generated by {@link com.google.gwt.core.ext.Generator Generator}s.
0673:             * Generated <code>CompilationUnitProviders</code> are not cached across
0674:             * reloads.
0675:             */
0676:            public void addGeneratedCup(CompilationUnitProvider generatedCup) {
0677:                assert (generatedCup != null);
0678:
0679:                generatedCupLocations.add(generatedCup.getLocation());
0680:            }
0681:
0682:            public void addGeneratedResource(String partialPath) {
0683:                generatedResources.add(partialPath);
0684:            }
0685:
0686:            /**
0687:             * This method returns the <code>TypeOracle</code> associated with this
0688:             * <code>CacheManager</code>.
0689:             */
0690:            public TypeOracle getTypeOracle() {
0691:                return oracle;
0692:            }
0693:
0694:            public boolean hasGeneratedResource(String partialPath) {
0695:                return generatedResources.contains(partialPath);
0696:            }
0697:
0698:            /**
0699:             * Ensures that all compilation units generated via generators are removed
0700:             * from the system so that they will be generated again, and thereby take into
0701:             * account input that may have changed since the last reload.
0702:             */
0703:            public void invalidateVolatileFiles() {
0704:                for (Iterator<CompilationUnitProvider> iter = addedCups
0705:                        .iterator(); iter.hasNext();) {
0706:                    CompilationUnitProvider cup = iter.next();
0707:                    if (isGeneratedCup(cup)) {
0708:                        iter.remove();
0709:                    }
0710:                }
0711:                generatedResources.clear();
0712:            }
0713:
0714:            /**
0715:             * This method adds byte.
0716:             * 
0717:             * @param logger
0718:             * @param binaryTypeName
0719:             * @param byteCode
0720:             * @return
0721:             */
0722:            boolean acceptIntoCache(TreeLogger logger, String binaryTypeName,
0723:                    ByteCode byteCode) {
0724:                synchronized (byteCodeCache) {
0725:                    if (getByteCode(binaryTypeName) == null) {
0726:                        byteCodeCache.put(binaryTypeName, byteCode, (!byteCode
0727:                                .isTransient()));
0728:                        logger.log(TreeLogger.SPAM, "Cached bytecode for "
0729:                                + binaryTypeName, null);
0730:                        return true;
0731:                    } else {
0732:                        logger.log(TreeLogger.SPAM,
0733:                                "Bytecode not re-cached for " + binaryTypeName,
0734:                                null);
0735:                        return false;
0736:                    }
0737:                }
0738:            }
0739:
0740:            /**
0741:             * Adds this compilation unit if it is not present, or is older. Otherwise
0742:             * does nothing.
0743:             * 
0744:             * @throws UnableToCompleteException thrown if we cannot figure out when this
0745:             *           cup was modified
0746:             */
0747:            void addCompilationUnit(CompilationUnitProvider cup)
0748:                    throws UnableToCompleteException {
0749:                Long lastModified = new Long(cup.getLastModified());
0750:                if (isCupUnchanged(cup, lastModified)) {
0751:                    return;
0752:                }
0753:                CompilationUnitProvider oldCup = getCup(cup);
0754:                if (oldCup != null) {
0755:                    addedCups.remove(oldCup);
0756:                    markCupChanged(cup);
0757:                }
0758:                timesByLocation.put(cup.getLocation(), lastModified);
0759:                cupsByLocation.put(cup.getLocation(), cup);
0760:                addedCups.add(cup);
0761:            }
0762:
0763:            /**
0764:             * This method modifies the field <code>changedFiles</code> to contain all
0765:             * of the additional files that are capable of reaching any of the files
0766:             * currently contained within <code>changedFiles</code>.
0767:             */
0768:            void addDependentsToChangedFiles() {
0769:                final Dependencies dependencies = new Dependencies();
0770:
0771:                DependencyVisitor trv = new DependencyVisitor(dependencies);
0772:
0773:                // Find references to type in units that aren't any longer valid.
0774:                //
0775:                for (CompilationUnitDeclaration cud : cudsByFileName.values()) {
0776:                    cud.traverse(trv, cud.scope);
0777:                }
0778:
0779:                Set<String> toTraverse = new HashSet<String>(changedFiles);
0780:                for (Iterator<String> iter = toTraverse.iterator(); iter
0781:                        .hasNext();) {
0782:                    String fileName = iter.next();
0783:                    changedFiles.addAll(dependencies
0784:                            .transitiveClosure(fileName));
0785:                }
0786:            }
0787:
0788:            ICompilationUnit findUnitForCup(CompilationUnitProvider cup) {
0789:                if (!unitsByCup.containsKey(cup.getLocation())) {
0790:                    unitsByCup.put(cup.getLocation(),
0791:                            new ICompilationUnitAdapter(cup));
0792:                }
0793:                return unitsByCup.get(cup.getLocation());
0794:            }
0795:
0796:            Set<CompilationUnitProvider> getAddedCups() {
0797:                return addedCups;
0798:            }
0799:
0800:            AstCompiler getAstCompiler() {
0801:                return astCompiler;
0802:            }
0803:
0804:            /**
0805:             * Gets the bytecode from the cache, rejecting it if an incompatible change
0806:             * occurred since it was cached.
0807:             */
0808:            ByteCode getByteCode(String binaryTypeName) {
0809:                synchronized (byteCodeCache) {
0810:                    ByteCode byteCode = (ByteCode) byteCodeCache
0811:                            .get(binaryTypeName);
0812:                    // we do not want bytecode created with a different classpath or os or
0813:                    // version of GWT.
0814:                    if ((byteCode != null)
0815:                            && byteCode.getSystemIdentifier() != null
0816:                            && (!(byteCode.getSystemIdentifier()
0817:                                    .equals(ByteCode
0818:                                            .getCurrentSystemIdentifier())))) {
0819:                        byteCodeCache.remove(binaryTypeName);
0820:                        byteCode = null;
0821:                    }
0822:                    if (byteCode != null) {
0823:                        // Found it.
0824:                        //
0825:                        return byteCode;
0826:                    } else {
0827:                        // This type has not been compiled before, or we tried but failed.
0828:                        //
0829:                        return null;
0830:                    }
0831:                }
0832:            }
0833:
0834:            Set<String> getChangedFiles() {
0835:                return changedFiles;
0836:            }
0837:
0838:            Map<String, CompilationUnitDeclaration> getCudsByFileName() {
0839:                return cudsByFileName;
0840:            }
0841:
0842:            CompilationUnitProvider getCup(CompilationUnitProvider cup) {
0843:                return getCupsByLocation().get(cup.getLocation());
0844:            }
0845:
0846:            Object getCupLastUpdateTime(CompilationUnitProvider cup) {
0847:                return getTimesByLocation().get(cup.getLocation());
0848:            }
0849:
0850:            Map<String, CompilationUnitProvider> getCupsByLocation() {
0851:                return cupsByLocation;
0852:            }
0853:
0854:            Mapper getIdentityMapper() {
0855:                return identityMapper;
0856:            }
0857:
0858:            Map<String, Long> getTimesByLocation() {
0859:                return timesByLocation;
0860:            }
0861:
0862:            JType getTypeForBinding(ReferenceBinding referenceBinding) {
0863:                return identityMapper.get(referenceBinding);
0864:            }
0865:
0866:            /**
0867:             * This removes all state changed since the last time the typeOracle was run.
0868:             * Since the typeOracle information is not cached on disk, this is not needed
0869:             * the first time.
0870:             * 
0871:             * @param typeOracle
0872:             */
0873:            void invalidateOnRefresh(TypeOracle typeOracle) {
0874:                // If a class is changed, the set of classes in the transitive closure
0875:                // of "refers to" must be marked changed as well.
0876:                // The initial change set is computed in addCompilationUnit.
0877:                // For the first time we do not do this because the compiler
0878:                // has no cached info.
0879:                if (!isTypeOracleBuilderFirstTime()) {
0880:                    changedFiles.addAll(generatedCupLocations);
0881:                    addDependentsToChangedFiles();
0882:
0883:                    for (Iterator<String> iter = changedFiles.iterator(); iter
0884:                            .hasNext();) {
0885:                        String location = iter.next();
0886:                        CompilationUnitProvider cup = getCupsByLocation().get(
0887:                                location);
0888:                        unitsByCup.remove(location);
0889:                        Util.invokeInaccessableMethod(TypeOracle.class,
0890:                                "invalidateTypesInCompilationUnit",
0891:                                new Class[] { CompilationUnitProvider.class },
0892:                                typeOracle, new Object[] { cup });
0893:                    }
0894:                    astCompiler.invalidateChangedFiles(changedFiles,
0895:                            invalidatedTypes);
0896:                } else {
0897:                    becomeTypeOracleNotFirstTime();
0898:                }
0899:            }
0900:
0901:            /**
0902:             * Was this cup, last modified at time lastModified modified since it was last
0903:             * processed by the system?
0904:             */
0905:            boolean isCupUnchanged(CompilationUnitProvider cup,
0906:                    Long lastModified) {
0907:                Long oldTime = (Long) getCupLastUpdateTime(cup);
0908:                if (oldTime != null) {
0909:                    if (oldTime.longValue() >= lastModified.longValue()
0910:                            && (!cup.isTransient())) {
0911:                        return true;
0912:                    }
0913:                }
0914:                return false;
0915:            }
0916:
0917:            /**
0918:             * This method is called when a cup is known to have changed. This will ensure
0919:             * that all the types defined in this cup are invalidated.
0920:             * 
0921:             * @param cup the cup modified
0922:             */
0923:            void markCupChanged(CompilationUnitProvider cup) {
0924:                changedFiles.add(String.valueOf(cup.getLocation()));
0925:            }
0926:
0927:            boolean removeFromCache(TreeLogger logger, String binaryTypeName) {
0928:                synchronized (byteCodeCache) {
0929:                    if (getByteCode(binaryTypeName) == null) {
0930:                        logger.log(TreeLogger.SPAM, "Bytecode for "
0931:                                + binaryTypeName
0932:                                + " was not cached, so not removing", null);
0933:                        return false;
0934:                    } else {
0935:                        byteCodeCache.remove(binaryTypeName);
0936:                        logger.log(TreeLogger.SPAM,
0937:                                "Bytecode not re-cached for " + binaryTypeName,
0938:                                null);
0939:                        return false;
0940:                    }
0941:                }
0942:            }
0943:
0944:            /**
0945:             * This method removes all of the bytecode which is out of date from the
0946:             * bytecode cache. The set of files needing to be changed are going to be the
0947:             * set already known to be changed plus those that are out of date in the
0948:             * bytecode cache.
0949:             */
0950:            void removeStaleByteCode(TreeLogger logger,
0951:                    AbstractCompiler compiler) {
0952:                if (cacheDir == null) {
0953:                    byteCodeCache.clear();
0954:                    return;
0955:                }
0956:                if (isFirstTime()) {
0957:                    Set<String> classNames = byteCodeCache.keySet();
0958:                    for (Iterator<String> iter = classNames.iterator(); iter
0959:                            .hasNext();) {
0960:                        String className = iter.next();
0961:                        ByteCode byteCode = ((ByteCode) (byteCodeCache
0962:                                .get(className)));
0963:                        if (byteCode == null) {
0964:                            iter.remove();
0965:                            continue;
0966:                        }
0967:                        String qname = byteCode.getBinaryTypeName();
0968:                        if (TRANSIENT_CLASS_NAMES.contains(qname)) {
0969:                            continue;
0970:                        }
0971:                        String location = byteCode.getLocation();
0972:                        if (byteCode.isTransient()) {
0973:                            // GWT transient classes; no need to test.
0974:                            // Either standardGeneratorContext created it
0975:                            // in which case we already know it is invalid
0976:                            // or its something like GWT and it lives.
0977:                            continue;
0978:                        }
0979:                        String fileName = Util.findFileName(location);
0980:                        CompilationUnitDeclaration compilationUnitDeclaration = cudsByFileName
0981:                                .get(location);
0982:                        if (compilationUnitDeclaration == null) {
0983:                            changedFiles.add(location);
0984:                            continue;
0985:                        }
0986:                        long srcLastModified = Long.MAX_VALUE;
0987:                        File srcLocation = new File(fileName);
0988:                        if (srcLocation.exists()) {
0989:                            srcLastModified = srcLocation.lastModified();
0990:                        }
0991:                        long byteCodeLastModified = byteCodeCache
0992:                                .lastModified(className);
0993:                        if (srcLastModified >= byteCodeLastModified) {
0994:                            changedFiles.add(location);
0995:                        }
0996:                    }
0997:                    addDependentsToChangedFiles();
0998:                }
0999:                becomeNotFirstTime();
1000:                invalidateChangedFiles(logger, compiler);
1001:            }
1002:
1003:            void setTypeForBinding(ReferenceBinding binding, JClassType type) {
1004:                identityMapper.put(binding, type);
1005:            }
1006:
1007:            private void becomeNotFirstTime() {
1008:                firstTime = false;
1009:            }
1010:
1011:            private void becomeTypeOracleNotFirstTime() {
1012:                typeOracleBuilderFirstTime = false;
1013:            }
1014:
1015:            /**
1016:             * Actually performs the work of removing the invalidated data from the
1017:             * system. At this point, changedFiles should be complete. After this method
1018:             * is called, changedFiles should now be empty, since all invalidation that is
1019:             * needed to be done.
1020:             * 
1021:             * @param logger logs the process
1022:             * @param compiler the compiler caches data, so must be invalidated
1023:             */
1024:            private void invalidateChangedFiles(TreeLogger logger,
1025:                    AbstractCompiler compiler) {
1026:                Set<String> invalidTypes = new HashSet<String>();
1027:                if (logger.isLoggable(TreeLogger.TRACE)) {
1028:                    TreeLogger branch = logger.branch(TreeLogger.TRACE,
1029:                            "The following compilation units have changed since "
1030:                                    + "the last compilation to bytecode", null);
1031:                    for (Iterator<String> iter = changedFiles.iterator(); iter
1032:                            .hasNext();) {
1033:                        String filename = iter.next();
1034:                        branch.log(TreeLogger.TRACE, filename, null);
1035:                    }
1036:                }
1037:                for (String key : byteCodeCache.keySet()) {
1038:                    ByteCode byteCode = ((ByteCode) (byteCodeCache.get(key)));
1039:                    if (byteCode != null) {
1040:                        String location = byteCode.getLocation();
1041:                        if (changedFiles.contains(location)) {
1042:                            String binaryTypeName = byteCode
1043:                                    .getBinaryTypeName();
1044:                            invalidTypes.add(binaryTypeName);
1045:                            removeFromCache(logger, binaryTypeName);
1046:                        }
1047:                    }
1048:                }
1049:                compiler.invalidateUnitsInFiles(changedFiles, invalidTypes);
1050:                changedFiles.clear();
1051:            }
1052:
1053:            private boolean isFirstTime() {
1054:                return firstTime;
1055:            }
1056:
1057:            private boolean isGeneratedCup(CompilationUnitProvider cup) {
1058:                return generatedCupLocations.contains(cup.getLocation());
1059:            }
1060:
1061:            private boolean isTypeOracleBuilderFirstTime() {
1062:                return typeOracleBuilderFirstTime;
1063:            }
1064:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.