001: /*
002: * Search.java
003: *
004: * Copyright (C) 1998-2004 Peter Graves
005: * $Id: Search.java,v 1.9 2004/09/05 20:01:33 piso Exp $
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: */
021:
022: package org.armedbear.j;
023:
024: import gnu.regexp.RE;
025: import gnu.regexp.REException;
026: import gnu.regexp.REMatch;
027:
028: public class Search implements Cloneable {
029: private String pattern;
030: private String lowerCasePattern;
031: private int patternLength;
032:
033: private RE re;
034: private REMatch match;
035:
036: private boolean ignoreCase;
037: private boolean wholeWordsOnly;
038: private boolean regularExpression;
039: private boolean isMultilinePattern;
040: private boolean restrictToSelection;
041:
042: private Region region;
043:
044: public Search() {
045: setPattern(new String());
046: }
047:
048: public Search(String pattern, boolean ignoreCase,
049: boolean wholeWordsOnly) {
050: setPattern(pattern);
051: this .ignoreCase = ignoreCase;
052: this .wholeWordsOnly = wholeWordsOnly;
053: }
054:
055: public final String getPattern() {
056: return pattern;
057: }
058:
059: public final void setPattern(String s) {
060: if (s != null) {
061: pattern = s;
062: lowerCasePattern = s.toLowerCase();
063: patternLength = s.length();
064: } else {
065: pattern = lowerCasePattern = "";
066: patternLength = 0;
067: }
068: }
069:
070: public final void appendCharToPattern(char c) {
071: pattern += c;
072: lowerCasePattern = pattern.toLowerCase();
073: ++patternLength;
074: }
075:
076: public final int getPatternLength() {
077: return patternLength;
078: }
079:
080: public final String getLowerCasePattern() {
081: return lowerCasePattern;
082: }
083:
084: public final RE getRE() {
085: return re;
086: }
087:
088: public final void setRE(RE re) {
089: this .re = re;
090: }
091:
092: public final REMatch getMatch() {
093: return match;
094: }
095:
096: public final boolean ignoreCase() {
097: return ignoreCase;
098: }
099:
100: public final void setIgnoreCase(boolean b) {
101: ignoreCase = b;
102: }
103:
104: public final boolean wholeWordsOnly() {
105: return wholeWordsOnly;
106: }
107:
108: public final void setWholeWordsOnly(boolean b) {
109: wholeWordsOnly = b;
110: }
111:
112: public final boolean isRegularExpression() {
113: return regularExpression;
114: }
115:
116: public final void setRegularExpression(boolean b) {
117: regularExpression = b;
118: }
119:
120: public final boolean isMultilinePattern() {
121: return isMultilinePattern;
122: }
123:
124: public final void setMultiline(boolean b) {
125: isMultilinePattern = b;
126: }
127:
128: public final boolean restrictToSelection() {
129: return restrictToSelection;
130: }
131:
132: public final void setRestrictToSelection(boolean b) {
133: restrictToSelection = b;
134: }
135:
136: public final Region getRegion() {
137: return region;
138: }
139:
140: public final void setRegion(Region r) {
141: region = r;
142: }
143:
144: protected Position findInBuffer(Buffer buffer) {
145: return find(buffer, new Position(buffer.getFirstLine(), 0));
146: }
147:
148: public final Position find(Buffer buffer, Position start) {
149: return regularExpression ? findRegExp(buffer, start)
150: : findString(buffer, start);
151: }
152:
153: public final Position reverseFind(Buffer buffer, Position start) {
154: if (regularExpression) {
155: if (isMultilinePattern)
156: return reverseFindMultilineRegExp(buffer, start);
157: else
158: return reverseFindRegExp(buffer, start);
159: } else
160: return reverseFindString(buffer, start);
161: }
162:
163: public final Position find(Mode mode, Position start) {
164: return regularExpression ? findRegExp(mode, start)
165: : findString(mode, start);
166: }
167:
168: public final Position findInLine(Mode mode, Position start) {
169: if (regularExpression)
170: return findRegExpInLine(mode, start.getLine(), start
171: .getOffset(), start.getLineLength());
172: else
173: return findStringInLine(mode, start.getLine(), start
174: .getOffset(), start.getLineLength());
175: }
176:
177: public final boolean find(String s) {
178: return regularExpression ? findRegExp(s) : findString(s);
179: }
180:
181: public final boolean findDelimited(String s, Mode mode) {
182: return regularExpression ? findRegExpDelimited(s, mode)
183: : findStringDelimited(s, mode);
184: }
185:
186: // Search is restricted to region if restrictToSelection is true and
187: // region is not null.
188: public final Position findString(Buffer buffer, Position start) {
189: return findString(buffer.getMode(), start);
190: }
191:
192: // Search is restricted to region if restrictToSelection is true and
193: // region is not null.
194: private Position findString(Mode mode, Position start) {
195: Debug
196: .assertTrue(lowerCasePattern.equals(pattern
197: .toLowerCase()));
198: Debug.assertTrue(patternLength == pattern.length());
199: Line line = start.getLine();
200: int begin = start.getOffset();
201: Line endLine;
202: if (restrictToSelection && region != null)
203: endLine = region.getEndLine();
204: else
205: endLine = null;
206: Position pos = null;
207: while (line != endLine) {
208: pos = findStringInLine(mode, line, begin, line.length());
209: if (pos != null)
210: return pos;
211: line = line.next();
212: begin = 0;
213: }
214: if (line != null) {
215: // End line of region.
216: pos = findStringInLine(mode, line, begin, region.getEnd()
217: .getOffset());
218: }
219: return pos;
220: }
221:
222: // Region is ignored.
223: public Position findString(Buffer buffer, Position start,
224: boolean wrapBuffer) {
225: Debug
226: .assertTrue(lowerCasePattern.equals(pattern
227: .toLowerCase()));
228: Debug.assertTrue(patternLength == pattern.length());
229: Mode mode = buffer.getMode();
230: Line line = start.getLine();
231: int begin = start.getOffset();
232: while (line != null) {
233: Position pos = findStringInLine(mode, line, begin, line
234: .length());
235: if (pos != null)
236: return pos;
237: line = line.next();
238: begin = 0;
239: }
240: if (wrapBuffer) {
241: line = buffer.getFirstLine();
242: Line endLine = start.getNextLine();
243: while (line != endLine) {
244: Position pos = findStringInLine(mode, line, 0, line
245: .length());
246: if (pos != null)
247: return pos;
248: line = line.next();
249: }
250: }
251: return null;
252: }
253:
254: private Position findStringInLine(Mode mode, Line line, int begin,
255: int end) {
256: String toBeSearched;
257: if (end < line.length())
258: toBeSearched = line.substring(0, end);
259: else
260: toBeSearched = line.getText();
261: int index = begin;
262: int limit = end - patternLength;
263: while (index <= limit) {
264: if (ignoreCase)
265: index = toBeSearched.toLowerCase().indexOf(
266: lowerCasePattern, index);
267: else
268: index = toBeSearched.indexOf(pattern, index);
269: if (index < 0)
270: break;
271: Position pos = new Position(line, index);
272: if (!wholeWordsOnly
273: || Utilities.isDelimited(mode, pos, patternLength))
274: return pos;
275: ++index;
276: }
277: return null;
278: }
279:
280: private boolean findString(String s) {
281: Debug.assertTrue(patternLength == pattern.length());
282: int index = 0;
283: int limit = s.length() - patternLength;
284: while (index <= limit) {
285: if (ignoreCase)
286: index = s.toLowerCase()
287: .indexOf(lowerCasePattern, index);
288: else
289: index = s.indexOf(pattern, index);
290: if (index < 0)
291: break;
292: if (!wholeWordsOnly
293: || Utilities.isDelimited(s, index, patternLength))
294: return true;
295: ++index;
296: }
297: return false;
298: }
299:
300: private boolean findStringDelimited(String s, Mode mode) {
301: Debug.assertTrue(wholeWordsOnly);
302: Debug.assertTrue(patternLength == pattern.length());
303: int index = 0;
304: int limit = s.length() - patternLength;
305: while (index <= limit) {
306: if (ignoreCase)
307: index = s.toLowerCase()
308: .indexOf(lowerCasePattern, index);
309: else
310: index = s.indexOf(pattern, index);
311: if (index < 0)
312: break;
313: if (Utilities.isDelimited(s, index, patternLength, mode))
314: return true;
315: ++index;
316: }
317: return false;
318: }
319:
320: // Region is ignored.
321: public Position reverseFindString(Buffer buffer, Position start) {
322: Debug
323: .assertTrue(lowerCasePattern.equals(pattern
324: .toLowerCase()));
325: Debug.assertTrue(patternLength == pattern.length());
326: Line line = start.getLine();
327: Position pos = reverseFindStringInLine(buffer, line, 0, start
328: .getOffset());
329: if (pos != null)
330: return pos;
331: line = line.previous();
332: while (line != null) {
333: pos = reverseFindStringInLine(buffer, line, 0, line
334: .length());
335: if (pos != null)
336: return pos;
337: line = line.previous();
338: }
339: return null;
340: }
341:
342: private Position reverseFindStringInLine(Buffer buffer, Line line,
343: int begin, int end) {
344: int index = end;
345: while (index >= begin) {
346: if (ignoreCase)
347: index = line.getText().toLowerCase().lastIndexOf(
348: lowerCasePattern, index);
349: else
350: index = line.getText().lastIndexOf(pattern, index);
351: if (index < 0)
352: break;
353: Position pos = new Position(line, index);
354: if (!wholeWordsOnly
355: || Utilities
356: .isDelimited(buffer, pos, patternLength))
357: return pos;
358: --index;
359: }
360: return null;
361: }
362:
363: // Search is restricted to region if restrictToSelection is true and
364: // region is not null.
365: public final Position findRegExp(Buffer buffer, Position start) {
366: if (isMultilinePattern)
367: return findMultilineRegExp(buffer, start);
368: else
369: return findRegExp(buffer.getMode(), start);
370: }
371:
372: public void setREFromPattern() throws REException {
373: int cflags = 0;
374: if (isMultilinePattern)
375: cflags |= RE.REG_MULTILINE;
376: if (ignoreCase)
377: cflags |= RE.REG_ICASE;
378: re = new RE(pattern, cflags);
379: }
380:
381: // Search is restricted to region if restrictToSelection is true and
382: // region is not null.
383: private Position findMultilineRegExp(Buffer buffer, Position start) {
384: if (re == null) {
385: try {
386: setREFromPattern();
387: } catch (Throwable t) {
388: Log.error(t);
389: return null;
390: }
391: }
392: final String s = buffer.getText();
393: int startIndex = buffer.getAbsoluteOffset(start);
394: int endIndex = -1;
395: if (restrictToSelection && region != null)
396: endIndex = buffer.getAbsoluteOffset(region.getEnd());
397: while (true) {
398: match = findMatch(s, startIndex, endIndex);
399: if (match == null)
400: return null;
401: if (!wholeWordsOnly)
402: break;
403: if (Utilities.isDelimited(buffer.getMode(), s, match
404: .getStartIndex(), match.getEndIndex()))
405: break;
406: startIndex = match.getStartIndex() + 1;
407: }
408: return buffer.getPosition(match.getStartIndex());
409: }
410:
411: private Position reverseFindMultilineRegExp(Buffer buffer,
412: Position start) {
413: if (re == null) {
414: try {
415: setREFromPattern();
416: } catch (Throwable t) {
417: Log.error(t);
418: return null;
419: }
420: }
421: int startIndex = 0;
422: int endIndex = buffer.getAbsoluteOffset(start);
423: final String s = buffer.getText().substring(0, endIndex);
424: REMatch lastMatch = null;
425: while (true) {
426: match = findMatch(s, startIndex, -1);
427: if (match == null)
428: break;
429: if (!wholeWordsOnly)
430: lastMatch = match;
431: else if (Utilities.isDelimited(buffer.getMode(), s, match
432: .getStartIndex(), match.getEndIndex()))
433: lastMatch = match;
434: startIndex = match.getStartIndex() + 1;
435: }
436: if (lastMatch == null)
437: return null;
438: match = lastMatch;
439: return buffer.getPosition(match.getStartIndex());
440: }
441:
442: // Search is restricted to region if endIndex >= 0.
443: private REMatch findMatch(String s, int startIndex, int endIndex) {
444: REMatch m = re.getMatch(s, startIndex);
445: if (m == null)
446: return null; // Not found at all.
447: if (endIndex >= 0 && m.getEndIndex() > endIndex)
448: return null; // Match extends past end of selected region.
449: return m;
450: }
451:
452: // Search is restricted to region if restrictToSelection is true and
453: // region is not null.
454: private Position findRegExp(Mode mode, Position start) {
455: if (re == null) {
456: try {
457: setREFromPattern();
458: } catch (Throwable t) {
459: Log.error(t);
460: return null;
461: }
462: }
463: match = null;
464: Line line = start.getLine();
465: int begin = start.getOffset();
466: Line endLine;
467: if (restrictToSelection && region != null)
468: endLine = region.getEndLine();
469: else
470: endLine = null;
471: Position pos = null;
472: while (line != endLine) {
473: pos = findRegExpInLine(mode, line, begin, line.length());
474: if (pos != null)
475: return pos;
476: line = line.next();
477: begin = 0;
478: }
479: if (line != null) {
480: // End line of region.
481: pos = findRegExpInLine(mode, line, begin, region
482: .getEndOffset());
483: }
484: return pos;
485: }
486:
487: private Position findRegExpInLine(Mode mode, Line line, int begin,
488: int end) {
489: String toBeSearched;
490: if (end < line.length())
491: toBeSearched = line.substring(0, end);
492: else
493: toBeSearched = line.getText();
494: int index = begin;
495: int limit = toBeSearched.length();
496: while (index <= limit) {
497: match = re.getMatch(toBeSearched, index);
498: if (match == null)
499: break;
500: Position pos = new Position(line, match.getStartIndex());
501: if (!wholeWordsOnly
502: || Utilities.isDelimited(mode, pos, match
503: .toString().length()))
504: return pos;
505: index = match.getStartIndex() + 1;
506: }
507: return null;
508: }
509:
510: private boolean findRegExp(String toBeSearched) {
511: int index = 0;
512: int limit = toBeSearched.length();
513: while (index <= limit) {
514: match = re.getMatch(toBeSearched, index);
515: if (match == null)
516: break;
517: if (!wholeWordsOnly
518: || Utilities
519: .isDelimited(toBeSearched, match
520: .getStartIndex(), match.toString()
521: .length()))
522: return true;
523: index = match.getStartIndex() + 1;
524: }
525: return false;
526: }
527:
528: private boolean findRegExpDelimited(String s, Mode mode) {
529: Debug.assertTrue(wholeWordsOnly);
530: int index = 0;
531: int limit = s.length();
532: while (index <= limit) {
533: match = re.getMatch(s, index);
534: if (match == null)
535: break;
536: if (Utilities.isDelimited(s, match.getStartIndex(), match
537: .toString().length(), mode))
538: return true;
539: index = match.getStartIndex() + 1;
540: }
541: return false;
542: }
543:
544: // Region is ignored.
545: public Position reverseFindRegExp(Buffer buffer, Position start) {
546: if (re == null) {
547: try {
548: re = new RE(pattern, ignoreCase ? RE.REG_ICASE : 0);
549: } catch (Throwable t) {
550: Log.error(t);
551: return null;
552: }
553: }
554: Line line = start.getLine();
555: match = null;
556: Position pos = reverseFindRegExpInLine(buffer, line, 0, start
557: .getOffset());
558: if (pos != null)
559: return pos;
560: line = line.previous();
561: while (line != null) {
562: pos = reverseFindRegExpInLine(buffer, line, 0, line
563: .length());
564: if (pos != null)
565: return pos;
566: line = line.previous();
567: }
568: return null;
569: }
570:
571: private Position reverseFindRegExpInLine(Buffer buffer, Line line,
572: int begin, int end) {
573: int index = end;
574: while (index >= begin) {
575: match = re.getMatch(line.getText(), index);
576: if (match != null && match.getStartIndex() <= end) {
577: Position pos = new Position(line, match.getStartIndex());
578: if (!wholeWordsOnly
579: || Utilities.isDelimited(buffer, pos, match
580: .toString().length()))
581: return pos;
582: }
583: --index;
584: }
585: return null;
586: }
587:
588: public void notFound(Editor editor) {
589: FastStringBuffer sb = new FastStringBuffer();
590: if (regularExpression)
591: sb.append("Regular expression ");
592: sb.append('"');
593: sb.append(pattern);
594: sb.append("\" not found");
595: editor.status(sb.toString());
596: }
597:
598: public boolean equals(Object object) {
599: if (this == object)
600: return true;
601: if (object instanceof Search) {
602: Search search = (Search) object;
603: if (pattern == null) {
604: if (search.pattern != null)
605: return false;
606: } else {
607: // pattern != null
608: if (!pattern.equals(search.pattern))
609: return false;
610: }
611: if (ignoreCase != search.ignoreCase)
612: return false;
613: if (wholeWordsOnly != search.wholeWordsOnly)
614: return false;
615: if (regularExpression != search.regularExpression)
616: return false;
617: if (restrictToSelection != search.restrictToSelection)
618: return false;
619: // Passed all tests.
620: return true;
621: }
622: return false;
623: }
624:
625: public Object clone() {
626: try {
627: return super .clone();
628: } catch (CloneNotSupportedException e) {
629: throw new InternalError(e.toString());
630: }
631: }
632: }
|