1 /**
2 * AUTOHIT 2003
3 * Copyright Erich P Gatejen (c) 1989,1997,2003,2004
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or (at
8 * your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * Additional license information can be found in the documentation.
19 * @author Erich P Gatejen
20 */
21 package autohit.call.modules;
22
23 import java.io.BufferedReader;
24 import java.io.InputStream;
25 import java.io.InputStreamReader;
26 import java.io.StringReader;
27 import java.util.StringTokenizer;
28
29 import autohit.call.CallException;
30 import autohit.common.Constants;
31 import autohit.universe.UniverseException;
32
33 /**
34 * Text reader module. It'll supply lines or tokens out of a
35 * text source. The source can be a String or a Universe object.
36 * If you start a new session over an old one, it will throw a
37 * fault. You must call done() first.<p>
38 *
39 * startstring(string) start a read on a target string<br>
40 * startuni(objname) start a read on a universe object<br>
41 * line() get the next full line. if reading tokens in a line, it will
42 * give the whole contents of the current line.<br>
43 * token() get the next whitespace delimited token. if there are no more
44 * tokens in the current line, it will eat lines until it finds one.<br>
45 * hasmore() return "true" if there are more tokens and/or lines, else false.<br>
46 * done() close read (do this for either type, please)<br>
47 *
48 * @author Erich P. Gatejen
49 * @version 1.0
50 * <i>Version History</i>
51 * <code>EPG - Initial - 7Jul03
52 */
53 public class TextReaderModule extends Module {
54
55 private final static String myNAME = "TextReader";
56
57 /**
58 * Current source
59 */
60 private BufferedReader in;
61
62 /**
63 * Flag is the reader is valid, meaning it has more to read
64 */
65 private boolean valid;
66
67 /**
68 * Line we are currently reading
69 */
70 private String currentLine;
71
72 /**
73 * Line we are currently tokenizing
74 */
75 private StringTokenizer currentTokens;
76
77 /**
78 * METHODS
79 */
80 private final static String method_STARTSTR = "startstring";
81 private final static String method_STARTSTR_1_STRING = "string";
82 private final static String method_STARTUNI = "startuni";
83 private final static String method_STARTUNI_1_NAME = "objname";
84 private final static String method_LINE = "line";
85 private final static String method_TOKEN = "token";
86 private final static String method_HASMORE = "hasmore";
87 private final static String method_DONE = "done";
88
89 /**
90 * Constructor
91 */
92 public TextReaderModule() {
93
94 }
95
96 // IMPLEMENTORS
97
98 /**
99 * Execute a named method. You must implement this method.
100 * You can call any of the helpers for data and services.
101 * The returned object better be a string (for now).
102 * @param name name of the method
103 * @see autohit.common.NOPair
104 * @throws CallException
105 */
106 public Object execute_chain(String name) throws CallException {
107
108 Object response = Constants.EMPTY_LEFT;
109 String param1;
110
111 if (name.equals(method_STARTSTR)) {
112 param1 = this.required(method_STARTSTR_1_STRING,name);
113 this.startstring(param1);
114
115 } else if (name.equals(method_STARTUNI)) {
116 param1 = this.required(method_STARTUNI_1_NAME,name);
117 this.startuni(param1);
118
119 } else if (name.equals(method_LINE)) {
120 response = this.line();
121
122 } else if (name.equals(method_TOKEN)) {
123 response = this.token();
124
125 } else if (name.equals(method_HASMORE)) {
126 response = this.hasmore();
127
128 } else if (name.equals(method_DONE)) {
129 this.done();
130
131 } else {
132 error(
133 "Not a provided method. method=" + name);
134 response = Constants.EMPTY_LEFT;
135 }
136 return response;
137 }
138
139 /**
140 * Allow the subclass a chance to initialize. At a minium, an
141 * implementor should create an empty method.
142 * @throws CallException
143 * @return the name
144 */
145 protected String instantiation_chain() throws CallException {
146 // Make sure we aren't started
147 in = null;
148 valid = false;
149 return myNAME;
150 }
151
152 /**
153 * Allow the subclass a chance to cleanup on free. At a minium, an
154 * implementor should create an empty method.
155 * @throws CallException
156 */
157 protected void free_chain() throws CallException {
158 // NOTHING AT THIS TIME
159 if (in != null) {
160 try {
161 in.close();
162 } catch (Exception e) {
163 // Ignore
164 }
165 }
166 }
167
168 // PRIVATE IMPLEMENTATIONS
169
170 /**
171 * Start method. It will set the reader to the string.
172 * If a session is already started, it will throw a fault.
173 * @param target the string we are going to read
174 * @throws CallException
175 */
176 private void startstring(String target) throws CallException {
177
178 // Invalidate the stream first
179 valid = false;
180
181 if (in != null) {
182 throw buildException(
183 "Tried to startstring a session over an existing session. You must call done() first to end the prior session.",
184 CallException.CODE_MODULE_FAULT);
185 }
186 try {
187 in = new BufferedReader(new StringReader(target));
188 if (this.eat()) {
189 // It's a valid stream
190 valid = true;
191 }
192 } catch (Exception e) {
193 throw buildException(
194 "Startstring failed to exception. message="
195 + e.getMessage(),
196 CallException.CODE_MODULE_FAULT,
197 e);
198 }
199 }
200
201 /**
202 * Start method. It will set the reader to stream from a universe object.
203 * If a session is already started, it will throw a fault.
204 * @param target the string we are going to read
205 * @throws CallException
206 */
207 private void startuni(String name) throws CallException {
208
209 // Invalidate the stream first
210 valid = false;
211
212 if (in != null) {
213 throw buildException(
214 "Tried to startuni a session over an existing session. You must call done() first to end the prior session.",
215 CallException.CODE_MODULE_FAULT);
216 }
217
218 try {
219
220 InputStream is = visUniverse.getStream(name);
221 in = new BufferedReader(new InputStreamReader(is));
222 if (this.eat()) {
223 // It's a valid stream
224 valid = true;
225 }
226
227 } catch (UniverseException ue) {
228 throw new CallException(
229 "Startuni failed with Universe exception. message="
230 + ue.getMessage(),
231 CallException.CODE_MODULE_FAULT,
232 ue);
233 } catch (Exception e) {
234 throw new CallException(
235 "Startuni failed to general exception. message="
236 + e.getMessage(),
237 CallException.CODE_MODULE_FAULT,
238 e);
239 }
240 }
241
242 /**
243 * Closes the session.
244 * @throws CallException
245 */
246 private void done() throws CallException {
247 if (in == null) {
248 error(
249 "module:" + myName + ":called done() when it wasn't started.");
250 return;
251 }
252 try {
253 valid = false;
254 in.close();
255 } catch (Exception e) {
256 // ignore
257 }
258 in = null;
259 }
260
261 /**
262 * line() return the next line. If there is no next line, it will
263 * return an empty string.
264 * @return the next line or an empty string
265 */
266 private String line() {
267
268 String result = Constants.EMPTY_LEFT;
269
270 if (valid) {
271 result = currentLine;
272 valid = this.eat();
273
274 } else {
275 debug(
276 "Line() from an empty source. Returning empty string.");
277 }
278 return result;
279 }
280
281 /**
282 * token() return the token. If there is no next token, it will
283 * return an empty string.
284 * @return the next line or an empty string
285 */
286 private String token() {
287
288 String result = Constants.EMPTY_LEFT;
289
290 if (valid) {
291
292 if (currentTokens.hasMoreTokens()) {
293 result = currentTokens.nextToken();
294 } else {
295 valid = this.eat();
296 if (valid == true) {
297 result = currentTokens.nextToken();
298 }
299 }
300
301 } else {
302 debug(
303 "Token() from an empty source. Returning empty string.");
304 }
305 return result;
306 }
307
308 /**
309 * hasmore() return "true" if there are more tokens and/or lines, else "false."
310 * @return "true" if there is more, otherwise "false"
311 */
312 private String hasmore() {
313
314 // Assume it will fail
315 if (valid) {
316 if (currentTokens.hasMoreTokens()) {
317 // current line has the goods
318 return Constants.TRUE;
319 } else {
320 // see if the next line has the goods
321 valid = this.eat();
322 if (valid) {
323 return Constants.TRUE;
324 }
325 }
326 }
327 return Constants.FALSE;
328 }
329
330 // HELPERS
331
332 /**
333 * chew lines until one is found with a token or there is nothing
334 * left to chew
335 * @return true if successful, false if we ran out
336 */
337 private boolean eat() {
338
339 try {
340 currentLine = in.readLine();
341 while (currentLine != null) {
342 currentTokens = new StringTokenizer(currentLine);
343 if (currentTokens.hasMoreTokens())
344 return true;
345 currentLine = in.readLine();
346 }
347 } catch (Exception e) {
348 // Don't care. false will bubble out
349 }
350 return false;
351 }
352
353 }
|