Source code for /engineering/autohit-2003/src/autohit/call/modules/TextReaderModule.javaOriginal file TextReaderModule.java
   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 }