001package components.simplereader; 002 003import java.io.BufferedReader; 004import java.io.IOException; 005import java.io.InputStreamReader; 006import java.net.URI; 007import java.net.URISyntaxException; 008import java.net.URL; 009import java.nio.file.Files; 010import java.nio.file.Path; 011import java.nio.file.Paths; 012 013/** 014 * {@code SimpleReader} represented as {@link java.io.BufferedReader 015 * java.io.BufferedReader} with implementations of primary methods. 016 * 017 * @correspondence <pre> 018 * this.is_open = [$this.rep is open] and 019 * this.ext_name = $this.name and 020 * if $this.lookAheadIsValid then 021 * this.contents = [$this.lookAhead * the contents of $this.rep] 022 * else 023 * this.contents = [the contents of $this.rep] 024 * </pre> 025 * @convention {@code 026 * [$this.rep is not null when the stream is open and 027 * lookAheadIsValid is true iff the contents of $this.rep is not <>] 028 * } 029 */ 030public class SimpleReader1L extends SimpleReaderSecondary { 031 032 /* 033 * Private members -------------------------------------------------------- 034 */ 035 036 /** 037 * The input stream. 038 */ 039 private BufferedReader rep; 040 /** 041 * The stream name. 042 */ 043 private String name; 044 /** 045 * The look ahead character buffer. 046 */ 047 private char lookAhead; 048 /** 049 * Flag to keep track of whether the lookAhead contains the first character 050 * in this.content. 051 */ 052 private boolean lookAheadIsValid; 053 054 /** 055 * Creator of initial representation. 056 */ 057 private void createNewRep() { 058 this.rep = new BufferedReader(new InputStreamReader(System.in)); 059 this.name = ""; 060 } 061 062 /** 063 * Inputs one character from $this.rep. 064 * 065 * @requires $this.lookAheadIsValid = false 066 * @ensures <pre> 067 * [$this.lookAhead contains the first character from #$this.rep and 068 * $thislookAheadIsValid is true unless the end of the stream has 069 * been reached. It is non-static because it needs to be able to 070 * modify the representation.] 071 * </pre> 072 */ 073 private void getOneChar() { 074 try { 075 int nextCh = this.rep.read(); 076 if (nextCh != -1) { 077 this.lookAhead = (char) nextCh; 078 this.lookAheadIsValid = true; 079 } 080 } catch (IOException e) { 081 throw new AssertionError("Violation of: can read from reader"); 082 } 083 084 } 085 086 /* 087 * Constructors ----------------------------------------------------------- 088 */ 089 090 /** 091 * No-argument constructor (for input from stdin). 092 */ 093 public SimpleReader1L() { 094 this.createNewRep(); 095 } 096 097 /** 098 * Constructor for input from given file. 099 * 100 * @param source 101 * the name of the file or of a URL to input from 102 */ 103 public SimpleReader1L(String source) { 104 assert source != null : "Violation of: name is not null"; 105 this.name = source; 106 107 URI uri = null; 108 boolean isUri = false; 109 110 // check if source is a valid URI 111 try { 112 uri = new URI(source); 113 isUri = (uri.getScheme() != null); 114 } catch (URISyntaxException e) { 115 // not a valid URI — likely a local file path 116 } 117 118 if (isUri) { 119 try { 120 URL url = uri.toURL(); 121 this.rep = new BufferedReader(new InputStreamReader(url.openStream())); 122 } catch (IOException e) { 123 throw new AssertionError( 124 "Violation of: " + source + " is a valid URL and is accessible"); 125 } 126 } else { 127 // treat as local file path 128 Path path = Paths.get(source); 129 if (!Files.exists(path)) { 130 throw new AssertionError("Violation of: " + source + " exists"); 131 } 132 if (!Files.isReadable(path)) { 133 throw new AssertionError("Violation of: " + source + " is readable"); 134 } 135 try { 136 this.rep = Files.newBufferedReader(path); 137 } catch (IOException e) { 138 throw new AssertionError("Violation of: " + source + " can be opened"); 139 } 140 } 141 } 142 143 /* 144 * Standard methods ------------------------------------------------------- 145 */ 146 147 @Override 148 public final SimpleReader newInstance() { 149 try { 150 return this.getClass().getConstructor().newInstance(); 151 } catch (ReflectiveOperationException e) { 152 throw new AssertionError( 153 "Cannot construct object of type " + this.getClass()); 154 } 155 } 156 157 @Override 158 public final void clear() { 159 this.createNewRep(); 160 } 161 162 @Override 163 public final void transferFrom(SimpleReader source) { 164 assert source != null : "Violation of: source is not null"; 165 assert source != this : "Violation of: source is not this"; 166 assert source instanceof SimpleReader1L 167 : "Violation of: source is of dynamic type SimpleReader1L"; 168 /* 169 * This cast cannot fail since the assert above would have stopped 170 * execution in that case. 171 */ 172 SimpleReader1L localSource = (SimpleReader1L) source; 173 this.rep = localSource.rep; 174 this.name = localSource.name; 175 this.lookAhead = localSource.lookAhead; 176 this.lookAheadIsValid = localSource.lookAheadIsValid; 177 localSource.createNewRep(); 178 } 179 180 /* 181 * Kernel methods --------------------------------------------------------- 182 */ 183 184 @Override 185 public final char read() { 186 assert this.rep != null : "Violation of: this is open"; 187 if (!this.lookAheadIsValid) { 188 this.getOneChar(); 189 } 190 assert this.lookAheadIsValid : "Violation of: this is not at end-of-stream"; 191 this.lookAheadIsValid = false; 192 return this.lookAhead; 193 } 194 195 @Override 196 public final char peek() { 197 assert this.rep != null : "Violation of: this is open"; 198 if (!this.lookAheadIsValid) { 199 this.getOneChar(); 200 } 201 assert this.lookAheadIsValid : "Violation of: this is not at end-of-stream"; 202 return this.lookAhead; 203 } 204 205 @Override 206 public final String name() { 207 return this.name; 208 } 209 210 @Override 211 public final boolean isOpen() { 212 return this.rep != null; 213 } 214 215 @Override 216 public final boolean atEOS() { 217 assert this.rep != null : "Violation of: this is open"; 218 if (!this.lookAheadIsValid) { 219 this.getOneChar(); 220 } 221 return !this.lookAheadIsValid; 222 } 223 224 @Override 225 public final void close() { 226 assert this.rep != null : "Violation of: this is open"; 227 try { 228 this.rep.close(); 229 this.rep = null; 230 } catch (IOException e) { 231 throw new AssertionError("Violation of: this can be closed"); 232 } 233 } 234 235}