KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > editor > SyntaxSupport


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.editor;
21
22 import java.io.File JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.beans.PropertyChangeEvent JavaDoc;
25 import java.util.HashMap JavaDoc;
26 import javax.swing.text.BadLocationException JavaDoc;
27 import javax.swing.text.Segment JavaDoc;
28
29 /**
30 * Support methods for syntax analyzes
31 *
32 * @author Miloslav Metelka
33 * @version 1.00
34 */

35
36 public class SyntaxSupport {
37
38     private static final int[] EMPTY_INT_ARRAY = new int[0];
39
40     private static final int MATCH_ARRAY_CACHE_SIZE = 3;
41
42     private HashMap JavaDoc supMap;
43
44     /** Document to work with */
45     private BaseDocument doc;
46
47     /** Whether all the token-ids this class deals with have valid
48     * numeric-ids. It's not necessary to set this flag, however
49     * it presents an optimization in testing whether a token
50     * belongs to some group of tokens or not. The testing whether
51     * the particular token belongs to some group is improved
52     * by creating a boolean array in which the numeric-ids serve
53     * as the array indexes.
54     */

55     protected boolean tokenNumericIDsValid;
56
57     private int[] tokenBlocks = EMPTY_INT_ARRAY;
58
59     private TokenID[][] lastTokenIDArrays = new TokenID[MATCH_ARRAY_CACHE_SIZE][];
60
61     private boolean[][] lastMatchArrays = new boolean[MATCH_ARRAY_CACHE_SIZE][];
62
63     public SyntaxSupport(BaseDocument doc) {
64         this.doc = doc;
65
66     }
67
68     /** Getter for the document that this support is associated to. */
69     public final BaseDocument getDocument() {
70         return doc;
71     }
72
73     /** Get the support that fits the requested support class
74     * in the best way. The value returned will be either instance
75     * of the requested class or its descendant or it will be null.
76     *
77     * @param syntaxSupportClass returned value will be instance of this
78     * class (or its descendant) or it will be null
79     * @return instance of syntaxSupportClass (or its descendant) or null
80     * if there's no fitting support.
81     */

82     public synchronized SyntaxSupport get(Class JavaDoc syntaxSupportClass) {
83         if (supMap == null) {
84             supMap = new HashMap JavaDoc(11);
85         }
86
87         SyntaxSupport sup = (SyntaxSupport)supMap.get(syntaxSupportClass);
88         if (sup == null) {
89             sup = createSyntaxSupport(syntaxSupportClass);
90             supMap.put(syntaxSupportClass, sup);
91         }
92
93         return sup;
94     }
95
96     protected SyntaxSupport createSyntaxSupport(Class JavaDoc syntaxSupportClass) {
97         if (syntaxSupportClass.isInstance(this)) {
98             return this;
99         }
100         return null;
101     }
102
103
104     /** Get the array of booleans with trues at indexes retrieved
105     * as numeric-ids from the token-id array.
106     */

107     private boolean[] getMatchArray(TokenID[] tokenIDArray) {
108         boolean[] matchArray = null;
109         int ind;
110         for (ind = 0; ind < MATCH_ARRAY_CACHE_SIZE; ind++) {
111             // Test only on array equality, not Arrays.equals(Ob1[], Ob2[])
112
// Supposing they will be static
113
if (tokenIDArray == lastTokenIDArrays[ind]) {
114                 matchArray = lastMatchArrays[ind];
115                 break;
116             }
117         }
118
119         if (matchArray == null) { // not found in cache
120
int maxTokenNumericID = -1;
121             if (tokenIDArray != null) {
122                 for (int i = 0; i < tokenIDArray.length; i++) {
123                     if (tokenIDArray[i].getNumericID() > maxTokenNumericID) {
124                         maxTokenNumericID = tokenIDArray[i].getNumericID();
125                     }
126                 }
127             }
128
129             matchArray = new boolean[maxTokenNumericID + 1];
130             for (int i = 0; i < tokenIDArray.length; i++) {
131                 matchArray[tokenIDArray[i].getNumericID()] = true;
132             }
133         }
134
135         if (ind > 0) {
136             ind = Math.min(ind, MATCH_ARRAY_CACHE_SIZE - 1);
137             System.arraycopy(lastTokenIDArrays, 0, lastTokenIDArrays, 1, ind);
138             System.arraycopy(lastMatchArrays, 0, lastMatchArrays, 1, ind);
139             lastTokenIDArrays[0] = tokenIDArray;
140             lastMatchArrays[0] = matchArray;
141         }
142
143         return matchArray;
144     }
145
146     /** Get position pairs covering the blocks that include only the tokens
147     * from the given token array. Although the startPos can be greater than
148     * endPos, the blocks are always returned in the natural order.
149     * @param doc document to work with
150     * @param startPos starting position of the requested document area.
151     * @param endPos ending position of the requested document area
152     * @param tokenIDArray the array of the token IDs that should be in the blocks.
153     */

154     public int[] getTokenBlocks(int startPos, int endPos,
155             TokenID[] tokenIDArray) throws BadLocationException JavaDoc {
156         doc.readLock();
157         try {
158             synchronized (this) {
159                 boolean matchArray[] = tokenNumericIDsValid ? getMatchArray(tokenIDArray) : null;
160                 int blkInd = 0;
161                 if (startPos > endPos) { // swap
162
int tmp = startPos;
163                     startPos = endPos;
164                     endPos = tmp;
165                 }
166
167                 SyntaxSeg.Slot slot = SyntaxSeg.getFreeSlot();
168                 Syntax syntax = doc.getFreeSyntax();
169                 try {
170                     doc.prepareSyntax(slot, syntax, startPos, endPos - startPos, true, false);
171
172                     int preScan = syntax.getPreScan();
173                     int pos = startPos - preScan;
174                     int blkStart = -1;
175
176                     boolean cont = true;
177                     while (cont) {
178                         TokenID tokenID = syntax.nextToken();
179                         if (tokenID == null) {
180                             cont = false;
181                         } else {
182                             // Test whether token-id belongs to the token-array
183
boolean matches = (tokenID != null)
184                                 && !(pos + syntax.getTokenLength() <= startPos);
185                             if (matches) {
186                                 if (matchArray != null) {
187                                     int numID = tokenID.getNumericID();
188                                     matches = (numID < matchArray.length && matchArray[numID]);
189                                 } else { // doesn't support numeric-ids
190
matches = false;
191                                     for (int i = 0; i < tokenIDArray.length; i++) {
192                                         if (tokenID == tokenIDArray[i]) {
193                                             matches = true;
194                                             break;
195                                         }
196                                     }
197                                 }
198                             }
199
200                             if (matches) {
201                                 if (blkStart >= 0) {
202                                     // still in token block
203
} else {
204                                     blkStart = Math.max(pos, startPos);
205                                 }
206                             } else { // not searched token
207
if (blkStart >= 0) {
208                                     tokenBlocks = addTokenBlock(tokenBlocks, blkInd, blkStart, pos);
209                                     blkInd += 2;
210                                     blkStart = -1;
211                                 } else {
212                                     // still not in block
213
}
214                             }
215                             pos += syntax.getTokenLength();
216                         }
217                     }
218
219                     if (blkStart >= 0) { // was in comment
220
tokenBlocks = addTokenBlock(tokenBlocks, blkInd, blkStart, endPos);
221                         blkInd += 2;
222                     }
223
224                 } finally {
225                     doc.releaseSyntax(syntax);
226                     SyntaxSeg.releaseSlot(slot);
227                 }
228
229                 int[] ret = new int[blkInd];
230                 System.arraycopy(tokenBlocks, 0, ret, 0, blkInd);
231                 return ret;
232             }
233         } finally {
234             doc.readUnlock();
235         }
236     }
237
238     private int[] addTokenBlock(int[] blks, int blkInd, int blkStartPos, int blkEndPos) {
239         if (blks.length < blkInd + 2) {
240             int[] tmp = new int[Math.max(2, blks.length * 2)];
241             System.arraycopy(blks, 0, tmp, 0, blkInd);
242             blks = tmp;
243         }
244
245         blks[blkInd++] = blkStartPos;
246         blks[blkInd] = blkEndPos;
247         return blks;
248     }
249
250     public int findInsideBlocks(Finder finder,
251                                 int startPos, int endPos, int[] blocks) throws BadLocationException JavaDoc {
252         boolean fwd = (startPos <= endPos);
253
254         if (fwd) {
255             for (int i = 0; i < blocks.length; i += 2) {
256                 int pos = doc.find(finder, blocks[i], blocks[i + 1]);
257                 if (pos >= 0) {
258                     return pos;
259                 }
260             }
261         } else { // find backward
262
for (int i = blocks.length - 2; i >= 0; i -= 2) {
263                 int pos = doc.find(finder, blocks[i + 1], blocks[i]);
264                 if (pos >= 0) {
265                     return pos;
266                 }
267             }
268         }
269         return -1;
270     }
271
272     public int findOutsideBlocks(Finder finder,
273                                  int startPos, int endPos, int[] blocks) throws BadLocationException JavaDoc {
274         boolean fwd = (startPos <= endPos);
275
276         if (fwd) {
277             int pos = doc.find(finder, startPos, (blocks.length > 0) ? blocks[0] : endPos);
278             if (pos >= 0) {
279                 return pos;
280             }
281
282             int ind = 2;
283             while (ind <= blocks.length) {
284                 pos = doc.find(finder, blocks[ind - 1], (ind >= blocks.length) ? endPos : blocks[ind]);
285                 if (pos >= 0) {
286                     return pos;
287                 }
288                 ind += 2;
289             }
290         } else { // find backward
291
int pos = doc.find(finder, startPos, (blocks.length > 0) ? blocks[blocks.length - 1] : endPos);
292             if (pos >= 0) {
293                 return pos;
294             }
295
296             int ind = blocks.length - 2;
297             while (ind >= 0) {
298                 pos = doc.find(finder, blocks[ind], (ind == 0) ? endPos : blocks[ind - 1]);
299                 if (pos >= 0) {
300                     return pos;
301                 }
302                 ind -= 2;
303             }
304         }
305         return -1;
306     }
307
308     /** Initialize the syntax so it's ready to scan the given area.
309     * @param syntax lexical analyzer to prepare
310     * @param startPos starting position of the scanning
311     * @param endPos ending position of the scanning
312     * @param forceLastBuffer force the syntax to think that the scanned area is the last
313     * in the document. This is useful for forcing the syntax to process all the characters
314     * in the given area.
315     * @param forceNotLastBuffer force the syntax to think that the scanned area is NOT
316     * the last buffer in the document. This is useful when the syntax will continue
317     * scanning on another buffer.
318     */

319     public void initSyntax(Syntax syntax, int startPos, int endPos,
320     boolean forceLastBuffer, boolean forceNotLastBuffer)
321     throws BadLocationException JavaDoc {
322         doc.readLock();
323         try {
324             Segment JavaDoc text = DocumentUtilities.SEGMENT_CACHE.getSegment();
325             try {
326                 int docLen = doc.getLength();
327                 doc.prepareSyntax(text, syntax, startPos, 0, forceLastBuffer, forceNotLastBuffer);
328                 int preScan = syntax.getPreScan();
329                 char[] buffer = doc.getChars(startPos - preScan, endPos - startPos + preScan);
330                 boolean lastBuffer = forceNotLastBuffer ? false
331                     : (forceLastBuffer || (endPos == docLen));
332                 syntax.relocate(buffer, preScan, endPos - startPos, lastBuffer, endPos);
333             } finally {
334                 DocumentUtilities.SEGMENT_CACHE.releaseSegment(text);
335             }
336         } finally {
337             doc.readUnlock();
338         }
339     }
340
341     /** Check whether the given word is identifier or not. */
342     public boolean isIdentifier(String JavaDoc word) {
343         if (word == null || word.length() == 0) {
344             return false; // not qualified as word
345
}
346
347         for (int i = 0; i < word.length(); i++) {
348             if (!doc.isIdentifierPart(word.charAt(i))) {
349                 return false;
350             }
351         }
352         return true;
353     }
354
355     /** Parse the text and pass the resulting tokens to the token processor.
356     * @param tp token processor that will be informed about the found tokens.
357     * @param startOffset starting position in the text
358     * @param endOffset ending position in the text
359     * @param forceLastBuffer force the syntax scanner to think that the requested
360     * area is the last in the document.
361     */

362     public void tokenizeText(TokenProcessor tp, int startOffset, int endOffset,
363     boolean forceLastBuffer) throws BadLocationException JavaDoc {
364         Syntax syntax = null;
365         doc.readLock();
366         try {
367             Segment JavaDoc text = DocumentUtilities.SEGMENT_CACHE.getSegment();
368             try {
369                 syntax = doc.getFreeSyntax();
370                 int docLen = doc.getLength();
371                 doc.prepareSyntax(text, syntax, startOffset,
372                     endOffset - startOffset, forceLastBuffer, false);
373                 int preScan = syntax.getPreScan();
374                 tp.nextBuffer(text.array, syntax.getOffset(), endOffset - startOffset,
375                               startOffset, preScan, syntax.lastBuffer);
376
377                 int bufferStartOffset = startOffset - syntax.getOffset();
378
379                 boolean cont = true;
380                 while (cont) {
381                     TokenID tokenID = syntax.nextToken();
382                     TokenContextPath tcp = syntax.getTokenContextPath();
383                     if (tokenID == null) { //EOT
384
int nextLen = tp.eot(syntax.tokenOffset);
385                         nextLen = Math.min(nextLen, docLen - endOffset);
386                         if (nextLen == 0) {
387                             cont = false;
388                         } else { // continue
389
preScan = syntax.getPreScan();
390                             doc.getText(endOffset - preScan, preScan + nextLen, text);
391
392                             boolean lastBuffer = forceLastBuffer || (endOffset + nextLen >= docLen);
393                             syntax.relocate(text.array, text.offset + preScan, nextLen,
394                                             lastBuffer, endOffset + nextLen);
395                             tp.nextBuffer(text.array, syntax.getOffset(), nextLen,
396                                           endOffset, preScan, lastBuffer);
397                             bufferStartOffset = endOffset - syntax.getOffset();
398                             endOffset += nextLen;
399                         }
400
401                     } else { // not EOT
402
int tokenLen = syntax.getTokenLength();
403                         int tokenOffset = syntax.getTokenOffset();
404
405                         // Check whether the token isn't too left
406
if (bufferStartOffset + tokenOffset + tokenLen > startOffset ) {
407                             if (!tp.token(tokenID, tcp, tokenOffset, tokenLen)) {
408                                 cont = false;
409                             }
410                         }
411                     }
412                 }
413             } finally {
414                 DocumentUtilities.SEGMENT_CACHE.releaseSegment(text);
415             }
416         } finally {
417             if (syntax != null) {
418                 doc.releaseSyntax(syntax);
419             }
420             doc.readUnlock();
421         }
422     }
423
424     /** Parse the text and pass the resulting tokens to the token processor.
425     * @param tp token processor that will be informed about the found tokens.
426     * @param text text to parse
427     */

428     public void tokenizeText(TokenProcessor tp, String JavaDoc text) {
429         Syntax syntax = null;
430         try {
431             syntax = doc.getFreeSyntax();
432             char[] buf = text.toCharArray();
433             syntax.load(null, buf, 0, buf.length, true, -1);
434
435             boolean cont = true;
436             while (cont) {
437                 TokenID tokenID = syntax.nextToken();
438                 TokenContextPath tcp = syntax.getTokenContextPath();
439                 if (tokenID == null) {
440                     tp.eot(syntax.tokenOffset);
441                     cont = false;
442
443                 } else {
444                     if (!tp.token(tokenID, tcp, syntax.getTokenOffset(), syntax.getTokenLength())) {
445                         cont = false;
446                     }
447                 }
448             }
449
450         } finally {
451             if (syntax != null) {
452                 doc.releaseSyntax(syntax);
453             }
454         }
455     }
456
457     
458     /** Get the member of the chain of the tokens for the given document position.
459      * @param offset position in the document for which the chain
460      * is being retrieved.
461      * @return token-item around the offset or right at the offset. Null
462      * is returned if offset is equal to document length.
463      */

464     public TokenItem getTokenChain(int offset) throws BadLocationException JavaDoc {
465         // null for end of document
466
if (doc.getLength() <= offset) {
467             return null;
468         }
469
470         return null;
471     }
472
473     /**
474      * Check whether the abbreviation expansion should be disabled
475      * at the given offset.
476      *
477      * @param offset offset at which the situation should be checked.
478      * @return true if the abbreviation expansion should be disabled
479      * at the given offset or false otherwise.
480      */

481     protected boolean isAbbrevDisabled(int offset) {
482         return false;
483     }
484
485 }
486
Popular Tags