001    package ps6;
002    
003    import ps4.StreetSegment;
004    
005    import java.io.*;
006    import java.util.*;
007    
008    /**
009     * A StreetSegReader reads StreetSegments from a set of Tiger Databases.
010     *
011     * <p> Tiger Databases are produced and maintained by the <a
012     * href="http://tiger.census.gov/">U.S. Census Bureau</a>.
013     *
014     * <p> Tiger Databases are distributed in zip-compressed files.  To
015     * allow for the loading of an arbitrary number of these databases,
016     * one creates each StreetSegReader with a source directory argument,
017     * which will then be searched for Tiger Database files when
018     * <code>streetSegments()</code> is called.  The subdirectories of the
019     * argument directory are not searched recursively for Tiger files as
020     * well; only the immediate contents of this.sourceDirectory will be
021     * considered as potential Tiger files.
022     *
023     * <p> StreetSegReader is only specified to operate correctly with
024     * directories that contain no <tt>.zip</tt> files besides the Tiger
025     * Databases. Files with other extensions (such as <tt>.pdf</tt>)
026     * will not affect the operation of StreetSegReader, but no other
027     * <tt>.zip</tt> files should be kept in the source directory for a
028     * StreetSegReader.
029     *
030     * @specfield sourceDirectory : String     // name of directory where Tiger files are located
031     * @specfield streetSegs      : Collection // contents of the database; each element is a StreetSegment
032     *
033     */
034    public class StreetSegReader
035    {
036    
037        private File sourceDirectory;
038    
039        /**
040         * @effects Constructs a StreetSegReader where sourceDirectory
041         *   contains the Tiger Database files.
042         *
043         * @throws InvalidSourceException if sourceDirectory is not valid
044         * database (most directories, even empty ones, count as valid).
045         **/
046        public StreetSegReader(/*@Nullable*/ String sourceDirectory)
047            throws InvalidSourceException
048        {
049            try {
050    
051                File source = null;
052                if (sourceDirectory != null) {
053                    source = new File(sourceDirectory);
054                }
055                reader = new StreetSegReader(source);
056    
057            } catch (StreetSegReader.InvalidSourceException e) {
058                throw new InvalidSourceException("" + e.getMessage());
059            }
060        }
061    
062        private StreetSegReader (/*@Nullable*/ File sourceDirFile)
063            throws InvalidSourceException {
064    
065            if (sourceDirFile == null) {
066                throw new InvalidSourceException("DIRECTORY for StreetSegReader constructor cannot be null");
067            }
068    
069            if (!sourceDirFile.isDirectory()) {
070                throw new InvalidSourceException(sourceDirFile + " is not a valid DIRECTORY for StreetSegReader");
071            }
072    
073            // java.io.File is immutable
074            this.sourceDirectory = sourceDirFile;
075        }
076    
077        // our adaptee
078        private StreetSegReader reader;
079    
080    
081        private static final FilenameFilter zipFileFilter = new ZipFilter();
082    
083        /**
084         * Filter class for only getting certain .zip files -- those that
085         * <b>match</b> the name.
086         */
087        private static class ZipFilter
088            implements FilenameFilter {
089            public boolean accept(File d, String name) {
090                return (name.toLowerCase().endsWith("zip"));
091            }
092        }
093    
094    
095        /**
096         * Returns an Iterator over this.streetSegs.
097         *
098         * @throws Error if IO error while reading the directory
099         * @returns an iterator that produces the contents of
100         *   this.streetSegs.  Each element produced by the Iterator is a
101         *   StreetSegment.
102         **/
103        public Iterator<StreetSegment> iterator() {
104            return reader.streetSegmentsInternal();
105        }
106    
107        /**
108         * @throws Error if IO error while reading the directory
109         */
110        protected StreetSegIterator streetSegmentsInternal() {
111            File[] filesToRead = sourceDirectory.listFiles(zipFileFilter);
112            if (filesToRead == null) {
113                // Some unknown I/O error ocurred while listing the files.
114                // This is rare and probably unrecoverable (sourceDirectory was
115                // already checked to be a directory).
116                // Not IOException so we don't have to declare it.
117                throw new Error("IO error while reading directory " + sourceDirectory);
118            }
119            StreetSegmentFilter killfilter = KillfileReader.fromDir(sourceDirectory);
120            return new StreetSegIterator(Arrays.asList(filesToRead).iterator(), killfilter);
121        }
122    
123        /**
124         * Returns an Iterator over this.streetSegs.
125         *
126         * @modifies: System.err
127         *
128         * @effects: If (debug) then prints messages to System.err on
129         *   progress of database reading.  Else no change to System.err
130         *
131         * @throws Error if IO error while reading the directory
132         *
133         * @returns an iterator that produces the contents of
134         *    this.streetSegs.  Each element produced by the Iterator is a
135         *    StreetSegment.
136         **/
137        public Iterator<StreetSegment> streetSegments() {
138            StreetSegIterator iter = reader.streetSegmentsInternal();
139            return iter;
140        }
141    
142        // Inner classes
143    
144        /**
145         * Exception indicating that the requested source for a
146         * tiger database is invalid
147         **/
148        public static class InvalidSourceException
149            extends Exception {
150            public static final long serialVersionUID = 4534;
151            public InvalidSourceException(String msg) { super(msg); }
152        }
153    
154    }