KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > editor > java > BracketCompletion


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.modules.editor.java;
21
22
23 import javax.swing.text.BadLocationException JavaDoc;
24 import javax.swing.text.Caret JavaDoc;
25 import javax.swing.text.Document JavaDoc;
26 import org.netbeans.editor.BaseDocument;
27 import org.netbeans.editor.SyntaxSupport;
28 import org.netbeans.editor.TokenID;
29 import org.netbeans.editor.TokenProcessor;
30 import org.netbeans.editor.TokenContextPath;
31 import org.netbeans.editor.ext.ExtSyntaxSupport;
32 import org.netbeans.editor.ext.java.JavaTokenContext;
33 import org.netbeans.editor.TokenItem;
34 import org.netbeans.editor.Settings;
35 import org.netbeans.editor.Utilities;
36 import org.netbeans.editor.ext.java.JavaSettingsNames;
37
38
39
40 /**
41  * This static class groups the whole aspect of bracket
42  * completion. It is defined to clearly separate the functionality
43  * and keep actions clean.
44  * The methods of the class are called from different actions as
45  * KeyTyped, DeletePreviousChar.
46  */

47 class BracketCompletion {
48
49   /**
50    * A hook method called after a character was inserted into the
51    * document. The function checks for special characters for
52    * completion ()[]'"{} and other conditions and optionally performs
53    * changes to the doc and or caret (complets braces, moves caret,
54    * etc.)
55    * @param doc the document where the change occurred
56    * @param dotPos position of the character insertion
57    * @param caret caret
58    * @param ch the character that was inserted
59    * @throws BadLocationException if dotPos is not correct
60    */

61   static void charInserted(BaseDocument doc,
62                int dotPos,
63                Caret JavaDoc caret,
64                char ch) throws BadLocationException JavaDoc {
65       SyntaxSupport syntaxSupport = doc.getSyntaxSupport();
66       if (!(syntaxSupport instanceof ExtSyntaxSupport) || !completionSettingEnabled()){
67           return;
68       }
69
70       ExtSyntaxSupport support = (ExtSyntaxSupport)syntaxSupport;
71       if (ch == ')'|| ch == ']'|| ch =='('|| ch =='[') {
72           TokenID tokenAtDot = support.getTokenID(dotPos);
73           if (tokenAtDot == JavaTokenContext.RBRACKET || tokenAtDot == JavaTokenContext.RPAREN) {
74               skipClosingBracket(doc, caret, ch);
75           } else if (tokenAtDot == JavaTokenContext.LBRACKET || tokenAtDot == JavaTokenContext.LPAREN) {
76               completeOpeningBracket(doc, dotPos, caret, ch);
77           }
78       } else if (ch == ';') {
79           moveSemicolon(doc, dotPos, caret);
80       }
81   }
82
83     private static void moveSemicolon(BaseDocument doc, int dotPos, Caret JavaDoc caret) throws BadLocationException JavaDoc {
84         int eolPos = Utilities.getRowEnd(doc, dotPos);
85         ExtSyntaxSupport ssup = (ExtSyntaxSupport)doc.getSyntaxSupport();
86         int lastParenPos = dotPos;
87         TokenItem token = ssup.getTokenChain(dotPos, eolPos);
88         for (TokenItem item = token.getNext(); item != null && item.getOffset() <= eolPos; item = item.getNext()) {
89             TokenID tokenID = item.getTokenID();
90             if (tokenID == JavaTokenContext.RPAREN) {
91                 lastParenPos = item.getOffset();
92             } else if (tokenID != JavaTokenContext.WHITESPACE) {
93                 return;
94             }
95         }
96         if (isForLoopSemicolon(token) || posWithinAnyQuote(doc,dotPos)) {
97             return;
98         }
99         doc.remove(dotPos, 1);
100         doc.insertString(lastParenPos, ";", null); // NOI18N
101
caret.setDot(lastParenPos + 1);
102     }
103    
104     private static boolean isForLoopSemicolon(TokenItem token) {
105         if (token == null || token.getTokenID() != JavaTokenContext.SEMICOLON) {
106             return false;
107         }
108         int parDepth = 0; // parenthesis depth
109
int braceDepth = 0; // brace depth
110
boolean semicolonFound = false; // next semicolon
111
token = token.getPrevious(); // ignore this semicolon
112
while (token != null) {
113             if (token.getTokenID() == JavaTokenContext.LPAREN) {
114                 if (parDepth == 0) { // could be a 'for ('
115
token = token.getPrevious();
116                     while(token !=null && (token.getTokenID() == JavaTokenContext.WHITESPACE || token.getTokenID() == JavaTokenContext.BLOCK_COMMENT || token.getTokenID() == JavaTokenContext.LINE_COMMENT)) {
117                         token = token.getPrevious();
118                     }
119                     if (token.getTokenID() == JavaTokenContext.FOR) {
120                         return true;
121                     }
122                     return false;
123                 } else { // non-zero depth
124
parDepth--;
125                 }
126             } else if (token.getTokenID() == JavaTokenContext.RPAREN) {
127                 parDepth++;
128             } else if (token.getTokenID() == JavaTokenContext.LBRACE) {
129                 if (braceDepth == 0) { // unclosed left brace
130
return false;
131                 }
132                 braceDepth--;
133             } else if (token.getTokenID() == JavaTokenContext.RBRACE) {
134                 braceDepth++;
135
136             } else if (token.getTokenID() == JavaTokenContext.SEMICOLON) {
137                 if (semicolonFound) { // one semicolon already found
138
return false;
139                 }
140                 semicolonFound = true;
141             }
142             token = token.getPrevious();
143         }
144         return false;
145     }
146
147     /**
148    * Hook called after a character *ch* was backspace-deleted from
149    * *doc*. The function possibly removes bracket or quote pair if
150    * appropriate.
151    * @param doc the document
152    * @param dotPos position of the change
153    * @param caret caret
154    * @param ch the character that was deleted
155    */

156   static void charBackspaced(BaseDocument doc,
157                  int dotPos,
158                  Caret JavaDoc caret,
159                  char ch) throws BadLocationException JavaDoc
160   {
161     if (completionSettingEnabled()) {
162       if (ch == '(' || ch == '[') {
163     TokenID tokenAtDot = ((ExtSyntaxSupport)doc.
164                   getSyntaxSupport()).getTokenID(dotPos);
165     if ((tokenAtDot == JavaTokenContext.RBRACKET && tokenBalance(doc, JavaTokenContext.LBRACKET, JavaTokenContext.RBRACKET) != 0) ||
166         (tokenAtDot == JavaTokenContext.RPAREN && tokenBalance(doc, JavaTokenContext.LPAREN, JavaTokenContext.RPAREN) != 0) ) {
167       doc.remove(dotPos, 1);
168     }
169       }
170       else if (ch == '\"') {
171     char match [] = doc.getChars(dotPos, 1);
172     if (match != null && match[0] == '\"') {
173       doc.remove(dotPos, 1);
174     }
175       }
176       else if (ch == '\'') {
177     char match [] = doc.getChars(dotPos, 1);
178     if (match != null && match[0] == '\'') {
179       doc.remove(dotPos, 1);
180     }
181       }
182     }
183   }
184
185     /**
186      * Resolve whether pairing right curly should be added automatically
187      * at the caret position or not.
188      * <br>
189      * There must be only whitespace or line comment or block comment
190      * between the caret position
191      * and the left brace and the left brace must be on the same line
192      * where the caret is located.
193      * <br>
194      * The caret must not be "contained" in the opened block comment token.
195      *
196      * @param doc document in which to operate.
197      * @param caretOffset offset of the caret.
198      * @return true if a right brace '}' should be added
199      * or false if not.
200      */

201     static boolean isAddRightBrace(BaseDocument doc, int caretOffset)
202     throws BadLocationException JavaDoc {
203         boolean addRightBrace = false;
204         if (completionSettingEnabled()) {
205             if (caretOffset > 0) {
206                 // Check whether line ends with '{' ignoring any whitespace
207
// or comments
208
int tokenOffset = caretOffset;
209                 TokenItem token = ((ExtSyntaxSupport)doc.getSyntaxSupport()).
210                 getTokenChain(tokenOffset -1, tokenOffset);
211                 
212                 addRightBrace = true; // suppose that right brace should be added
213

214                 // Disable right brace adding if caret not positioned within whitespace
215
// or line comment
216
int off = (caretOffset - token.getOffset());
217                 if (off > 0 && off < token.getImage().length()) { // caret contained in token
218
switch (token.getTokenID().getNumericID()) {
219                         case JavaTokenContext.WHITESPACE_ID:
220                         case JavaTokenContext.LINE_COMMENT_ID:
221                             break; // the above tokens are OK
222

223                         default:
224                             // Disable brace adding for the remaining ones
225
addRightBrace = false;
226                     }
227                 }
228                 
229                 if (addRightBrace) { // still candidate for adding
230
int caretRowStartOffset = Utilities.getRowStart(doc, caretOffset);
231                     
232                     // Check whether there are only whitespace or comment tokens
233
// between caret and left brace and check only on the line
234
// with the caret
235
while (token != null && token.getOffset() >= caretRowStartOffset) {
236                         boolean ignore = false;
237                         // Assuming java token context here
238
switch (token.getTokenID().getNumericID()) {
239                             case JavaTokenContext.WHITESPACE_ID:
240                             case JavaTokenContext.BLOCK_COMMENT_ID:
241                             case JavaTokenContext.LINE_COMMENT_ID:
242                                 // skip
243
ignore = true;
244                                 break;
245                         }
246                         
247                         if (ignore) {
248                             token = token.getPrevious();
249                         } else { // break on the current token
250
break;
251                         }
252                     }
253                     
254                     if (token == null
255                     || token.getTokenID() != JavaTokenContext.LBRACE // must be left brace
256
|| token.getOffset() < caretRowStartOffset // on the same line as caret
257
) {
258                         addRightBrace = false;
259                     }
260                     
261                 }
262                 
263                 if (addRightBrace) { // Finally check the brace balance whether there are any missing right braces
264
addRightBrace = (braceBalance(doc) > 0);
265                 }
266             }
267         }
268         return addRightBrace;
269     }
270     
271     /**
272      * Returns position of the first unpaired closing paren/brace/bracket from the caretOffset
273      * till the end of caret row. If there is no such element, position after the last non-white
274      * character on the caret row is returned.
275      */

276     static int getRowOrBlockEnd(BaseDocument doc, int caretOffset) throws BadLocationException JavaDoc {
277         int rowEnd = Utilities.getRowLastNonWhite(doc, caretOffset);
278         if (rowEnd == -1 || caretOffset >= rowEnd){
279             return caretOffset;
280         }
281         rowEnd += 1;
282         int parenBalance = 0;
283         int braceBalance = 0;
284         int bracketBalance = 0;
285         ExtSyntaxSupport ssup = (ExtSyntaxSupport)doc.getSyntaxSupport();
286         TokenItem token = ssup.getTokenChain(caretOffset, rowEnd);
287         while (token != null && token.getOffset() < rowEnd) {
288             switch (token.getTokenID().getNumericID()) {
289                 case JavaTokenContext.LPAREN_ID:
290                     parenBalance++;
291                     break;
292                 case JavaTokenContext.RPAREN_ID:
293                     if (parenBalance-- == 0)
294                         return token.getOffset();
295                 case JavaTokenContext.LBRACE_ID:
296                     braceBalance++;
297                     break;
298                 case JavaTokenContext.RBRACE_ID:
299                     if (braceBalance-- == 0)
300                         return token.getOffset();
301                 case JavaTokenContext.LBRACKET_ID:
302                     bracketBalance++;
303                     break;
304                 case JavaTokenContext.RBRACKET_ID:
305                     if (bracketBalance-- == 0)
306                         return token.getOffset();
307             }
308             token = token.getNext();
309         }
310         return rowEnd;
311     }
312     
313   /**
314    * Counts the number of braces starting at dotPos to the end of the
315    * document. Every occurence of { increses the count by 1, every
316    * occurrence of } decreses the count by 1. The result is returned.
317    * @return The number of { - number of } (>0 more { than } ,<0 more } than {)
318    */

319   private static int braceBalance(BaseDocument doc)
320     throws BadLocationException JavaDoc
321   {
322     return tokenBalance(doc, JavaTokenContext.LBRACE, JavaTokenContext.RBRACE);
323   }
324
325   /**
326    * The same as braceBalance but generalized to any pair of matching
327    * tokens.
328    * @param open the token that increses the count
329    * @param close the token that decreses the count
330    */

331   private static int tokenBalance(BaseDocument doc, TokenID open, TokenID close)
332   throws BadLocationException JavaDoc {
333       
334       ExtSyntaxSupport sup = (ExtSyntaxSupport)doc.getSyntaxSupport();
335       BalanceTokenProcessor balanceTP = new BalanceTokenProcessor(open, close);
336       sup.tokenizeText(balanceTP, 0, doc.getLength(), true);
337       return balanceTP.getBalance();
338   }
339
340   /**
341    * A hook to be called after closing bracket ) or ] was inserted into
342    * the document. The method checks if the bracket should stay there
343    * or be removed and some exisitng bracket just skipped.
344    *
345    * @param doc the document
346    * @param dotPos position of the inserted bracket
347    * @param caret caret
348    * @param bracket the bracket character ']' or ')'
349    */

350   private static void skipClosingBracket(BaseDocument doc, Caret JavaDoc caret, char bracket)
351   throws BadLocationException JavaDoc {
352
353       TokenID bracketId = (bracket == ')')
354           ? JavaTokenContext.RPAREN
355           : JavaTokenContext.RBRACKET;
356
357       int caretOffset = caret.getDot();
358       if (isSkipClosingBracket(doc, caretOffset, bracketId)) {
359           doc.remove(caretOffset - 1, 1);
360           caret.setDot(caretOffset); // skip closing bracket
361
}
362   }
363   
364   /**
365    * Check whether the typed bracket should stay in the document
366    * or be removed.
367    * <br>
368    * This method is called by <code>skipClosingBracket()</code>.
369    *
370    * @param doc document into which typing was done.
371    * @param caretOffset
372    */

373   static boolean isSkipClosingBracket(BaseDocument doc, int caretOffset, TokenID bracketId)
374   throws BadLocationException JavaDoc {
375
376       // First check whether the caret is not after the last char in the document
377
// because no bracket would follow then so it could not be skipped.
378
if (caretOffset == doc.getLength()) {
379           return false; // no skip in this case
380
}
381       
382       boolean skipClosingBracket = false; // by default do not remove
383

384       // Examine token at the caret offset
385
TokenItem token = ((ExtSyntaxSupport)doc.getSyntaxSupport()).getTokenChain(
386           caretOffset, caretOffset + 1);
387
388       // Check whether character follows the bracket is the same bracket
389
if (token != null && token.getTokenID() == bracketId) {
390           int bracketIntId = bracketId.getNumericID();
391           int leftBracketIntId = (bracketIntId == JavaTokenContext.RPAREN_ID)
392           ? JavaTokenContext.LPAREN_ID
393           : JavaTokenContext.LBRACKET_ID;
394           
395         // Skip all the brackets of the same type that follow the last one
396
TokenItem nextToken = token.getNext();
397           while (nextToken != null && nextToken.getTokenID() == bracketId) {
398               token = nextToken;
399               nextToken = nextToken.getNext();
400           }
401           // token var points to the last bracket in a group of two or more right brackets
402
// Attempt to find the left matching bracket for it
403
// Search would stop on an extra opening left brace if found
404
int braceBalance = 0; // balance of '{' and '}'
405
int bracketBalance = -1; // balance of the brackets or parenthesis
406
TokenItem lastRBracket = token;
407           token = token.getPrevious();
408           boolean finished = false;
409           while (!finished && token != null) {
410               int tokenIntId = token.getTokenID().getNumericID();
411               switch (tokenIntId) {
412                   case JavaTokenContext.LPAREN_ID:
413                   case JavaTokenContext.LBRACKET_ID:
414                       if (tokenIntId == bracketIntId) {
415                           bracketBalance++;
416                           if (bracketBalance == 0) {
417                               if (braceBalance != 0) {
418                                   // Here the bracket is matched but it is located
419
// inside an unclosed brace block
420
// e.g. ... ->( } a()|)
421
// which is in fact illegal but it's a question
422
// of what's best to do in this case.
423
// We chose to leave the typed bracket
424
// by setting bracketBalance to 1.
425
// It can be revised in the future.
426
bracketBalance = 1;
427                               }
428                               finished = true;
429                           }
430                       }
431                       break;
432                       
433                   case JavaTokenContext.RPAREN_ID:
434                   case JavaTokenContext.RBRACKET_ID:
435                       if (tokenIntId == bracketIntId) {
436                           bracketBalance--;
437                       }
438                       break;
439
440                   case JavaTokenContext.LBRACE_ID:
441                       braceBalance++;
442                       if (braceBalance > 0) { // stop on extra left brace
443
finished = true;
444                       }
445                       break;
446                       
447                   case JavaTokenContext.RBRACE_ID:
448                       braceBalance--;
449                       break;
450                       
451               }
452               
453               token = token.getPrevious(); // done regardless of finished flag state
454
}
455           
456           if (bracketBalance != 0) { // not found matching bracket
457
// Remove the typed bracket as it's unmatched
458
skipClosingBracket = true;
459               
460           } else { // the bracket is matched
461
// Now check whether the bracket would be matched
462
// when the closing bracket would be removed
463
// i.e. starting from the original lastRBracket token
464
// and search for the same bracket to the right in the text
465
// The search would stop on an extra right brace if found
466
braceBalance = 0;
467               bracketBalance = 1; // simulate one extra left bracket
468
token = lastRBracket.getNext();
469               finished = false;
470               while (!finished && token != null) {
471                   int tokenIntId = token.getTokenID().getNumericID();
472                   switch (tokenIntId) {
473                       case JavaTokenContext.LPAREN_ID:
474                       case JavaTokenContext.LBRACKET_ID:
475                           if (tokenIntId == leftBracketIntId) {
476                               bracketBalance++;
477                           }
478                           break;
479                           
480                       case JavaTokenContext.RPAREN_ID:
481                       case JavaTokenContext.RBRACKET_ID:
482                           if (tokenIntId == bracketIntId) {
483                               bracketBalance--;
484                               if (bracketBalance == 0) {
485                                   if (braceBalance != 0) {
486                                       // Here the bracket is matched but it is located
487
// inside an unclosed brace block
488
// which is in fact illegal but it's a question
489
// of what's best to do in this case.
490
// We chose to leave the typed bracket
491
// by setting bracketBalance to -1.
492
// It can be revised in the future.
493
bracketBalance = -1;
494                                   }
495                                   finished = true;
496                               }
497                           }
498                           break;
499                           
500                       case JavaTokenContext.LBRACE_ID:
501                           braceBalance++;
502                           break;
503                           
504                       case JavaTokenContext.RBRACE_ID:
505                           braceBalance--;
506                           if (braceBalance < 0) { // stop on extra right brace
507
finished = true;
508                           }
509                           break;
510                           
511                   }
512                   
513                   token = token.getPrevious(); // done regardless of finished flag state
514
}
515               
516               // If bracketBalance == 0 the bracket would be matched
517
// by the bracket that follows the last right bracket.
518
skipClosingBracket = (bracketBalance == 0);
519           }
520       }
521       return skipClosingBracket;
522   }
523   
524   /**
525    * Check for various conditions and possibly add a pairing bracket
526    * to the already inserted.
527    * @param doc the document
528    * @param dotPos position of the opening bracket (already in the doc)
529    * @param caret caret
530    * @param bracket the bracket that was inserted
531    */

532   private static void completeOpeningBracket(BaseDocument doc,
533                           int dotPos,
534                           Caret JavaDoc caret,
535                           char bracket) throws BadLocationException JavaDoc
536   {
537     if (isCompletablePosition(doc, dotPos+1)) {
538       String JavaDoc matchinBracket = "" + matching(bracket);
539       doc.insertString(dotPos + 1, matchinBracket,null);
540       caret.setDot(dotPos+1);
541     }
542   }
543   
544   private static boolean isEscapeSequence(BaseDocument doc, int dotPos) throws BadLocationException JavaDoc{
545       if (dotPos <= 0) return false;
546       char previousChar = doc.getChars(dotPos-1,1)[0];
547       return previousChar == '\\';
548   }
549
550   /**
551    * Check for conditions and possibly complete an already inserted
552    * quote .
553    * @param doc the document
554    * @param dotPos position of the opening bracket (already in the doc)
555    * @param caret caret
556    * @param bracket the character that was inserted
557    */

558   static boolean completeQuote(BaseDocument doc, int dotPos, Caret JavaDoc caret,
559           char bracket) throws BadLocationException JavaDoc {
560
561     if (!completionSettingEnabled()){
562         return false;
563     }
564     
565     if (isEscapeSequence(doc, dotPos)){ // \" or \' typed
566
return false;
567     }
568     
569     SyntaxSupport s = doc.getSyntaxSupport();
570     if (!(s instanceof ExtSyntaxSupport)){
571         return false;
572     }
573
574     ExtSyntaxSupport syntax = (ExtSyntaxSupport)s;
575     // Examine token at the caret offset
576
TokenID token = null;
577     if (doc.getLength() > dotPos){
578         token = syntax.getTokenID(dotPos);
579     }
580
581     int lastNonWhite = Utilities.getRowLastNonWhite(doc, dotPos);
582     // eol - true if the caret is at the end of line (ignoring whitespaces)
583
boolean eol = lastNonWhite < dotPos;
584     
585     if (token == JavaTokenContext.BLOCK_COMMENT || token == JavaTokenContext.LINE_COMMENT){
586         return false;
587     } else if (token == JavaTokenContext.WHITESPACE && eol && dotPos-1 > 0){
588         // check if the caret is at the very end of the line comment
589
token = syntax.getTokenID(dotPos-1);
590         if (token == JavaTokenContext.LINE_COMMENT){
591             return false;
592         }
593     }
594     
595     boolean completablePosition = isQuoteCompletablePosition(doc, dotPos);
596     boolean insideString =
597             token == JavaTokenContext.STRING_LITERAL ||
598             token == JavaTokenContext.CHAR_LITERAL;
599     
600     if (!insideString){
601         // check if the caret is at the very end of the line and there
602
// is an unterminated string literal
603
if (token == JavaTokenContext.WHITESPACE && eol){
604             if (dotPos-1 > 0){
605                 token = syntax.getTokenID(dotPos-1);
606                 insideString =
607                     token == JavaTokenContext.STRING_LITERAL ||
608                     token == JavaTokenContext.CHAR_LITERAL;
609             }
610         }
611     }
612
613     if (insideString){
614         if (eol){
615             return false; // do not complete
616
} else {
617             //#69524
618
char chr = doc.getChars(dotPos,1)[0];
619             if (chr == bracket){
620                 doc.insertString(dotPos, "" + bracket , null); //NOI18N
621
doc.remove(dotPos, 1);
622                 return true;
623             }
624         }
625     }
626     
627     if ((completablePosition && !insideString) || eol){
628         doc.insertString(dotPos, "" + bracket + bracket , null); //NOI18N
629
return true;
630     }
631     
632     return false;
633   }
634
635   /**
636    * Checks whether dotPos is a position at which bracket and quote
637    * completion is performed. Brackets and quotes are not completed
638    * everywhere but just at suitable places .
639    * @param doc the document
640    * @param dotPos position to be tested
641    */

642   private static boolean isCompletablePosition(BaseDocument doc, int dotPos)
643     throws BadLocationException JavaDoc
644   {
645     if (dotPos == doc.getLength()) // there's no other character to test
646
return true;
647     else {
648       // test that we are in front of ) , " or '
649
char chr = doc.getChars(dotPos,1)[0];
650       return (chr == ')' ||
651           chr == ',' ||
652           chr == '\"'||
653           chr == '\''||
654           chr == ' ' ||
655           chr == ']' ||
656           chr == '}' ||
657           chr == '\n'||
658           chr == '\t'||
659           chr == ';');
660     }
661   }
662
663   private static boolean isQuoteCompletablePosition(BaseDocument doc, int dotPos)
664           throws BadLocationException JavaDoc{
665       if (dotPos == doc.getLength()) // there's no other character to test
666
return true;
667       else {
668           // test that we are in front of ) , " or ' ... etc.
669
int eol = Utilities.getRowEnd(doc, dotPos);
670           if (dotPos == eol || eol == -1){
671               return false;
672           }
673           int firstNonWhiteFwd = Utilities.getFirstNonWhiteFwd(doc, dotPos, eol);
674           if (firstNonWhiteFwd == -1){
675               return false;
676           }
677           char chr = doc.getChars(firstNonWhiteFwd,1)[0];
678           return (chr == ')' ||
679                   chr == ',' ||
680                   chr == '+'||
681                   chr == '}' ||
682                   chr == ';');
683       }
684   }
685
686   /**
687    * Returns true if bracket completion is enabled in options.
688    */

689   private static boolean completionSettingEnabled() {
690     return ((Boolean JavaDoc)Settings.getValue(JavaKit.class, JavaSettingsNames.PAIR_CHARACTERS_COMPLETION)).booleanValue();
691   }
692
693   /**
694    * Returns for an opening bracket or quote the appropriate closing
695    * character.
696    */

697   private static char matching(char bracket) {
698     switch (bracket) {
699     case '(' : return ')';
700     case '[' : return ']';
701     case '\"' : return '\"'; // NOI18N
702
case '\'' : return '\'';
703     default: return ' ';
704     }
705   }
706
707
708
709   /**
710    * posWithinString(doc, pos) iff position *pos* is within a string
711    * literal in document doc.
712    * @param doc the document
713    * @param dotPos position to be tested
714    */

715   static boolean posWithinString(BaseDocument doc, int dotPos) {
716     return posWithinQuotes(doc, dotPos, '\"', JavaTokenContext.STRING_LITERAL);
717   }
718
719   /**
720    * Generalized posWithingString to any token and delimiting
721    * character. It works for tokens are delimited by *quote* and
722    * extend up to the other *quote* or whitespace in case of an
723    * incomplete token.
724    * @param doc the document
725    * @param dotPos position to be tested
726    */

727   static boolean posWithinQuotes(BaseDocument doc, int dotPos, char quote, TokenID tokenID)
728   {
729     try {
730       MyTokenProcessor proc = new MyTokenProcessor();
731       doc.getSyntaxSupport().tokenizeText( proc,
732                        dotPos-1,
733                        doc.getLength(), true);
734       return proc.tokenID == tokenID &&
735       (dotPos - proc.tokenStart == 1 || doc.getChars(dotPos-1,1)[0]!=quote);
736     } catch (BadLocationException JavaDoc ex) {
737       return false;
738     }
739   }
740
741   static boolean posWithinAnyQuote(BaseDocument doc, int dotPos) {
742       try {
743           MyTokenProcessor proc = new MyTokenProcessor();
744           doc.getSyntaxSupport().tokenizeText( proc,
745                   dotPos-1,
746                   doc.getLength(), true);
747           if(proc.tokenID == JavaTokenContext.STRING_LITERAL ||
748                   proc.tokenID == JavaTokenContext.CHAR_LITERAL) {
749               char[] ch = doc.getChars(dotPos-1,1);
750               return dotPos - proc.tokenStart == 1 || (ch[0]!='\"' && ch[0]!='\'');
751           }
752           return false;
753       } catch (BadLocationException JavaDoc ex) {
754           return false;
755       }
756   }
757
758
759   static boolean isUnclosedStringAtLineEnd(BaseDocument doc, int dotPos) {
760     try {
761       MyTokenProcessor proc = new MyTokenProcessor();
762         doc.getSyntaxSupport().tokenizeText(proc, Utilities.getRowLastNonWhite(doc, dotPos), doc.getLength(), true);
763         return proc.tokenID == JavaTokenContext.STRING_LITERAL;
764     } catch (BadLocationException JavaDoc ex) {
765       return false;
766     }
767   }
768
769   /**
770    * A token processor used to find out the length of a token.
771    */

772   static class MyTokenProcessor implements TokenProcessor {
773     public TokenID tokenID = null;
774     public int tokenStart = -1;
775
776     public boolean token(TokenID tokenID, TokenContextPath tcp,
777              int tokBuffOffset, int tokLength) {
778       this.tokenStart = tokenBuffer2DocumentOffset(tokBuffOffset);
779       this.tokenID = tokenID;
780
781       // System.out.println("token " + tokenID.getName() + " at " + tokenStart + " (" +
782
// tokBuffOffset + ") len:" + tokLength);
783

784
785       return false;
786     }
787
788     public int eot(int offset) { // System.out.println("EOT");
789
return 0;}
790
791     public void nextBuffer(char [] buffer, int offset, int len, int startPos, int preScan, boolean lastBuffer) {
792       // System.out.println("nextBuffer "+ new String(buffer) + "," + offset + "len: " + len + " startPos:"+startPos + " preScan:" + preScan + " lastBuffer:" + lastBuffer);
793

794       this.bufferStartPos = startPos - offset;
795     }
796
797     private int bufferStartPos = 0;
798     private int tokenBuffer2DocumentOffset(int offs) { return offs + bufferStartPos;}
799   }
800
801     /**
802      * Token processor for finding of balance of brackets and braces.
803      */

804     private static class BalanceTokenProcessor implements TokenProcessor {
805
806         private TokenID leftTokenID;
807         private TokenID rightTokenID;
808         
809         private int balance;
810         
811         BalanceTokenProcessor(TokenID leftTokenID, TokenID rightTokenID) {
812             this.leftTokenID = leftTokenID;
813             this.rightTokenID = rightTokenID;
814         }
815         
816         public boolean token(TokenID tokenID, TokenContextPath tcp,
817         int tokBuffOffset, int tokLength) {
818             
819             if (tokenID == leftTokenID) {
820                 balance++;
821             } else if (tokenID == rightTokenID) {
822                 balance--;
823             }
824
825             return true;
826         }
827         
828         public int eot(int offset) {
829             return 0;
830         }
831         
832         public void nextBuffer(char [] buffer, int offset, int len, int startPos, int preScan, boolean lastBuffer) {
833         }
834         
835         public int getBalance() {
836             return balance;
837         }
838         
839     }
840     
841
842 }
843
Popular Tags