001    /* Copyright 2000, 2001, Compaq Computer Corporation */
002    
003    package javafe.reader;
004    
005    import java.util.Hashtable;
006    
007    import javafe.ast.CompilationUnit;
008    import javafe.genericfile.*;
009    
010    /**
011     * CachedReader takes a uncached Reader and produces a cached version
012     * of it using a simple implementation of caching via a HashTable.
013     *
014     * <p> Reads from GenericFiles with null canonicalIDs are not cached.
015     */
016    
017    public class CachedReader extends Reader
018    {
019        /***************************************************
020         *                                                 *
021         * Creation:                                       *
022         *                                                 *
023         **************************************************/
024    
025        /**
026         * The underlying Reader whose results we are caching.
027         */
028        //@ invariant underlyingReader != null;
029        protected Reader underlyingReader;
030    
031        /**
032         * Creating a cached version of a Reader:
033         */
034        //@ requires reader != null;
035        public CachedReader(Reader reader) {
036            underlyingReader = reader;
037    
038            //+@ set cache.keyType = \type(String);
039            //+@ set cache.elementType = \type(Object);
040        }
041    
042    
043        /***************************************************
044         *                                                 *
045         * The Cache itself:                               *
046         *                                                 *
047         **************************************************/
048    
049        /**
050         * Our cache; maps the non-null canonicalID (if it has one) of a
051         * GenericFile (see GenericFile.getCanonicalID) to either a
052         * CompilationUnit or a CachedReader_Null.
053         */
054        //@ invariant cache != null;
055        //+@ invariant cache.keyType == \type(String);
056        //+@ invariant cache.elementType == \type(Object);
057        protected Hashtable cache = new Hashtable();
058    
059    
060        /***************************************************
061         *                                                 *
062         * Caching methods:                                *
063         *                                                 *
064         **************************************************/
065    
066        /**
067         * Lookup a non-null GenericFile in the cache.
068         */
069        //@ requires target != null;
070        final protected Object get(GenericFile target) {
071            String canonicalID = target.getCanonicalID();
072            if (canonicalID==null)
073                return null;
074    
075            return cache.get(canonicalID);
076        }
077    
078    
079        /**
080         * Store information about a non-null GenericFile in the cache;
081         * this has no effect if the GenericFile has a null canonicalID.
082         */
083        //@ requires target != null;
084        final protected void put(GenericFile target, CompilationUnit value) {
085            String canonicalID = target.getCanonicalID();
086            if (canonicalID==null)
087                return;
088    
089            if (value != null)
090                cache.put(canonicalID, value);
091            else
092                cache.put(canonicalID, new CachedReader_Null());
093        }
094    
095    
096        /**
097         * Is the result of read on target cached for this Reader?<p>
098         *
099         * Target must be non-null.<p> 
100         */
101        //@ requires target != null;
102        public boolean isCached(GenericFile target) {
103            String canonicalID = target.getCanonicalID();
104            if (canonicalID==null)
105                return false;
106    
107            return cache.containsKey(canonicalID);
108        }
109    
110        /**
111         * Flush the saved info (if any) for target for this Reader.
112         *
113         * Target must be non-null.<p>
114         */
115        //@ requires target != null;
116        public void flushTarget(GenericFile target) {
117            String canonicalID = target.getCanonicalID();
118            if (canonicalID==null)
119                return;
120    
121            cache.remove(canonicalID);
122        }
123    
124        /**
125         * Flush all cached information for this Reader.
126         */
127        public void flushAll() {
128            cache = new Hashtable();
129    
130            //+@ set cache.keyType = \type(String);
131            //+@ set cache.elementType = \type(Object);
132        }
133    
134    
135        /***************************************************
136         *                                                 *
137         * Reading:                                        *
138         *                                                 *
139         **************************************************/
140    
141        /**
142         * Attempt to read and parse a CompilationUnit from target.
143         * Any errors encountered are reported via javafe.util.ErrorSet.
144         * Null is returned iff an error was encountered.<p>
145         *
146         *
147         * By default, we attempt to read only a spec (e.g., specOnly is set
148         * in the resulting CompilationUnit) to save time.  If avoidSpec is
149         * true, we attempt to return a non-spec, although this may not
150         * always be possible.<p>
151         *
152         *
153         * The results of this function (including null results, but not
154         * the action of reporting error messages) are cached.
155         * Only the value of avoidSpec used the first time a given file is
156         * read is used.  This may result in a spec being returned
157         * unnecessarily when avoidSpec is true.<p>
158         *
159         * Target must be non-null.<p>
160         */
161        public CompilationUnit read(GenericFile target, boolean avoidSpec) {
162            Object result = get(target);
163            if (result != null) {
164                if (result instanceof CompilationUnit)
165                    return (CompilationUnit)result;
166                else if (result instanceof CachedReader_Null)
167                    return null;
168                else
169                    javafe.util.Assert.fail("impossible");
170            }
171    
172            CompilationUnit newResult = underlyingReader.read(target, avoidSpec);
173            put(target, newResult);
174            return newResult;
175        }
176    }
177    
178    
179    /**
180     * Instances of this class are used privately by CachedReader to
181     * represent null values in Hashtables.
182     */
183    /*package*/ class CachedReader_Null {
184    }