001: /*
002: * SSHTools - Java SSH2 API
003: *
004: * Copyright (C) 2002-2003 Lee David Painter and Contributors.
005: *
006: * Contributions made by:
007: *
008: * Brett Smith
009: * Richard Pernavas
010: * Erwin Bolwidt
011: *
012: * This program is free software; you can redistribute it and/or
013: * modify it under the terms of the GNU General Public License
014: * as published by the Free Software Foundation; either version 2
015: * of the License, or (at your option) any later version.
016: *
017: * This program is distributed in the hope that it will be useful,
018: * but WITHOUT ANY WARRANTY; without even the implied warranty of
019: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
020: * GNU General Public License for more details.
021: *
022: * You should have received a copy of the GNU General Public License
023: * along with this program; if not, write to the Free Software
024: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
025: */
026: // ===========================================================================
027: // CONTENT : CLASS StringPattern
028: // AUTHOR : Manfred Duchrow
029: // VERSION : 1.7 - 13/02/2003
030: // HISTORY :
031: // 24/01/2000 duma CREATED
032: // 08/01/2002 duma bugfix -> Handle *xxx (equal characters after star) correctly
033: // 16/01/2002 duma changed -> Implements Serializable
034: // 06/07/2002 duma bugfix -> Couldn't match "London" on "L*n"
035: // 19/09/2002 duma bugfix -> Couldn't match "MA_DR_HRBLUB" on "*_HR*"
036: // 19/09/2002 duma changed -> Using now StringExaminer instead of CharacterIterator
037: // 29/09/2002 duma changed -> Refactored: Using StringExaminer instead of StringScanner
038: // 26/12/2002 duma changed -> Comment of matches() was wrong / new hasWildcard()
039: // 13/02/2003 duma added -> setDigitWildcardChar()
040: //
041: // Copyright (c) 2000-2003, by Manfred Duchrow. All rights reserved.
042: // ===========================================================================
043: package com.sshtools.daemon.util;
044:
045: // ===========================================================================
046: // IMPORTS
047: // ===========================================================================
048: import java.io.*;
049:
050: /**
051: * This class provides services for checking strings against string-patterns.
052: * Currently it supports the wildcards<br>
053: * '' for any number of any character and <br>
054: * '?' for any one character. The API is very simple:<br>
055: * <br>
056: * There are only the two class methods <i>match()</i> and
057: * <i>matchIgnoreCase()</i>. <br>
058: * Example: <br>
059: * StringPattern.match( 'Hello World", "H W" ) ; --> evaluates to true <br>
060: * StringPattern.matchIgnoreCase( 'StringPattern", "str???pat" ) ; -->
061: * evaluates to true <br>
062: *
063: * @author Manfred Duchrow
064: * @version 1.7
065: */
066: public class StringPattern implements Serializable {
067: // =========================================================================
068: // CONSTANTS
069: // =========================================================================
070:
071: /** */
072: protected final static String MULTI_WILDCARD = "*";
073:
074: /** */
075: protected final static char MULTICHAR_WILDCARD = '*';
076:
077: /** */
078: protected final static char SINGLECHAR_WILDCARD = '?';
079:
080: // =========================================================================
081: // INSTANCE VARIABLES
082: // =========================================================================
083: private boolean ignoreCase = false;
084: private String pattern = null;
085:
086: // -------------------------------------------------------------------------
087: private Character digitWildcard = null;
088:
089: // -------------------------------------------------------------------------
090: // =========================================================================
091: // CONSTRUCTORS
092: // =========================================================================
093:
094: /**
095: * Initializes the new instance with the string pattern and the selecteion,
096: * if case should be ignored when comparing characters.
097: *
098: * @param pattern The pattern to check against ( May contain '' and '?'
099: * wildcards )
100: * @param ignoreCase Definition, if case sensitive character comparison or
101: * not.
102: */
103: public StringPattern(String pattern, boolean ignoreCase) {
104: this .setPattern(pattern);
105: this .setIgnoreCase(ignoreCase);
106: }
107:
108: // StringPattern()
109: // -------------------------------------------------------------------------
110:
111: /**
112: * Initializes the new instance with the string pattern. The default is
113: * case sensitive checking.
114: *
115: * @param pattern The pattern to check against ( May contain '' and '?'
116: * wildcards )
117: */
118: public StringPattern(String pattern) {
119: this (pattern, false);
120: }
121:
122: // StringPattern()
123: // -------------------------------------------------------------------------
124:
125: /**
126: * Initializes the new instance with the string pattern and a digit
127: * wildcard character. The default is case sensitive checking.
128: *
129: * @param pattern The pattern to check against ( May contain '', '?'
130: * wildcards and the digit wildcard )
131: * @param digitWildcard A wildcard character that stands as placeholder for
132: * digits
133: */
134: public StringPattern(String pattern, char digitWildcard) {
135: this (pattern, false, digitWildcard);
136: }
137:
138: // StringPattern()
139: // -------------------------------------------------------------------------
140:
141: /**
142: * Initializes the new instance with the string pattern and the selecteion,
143: * if case should be ignored when comparing characters plus a wildcard
144: * character for digits.
145: *
146: * @param pattern The pattern to check against ( May contain '' and '?'
147: * wildcards )
148: * @param ignoreCase Definition, if case sensitive character comparison or
149: * not.
150: * @param digitWildcard A wildcard character that stands as placeholder for
151: * digits
152: */
153: public StringPattern(String pattern, boolean ignoreCase,
154: char digitWildcard) {
155: this .setPattern(pattern);
156: this .setIgnoreCase(ignoreCase);
157: this .setDigitWildcardChar(digitWildcard);
158: }
159:
160: // StringPattern()
161:
162: /**
163: * Returns whether or not the pattern matching ignores upper and lower case
164: *
165: * @return
166: */
167: public boolean getIgnoreCase() {
168: return ignoreCase;
169: }
170:
171: /**
172: * Sets whether the pattern matching should ignore case or not
173: *
174: * @param newValue
175: */
176: public void setIgnoreCase(boolean newValue) {
177: ignoreCase = newValue;
178: }
179:
180: /**
181: * Returns the pattern as string.
182: *
183: * @return
184: */
185: public String getPattern() {
186: return pattern;
187: }
188:
189: /**
190: * Sets the pattern to a new value
191: *
192: * @param newValue
193: */
194: public void setPattern(String newValue) {
195: pattern = newValue;
196: }
197:
198: /**
199: *
200: *
201: * @return
202: */
203: protected Character digitWildcard() {
204: return digitWildcard;
205: }
206:
207: /**
208: *
209: *
210: * @param newValue
211: */
212: protected void digitWildcard(Character newValue) {
213: digitWildcard = newValue;
214: }
215:
216: // =========================================================================
217: // CLASS METHODS
218: // =========================================================================
219:
220: /**
221: * Returns true, if the given probe string matches the given pattern. <br>
222: * The character comparison is done case sensitive.
223: *
224: * @param probe The string to check against the pattern.
225: * @param pattern The patter, that probably contains wildcards ( '' or '?'
226: * )
227: *
228: * @return
229: */
230: public static boolean match(String probe, String pattern) {
231: StringPattern stringPattern = new StringPattern(pattern, false);
232:
233: return (stringPattern.matches(probe));
234: }
235:
236: // match()
237: // -------------------------------------------------------------------------
238:
239: /**
240: * Returns true, if the given probe string matches the given pattern. <br>
241: * The character comparison is done ignoring upper/lower-case.
242: *
243: * @param probe The string to check against the pattern.
244: * @param pattern The patter, that probably contains wildcards ( '' or '?'
245: * )
246: *
247: * @return
248: */
249: public static boolean matchIgnoreCase(String probe, String pattern) {
250: StringPattern stringPattern = new StringPattern(pattern, true);
251:
252: return (stringPattern.matches(probe));
253: }
254:
255: // matchIgnoreCase()
256: // -------------------------------------------------------------------------
257: // =========================================================================
258: // PUBLIC INSTANCE METHODS
259: // =========================================================================
260:
261: /**
262: * Tests if a specified string matches the pattern.
263: *
264: * @param probe The string to compare to the pattern
265: *
266: * @return true if and only if the probe matches the pattern, false
267: * otherwise.
268: */
269: public boolean matches(String probe) {
270: StringExaminer patternIterator = null;
271: StringExaminer probeIterator = null;
272: char patternCh = '-';
273: char probeCh = '-';
274: String newPattern = null;
275: String subPattern = null;
276: int charIndex = 0;
277:
278: if (probe == null) {
279: return false;
280: }
281:
282: if (probe.length() == 0) {
283: return false;
284: }
285:
286: patternIterator = this .newExaminer(this .getPattern());
287: probeIterator = this .newExaminer(probe);
288: probeCh = probeIterator.nextChar();
289: patternCh = this .getPatternChar(patternIterator, probeCh);
290:
291: while ((this .endNotReached(patternCh))
292: && (this .endNotReached(probeCh))) {
293: if (patternCh == MULTICHAR_WILDCARD) {
294: patternCh = this .skipWildcards(patternIterator);
295:
296: if (this .endReached(patternCh)) {
297: return true; // No more characters after multi wildcard - So everything matches
298: } else {
299: patternIterator.skip(-1);
300: newPattern = this .upToEnd(patternIterator);
301: charIndex = newPattern.indexOf(MULTICHAR_WILDCARD);
302:
303: if (charIndex >= 0) {
304: subPattern = newPattern.substring(0, charIndex);
305:
306: if (this .skipAfter(probeIterator, subPattern)) {
307: patternIterator = this
308: .newExaminer(newPattern
309: .substring(charIndex));
310: patternCh = probeCh;
311: } else {
312: return false;
313: }
314: } else {
315: probeIterator.skip(-1);
316:
317: return this .matchReverse(newPattern,
318: probeIterator);
319: }
320: }
321: }
322:
323: if (this .charsAreEqual(probeCh, patternCh)) {
324: if (this .endNotReached(patternCh)) {
325: probeCh = probeIterator.nextChar();
326: patternCh = this .getPatternChar(patternIterator,
327: probeCh);
328: }
329: } else {
330: if (patternCh != MULTICHAR_WILDCARD) {
331: return false; // character is not matching - return immediately
332: }
333: }
334: }
335:
336: // while()
337: return ((this .endReached(patternCh)) && (this
338: .endReached(probeCh)));
339: }
340:
341: // matches()
342: // -------------------------------------------------------------------------
343:
344: /**
345: * Returns the pattern string.
346: *
347: * @see java.lang.Object#toString()
348: */
349: public String toString() {
350: if (this .getPattern() == null) {
351: return super .toString();
352: } else {
353: return this .getPattern();
354: }
355: }
356:
357: // toString()
358: // -------------------------------------------------------------------------
359:
360: /**
361: * Returns true if the pattern contains any '' or '?' wildcard character.
362: *
363: * @return
364: */
365: public boolean hasWildcard() {
366: if (this .getPattern() == null) {
367: return false;
368: }
369:
370: if (this .hasDigitWildcard()) {
371: if (this .getPattern().indexOf(this .digitWildcardChar()) >= 0) {
372: return true;
373: }
374: }
375:
376: return (this .getPattern().indexOf(MULTI_WILDCARD) >= 0)
377: || (this .getPattern().indexOf(SINGLECHAR_WILDCARD) >= 0);
378: }
379:
380: // hasWildcard()
381: // -------------------------------------------------------------------------
382:
383: /**
384: * Sets the given character as a wildcard character in this pattern to
385: * match only digits ('0'-'9'). <br>
386: *
387: * @param digitWildcard The placeholder character for digits
388: */
389: public void setDigitWildcardChar(char digitWildcard) {
390: if (digitWildcard <= 0) {
391: this .digitWildcard(null);
392: } else {
393: this .digitWildcard(new Character(digitWildcard));
394: }
395: }
396:
397: // setDigitWildcardChar()
398:
399: /**
400: *
401: *
402: * @return
403: */
404: protected boolean hasDigitWildcard() {
405: return this .digitWildcard() != null;
406: }
407:
408: // hasDigitWildcard()
409: // -------------------------------------------------------------------------
410: protected char digitWildcardChar() {
411: if (this .hasDigitWildcard()) {
412: return this .digitWildcard().charValue();
413: } else {
414: return '\0';
415: }
416: }
417:
418: // digitWildcardChar()
419: // -------------------------------------------------------------------------
420:
421: /**
422: * Moves the iterator position to the next character that is no wildcard.
423: * Doesn't skip digit wildcards !
424: *
425: * @param iterator
426: *
427: * @return
428: */
429: protected char skipWildcards(StringExaminer iterator) {
430: char result = '-';
431:
432: do {
433: result = iterator.nextChar();
434: } while ((result == MULTICHAR_WILDCARD)
435: || (result == SINGLECHAR_WILDCARD));
436:
437: return result;
438: }
439:
440: // skipWildcards()
441: // -------------------------------------------------------------------------
442:
443: /**
444: * Increments the given iterator up to the last character that matched the
445: * character sequence in the given matchString. Returns true, if the
446: * matchString was found, otherwise false.
447: *
448: * @param examiner
449: * @param matchString The string to be found (must not contain )
450: *
451: * @return
452: */
453: protected boolean skipAfter(StringExaminer examiner,
454: String matchString) {
455: // Do not use the method of StringExaminer anymore, because digit wildcard
456: // support is in the charsAreEqual() method which is unknown to the examiner.
457: // return examiner.skipAfter( matchString ) ;
458: char ch = '-';
459: char matchChar = ' ';
460: boolean found = false;
461: int index = 0;
462:
463: if ((matchString == null) || (matchString.length() == 0)) {
464: return false;
465: }
466:
467: ch = examiner.nextChar();
468:
469: while ((examiner.endNotReached(ch)) && (!found)) {
470: matchChar = matchString.charAt(index);
471:
472: if (this .charsAreEqual(ch, matchChar)) {
473: index++;
474:
475: if (index >= matchString.length()) { // whole matchString checked ?
476: found = true;
477: } else {
478: ch = examiner.nextChar();
479: }
480: } else {
481: if (index == 0) {
482: ch = examiner.nextChar();
483: } else {
484: index = 0;
485: }
486: }
487: }
488:
489: return found;
490: }
491:
492: // skipAfter()
493: // -------------------------------------------------------------------------
494: protected String upToEnd(StringExaminer iterator) {
495: return iterator.upToEnd();
496: }
497:
498: // upToEnd()
499: // -------------------------------------------------------------------------
500: protected boolean matchReverse(String pattern,
501: StringExaminer probeIterator) {
502: String newPattern;
503: String newProbe;
504: StringPattern newMatcher;
505: newPattern = MULTI_WILDCARD + pattern;
506: newProbe = this .upToEnd(probeIterator);
507: newPattern = this .strUtil().reverse(newPattern);
508: newProbe = this .strUtil().reverse(newProbe);
509: newMatcher = new StringPattern(newPattern, this .getIgnoreCase());
510:
511: if (this .hasDigitWildcard()) {
512: newMatcher.setDigitWildcardChar(this .digitWildcardChar());
513: }
514:
515: return newMatcher.matches(newProbe);
516: }
517:
518: // matchReverse()
519: // -------------------------------------------------------------------------
520: protected boolean charsAreEqual(char probeChar, char patternChar) {
521: if (this .hasDigitWildcard()) {
522: if (patternChar == this .digitWildcardChar()) {
523: return Character.isDigit(probeChar);
524: }
525: }
526:
527: if (this .getIgnoreCase()) {
528: return (Character.toUpperCase(probeChar) == Character
529: .toUpperCase(patternChar));
530: } else {
531: return (probeChar == patternChar);
532: }
533: }
534:
535: // charsAreEqual()
536: // -------------------------------------------------------------------------
537: protected boolean endReached(char character) {
538: return (character == StringExaminer.END_REACHED);
539: }
540:
541: // endReached()
542: // -------------------------------------------------------------------------
543: protected boolean endNotReached(char character) {
544: return (!endReached(character));
545: }
546:
547: // endNotReached()
548: // -------------------------------------------------------------------------
549: protected char getPatternChar(StringExaminer patternIterator,
550: char probeCh) {
551: char patternCh;
552: patternCh = patternIterator.nextChar();
553:
554: return ((patternCh == SINGLECHAR_WILDCARD) ? probeCh
555: : patternCh);
556: }
557:
558: // getPatternChar()
559: // -------------------------------------------------------------------------
560: protected StringExaminer newExaminer(String str) {
561: return new StringExaminer(str, this .getIgnoreCase());
562: }
563:
564: // newExaminer()
565: // -------------------------------------------------------------------------
566: protected StringUtil strUtil() {
567: return StringUtil.current();
568: }
569:
570: // strUtil()
571: // -------------------------------------------------------------------------
572: }
573:
574: // class StringPattern
|