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 }