Source code for /engineering/autohit-2003/src/autohit/call/modules/SimpleScannerModule.javaOriginal file SimpleScannerModule.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.util.HashMap;
  24 import java.util.regex.Matcher;
  25 import java.util.regex.Pattern;
  26 
  27 import autohit.call.CallException;
  28 import autohit.common.Constants;
  29 
  30 /**
  31  * Simple scanner module.
  32  *
  33  * start(target) start a scan of the target string
  34  * add(name, pattern) add a pattern to the pattern cache.
  35  * reset() reset cursor to start
  36  * find(name) return size of pattern, if found.  zero if not found.  cursor left at beginning.  Cursor does not move if match fails.
  37  * seek(s) return "true" if found, otherwise "false".  seek an exact string.  cursor left at beginning
  38  * seekinsensitive(s) return "true" if found, otherwise "false".  seek a string, without regard to case.  cursor left at beginning
  39  * substring(start, end-1) return string.  exception if error.
  40  * set(int spot) move cursor to a spot
  41  * get() get the cursor position
  42  * move(add) move the position forward by add spots
  43  * 
  44  * @author Erich P. Gatejen
  45  * @version 1.0
  46  * <i>Version History</i>
  47  * <code>EPG - Initial - 3Jul03 
  48  * 
  49  */
  50 public class SimpleScannerModule extends Module {
  51 
  52 	private final static String myNAME = "SimpleScanner";
  53 
  54 	/**
  55 	 * Pattern cache
  56 	 */
  57 	private HashMap patterncache;
  58 
  59 	/**
  60 	 * Current target
  61 	 */
  62 	private String currenttarget;
  63 
  64 	/**
  65 	 * Cursor location
  66 	 */
  67 	private int cursor;
  68 
  69 	/**
  70 	 * METHODS
  71 	 */
  72 	private final static String method_START = "start";
  73 	private final static String method_START_1_TARGET = "target";
  74 	private final static String method_ADDPATTERN = "add";
  75 	private final static String method_ADDPATTERN_1_NAME = "name";
  76 	private final static String method_ADDPATTERN_2_PATTERN = "pattern";
  77 	private final static String method_RESET = "reset";
  78 	private final static String method_FIND = "find";
  79 	private final static String method_FIND_1_NAME = "name";
  80 	private final static String method_SEEK = "seek";
  81 	private final static String method_SEEK_1_STRING = "string";
  82 	private final static String method_SEEK_CI = "seekinsensitive";
  83 	private final static String method_SEEK_CI_1_STRING = "string";	
  84 	private final static String method_CURSOR = "cursor";
  85 	private final static String method_SUBSTRING = "substring";
  86 	private final static String method_SUBSTRING_1_START = "start";
  87 	private final static String method_SUBSTRING_2_END = "end";
  88 	private final static String method_SET = "set";
  89 	private final static String method_SET_1_SPOT = "spot";
  90 	private final static String method_GET = "get";
  91 	private final static String method_MOVE = "move";
  92 	private final static String method_MOVE_1_ADD = "add";
  93 
  94 	/**
  95 	 * Constructor
  96 	 */
  97 	public SimpleScannerModule() {
  98 
  99 	}
 100 
 101 	// IMPLEMENTORS
 102 
 103 	/**
 104 	 * Execute a named method.  You must implement this method.
 105 	 * You can call any of the helpers for data and services.
 106 	 * The returned object better be a string (for now).
 107 	 * @param name name of the method
 108 	 * @see autohit.common.NOPair
 109 	 * @throws CallException
 110 	 */
 111 	public Object execute_chain(String name) throws CallException {
 112 
 113 		Object response = Constants.EMPTY_LEFT;
 114 		String param1;
 115 		String param2;
 116 		Object thingie;
 117 
 118 		if (name.equals(method_START)) {
 119 			param1 = this.required(method_START_1_TARGET,name);
 120 			this.start(param1);
 121 
 122 		} else if (name.equals(method_ADDPATTERN)) {
 123 			param1 = this.desired(method_ADDPATTERN_1_NAME,name);
 124 			param2 = this.desired(method_ADDPATTERN_2_PATTERN,name);
 125 			this.addpattern(param1, param2);
 126 
 127 		} else if (name.equals(method_RESET)) {
 128 			this.reset();
 129 
 130 		} else if (name.equals(method_FIND)) {
 131 			param1 = this.required(method_FIND_1_NAME,name);
 132 			response = this.find(param1);
 133 
 134 		} else if (name.equals(method_SEEK)) {
 135 			param1 = this.required(method_SEEK_1_STRING,name);
 136 			response = this.seek(param1);
 137 			
 138 		} else if (name.equals(method_SEEK_CI)) {
 139 			param1 = this.required(method_SEEK_CI_1_STRING,name);
 140 			response = this.seekinsensitive(param1);
 141 
 142 		} else if (name.equals(method_SET)) {
 143 			param1 = this.required(method_SET_1_SPOT,name);
 144 			this.set(param1);
 145 
 146 		} else if (name.equals(method_MOVE)) {
 147 			param1 = this.required(method_MOVE_1_ADD,name);
 148 			this.move(param1);
 149 
 150 		} else if (name.equals(method_GET)) {
 151 			response = this.get();
 152 
 153 		} else if (name.equals(method_SUBSTRING)) {
 154 			param1 = this.desired(method_SUBSTRING_1_START,name);
 155 			param2 = this.desired(method_SUBSTRING_2_END,name);
 156 			response = this.substring(param1, param2);
 157 
 158 		} else {
 159 			error("Not a provided method.  method=" + name);
 160 			response = Constants.EMPTY_LEFT;
 161 		}
 162 		return response;
 163 	}
 164 
 165 	/**
 166 	 * Allow the subclass a chance to initialize.  At a minium, an 
 167 	 * implementor should create an empty method.
 168 	 * @throws CallException
 169 	 * @return the name
 170 	 */
 171 	protected String instantiation_chain() throws CallException {
 172 		// At least reset the pattern cache
 173 		patterncache = new HashMap();
 174 		currenttarget = null;
 175 		return myNAME;
 176 	}
 177 
 178 	/**
 179 	 * Allow the subclass a chance to cleanup on free.  At a minium, an 
 180 	 * implementor should create an empty method.
 181 	 * @throws CallException
 182 	 */
 183 	protected void free_chain() throws CallException {
 184 		// NOTHING AT THIS TIME
 185 	}
 186 
 187 	// PRIVATE IMPLEMENTATIONS
 188 
 189 	/**
 190 	 * Start method.  It will set the target for the scan.
 191 	 * @param target the string we are going to scan
 192 	 * @throws CallException
 193 	 */
 194 	private void start(String target) throws CallException {
 195 		currenttarget = target;
 196 		cursor = 0;
 197 	}
 198 
 199 	/**
 200 	 * Adds a pattern to the scanner.  It uses the java regex package
 201 	 * for compiling a pattern.
 202 	 * @param name of the pattern
 203 	 * @param pattern the regular expression pattern
 204 	 * @throws CallException
 205 	 * @see java.util.regex.Pattern
 206 	 */
 207 	private void addpattern(String name, String pattern) throws CallException {
 208 		try {
 209 			Pattern p = Pattern.compile(pattern);
 210 			patterncache.put(name, p);
 211 		} catch (Exception e) {
 212 			error("Could not compile a pattern.  pattern=" + pattern);
 213 		}
 214 	}
 215 
 216 	/**
 217 	 * Resets the cursor
 218 	 * @throws CallException
 219 	 */
 220 	private void reset() throws CallException {
 221 		cursor = 0;
 222 	}
 223 
 224 	/**
 225 	 * Find a pattern.   return size of pattern, if found.  zero if not found.  cursor left at beginning.  Cursor does not move if match fails.
 226 	 * @param name name of pattern already added to the scanner.
 227 	 * @return   size of pattern, if found.  zero if not found.
 228 	 * @throws CallException
 229 	 */
 230 	private String find(String name) throws CallException {
 231 		String result = Constants.ZERO;
 232 		Pattern p;
 233 		int local;
 234 
 235 		// check cursor
 236 		if (cursor >= currenttarget.length()) {
 237 			log("Cursor at end.  find() ignored.");
 238 			return result;
 239 		}
 240 
 241 		// check pattern cache
 242 		if (patterncache.containsKey(name)) {
 243 			p = (Pattern) patterncache.get(name);
 244 		} else {
 245 			// This is a bad one
 246 			throw buildException(
 247 				"Pattern for find() not added.  pattern name =" + name,
 248 				CallException.CODE_MODULE_FAULT);
 249 		}
 250 
 251 		try {
 252 			Matcher m = p.matcher(currenttarget.substring(cursor));
 253 			if (m.find()) {
 254 				local = m.start();
 255 				result = Integer.toString(m.end() - local);
 256 				cursor = cursor + local;
 257 				debug("matched.  Cursor=" + cursor + "  result=" + result);
 258 			}
 259 
 260 		} catch (Exception e) {
 261 			error("Non-fatal Exception in find().  message=" + e.getMessage());
 262 		}
 263 		return result;
 264 	}
 265 
 266 	/**
 267 	 * seek(string) return "true" if found, otherwise "false".  seek an exact string.  cursor left at beginning
 268 	 * if it isn't found, cursor is left at end
 269 	 * @param s string to seek
 270 	 * @throws CallException
 271 	 * @return return "true" if found, otherwise "false"
 272 	 */
 273 	private String seek(String s) throws CallException {
 274 
 275 		String result = Constants.FALSE;
 276 
 277 		// check cursor
 278 		if (cursor >= currenttarget.length()) {
 279 			log("Cursor at end.  seek() ignored.");
 280 			return result;
 281 		}
 282 
 283 		try {
 284 
 285 			String t = currenttarget.substring(cursor);
 286 			int idx = t.indexOf(s);
 287 			if (idx >= 0) {
 288 				cursor = idx + cursor; // Add to the original cursor
 289 				result = Constants.TRUE;
 290 				debug("Seek found.  Cursor=" + cursor);
 291 			}
 292 
 293 		} catch (Exception e) {
 294 			// just fall out.  FALSE should be returned
 295 		}
 296 		return result;
 297 	}
 298 
 299 	/**
 300 	 * seekinsensitive(string) return "true" if found, otherwise "false".  seek a string without regard to case.  cursor left at beginning
 301 	 * if it isn't found, cursor is left at end
 302 	 * @param s string to seek
 303 	 * @throws CallException
 304 	 * @return return "true" if found, otherwise "false"
 305 	 */
 306 	private String seekinsensitive(String s) throws CallException {
 307 
 308 		String result = Constants.FALSE;
 309 		
 310 		try {
 311 		
 312 		    int sourceLength = currenttarget.length();
 313 		    int compareLength = s.length();
 314 		    int runLength = 0;
 315 		    int proposedCursor = cursor;
 316 		    int rovingCursor = cursor;
 317 		    char sourceCandidate;
 318 		    char compareCandidate;
 319 		    while (rovingCursor < sourceLength) { 
 320 		        
 321 		        // Get the candidates
 322 		        sourceCandidate = currenttarget.charAt(rovingCursor);
 323 		        if (Character.isUpperCase(sourceCandidate))  sourceCandidate=Character.toLowerCase(sourceCandidate);
 324 		        compareCandidate = s.charAt(runLength);
 325 		        if (Character.isUpperCase(compareCandidate))  compareCandidate=Character.toLowerCase(compareCandidate);
 326 		      
 327 		        // Pop ahead one
 328 		        rovingCursor++;
 329 
 330 		        // Are they the same?
 331 		        if (sourceCandidate == compareCandidate) {
 332 		            
 333 		            // Start a run
 334 			        runLength++;		            
 335 		            if (runLength==compareLength) {
 336 		                // YAHOO!
 337 		                cursor = proposedCursor;
 338 		                result = Constants.TRUE;
 339 		                break;
 340 		            }
 341 		            
 342 		        } else {
 343 		            // Nope.  Kill the run.
 344 		            runLength = 0;
 345 			        proposedCursor = rovingCursor;
 346 		        }		        
 347 		    }		    
 348 		    
 349 		} catch (Exception e) {
 350 			// just fall out.  FALSE should be returned
 351 		}
 352 		return result; 
 353 	}
 354 	
 355 	/**
 356 	 * set(spot) move cursor to a spot
 357 	 * if it is out of bounds, it will throw an exception
 358 	 * @param spot spot to set the cursor as a parsable Integer
 359 	 * @throws CallException
 360 	 */
 361 	private void set(String spot) throws CallException {
 362 
 363 		try {
 364 			int s = Integer.parseInt(spot);
 365 			if (s > currenttarget.length()) {
 366 				throw new Exception("Out of bounds.");
 367 			}
 368 			cursor = s;
 369 
 370 		} catch (Exception e) {
 371 			throw buildException(
 372 				"Set failed due to " + e.getMessage(),
 373 				CallException.CODE_MODULE_REPORTED_ERROR,
 374 				e);
 375 		}
 376 	}
 377 
 378 	/**
 379 	 * move(add) move cursor by adding to it
 380 	 * if it is out of bounds, it will throw an exception
 381 	 * @param add positions to move it forward
 382 	 * @throws CallException
 383 	 */
 384 	private void move(String add) throws CallException {
 385 
 386 		try {
 387 			int s = Integer.parseInt(add) + cursor;
 388 			if (s > currenttarget.length()) {
 389 				throw new Exception("Out of bounds.");
 390 			}
 391 			cursor = s;
 392 
 393 		} catch (Exception e) {
 394 			throw buildException(
 395 				"Move failed due to " + e.getMessage(),
 396 				CallException.CODE_MODULE_REPORTED_ERROR,
 397 				e);
 398 		}
 399 	}
 400 
 401 	/**
 402 	 * get the cursor position
 403 	 * @throws CallException
 404 	 */
 405 	private String get() throws CallException {
 406 
 407 		return Integer.toString(cursor);
 408 	}
 409 
 410 	/**
 411 	 * substring(start, end-1) return string in range specified.  exception if error.
 412 	 * @param start start spot as a parsable integer
 413 	 * @param end end spot as a parsable integer minus one
 414 	 * @return the string
 415 	 * @throws CallException
 416 	 * @see java.util.regex.Pattern
 417 	 */
 418 	private String substring(String start, String end) throws CallException {
 419 
 420 		String result = Constants.EMPTY_LEFT;
 421 
 422 		try {
 423 			int s = Integer.parseInt(start);
 424 			int e = Integer.parseInt(end);
 425 			result = currenttarget.substring(s, e);
 426 
 427 		} catch (Exception e) {
 428 			throw buildException(
 429 				"Substring failed due to " + e.getMessage(),
 430 				CallException.CODE_MODULE_REPORTED_ERROR,
 431 				e);
 432 		}
 433 		return result;
 434 	}
 435 }