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 }
|