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