0001: /*
0002: * Sun Public License Notice
0003: *
0004: * The contents of this file are subject to the Sun Public License
0005: * Version 1.0 (the "License"). You may not use this file except in
0006: * compliance with the License. A copy of the License is available at
0007: * http://www.sun.com/
0008: *
0009: * The Original Code is NetBeans. The Initial Developer of the Original
0010: * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
0011: * Microsystems, Inc. All Rights Reserved.
0012: */
0013:
0014: package org.netbeans.editor;
0015:
0016: /**
0017: * Various finders are located here.
0018: *
0019: * @author Miloslav Metelka
0020: * @version 1.00
0021: */
0022:
0023: public class FinderFactory {
0024:
0025: /**
0026: * Abstract finder implementation. The only <CODE>find()</CODE> method
0027: * must be redefined.
0028: */
0029: public static abstract class AbstractFinder implements Finder {
0030:
0031: /** Was the string found? */
0032: protected boolean found;
0033:
0034: /** Was the string found? */
0035: public final boolean isFound() {
0036: return found;
0037: }
0038:
0039: /** Reset the finder */
0040: public void reset() {
0041: found = false;
0042: }
0043:
0044: }
0045:
0046: /** Return successful match on the first searched char */
0047: public static class TrueFinder extends AbstractFinder {
0048:
0049: public int find(int bufferStartPos, char buffer[], int offset1,
0050: int offset2, int reqPos, int limitPos) {
0051: found = true;
0052: return reqPos;
0053: }
0054:
0055: }
0056:
0057: /** Request non-existent position immediately */
0058: public static class FalseFinder extends AbstractFinder implements
0059: StringFinder {
0060:
0061: public int find(int bufferStartPos, char buffer[], int offset1,
0062: int offset2, int reqPos, int limitPos) {
0063: return -1;
0064: }
0065:
0066: public int getFoundLength() {
0067: return 0;
0068: }
0069:
0070: }
0071:
0072: /** Finds end of current line in forward direction */
0073: static final class EOLFwdFinder extends AbstractFinder {
0074:
0075: /** Finds EOL in forward direction */
0076: public int find(int bufferStartPos, char buffer[], int offset1,
0077: int offset2, int reqPos, int limitPos) {
0078: int offset = reqPos - bufferStartPos;
0079: while (offset < offset2) {
0080: if (buffer[offset] == '\n') {
0081: found = true;
0082: return bufferStartPos + offset;
0083: }
0084: offset++;
0085: }
0086: return bufferStartPos + offset;
0087: }
0088:
0089: }
0090:
0091: /**
0092: * Finder for going n-lines forward where n is greater or equal 1 and stops
0093: * on the first position of next line.
0094: */
0095: static final class NBOLFwdFinder extends AbstractFinder {
0096:
0097: /** How many lines go forward */
0098: int fwdLines;
0099:
0100: /** Finds BOL in forward direction */
0101: public int find(int bufferStartPos, char buffer[], int offset1,
0102: int offset2, int reqPos, int limitPos) {
0103: int offset = reqPos - bufferStartPos;
0104: while (offset < offset2) {
0105: if (buffer[offset] == '\n') {
0106: fwdLines--;
0107: if (fwdLines == 0) {
0108: found = true;
0109: return bufferStartPos + offset + 1;
0110: }
0111: }
0112: offset++;
0113: }
0114: return bufferStartPos + offset;
0115: }
0116:
0117: }
0118:
0119: /**
0120: * Finder for going n-lines forward where n is greater or equal 1 and
0121: * finding and storing the BOL position of the line and then going till EOL
0122: * is found.
0123: */
0124: static final class BEOLLineFwdFinder extends AbstractFinder {
0125:
0126: /** How many lines go forward */
0127: int fwdLines;
0128:
0129: /** Position of BOL */
0130: int bolPos;
0131:
0132: public void reset() {
0133: bolPos = -1;
0134: }
0135:
0136: /** Finds BOL and EOL in forward direction */
0137: public int find(int bufferStartPos, char buffer[], int offset1,
0138: int offset2, int reqPos, int limitPos) {
0139: int offset = reqPos - bufferStartPos;
0140: while (offset < offset2) {
0141: if (buffer[offset] == '\n') {
0142: if (fwdLines-- == 0) {
0143: found = true;
0144: return bufferStartPos + offset; // store eol
0145: }
0146: bolPos = bufferStartPos + offset + 1; // store bol
0147: }
0148: offset++;
0149: }
0150: return bufferStartPos + offset;
0151: }
0152:
0153: }
0154:
0155: /**
0156: * Finder for finding the line, bol and eol from position.
0157: */
0158: static final class BEOLPosFwdFinder extends AbstractFinder {
0159:
0160: /** How many lines went forward */
0161: int line;
0162:
0163: /** Target position */
0164: int tgtPos;
0165:
0166: /** Begin of line position */
0167: int bolPos;
0168:
0169: public void reset() {
0170: line = 0;
0171: bolPos = -1;
0172: }
0173:
0174: /** Finds BOL and EOL in forward direction */
0175: public int find(int bufferStartPos, char buffer[], int offset1,
0176: int offset2, int reqPos, int limitPos) {
0177: int offset = reqPos - bufferStartPos;
0178: while (offset < offset2) {
0179: if (buffer[offset] == '\n') {
0180: if (tgtPos <= bufferStartPos + offset) {
0181: found = true;
0182: return bufferStartPos + offset; // store eol
0183: }
0184: line++;
0185: bolPos = bufferStartPos + offset + 1; // store bol
0186: }
0187: offset++;
0188: }
0189: return bufferStartPos + offset;
0190: }
0191:
0192: }
0193:
0194: /**
0195: * Finder for going back to the first new line which helps to find the
0196: * begining of the line.
0197: */
0198: public static final class BOLBwdFinder extends AbstractFinder {
0199:
0200: /** finds BOL on current line */
0201: public int find(int bufferStartPos, char buffer[], int offset1,
0202: int offset2, int reqPos, int limitPos) {
0203: int offset = reqPos - bufferStartPos;
0204: while (offset >= offset1) {
0205: if (buffer[offset] == '\n') {
0206: found = true;
0207: return bufferStartPos + offset;
0208: }
0209: offset--;
0210: }
0211: return bufferStartPos + offset;
0212: }
0213:
0214: }
0215:
0216: /**
0217: * Finder for getting visual column value for particular position. The
0218: * starting position for find must be the start of particular line. The
0219: * limit position should be set to position for which the visual column is
0220: * requested. This method can be used only in case the font is superfixed
0221: * i.e. all the characters of all font styles have the same width.
0222: */
0223: public static final class PosVisColFwdFinder extends AbstractFinder {
0224:
0225: /** Visual column on line */
0226: int visCol;
0227:
0228: /** Tab size for particular document scanned */
0229: int tabSize;
0230:
0231: /** Get visual column that this finder computed */
0232: public int getVisCol() {
0233: return visCol;
0234: }
0235:
0236: public void setTabSize(int tabSize) {
0237: this .tabSize = tabSize;
0238: }
0239:
0240: /** Mark that first call will follow */
0241: public void reset() {
0242: super .reset();
0243: visCol = 0;
0244: }
0245:
0246: /** finds BOL on current line */
0247: public int find(int bufferStartPos, char buffer[], int offset1,
0248: int offset2, int reqPos, int limitPos) {
0249:
0250: int offset = reqPos - bufferStartPos;
0251: while (offset < offset2) {
0252: if (buffer[offset] == '\t') {
0253: visCol = (visCol + tabSize) / tabSize * tabSize;
0254: } else {
0255: visCol++;
0256: }
0257: offset++;
0258: }
0259: return bufferStartPos + offset;
0260: }
0261:
0262: }
0263:
0264: /**
0265: * Finder for getting position from visual column knowledge. It is kind of
0266: * reverse finder for <CODE>PosVisColFwdFinder</CODE>. The starting
0267: * position for find should be the start of particular line. The found
0268: * position will be that position in document that corresponds to the column
0269: * position. This method can be used only in case the font is superfixed
0270: * i.e. all the characters of all font styles have the same width.
0271: */
0272: public static final class VisColPosFwdFinder extends AbstractFinder {
0273:
0274: /** Visual column position on line */
0275: int visCol;
0276:
0277: /** Current visual position as tracked by finder */
0278: int curVisCol;
0279:
0280: /** Tab size for particular document scanned */
0281: int tabSize;
0282:
0283: /** Extended UI to get character widths */
0284: EditorUI editorUI;
0285:
0286: /** Set visual column that this finder will try to reach */
0287: public void setVisCol(int visCol) {
0288: this .visCol = visCol;
0289: }
0290:
0291: public void setTabSize(int tabSize) {
0292: this .tabSize = tabSize;
0293: }
0294:
0295: /** Mark that first call will follow */
0296: public void reset() {
0297: super .reset();
0298: curVisCol = 0;
0299: }
0300:
0301: /** finds BOL on current line */
0302: public int find(int bufferStartPos, char buffer[], int offset1,
0303: int offset2, int reqPos, int limitPos) {
0304:
0305: int offset = reqPos - bufferStartPos;
0306: while (offset < offset2) {
0307: if (curVisCol >= visCol) {
0308: found = true;
0309: return bufferStartPos + offset;
0310: }
0311:
0312: switch (buffer[offset]) {
0313: case '\t':
0314: curVisCol = (curVisCol + tabSize) / tabSize
0315: * tabSize;
0316: break;
0317: case '\n':
0318: found = true;
0319: return bufferStartPos + offset;
0320: default:
0321: curVisCol++;
0322: }
0323: offset++;
0324: }
0325: return bufferStartPos + offset;
0326: }
0327:
0328: }
0329:
0330: /** Generic forward finder that simplifies the search process. */
0331: public static abstract class GenericFwdFinder extends
0332: AbstractFinder {
0333:
0334: public final int find(int bufferStartPos, char buffer[],
0335: int offset1, int offset2, int reqPos, int limitPos) {
0336: int offset = reqPos - bufferStartPos;
0337: int limitOffset = limitPos - bufferStartPos - 1;
0338: while (offset >= offset1 && offset < offset2) {
0339: offset += scan(buffer[offset], (offset == limitOffset));
0340: if (found) {
0341: break;
0342: }
0343: }
0344: return bufferStartPos + offset;
0345: }
0346:
0347: /**
0348: * This function decides if it found a desired string or not. The
0349: * function receives currently searched character and flag if it's the
0350: * last one that is searched or not.
0351: *
0352: * @return if the function decides that it found a desired string it
0353: * sets <CODE>found = true</CODE> and returns how many
0354: * characters back the searched string begins in forward
0355: * direction (0 stands for current character). For example if
0356: * the function looks for word 'yes' and it gets 's' as
0357: * parameter it sets found = true and returns -2. If the string
0358: * is not yet found it returns how many characters it should go
0359: * in forward direction (in this case it would usually be 1).
0360: * The next searched character will be that one requested.
0361: */
0362: protected abstract int scan(char ch, boolean lastChar);
0363:
0364: }
0365:
0366: /** Generic backward finder that simplifies the search process. */
0367: public static abstract class GenericBwdFinder extends
0368: AbstractFinder {
0369:
0370: public final int find(int bufferStartPos, char buffer[],
0371: int offset1, int offset2, int reqPos, int limitPos) {
0372: int offset = reqPos - bufferStartPos;
0373: int limitOffset = limitPos - bufferStartPos;
0374: while (offset >= offset1 && offset < offset2) {
0375: offset += scan(buffer[offset], (offset == limitOffset));
0376: if (found) {
0377: break;
0378: }
0379: }
0380: return bufferStartPos + offset;
0381: }
0382:
0383: /**
0384: * This function decides if it found a desired string or not. The
0385: * function receives currently searched character and flag if it's the
0386: * last one that is searched or not.
0387: *
0388: * @return if the function decides that it found a desired string it
0389: * sets <CODE>found = true</CODE> and returns how many
0390: * characters back the searched string begins in backward
0391: * direction (0 stands for current character). It is usually 0
0392: * as the finder usually decides after the last required
0393: * character but it's not always the case e.g. for
0394: * whole-words-only search it can be 1 or so. If the string is
0395: * not yet found it returns how many characters it should go in
0396: * backward direction (in this case it would usually be -1). The
0397: * next searched character will be that one requested.
0398: */
0399: protected abstract int scan(char ch, boolean lastChar);
0400:
0401: }
0402:
0403: public static abstract class GenericFinder extends AbstractFinder {
0404:
0405: /** Flag that determines whether the search is in the forward direction */
0406: protected boolean forward;
0407:
0408: public boolean isForward() {
0409: return forward;
0410: }
0411:
0412: public final int find(int bufferStartPos, char buffer[],
0413: int offset1, int offset2, int reqPos, int limitPos) {
0414: int offset = reqPos - bufferStartPos;
0415: int limitOffset = limitPos - bufferStartPos;
0416: if (forward) {
0417: limitOffset--; // decrease limit offset for the forward search
0418: }
0419: while (offset >= offset1 && offset < offset2) {
0420: offset += scan(buffer[offset], (offset == limitOffset));
0421: if (found) {
0422: break;
0423: }
0424: }
0425: return bufferStartPos + offset;
0426: }
0427:
0428: /**
0429: * The method that gets the actual character and whether that character
0430: * is the last in the search. It can generally set the found flag to
0431: * true to signal the successive search or it can return positive number
0432: * to go forward or negative number to go back.
0433: */
0434: protected abstract int scan(char ch, boolean lastChar);
0435: }
0436:
0437: /** Searches for the specified char in forward direction. */
0438: public static class CharFwdFinder extends GenericFwdFinder {
0439:
0440: char searchChar;
0441:
0442: public CharFwdFinder(char searchChar) {
0443: this .searchChar = searchChar;
0444: }
0445:
0446: protected int scan(char ch, boolean lastChar) {
0447: if (ch == searchChar) {
0448: found = true;
0449: return 0;
0450: }
0451: return +1;
0452: }
0453:
0454: }
0455:
0456: /** Searches for the specified char in backward direction. */
0457: public static class CharBwdFinder extends GenericBwdFinder {
0458:
0459: char searchChar;
0460:
0461: public CharBwdFinder(char searchChar) {
0462: this .searchChar = searchChar;
0463: }
0464:
0465: protected int scan(char ch, boolean lastChar) {
0466: if (ch == searchChar) {
0467: found = true;
0468: return 0;
0469: }
0470: return -1;
0471: }
0472:
0473: }
0474:
0475: /** Searches for anyone of the specified chars in forward direction. */
0476: public static class CharArrayFwdFinder extends GenericFwdFinder {
0477:
0478: char searchChars[];
0479:
0480: char foundChar;
0481:
0482: public CharArrayFwdFinder(char searchChars[]) {
0483: this .searchChars = searchChars;
0484: }
0485:
0486: protected int scan(char ch, boolean lastChar) {
0487: for (int i = 0; i < searchChars.length; i++) {
0488: if (ch == searchChars[i]) {
0489: foundChar = searchChars[i];
0490: found = true;
0491: return 0;
0492: }
0493: }
0494: return +1;
0495: }
0496:
0497: public char getFoundChar() {
0498: return foundChar;
0499: }
0500:
0501: }
0502:
0503: public static class AcceptorFwdFinder extends GenericFwdFinder {
0504:
0505: Acceptor a;
0506:
0507: public AcceptorFwdFinder(Acceptor a) {
0508: this .a = a;
0509: }
0510:
0511: protected int scan(char ch, boolean lastChar) {
0512: if (!a.accept(ch)) {
0513: found = true;
0514: return 0;
0515: }
0516: return +1;
0517: }
0518:
0519: }
0520:
0521: /** Searches for anyone of the specified chars in backward direction. */
0522: public static class CharArrayBwdFinder extends GenericBwdFinder {
0523:
0524: char searchChars[];
0525:
0526: char foundChar;
0527:
0528: public CharArrayBwdFinder(char searchChars[]) {
0529: this .searchChars = searchChars;
0530: }
0531:
0532: protected int scan(char ch, boolean lastChar) {
0533: for (int i = 0; i < searchChars.length; i++) {
0534: if (ch == searchChars[i]) {
0535: foundChar = searchChars[i];
0536: found = true;
0537: return 0;
0538: }
0539: }
0540: return -1;
0541: }
0542:
0543: public char getFoundChar() {
0544: return foundChar;
0545: }
0546:
0547: }
0548:
0549: public static class AcceptorBwdFinder extends GenericBwdFinder {
0550:
0551: Acceptor a;
0552:
0553: public AcceptorBwdFinder(Acceptor a) {
0554: this .a = a;
0555: }
0556:
0557: protected int scan(char ch, boolean lastChar) {
0558: if (!a.accept(ch)) {
0559: found = true;
0560: return 0;
0561: }
0562: return -1;
0563: }
0564:
0565: }
0566:
0567: /** Next word forward finder */
0568: public static class NextWordFwdFinder extends GenericFwdFinder {
0569:
0570: /** Document used to recognize the character types */
0571: BaseDocument doc;
0572:
0573: /** Currently inside whitespace */
0574: boolean inWhitespace;
0575:
0576: /** Currently inside identifier */
0577: boolean inIdentifier;
0578:
0579: /** Currently inside not in word and not in whitespace */
0580: boolean inPunct;
0581:
0582: /** Whether scanning the first character */
0583: boolean firstChar;
0584:
0585: /** Whether stop on EOL */
0586: boolean stopOnEOL;
0587:
0588: /** Stop with successful find on the first white character */
0589: boolean stopOnWhitespace;
0590:
0591: public NextWordFwdFinder(BaseDocument doc, boolean stopOnEOL,
0592: boolean stopOnWhitespace) {
0593: this .doc = doc;
0594: this .stopOnEOL = stopOnEOL;
0595: this .stopOnWhitespace = stopOnWhitespace;
0596: }
0597:
0598: public void reset() {
0599: super .reset();
0600: inWhitespace = false;
0601: inIdentifier = false;
0602: inPunct = false;
0603: firstChar = true;
0604: }
0605:
0606: protected int scan(char ch, boolean lastChar) {
0607: if (stopOnEOL) {
0608: if (ch == '\n') {
0609: found = true;
0610: return firstChar ? 1 : 0;
0611: }
0612: firstChar = false;
0613: }
0614:
0615: if (doc.isWhitespace(ch)) { // whitespace char found
0616: if (stopOnWhitespace) {
0617: found = true;
0618: return 0;
0619: } else {
0620: inWhitespace = true;
0621: return 1;
0622: }
0623: }
0624:
0625: if (inWhitespace) {
0626: found = true;
0627: return 0;
0628: }
0629: if (inIdentifier) { // inside word
0630: if (doc.isIdentifierPart(ch)) { // still in word
0631: return 1;
0632: }
0633: found = true;
0634: return 0; // found punct
0635: }
0636: if (inPunct) { // inside punctuation
0637: if (doc.isIdentifierPart(ch)) { // a word starts after punct
0638: found = true;
0639: return 0;
0640: }
0641: return 1; // still in punct
0642: }
0643:
0644: // just starting - no state assigned yet
0645: if (doc.isIdentifierPart(ch)) {
0646: inIdentifier = true;
0647: return 1;
0648: } else {
0649: inPunct = true;
0650: return 1;
0651: }
0652: }
0653:
0654: }
0655:
0656: /**
0657: * Find start of the word. This finder can be used to go to previous word or
0658: * to the start of the current word.
0659: */
0660: public static class PreviousWordBwdFinder extends GenericBwdFinder {
0661:
0662: BaseDocument doc;
0663:
0664: /** Currently inside identifier */
0665: boolean inIdentifier;
0666:
0667: /** Currently inside not in word and not in whitespace */
0668: boolean inPunct;
0669:
0670: /** Stop on EOL */
0671: boolean stopOnEOL;
0672:
0673: /** Stop with successful find on the first white character */
0674: boolean stopOnWhitespace;
0675:
0676: boolean firstChar;
0677:
0678: public PreviousWordBwdFinder(BaseDocument doc,
0679: boolean stopOnEOL, boolean stopOnWhitespace) {
0680: this .doc = doc;
0681: this .stopOnEOL = stopOnEOL;
0682: this .stopOnWhitespace = stopOnWhitespace;
0683: }
0684:
0685: public void reset() {
0686: super .reset();
0687: inIdentifier = false;
0688: inPunct = false;
0689: firstChar = true;
0690: }
0691:
0692: protected int scan(char ch, boolean lastChar) {
0693: if (stopOnEOL) {
0694: if (ch == '\n') {
0695: found = true;
0696: return firstChar ? 0 : 1;
0697: }
0698: firstChar = false;
0699: }
0700:
0701: if (inIdentifier) { // inside word
0702: if (doc.isIdentifierPart(ch)) {
0703: if (lastChar) {
0704: found = true;
0705: return 0;
0706: }
0707: return -1;
0708: }
0709: found = true;
0710: return 1; // found punct or whitespace
0711: }
0712: if (inPunct) { // inside punctuation
0713: if (doc.isIdentifierPart(ch) || doc.isWhitespace(ch)
0714: || lastChar) {
0715: found = true;
0716: return 1;
0717: }
0718: return -1; // still in punct
0719: }
0720: if (doc.isWhitespace(ch)) {
0721: if (stopOnWhitespace) {
0722: found = true;
0723: return 1;
0724: }
0725: return -1;
0726: }
0727: if (doc.isIdentifierPart(ch)) {
0728: inIdentifier = true;
0729: if (lastChar) {
0730: found = true;
0731: return 0;
0732: }
0733: return -1;
0734: }
0735: inPunct = true;
0736: return -1;
0737: }
0738:
0739: }
0740:
0741: /** Find first white character forward */
0742: public static class WhiteFwdFinder extends GenericFwdFinder {
0743:
0744: BaseDocument doc;
0745:
0746: private char foundChar;
0747:
0748: public WhiteFwdFinder(BaseDocument doc) {
0749: this .doc = doc;
0750: }
0751:
0752: public char getFoundChar() {
0753: return foundChar;
0754: }
0755:
0756: protected int scan(char ch, boolean lastChar) {
0757: if (doc.isWhitespace(ch)) {
0758: found = true;
0759: foundChar = ch;
0760: return 0;
0761: }
0762: return 1;
0763: }
0764: }
0765:
0766: /** Find first white character backward */
0767: public static class WhiteBwdFinder extends GenericBwdFinder {
0768:
0769: BaseDocument doc;
0770:
0771: private char foundChar;
0772:
0773: public WhiteBwdFinder(BaseDocument doc) {
0774: this .doc = doc;
0775: }
0776:
0777: public char getFoundChar() {
0778: return foundChar;
0779: }
0780:
0781: protected int scan(char ch, boolean lastChar) {
0782: if (doc.isWhitespace(ch)) {
0783: found = true;
0784: foundChar = ch;
0785: return 0;
0786: }
0787: return -1;
0788: }
0789: }
0790:
0791: /** Find first non-white character forward */
0792: public static class NonWhiteFwdFinder extends GenericFwdFinder {
0793:
0794: BaseDocument doc;
0795:
0796: private char foundChar;
0797:
0798: public NonWhiteFwdFinder(BaseDocument doc) {
0799: this .doc = doc;
0800: }
0801:
0802: public char getFoundChar() {
0803: return foundChar;
0804: }
0805:
0806: protected int scan(char ch, boolean lastChar) {
0807: if (!doc.isWhitespace(ch)) {
0808: found = true;
0809: foundChar = ch;
0810: return 0;
0811: }
0812: return 1;
0813: }
0814: }
0815:
0816: /** Find first non-white character backward */
0817: public static class NonWhiteBwdFinder extends GenericBwdFinder {
0818:
0819: BaseDocument doc;
0820:
0821: private char foundChar;
0822:
0823: public NonWhiteBwdFinder(BaseDocument doc) {
0824: this .doc = doc;
0825: }
0826:
0827: public char getFoundChar() {
0828: return foundChar;
0829: }
0830:
0831: protected int scan(char ch, boolean lastChar) {
0832: if (!doc.isWhitespace(ch)) {
0833: found = true;
0834: foundChar = ch;
0835: return 0;
0836: }
0837: return -1;
0838: }
0839: }
0840:
0841: /** String forward finder */
0842: public static final class StringFwdFinder extends GenericFwdFinder
0843: implements StringFinder {
0844:
0845: char chars[];
0846:
0847: int stringInd;
0848:
0849: boolean matchCase;
0850:
0851: public StringFwdFinder(String s, boolean matchCase) {
0852: this .matchCase = matchCase;
0853: chars = (matchCase ? s : s.toLowerCase()).toCharArray();
0854: }
0855:
0856: public int getFoundLength() {
0857: return chars.length;
0858: }
0859:
0860: public void reset() {
0861: super .reset();
0862: stringInd = 0;
0863: }
0864:
0865: protected int scan(char ch, boolean lastChar) {
0866: if (!matchCase) {
0867: ch = Character.toLowerCase(ch);
0868: }
0869: if (ch == chars[stringInd]) {
0870: stringInd++;
0871: if (stringInd == chars.length) { // found whole string
0872: found = true;
0873: return 1 - stringInd; // how many chars back the string
0874: // starts
0875: }
0876: return 1; // successfully matched char, go to next char
0877: } else {
0878: if (stringInd == 0) {
0879: return 1;
0880: } else {
0881: int back = 1 - stringInd;
0882: stringInd = 0;
0883: return back;
0884: }
0885: }
0886: }
0887:
0888: }
0889:
0890: /** String backward finder */
0891: public static class StringBwdFinder extends GenericBwdFinder
0892: implements StringFinder {
0893:
0894: char chars[];
0895:
0896: int stringInd;
0897:
0898: boolean matchCase;
0899:
0900: int endInd;
0901:
0902: public StringBwdFinder(String s, boolean matchCase) {
0903: this .matchCase = matchCase;
0904: chars = (matchCase ? s : s.toLowerCase()).toCharArray();
0905: endInd = chars.length - 1;
0906: }
0907:
0908: public int getFoundLength() {
0909: return chars.length;
0910: }
0911:
0912: public void reset() {
0913: super .reset();
0914: stringInd = endInd;
0915: }
0916:
0917: protected int scan(char ch, boolean lastChar) {
0918: if (!matchCase) {
0919: ch = Character.toLowerCase(ch);
0920: }
0921: if (ch == chars[stringInd]) {
0922: stringInd--;
0923: if (stringInd == -1) {
0924: found = true;
0925: return 0;
0926: }
0927: return -1;
0928: } else {
0929: if (stringInd == endInd) {
0930: return -1;
0931: } else {
0932: int back = chars.length - 2 - stringInd;
0933: stringInd = endInd;
0934: return back;
0935: }
0936: }
0937: }
0938:
0939: }
0940:
0941: /**
0942: * String forward finder that finds whole words only. There are some speed
0943: * optimizations attempted.
0944: */
0945: public static final class WholeWordsFwdFinder extends
0946: GenericFwdFinder implements StringFinder {
0947:
0948: char chars[];
0949:
0950: int stringInd;
0951:
0952: boolean matchCase;
0953:
0954: BaseDocument doc;
0955:
0956: boolean insideWord;
0957:
0958: boolean firstCharWordPart;
0959:
0960: boolean wordFound;
0961:
0962: public WholeWordsFwdFinder(BaseDocument doc, String s,
0963: boolean matchCase) {
0964: this .doc = doc;
0965: this .matchCase = matchCase;
0966: chars = (matchCase ? s : s.toLowerCase()).toCharArray();
0967: firstCharWordPart = doc.isIdentifierPart(chars[0]);
0968: }
0969:
0970: public int getFoundLength() {
0971: return chars.length;
0972: }
0973:
0974: public void reset() {
0975: super .reset();
0976: insideWord = false;
0977: wordFound = false;
0978: stringInd = 0;
0979: }
0980:
0981: protected int scan(char ch, boolean lastChar) {
0982: if (!matchCase) {
0983: ch = Character.toLowerCase(ch);
0984: }
0985:
0986: // whole word already found but must verify next char
0987: if (wordFound) {
0988: if (doc.isIdentifierPart(ch)) { // word continues
0989: wordFound = false;
0990: insideWord = firstCharWordPart;
0991: stringInd = 0;
0992: return 1 - chars.length;
0993: } else {
0994: found = true;
0995: return -chars.length;
0996: }
0997: }
0998:
0999: if (stringInd == 0) { // special case for first char
1000: if (ch != chars[0] || insideWord) { // first char doesn't match
1001: insideWord = doc.isIdentifierPart(ch);
1002: return 1;
1003: } else { // first char matches
1004: stringInd = 1; // matched and not inside word
1005: if (chars.length == 1) {
1006: if (lastChar) {
1007: found = true;
1008: return 0;
1009: } else {
1010: wordFound = true;
1011: return 1;
1012: }
1013: }
1014: return 1;
1015: }
1016: } else { // already matched at least one char
1017: if (ch == chars[stringInd]) { // matches current char
1018: stringInd++;
1019: if (stringInd == chars.length) { // found whole string
1020: if (lastChar) {
1021: found = true;
1022: return 1 - chars.length; // how many chars back
1023: // the string starts
1024: } else {
1025: wordFound = true;
1026: return 1;
1027: }
1028: }
1029: return 1; // successfully matched char, go to next char
1030: } else { // current char doesn't match, stringInd > 0
1031: int back = 1 - stringInd;
1032: stringInd = 0;
1033: insideWord = firstCharWordPart;
1034: return back; // go back to search from the next to first
1035: // char
1036: }
1037: }
1038: }
1039:
1040: }
1041:
1042: /**
1043: * String backward finder that finds whole words only. There are some speed
1044: * optimizations attemted.
1045: */
1046: public static final class WholeWordsBwdFinder extends
1047: GenericBwdFinder implements StringFinder {
1048:
1049: char chars[];
1050:
1051: int stringInd;
1052:
1053: boolean matchCase;
1054:
1055: boolean insideWord;
1056:
1057: boolean lastCharWordPart;
1058:
1059: boolean wordFound;
1060:
1061: int endInd;
1062:
1063: BaseDocument doc;
1064:
1065: public WholeWordsBwdFinder(BaseDocument doc, String s,
1066: boolean matchCase) {
1067: this .doc = doc;
1068: this .matchCase = matchCase;
1069: chars = (matchCase ? s : s.toLowerCase()).toCharArray();
1070: endInd = chars.length - 1;
1071: doc.isIdentifierPart(chars[endInd]);
1072: }
1073:
1074: public int getFoundLength() {
1075: return chars.length;
1076: }
1077:
1078: public void reset() {
1079: super .reset();
1080: insideWord = false;
1081: wordFound = false;
1082: stringInd = endInd;
1083: }
1084:
1085: protected int scan(char ch, boolean lastChar) {
1086: if (!matchCase) {
1087: ch = Character.toLowerCase(ch);
1088: }
1089:
1090: // whole word already found but must verify next char
1091: if (wordFound) {
1092: if (doc.isIdentifierPart(ch)) { // word continues
1093: wordFound = false;
1094: insideWord = lastCharWordPart;
1095: stringInd = endInd;
1096: return endInd;
1097: } else {
1098: found = true;
1099: return 1;
1100: }
1101: }
1102:
1103: if (stringInd == endInd) { // special case for last char
1104: if (ch != chars[endInd] || insideWord) { // first char
1105: // doesn't match
1106: insideWord = doc.isIdentifierPart(ch);
1107: return -1;
1108: } else { // first char matches
1109: stringInd = endInd - 1; // matched and not inside word
1110: if (chars.length == 1) {
1111: if (lastChar) {
1112: found = true;
1113: return 0;
1114: } else {
1115: wordFound = true;
1116: return -1;
1117: }
1118: }
1119: return -1;
1120: }
1121: } else { // already matched at least one char
1122: if (ch == chars[stringInd]) { // matches current char
1123: stringInd--;
1124: if (stringInd == -1) { // found whole string
1125: if (lastChar) {
1126: found = true;
1127: return 0;
1128: } else {
1129: wordFound = true;
1130: return -1;
1131: }
1132: }
1133: return -1; // successfully matched char, go to next char
1134: } else { // current char doesn't match, stringInd > 0
1135: int back = chars.length - 2 - stringInd;
1136: stringInd = endInd;
1137: insideWord = lastCharWordPart;
1138: return back;
1139: }
1140: }
1141: }
1142: }
1143:
1144: /** Support for creating blocks finders. */
1145: public static abstract class AbstractBlocksFinder extends
1146: AbstractFinder implements BlocksFinder {
1147:
1148: private static int[] EMPTY_INT_ARRAY = new int[0];
1149:
1150: private int[] blocks = EMPTY_INT_ARRAY;
1151:
1152: private int blocksInd;
1153:
1154: private boolean closed;
1155:
1156: public void reset() {
1157: blocksInd = 0;
1158: closed = false;
1159: }
1160:
1161: public int[] getBlocks() {
1162: if (!closed) { // not closed yet
1163: closeBlocks();
1164: closed = true;
1165: }
1166: return blocks;
1167: }
1168:
1169: public void setBlocks(int[] blocks) {
1170: this .blocks = blocks;
1171: closed = false;
1172: }
1173:
1174: protected void addBlock(int blkStartPos, int blkEndPos) {
1175: if (blocksInd == blocks.length) {
1176: int[] dbl = new int[blocks.length * 2];
1177: System.arraycopy(blocks, 0, dbl, 0, blocks.length);
1178: blocks = dbl;
1179: }
1180: blocks[blocksInd++] = blkStartPos;
1181: blocks[blocksInd++] = blkEndPos;
1182: }
1183:
1184: /** Insert closing sequence [-1, -1] */
1185: protected void closeBlocks() {
1186: addBlock(-1, -1);
1187: }
1188:
1189: public String debugBlocks() {
1190: StringBuffer buf = new StringBuffer();
1191: int ind = 0;
1192: while (blocks[ind] != -1) {
1193: buf.append((ind / 2 + 1) + ": [" + blocks[ind] + ", "
1194: + blocks[ind + 1] + "]\n"); // NOI18N
1195: ind += 2;
1196: }
1197: return buf.toString();
1198: }
1199:
1200: }
1201:
1202: public static final class FalseBlocksFinder extends
1203: AbstractBlocksFinder {
1204:
1205: public int find(int bufferStartPos, char buffer[], int offset1,
1206: int offset2, int reqPos, int limitPos) {
1207: return -1;
1208: }
1209:
1210: }
1211:
1212: /** String forward finder that creates position blocks */
1213: public static final class StringBlocksFinder extends
1214: AbstractBlocksFinder {
1215:
1216: char chars[];
1217:
1218: int stringInd;
1219:
1220: boolean matchCase;
1221:
1222: public StringBlocksFinder(String s, boolean matchCase) {
1223: this .matchCase = matchCase;
1224: chars = (matchCase ? s : s.toLowerCase()).toCharArray();
1225: }
1226:
1227: public void reset() {
1228: super .reset();
1229: stringInd = 0;
1230: }
1231:
1232: public int find(int bufferStartPos, char buffer[], int offset1,
1233: int offset2, int reqPos, int limitPos) {
1234: int offset = reqPos - bufferStartPos;
1235: while (offset >= offset1 && offset < offset2) {
1236: char ch = buffer[offset];
1237:
1238: if (!matchCase) {
1239: ch = Character.toLowerCase(ch);
1240: }
1241: if (ch == chars[stringInd]) {
1242: stringInd++;
1243: if (stringInd == chars.length) {
1244: int blkEnd = bufferStartPos + offset + 1;
1245: addBlock(blkEnd - stringInd, blkEnd);
1246: stringInd = 0;
1247: }
1248: offset++;
1249: } else {
1250: offset += 1 - stringInd;
1251: stringInd = 0;
1252: }
1253:
1254: }
1255: reqPos = bufferStartPos + offset;
1256: return reqPos;
1257: }
1258:
1259: }
1260:
1261: /**
1262: * String forward finder that finds whole words only and that creates
1263: * position blocks. There are some speed optimizations attempted.
1264: */
1265: public static final class WholeWordsBlocksFinder extends
1266: AbstractBlocksFinder {
1267:
1268: char chars[];
1269:
1270: int stringInd;
1271:
1272: boolean matchCase;
1273:
1274: boolean insideWord;
1275:
1276: boolean firstCharWordPart;
1277:
1278: boolean wordFound;
1279:
1280: BaseDocument doc;
1281:
1282: public WholeWordsBlocksFinder(BaseDocument doc, String s,
1283: boolean matchCase) {
1284: this .doc = doc;
1285: this .matchCase = matchCase;
1286: chars = (matchCase ? s : s.toLowerCase()).toCharArray();
1287: firstCharWordPart = doc.isIdentifierPart(chars[0]);
1288: }
1289:
1290: public void reset() {
1291: super .reset();
1292: insideWord = false;
1293: wordFound = false;
1294: stringInd = 0;
1295: }
1296:
1297: public int find(int bufferStartPos, char buffer[], int offset1,
1298: int offset2, int reqPos, int limitPos) {
1299: int offset = reqPos - bufferStartPos;
1300: int limitOffset = limitPos - bufferStartPos - 1;
1301: while (offset >= offset1 && offset < offset2) {
1302: char ch = buffer[offset];
1303:
1304: if (!matchCase) {
1305: ch = Character.toLowerCase(ch);
1306: }
1307:
1308: // whole word already found but must verify next char
1309: if (wordFound) {
1310: if (doc.isIdentifierPart(ch)) { // word continues
1311: insideWord = firstCharWordPart;
1312: offset -= chars.length - 1;
1313: } else {
1314: int blkEnd = bufferStartPos + offset;
1315: addBlock(blkEnd - chars.length, blkEnd);
1316: insideWord = false;
1317: offset++;
1318: }
1319: wordFound = false;
1320: stringInd = 0;
1321: continue;
1322: }
1323:
1324: if (stringInd == 0) { // special case for first char
1325: if (ch != chars[0] || insideWord) { // first char doesn't
1326: // match
1327: insideWord = doc.isIdentifierPart(ch);
1328: offset++;
1329: } else { // first char matches
1330: stringInd = 1; // matched and not inside word
1331: if (chars.length == 1) {
1332: if (offset == limitOffset) {
1333: int blkStart = bufferStartPos + offset;
1334: addBlock(blkStart, blkStart + 1);
1335: } else {
1336: wordFound = true;
1337: }
1338: }
1339: offset++;
1340: }
1341: } else { // already matched at least one char
1342: if (ch == chars[stringInd]) { // matches current char
1343: stringInd++;
1344: if (stringInd == chars.length) { // found whole
1345: // string
1346: if (offset == limitOffset) {
1347: int blkEnd = bufferStartPos + 1;
1348: addBlock(blkEnd - stringInd, blkEnd);
1349: } else {
1350: wordFound = true;
1351: }
1352: }
1353: offset++;
1354: } else { // current char doesn't match, stringInd > 0
1355: offset += 1 - stringInd;
1356: stringInd = 0;
1357: insideWord = firstCharWordPart;
1358: }
1359: }
1360:
1361: }
1362: reqPos = bufferStartPos + offset;
1363: return reqPos;
1364: }
1365:
1366: }
1367:
1368: /**
1369: * Finder that looks for some search expression expressed by string. It can
1370: * be either simple string or some form of regular expression expressed by
1371: * string.
1372: */
1373: public interface StringFinder extends Finder {
1374:
1375: /**
1376: * Get the length of the found string. This is useful for regular
1377: * expressions, because the length of the regular expression can be
1378: * different than the length of the string that matched the expression.
1379: */
1380: public int getFoundLength();
1381:
1382: }
1383:
1384: /**
1385: * Finder that constructs [begin-pos, end-pos] blocks. This is useful for
1386: * highlight-search draw layer. The block-finders are always forward-search
1387: * finders.
1388: */
1389: public interface BlocksFinder extends Finder {
1390:
1391: /**
1392: * Set the array into which the finder puts the position blocks. If the
1393: * length of array is not sufficient the finder extends the array. The
1394: * last block is set to [-1, -1].
1395: */
1396: public void setBlocks(int[] blocks);
1397:
1398: /**
1399: * Get the array filled with position blocks. It is either original
1400: * array passed to setBlocks() or the new array if the finder extended
1401: * the array.
1402: */
1403: public int[] getBlocks();
1404:
1405: }
1406:
1407: }
|