KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > editor > ext > java > JavaFormatSupport


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.ext.java;
21
22 import org.netbeans.editor.TokenID;
23 import org.netbeans.editor.TokenContextPath;
24 import org.netbeans.editor.TokenItem;
25 import org.netbeans.editor.ext.FormatTokenPosition;
26 import org.netbeans.editor.ext.ExtFormatSupport;
27 import org.netbeans.editor.ext.FormatWriter;
28
29 /**
30 * Java indentation services are located here
31 *
32 * @author Miloslav Metelka
33 * @version 1.00
34 */

35
36 public class JavaFormatSupport extends ExtFormatSupport {
37
38     private TokenContextPath tokenContextPath;
39
40     public JavaFormatSupport(FormatWriter formatWriter) {
41         this(formatWriter, JavaTokenContext.contextPath);
42     }
43
44     public JavaFormatSupport(FormatWriter formatWriter, TokenContextPath tokenContextPath) {
45         super(formatWriter);
46         this.tokenContextPath = tokenContextPath;
47     }
48
49     public TokenContextPath getTokenContextPath() {
50         return tokenContextPath;
51     }
52
53     public boolean isComment(TokenItem token, int offset) {
54         TokenID tokenID = token.getTokenID();
55         return (token.getTokenContextPath() == tokenContextPath
56                 && (tokenID == JavaTokenContext.LINE_COMMENT
57                     || tokenID == JavaTokenContext.BLOCK_COMMENT));
58     }
59
60     public boolean isMultiLineComment(TokenItem token) {
61         return (token.getTokenID() == JavaTokenContext.BLOCK_COMMENT);
62     }
63
64     public boolean isMultiLineComment(FormatTokenPosition pos) {
65         TokenItem token = pos.getToken();
66         return (token == null) ? false : isMultiLineComment(token);
67     }
68
69     /** Check whether the given token is multi-line comment
70      * that starts with slash and two stars.
71      */

72     public boolean isJavaDocComment(TokenItem token) {
73         return isMultiLineComment(token)
74             && token.getImage().startsWith("/**");
75     }
76
77     public TokenID getWhitespaceTokenID() {
78         return JavaTokenContext.WHITESPACE;
79     }
80
81     public TokenContextPath getWhitespaceTokenContextPath() {
82         return tokenContextPath;
83     }
84
85     public boolean canModifyWhitespace(TokenItem inToken) {
86         if (inToken.getTokenContextPath() == JavaTokenContext.contextPath) {
87             switch (inToken.getTokenID().getNumericID()) {
88                 case JavaTokenContext.BLOCK_COMMENT_ID:
89                 case JavaTokenContext.WHITESPACE_ID:
90                     return true;
91             }
92         }
93
94         return false;
95     }
96
97
98     /** Find the starting token of the statement before
99      * the given position and also return all the command
100      * delimiters. It searches in the backward direction
101      * for all the delimiters and statement starts and
102      * return all the tokens that are either command starts
103      * or delimiters. As the first step it uses
104      * <code>getPreviousToken()</code> so it ignores the initial token.
105      * @param token token before which the statement-start
106      * and delimiter is being searched.
107      * @return token that is start of the given statement
108      * or command delimiter.
109      * If the start of the statement is not found, null is retrurned.
110      */

111     public TokenItem findStatement(TokenItem token) {
112         TokenItem lit = null; // last important token
113
TokenItem t = getPreviousToken(token);
114
115         while (t != null) {
116             if (t.getTokenContextPath() == tokenContextPath) {
117
118                 switch (t.getTokenID().getNumericID()) {
119                     case JavaTokenContext.SEMICOLON_ID:
120                         if (!isForLoopSemicolon(t)) {
121                             return (lit != null) ? lit : t;
122                         }
123                         break;
124
125                     case JavaTokenContext.LBRACE_ID:
126                     case JavaTokenContext.ELSE_ID:
127                         return (lit != null) ? lit : t;
128                         
129                     case JavaTokenContext.RBRACE_ID:
130                         // Check whether this is an array initialization block
131
if (!isArrayInitializationBraceBlock(t, null)) {
132                             return (lit != null) ? lit : t;
133                         } else { // skip the array initialization block
134
t = findMatchingToken(t, null, JavaTokenContext.LBRACE, true);
135                         }
136                         break;
137
138                     case JavaTokenContext.COLON_ID:
139                         TokenItem tt = findAnyToken(t, null, new TokenID[] {JavaTokenContext.CASE, JavaTokenContext.DEFAULT, JavaTokenContext.FOR, JavaTokenContext.QUESTION, JavaTokenContext.ASSERT}, t.getTokenContextPath(), true);
140                         if (tt != null) {
141                             switch (tt.getTokenID().getNumericID()) {
142                                 case JavaTokenContext.CASE_ID:
143                                 case JavaTokenContext.DEFAULT_ID:
144                                 case JavaTokenContext.FOR_ID:
145                                     return (lit != null) ? lit : t;
146                             }
147                         }
148                         break;
149
150                     case JavaTokenContext.DO_ID:
151                     case JavaTokenContext.SWITCH_ID:
152                     case JavaTokenContext.CASE_ID:
153                     case JavaTokenContext.DEFAULT_ID:
154                         return t;
155
156                     case JavaTokenContext.FOR_ID:
157                     case JavaTokenContext.IF_ID:
158                     case JavaTokenContext.WHILE_ID:
159                         /* Try to find the statement after ( ... )
160                          * If it exists, then the first important
161                          * token after it is the stmt start. Otherwise
162                          * it's this token.
163                          */

164                         if (lit != null && lit.getTokenID() == JavaTokenContext.LPAREN) {
165                             // Find matching right paren in fwd dir
166
TokenItem mt = findMatchingToken(lit, token,
167                                     JavaTokenContext.RPAREN, false);
168                             if (mt != null && mt.getNext() != null) {
169                                 mt = findImportantToken(mt.getNext(), token, false);
170                                 if (mt != null) {
171                                     return mt;
172                                 }
173                             }
174                         }
175
176                         // No further stmt found, return this one
177
return t;
178
179                 }
180
181                 // Remember last important token
182
if (isImportant(t, 0)) {
183                     lit = t;
184                 }
185
186             }
187
188             t = t.getPrevious();
189         }
190
191         return lit;
192     }
193
194
195     /** Find the 'if' when the 'else' is provided.
196      * @param elseToken the token with the 'else' command
197      * for which the 'if' is being searched.
198      * @return corresponding 'if' token or null if there's
199      * no corresponding 'if' statement.
200      */

201     public TokenItem findIf(TokenItem elseToken) {
202         if (elseToken == null || !tokenEquals(elseToken,
203                     JavaTokenContext.ELSE, tokenContextPath)
204         ) {
205             throw new IllegalArgumentException JavaDoc("Only accept 'else'."); // NOI18N
206
}
207
208         int braceDepth = 0; // depth of the braces
209
int elseDepth = 0; // depth of multiple else stmts
210
while (true) {
211             elseToken = findStatement(elseToken);
212             if (elseToken == null) {
213                 return null;
214             }
215             
216             switch (elseToken.getTokenID().getNumericID()) {
217                 case JavaTokenContext.LBRACE_ID:
218                     if (--braceDepth < 0) {
219                         return null; // no corresponding right brace
220
}
221                     break;
222
223                 case JavaTokenContext.RBRACE_ID:
224                     braceDepth++;
225                     break;
226
227                 case JavaTokenContext.ELSE_ID:
228                     if (braceDepth == 0) {
229                         elseDepth++;
230                     }
231                     break;
232
233                 case JavaTokenContext.SEMICOLON_ID:
234                 case JavaTokenContext.COLON_ID:
235                 case JavaTokenContext.DO_ID:
236                 case JavaTokenContext.CASE_ID:
237                 case JavaTokenContext.DEFAULT_ID:
238                 case JavaTokenContext.FOR_ID:
239                 case JavaTokenContext.WHILE_ID:
240                     break;
241
242                 case JavaTokenContext.IF_ID:
243                     if (braceDepth == 0) {
244                         if (elseDepth-- == 0) {
245                             return elseToken; // successful search
246
}
247                     }
248                     break;
249             }
250         }
251     }
252
253
254     /** Find the 'switch' when the 'case' is provided.
255      * @param caseToken the token with the 'case' command
256      * for which the 'switch' is being searched.
257      * @return corresponding 'switch' token or null if there's
258      * no corresponding 'switch' statement.
259      */

260     public TokenItem findSwitch(TokenItem caseToken) {
261         if (caseToken == null ||
262              (!tokenEquals(caseToken, JavaTokenContext.CASE,
263                tokenContextPath)
264                && !tokenEquals(caseToken, JavaTokenContext.DEFAULT,
265                     tokenContextPath))
266         ) {
267             throw new IllegalArgumentException JavaDoc("Only accept 'case' or 'default'."); // NOI18N
268
}
269
270         int braceDepth = 1; // depth of the braces - need one more left
271
while (true) {
272             caseToken = findStatement(caseToken);
273             if (caseToken == null) {
274                 return null;
275             }
276             
277             switch (caseToken.getTokenID().getNumericID()) {
278                 case JavaTokenContext.LBRACE_ID:
279                     if (--braceDepth < 0) {
280                         return null; // no corresponding right brace
281
}
282                     break;
283
284                 case JavaTokenContext.RBRACE_ID:
285                     braceDepth++;
286                     break;
287
288                 case JavaTokenContext.SWITCH_ID:
289                 case JavaTokenContext.DEFAULT_ID:
290                     if (braceDepth == 0) {
291                         return caseToken;
292                     }
293                     break;
294             }
295         }
296     }
297
298     /** Find the 'try' when the 'catch' is provided.
299      * @param catchToken the token with the 'catch' command
300      * for which the 'try' is being searched.
301      * @return corresponding 'try' token or null if there's
302      * no corresponding 'try' statement.
303      */

304     public TokenItem findTry(TokenItem catchToken) {
305         if (catchToken == null ||
306              (!tokenEquals(catchToken, JavaTokenContext.CATCH,
307                tokenContextPath))
308         ) {
309             throw new IllegalArgumentException JavaDoc("Only accept 'catch'."); // NOI18N
310
}
311
312         int braceDepth = 0; // depth of the braces
313
while (true) {
314             catchToken = findStatement(catchToken);
315             if (catchToken == null) {
316                 return null;
317             }
318             
319             switch (catchToken.getTokenID().getNumericID()) {
320                 case JavaTokenContext.LBRACE_ID:
321                     if (--braceDepth < 0) {
322                         return null; // no corresponding right brace
323
}
324                     break;
325
326                 case JavaTokenContext.RBRACE_ID:
327                     braceDepth++;
328                     break;
329
330                 case JavaTokenContext.TRY_ID:
331                     if (braceDepth == 0) {
332                         return catchToken;
333                     }
334                     break;
335             }
336         }
337     }
338     
339     /** Find the start of the statement.
340      * @param token token from which to start. It searches
341      * backward using <code>findStatement()</code> so it ignores
342      * the given token.
343      * @return the statement start token (outer statement start for nested
344      * statements).
345      * It returns the same token if there is '{' before
346      * the given token.
347      */

348     public TokenItem findStatementStart(TokenItem token) {
349         return findStatementStart(token, true);
350     }
351     
352     public TokenItem findStatementStart(TokenItem token, boolean outermost) {
353         TokenItem t = findStatement(token);
354         if (t != null) {
355             switch (t.getTokenID().getNumericID()) {
356                 case JavaTokenContext.SEMICOLON_ID: // ';' found
357
TokenItem scss = findStatement(t);
358                     
359                     // fix for issue 14274
360
if (scss == null)
361                         return token;
362                     
363                     switch (scss.getTokenID().getNumericID()) {
364                         case JavaTokenContext.LBRACE_ID: // '{' then ';'
365
case JavaTokenContext.RBRACE_ID: // '}' then ';'
366
case JavaTokenContext.COLON_ID: // ':' then ';'
367
case JavaTokenContext.CASE_ID: // 'case' then ';'
368
case JavaTokenContext.DEFAULT_ID:
369                         case JavaTokenContext.SEMICOLON_ID: // ';' then ';'
370
return t; // return ';'
371

372                         case JavaTokenContext.DO_ID:
373                         case JavaTokenContext.FOR_ID:
374                         case JavaTokenContext.IF_ID:
375                         case JavaTokenContext.WHILE_ID:
376                         case JavaTokenContext.SYNCHRONIZED_ID:
377                             return findStatementStart(t, outermost);
378
379                         case JavaTokenContext.ELSE_ID: // 'else' then ';'
380
// Find the corresponding 'if'
381
TokenItem ifss = findIf(scss);
382                             if (ifss != null) { // 'if' ... 'else' then ';'
383
return findStatementStart(ifss, outermost);
384
385                             } else { // no valid starting 'if'
386
return scss; // return 'else'
387
}
388
389                         default: // something usual then ';'
390
TokenItem bscss = findStatement(scss);
391                             if (bscss != null) {
392                                 switch (bscss.getTokenID().getNumericID()) {
393                                     case JavaTokenContext.SEMICOLON_ID: // ';' then stmt ending with ';'
394
case JavaTokenContext.LBRACE_ID:
395                                     case JavaTokenContext.RBRACE_ID:
396                                     case JavaTokenContext.COLON_ID:
397                                         return scss; //
398

399                                     case JavaTokenContext.DO_ID:
400                                     case JavaTokenContext.FOR_ID:
401                                     case JavaTokenContext.IF_ID:
402                                     case JavaTokenContext.WHILE_ID:
403                                     case JavaTokenContext.SYNCHRONIZED_ID:
404                                         return findStatementStart(bscss, outermost);
405
406                                     case JavaTokenContext.ELSE_ID:
407                                         // Find the corresponding 'if'
408
ifss = findIf(bscss);
409                                         if (ifss != null) { // 'if' ... 'else' ... ';'
410
return findStatementStart(ifss, outermost);
411
412                                         } else { // no valid starting 'if'
413
return bscss; // return 'else'
414
}
415                                 }
416                             }
417
418                             return scss;
419                     } // semicolon servicing end
420

421                 case JavaTokenContext.LBRACE_ID: // '{' found
422
return token; // return original token
423

424                 case JavaTokenContext.RBRACE_ID: // '}' found
425
TokenItem lb = findMatchingToken(t, null,
426                             JavaTokenContext.LBRACE, true);
427                     if (lb != null) { // valid matching left-brace
428
// Find a stmt-start of the '{'
429
TokenItem lbss = findStatement(lb);
430                         if (lbss != null) {
431                             switch (lbss.getTokenID().getNumericID()) {
432                                 case JavaTokenContext.ELSE_ID: // 'else {'
433
// Find the corresponding 'if'
434
TokenItem ifss = findIf(lbss);
435                                     if (ifss != null) { // valid 'if'
436
return findStatementStart(ifss, outermost);
437                                     } else {
438                                         return lbss; // return 'else'
439
}
440
441                                 case JavaTokenContext.CATCH_ID: // 'catch (...) {'
442
// Find the corresponding 'try'
443
TokenItem tryss = findTry(lbss);
444                                     if (tryss != null) { // valid 'try'
445
return findStatementStart(tryss, outermost);
446                                     } else {
447                                         return lbss; // return 'catch'
448
}
449                                     
450                                 case JavaTokenContext.DO_ID:
451                                 case JavaTokenContext.FOR_ID:
452                                 case JavaTokenContext.IF_ID:
453                                 case JavaTokenContext.WHILE_ID:
454                                 case JavaTokenContext.SYNCHRONIZED_ID:
455                                     return findStatementStart(lbss, outermost);
456
457                             }
458                             
459                             // another hack to prevent problem described in issue 17033
460
if (lbss.getTokenID().getNumericID() == JavaTokenContext.LBRACE_ID) {
461                                 return t; // return right brace
462
}
463                             
464                             return lbss;
465                         }
466
467                     }
468                     return t; // return right brace
469

470                 case JavaTokenContext.COLON_ID:
471                 case JavaTokenContext.CASE_ID:
472                 case JavaTokenContext.DEFAULT_ID:
473                     return token;
474
475                 case JavaTokenContext.ELSE_ID:
476                     // Find the corresponding 'if'
477
TokenItem ifss = findIf(t);
478                     return (ifss != null) ? findStatementStart(ifss, outermost) : t;
479
480                 case JavaTokenContext.DO_ID:
481                 case JavaTokenContext.FOR_ID:
482                 case JavaTokenContext.IF_ID:
483                 case JavaTokenContext.WHILE_ID:
484                 case JavaTokenContext.SYNCHRONIZED_ID:
485                     if (!outermost) {
486                         return t;
487                     } else {
488                         return findStatementStart(t, outermost);
489                     }
490                     
491                 case JavaTokenContext.IDENTIFIER_ID:
492                     return t;
493                 default:
494                     return t;
495             }
496         }
497
498         return token; // return original token
499
}
500
501     /** Get the indentation for the given token.
502      * It first searches whether there's an non-whitespace and a non-leftbrace
503      * character on the line with the token and if so,
504      * it takes indent of the non-ws char instead.
505      * @param token token for which the indent is being searched.
506      * The token itself is ignored and the previous token
507      * is used as a base for the search.
508      * @param forceFirstNonWhitespace set true to ignore leftbrace and search
509      * directly for first non-whitespace
510      */

511     public int getTokenIndent(TokenItem token, boolean forceFirstNonWhitespace) {
512         FormatTokenPosition tp = getPosition(token, 0);
513         // this is fix for bugs: 7980 and 9111
514
// see the findLineFirstNonWhitespaceAndNonLeftBrace definition
515
// for more info about the fix
516
FormatTokenPosition fnw;
517         if (forceFirstNonWhitespace)
518             fnw = findLineFirstNonWhitespace(tp);
519         else
520             fnw = findLineFirstNonWhitespaceAndNonLeftBrace(tp);
521         
522         if (fnw != null) { // valid first non-whitespace
523
tp = fnw;
524         }
525         return getVisualColumnOffset(tp);
526     }
527
528     public int getTokenIndent(TokenItem token) {
529         return getTokenIndent(token, false);
530     }
531     
532     /** Find the indentation for the first token on the line.
533      * The given token is also examined in some cases.
534      */

535     public int findIndent(TokenItem token) {
536         int indent = -1; // assign invalid indent
537

538         // First check the given token
539
if (token != null) {
540             switch (token.getTokenID().getNumericID()) {
541                 case JavaTokenContext.ELSE_ID:
542                     TokenItem ifss = findIf(token);
543                     if (ifss != null) {
544                         indent = getTokenIndent(ifss);
545                     }
546                     break;
547
548                 case JavaTokenContext.LBRACE_ID:
549                     TokenItem stmt = findStatement(token);
550                     if (stmt == null) {
551                         indent = 0;
552
553                     } else {
554                         switch (stmt.getTokenID().getNumericID()) {
555                             case JavaTokenContext.DO_ID:
556                             case JavaTokenContext.FOR_ID:
557                             case JavaTokenContext.IF_ID:
558                             case JavaTokenContext.WHILE_ID:
559                             case JavaTokenContext.ELSE_ID:
560                                 indent = getTokenIndent(stmt);
561                                 break;
562                                 
563                             case JavaTokenContext.LBRACE_ID:
564                                 indent = getTokenIndent(stmt) + getShiftWidth();
565                                 break;
566                                 
567                             default:
568                                 stmt = findStatementStart(token);
569                                 if (stmt == null) {
570                                     indent = 0;
571
572                                 } else if (stmt == token) {
573                                     stmt = findStatement(token); // search for delimiter
574
indent = (stmt != null) ? indent = getTokenIndent(stmt) : 0;
575
576                                 } else { // valid statement
577
indent = getTokenIndent(stmt);
578                                     switch (stmt.getTokenID().getNumericID()) {
579                                         case JavaTokenContext.LBRACE_ID:
580                                             indent += getShiftWidth();
581                                             break;
582                                     }
583                                 }
584                         }
585                     }
586                     break;
587
588                 case JavaTokenContext.RBRACE_ID:
589                     TokenItem rbmt = findMatchingToken(token, null,
590                                 JavaTokenContext.LBRACE, true);
591                     if (rbmt != null) { // valid matching left-brace
592
TokenItem t = findStatement(rbmt);
593                         boolean forceFirstNonWhitespace = false;
594                         if (t == null) {
595                             t = rbmt; // will get indent of the matching brace
596

597                         } else {
598                             switch (t.getTokenID().getNumericID()) {
599                                 case JavaTokenContext.SEMICOLON_ID:
600                                 case JavaTokenContext.LBRACE_ID:
601                                 case JavaTokenContext.RBRACE_ID:
602                                 {
603                                     t = rbmt;
604                                     forceFirstNonWhitespace = true;
605                                 }
606                             }
607                         }
608                         // the right brace must be indented to the first
609
// non-whitespace char - forceFirstNonWhitespace=true
610
indent = getTokenIndent(t, forceFirstNonWhitespace);
611
612                     } else { // no matching left brace
613
indent = getTokenIndent(token); // leave as is
614
}
615                     break;
616
617                 case JavaTokenContext.CASE_ID:
618                 case JavaTokenContext.DEFAULT_ID:
619                     TokenItem swss = findSwitch(token);
620                     if (swss != null) {
621                         indent = getTokenIndent(swss);
622                     }
623                     break;
624
625             }
626         }
627
628         // If indent not found, search back for the first important token
629
if (indent < 0) { // if not yet resolved
630
TokenItem t = findImportantToken(token, null, true);
631             if (t != null) { // valid important token
632
if (t.getTokenContextPath() != tokenContextPath) {
633                     // For non-java tokens such as jsp return indent
634
// of last non-java line
635
return getTokenIndent(t);
636                 }
637                 
638                 switch (t.getTokenID().getNumericID()) {
639                     case JavaTokenContext.SEMICOLON_ID: // semicolon found
640
TokenItem tt = findStatementStart(token);
641                         indent = getTokenIndent(tt);
642                             
643                         break;
644
645                     case JavaTokenContext.LBRACE_ID:
646                         TokenItem lbss = findStatementStart(t, false);
647                         if (lbss == null) {
648                             lbss = t;
649                         }
650                         indent = getTokenIndent(lbss) + getShiftWidth();
651                         break;
652
653                     case JavaTokenContext.RBRACE_ID:
654                         if (true) {
655                             TokenItem t3 = findStatementStart(token);
656                             indent = getTokenIndent(t3);
657                             break;
658                         }
659
660                         /** Check whether the following situation occurs:
661                          * if (t1)
662                          * if (t2) {
663                          * ...
664                          * }
665                          *
666                          * In this case the indentation must be shifted
667                          * one level back.
668                          */

669                         TokenItem rbmt = findMatchingToken(t, null,
670                                 JavaTokenContext.LBRACE, true);
671                         if (rbmt != null) { // valid matching left-brace
672
// Check whether there's a indent stmt
673
TokenItem t6 = findStatement(rbmt);
674                             if (t6 != null) {
675                                 switch (t6.getTokenID().getNumericID()) {
676                                     case JavaTokenContext.ELSE_ID:
677                                         /* Check the following situation:
678                                          * if (t1)
679                                          * if (t2)
680                                          * c1();
681                                          * else {
682                                          * c2();
683                                          * }
684                                          */

685
686                                         // Find the corresponding 'if'
687
t6 = findIf(t6);
688                                         if (t6 != null) { // valid 'if'
689
TokenItem t7 = findStatement(t6);
690                                             if (t7 != null) {
691                                                 switch (t7.getTokenID().getNumericID()) {
692                                                     case JavaTokenContext.DO_ID:
693                                                     case JavaTokenContext.FOR_ID:
694                                                     case JavaTokenContext.IF_ID:
695                                                     case JavaTokenContext.WHILE_ID:
696                                                         indent = getTokenIndent(t7);
697                                                         break;
698
699                                                     case JavaTokenContext.ELSE_ID:
700                                                         indent = getTokenIndent(findStatementStart(t6));
701                                                 }
702                                             }
703                                         }
704                                         break;
705
706                                     case JavaTokenContext.DO_ID:
707                                     case JavaTokenContext.FOR_ID:
708                                     case JavaTokenContext.IF_ID:
709                                     case JavaTokenContext.WHILE_ID:
710                                         /* Check the following:
711                                          * if (t1)
712                                          * if (t2) {
713                                          * c1();
714                                          * }
715                                          */

716                                         TokenItem t7 = findStatement(t6);
717                                         if (t7 != null) {
718                                             switch (t7.getTokenID().getNumericID()) {
719                                                 case JavaTokenContext.DO_ID:
720                                                 case JavaTokenContext.FOR_ID:
721                                                 case JavaTokenContext.IF_ID:
722                                                 case JavaTokenContext.WHILE_ID:
723                                                     indent = getTokenIndent(t7);
724                                                     break;
725
726                                                 case JavaTokenContext.ELSE_ID:
727                                                     indent = getTokenIndent(findStatementStart(t6));
728
729                                             }
730                                         }
731                                         break;
732
733                                     case JavaTokenContext.LBRACE_ID: // '{' ... '{'
734
indent = getTokenIndent(rbmt);
735                                         break;
736
737                                 }
738
739                             }
740
741                             if (indent < 0) {
742                                 indent = getTokenIndent(t); // indent of original rbrace
743
}
744
745                         } else { // no matching left-brace
746
indent = getTokenIndent(t); // return indent of '}'
747
}
748                         break;
749
750                     case JavaTokenContext.COLON_ID:
751                         TokenItem ttt = findAnyToken(t, null, new TokenID[] {JavaTokenContext.CASE, JavaTokenContext.DEFAULT, JavaTokenContext.FOR, JavaTokenContext.QUESTION, JavaTokenContext.ASSERT}, t.getTokenContextPath(), true);
752                         if (ttt != null && ttt.getTokenID().getNumericID() == JavaTokenContext.QUESTION_ID) {
753                             indent = getTokenIndent(ttt) + getShiftWidth();
754                         } else {
755                             // Indent of line with ':' plus one indent level
756
indent = getTokenIndent(t) + getShiftWidth();
757                         }
758                         break;
759
760                     case JavaTokenContext.QUESTION_ID:
761                     case JavaTokenContext.DO_ID:
762                     case JavaTokenContext.ELSE_ID:
763                         indent = getTokenIndent(t) + getShiftWidth();
764                         break;
765
766                     case JavaTokenContext.RPAREN_ID:
767                         // Try to find the matching left paren
768
TokenItem rpmt = findMatchingToken(t, null, JavaTokenContext.LPAREN, true);
769                         if (rpmt != null) {
770                             rpmt = findImportantToken(rpmt, null, true);
771                             // Check whether there are the indent changing kwds
772
if (rpmt != null && rpmt.getTokenContextPath() == tokenContextPath) {
773                                 switch (rpmt.getTokenID().getNumericID()) {
774                                     case JavaTokenContext.FOR_ID:
775                                     case JavaTokenContext.IF_ID:
776                                     case JavaTokenContext.WHILE_ID:
777                                         // Indent one level
778
indent = getTokenIndent(rpmt) + getShiftWidth();
779                                         break;
780                                 }
781                             }
782                         }
783                         if (indent < 0) {
784                             indent = computeStatementIndent(t);
785                         }
786                         break;
787                         
788                     case JavaTokenContext.COMMA_ID:
789                         if (isEnumComma(t)) {
790                             indent = getTokenIndent(t);
791                             break;
792                         } // else continue to default
793

794                     default: {
795                         indent = computeStatementIndent(t);
796                         break;
797                     }
798                 }
799
800                 if (indent < 0) { // no indent found yet
801
indent = getTokenIndent(t);
802                 }
803             }
804         }
805         
806         if (indent < 0) { // no important token found
807
indent = 0;
808         }
809
810         return indent;
811     }
812
813     private int computeStatementIndent(final TokenItem t) {
814         int indent;
815         // Find stmt start and add continuation indent
816
TokenItem stmtStart = findStatementStart(t);
817         indent = getTokenIndent(stmtStart);
818         int tindent = getTokenIndent(t);
819         if (tindent > indent)
820             return tindent;
821         
822         if (stmtStart != null) {
823             // Check whether there is a comma on the previous line end
824
// and if so then also check whether the present
825
// statement is inside array initialization statement
826
// and not inside parents and if so then do not indent
827
// statement continuation
828
if (t != null && tokenEquals(t, JavaTokenContext.COMMA, tokenContextPath)) {
829                 if (isArrayInitializationBraceBlock(t, null) && !isInsideParens(t, stmtStart)) {
830                     // Eliminate the later effect of statement continuation shifting
831
indent -= getFormatStatementContinuationIndent();
832                 }
833             }
834             // Check whether there is an annotation '@' on the previous line begining
835
// and if so then do not add the continuation indent
836
if (t != null) {
837                 FormatTokenPosition pos = findLineFirstNonWhitespace(getPosition(t, 0));
838                 if (pos != null) {
839                     TokenItem maybeAnno = pos.getToken();
840                     if (maybeAnno != null && maybeAnno.getTokenID() == JavaTokenContext.ANNOTATION) {
841                         indent -= getFormatStatementContinuationIndent();
842                     }
843                 }
844             }
845             indent += getFormatStatementContinuationIndent();
846         }
847         return indent;
848     }
849
850     public FormatTokenPosition indentLine(FormatTokenPosition pos) {
851         int indent = 0; // Desired indent
852

853         // Get the first non-whitespace position on the line
854
FormatTokenPosition firstNWS = findLineFirstNonWhitespace(pos);
855         if (firstNWS != null) { // some non-WS on the line
856
if (isComment(firstNWS)) { // comment is first on the line
857
if (isMultiLineComment(firstNWS) && firstNWS.getOffset() != 0) {
858
859                     // Indent the inner lines of the multi-line comment by one
860
indent = getLineIndent(getPosition(firstNWS.getToken(), 0), true) + 1;
861
862                     // If the line is inside multi-line comment and doesn't contain '*'
863
if (!isIndentOnly()) {
864                         if (getChar(firstNWS) != '*') {
865                             if (isJavaDocComment(firstNWS.getToken())) {
866                                 if (getFormatLeadingStarInComment()) {
867                                     // For java-doc it should be OK to add the star
868
insertString(firstNWS, "* "); // NOI18N
869
}
870
871                             } else {
872                                 // For non-java-doc not because it can be commented code
873
indent = getLineIndent(pos, true);
874                             }
875                         } else {
876                             if (isJavaDocComment(firstNWS.getToken())) {
877                                 if (!getFormatLeadingStarInComment()) {
878                                     // For java-doc it should be OK to remove the star
879
int len = -1;
880                                     if (firstNWS.getOffset() + 1 < firstNWS.getToken().getImage().length()) {
881                                         FormatTokenPosition nextCharPos = getPosition(firstNWS.getToken(), firstNWS.getOffset() + 1);
882                                         char nextChar = getChar(nextCharPos);
883                                         if (nextChar != '/') {
884                                             len = getChar(nextCharPos) == ' ' ? 2 : 1;
885                                         }
886                                     } else {
887                                         len = 1;
888                                     }
889                                     
890                                     if (len != -1) {
891                                         remove(firstNWS, len);
892                                     }
893                                 }
894                             }
895                         }
896
897                     } else { // in indent mode (not formatting)
898
if (getChar(firstNWS) != '*') { // e.g. not for '*/'
899
if (isJavaDocComment(firstNWS.getToken())) {
900                                 if (getFormatLeadingStarInComment()) {
901                                     insertString(firstNWS, "* "); // NOI18N
902
setIndentShift(2);
903                                 }
904                             }
905                         }
906                     }
907
908                 } else if (!isMultiLineComment(firstNWS)) { // line-comment
909
indent = findIndent(firstNWS.getToken());
910                 } else { // multi-line comment
911
if (isJavaDocComment(firstNWS.getToken()))
912                     {
913                         indent = findIndent(firstNWS.getToken());
914                     }
915                     else
916                     {
917                         // check whether the multiline comment isn't finished on the same line (see issue 12821)
918
if (firstNWS.getToken().getImage().indexOf('\n') == -1)
919                             indent = findIndent(firstNWS.getToken());
920                         else
921                         indent = getLineIndent(firstNWS, true);
922                     }
923                 }
924
925             } else { // first non-WS char is not comment
926
indent = findIndent(firstNWS.getToken());
927             }
928
929         } else { // whole line is WS
930
// Can be empty line inside multi-line comment
931
TokenItem token = pos.getToken();
932             if (token == null) {
933                 token = findLineStart(pos).getToken();
934                 if (token == null) { // empty line
935
token = getLastToken();
936                 }
937             }
938
939             if (token != null && isMultiLineComment(token)) {
940                 if (getFormatLeadingStarInComment()
941                     && (isIndentOnly() || isJavaDocComment(token))
942                 ) {
943                     // Insert initial '* '
944
insertString(pos, "* "); // NOI18N
945
setIndentShift(2);
946                 }
947
948                 // Indent the multi-comment by one more space
949
indent = getVisualColumnOffset(getPosition(token, 0)) + 1;
950
951             } else { // non-multi-line comment
952
indent = findIndent(pos.getToken());
953             }
954         }
955
956         // For indent-only always indent
957
return changeLineIndent(pos, indent);
958     }
959
960     /** Check whether the given semicolon is inside
961      * the for() statement.
962      * @param token token to check. It must be a semicolon.
963      * @return true if the given semicolon is inside
964      * the for() statement, or false otherwise.
965      */

966     public boolean isForLoopSemicolon(TokenItem token) {
967         if (token == null || !tokenEquals(token,
968                     JavaTokenContext.SEMICOLON, tokenContextPath)
969         ) {
970             throw new IllegalArgumentException JavaDoc("Only accept ';'."); // NOI18N
971
}
972
973         int parDepth = 0; // parenthesis depth
974
int braceDepth = 0; // brace depth
975
boolean semicolonFound = false; // next semicolon
976
token = token.getPrevious(); // ignore this semicolon
977
while (token != null) {
978             if (tokenEquals(token, JavaTokenContext.LPAREN, tokenContextPath)) {
979                 if (parDepth == 0) { // could be a 'for ('
980
FormatTokenPosition tp = getPosition(token, 0);
981                     tp = findImportant(tp, null, false, true);
982                     if (tp != null && tokenEquals(tp.getToken(),
983                             JavaTokenContext.FOR, tokenContextPath)
984                     ) {
985                         return true;
986                     }
987                     return false;
988
989                 } else { // non-zero depth
990
parDepth--;
991                 }
992
993             } else if (tokenEquals(token, JavaTokenContext.RPAREN, tokenContextPath)) {
994                 parDepth++;
995
996             } else if (tokenEquals(token, JavaTokenContext.LBRACE, tokenContextPath)) {
997                 if (braceDepth == 0) { // unclosed left brace
998
return false;
999                 }
1000                braceDepth--;
1001
1002            } else if (tokenEquals(token, JavaTokenContext.RBRACE, tokenContextPath)) {
1003                braceDepth++;
1004
1005            } else if (tokenEquals(token, JavaTokenContext.SEMICOLON, tokenContextPath)) {
1006                if (semicolonFound) { // one semicolon already found
1007
return false;
1008                }
1009                semicolonFound = true;
1010            }
1011
1012            token = token.getPrevious();
1013        }
1014
1015        return false;
1016    }
1017    
1018    /**
1019     * Check whether there are left parenthesis before the given token
1020     * until the limit token.
1021     *
1022     * @param token non-null token from which to start searching back.
1023     * @param limitToken limit token when reached the search will stop
1024     * with returning false.
1025     * @return true if there is LPAREN token before the given token
1026     * (while respecting paren nesting).
1027     */

1028    private boolean isInsideParens(TokenItem token, TokenItem limitToken) {
1029        int depth = 0;
1030        token = token.getPrevious();
1031
1032        while (token != null && token != limitToken) {
1033            if (tokenEquals(token, JavaTokenContext.LPAREN, tokenContextPath)) {
1034                if (--depth < 0) {
1035                    return true;
1036                }
1037
1038            } else if (tokenEquals(token, JavaTokenContext.RPAREN, tokenContextPath)) {
1039                depth++;
1040            }
1041            token = token.getPrevious();
1042        }
1043        return false;
1044    }
1045
1046    /**
1047     * Check whether the given token is located in array initialization block.
1048     *
1049     * @param token non-null token from which to start searching back.
1050     * @param limitToken limit token when reached the search will stop
1051     * with returning false.
1052     * @return true if the token is located inside the brace block of array
1053     * initialization.
1054     */

1055    private boolean isArrayInitializationBraceBlock(TokenItem token, TokenItem limitToken) {
1056        int depth = 0;
1057        token = token.getPrevious();
1058
1059        while (token != null && token != limitToken && token.getTokenContextPath() == tokenContextPath) {
1060            switch (token.getTokenID().getNumericID()) {
1061                case JavaTokenContext.RBRACE_ID:
1062                    depth++;
1063                    break;
1064
1065                case JavaTokenContext.LBRACE_ID:
1066                    depth--;
1067                    if (depth < 0) {
1068                        TokenItem prev = findImportantToken(token, limitToken, true);
1069                        // Array initialization left brace should be preceded
1070
// by either '=' or ']' i.e.
1071
// either "String array = { "a", "b", ... }"
1072
// or "String array = new String[] { "a", "b", ... }"
1073
return (prev != null && prev.getTokenContextPath() == tokenContextPath
1074                                && (JavaTokenContext.RBRACKET.equals(prev.getTokenID())
1075                                 || JavaTokenContext.EQ.equals(prev.getTokenID())));
1076                    }
1077                    break;
1078
1079                // Array initialization block should not contain statements or ';'
1080
case JavaTokenContext.DO_ID:
1081                case JavaTokenContext.FOR_ID:
1082                case JavaTokenContext.IF_ID:
1083                case JavaTokenContext.WHILE_ID:
1084                case JavaTokenContext.SEMICOLON_ID:
1085                    if (depth == 0) {
1086                        return false;
1087                    }
1088            }
1089            token = token.getPrevious();
1090        }
1091        return false;
1092    }
1093
1094    public boolean isEnumComma(TokenItem token) {
1095        while (token != null && tokenEquals(token, JavaTokenContext.COMMA, tokenContextPath)) {
1096            TokenItem itm = findStatementStart(token);
1097            if (itm == token)
1098                break;
1099            token = itm;
1100        }
1101        if (token != null && tokenEquals(token, JavaTokenContext.IDENTIFIER, tokenContextPath)) {
1102            TokenItem itm = findImportantToken(token, null, true);
1103            if (itm != null && tokenEquals(itm, JavaTokenContext.LBRACE, tokenContextPath)) {
1104                TokenItem startItem = findStatementStart(itm);
1105                if (startItem != null && findToken(startItem, itm, JavaTokenContext.ENUM, tokenContextPath, null, false) != null)
1106                    return true;
1107            }
1108        }
1109        return false;
1110    }
1111
1112    public boolean getFormatSpaceBeforeParenthesis() {
1113        return getSettingBoolean(JavaSettingsNames.JAVA_FORMAT_SPACE_BEFORE_PARENTHESIS,
1114                                 JavaSettingsDefaults.defaultJavaFormatSpaceBeforeParenthesis);
1115    }
1116
1117    public boolean getFormatSpaceAfterComma() {
1118        return getSettingBoolean(JavaSettingsNames.JAVA_FORMAT_SPACE_AFTER_COMMA,
1119                                 JavaSettingsDefaults.defaultJavaFormatSpaceAfterComma);
1120    }
1121
1122    public boolean getFormatNewlineBeforeBrace() {
1123        return getSettingBoolean(JavaSettingsNames.JAVA_FORMAT_NEWLINE_BEFORE_BRACE,
1124                                 JavaSettingsDefaults.defaultJavaFormatNewlineBeforeBrace);
1125    }
1126
1127    public boolean getFormatLeadingSpaceInComment() {
1128        return getSettingBoolean(JavaSettingsNames.JAVA_FORMAT_LEADING_SPACE_IN_COMMENT,
1129                                 JavaSettingsDefaults.defaultJavaFormatLeadingSpaceInComment);
1130    }
1131
1132    public boolean getFormatLeadingStarInComment() {
1133        return getSettingBoolean(JavaSettingsNames.JAVA_FORMAT_LEADING_STAR_IN_COMMENT,
1134                                 JavaSettingsDefaults.defaultJavaFormatLeadingStarInComment);
1135    }
1136
1137    private int getFormatStatementContinuationIndent() {
1138        return getSettingInteger(JavaSettingsNames.JAVA_FORMAT_STATEMENT_CONTINUATION_INDENT,
1139                                 JavaSettingsDefaults.defaultJavaFormatStatementContinuationIndent);
1140    }
1141
1142
1143    
1144    /* this is fix for bugs: 7980 and 9111. if user enters
1145     * { foo();
1146     * and press enter at the end of the line, she wants
1147     * to be indented just under "f" in "foo();" and not under the "{"
1148     * as it happens now. and this is what findLineFirstNonWhitespaceAndNonLeftBrace checks
1149     */

1150    public FormatTokenPosition findLineFirstNonWhitespaceAndNonLeftBrace(FormatTokenPosition pos) {
1151        // first call the findLineFirstNonWhitespace
1152
FormatTokenPosition ftp = super.findLineFirstNonWhitespace(pos);
1153        if (ftp == null) { // no line start, no WS
1154
return null;
1155        }
1156
1157        // now checks if the first non-whitespace char is "{"
1158
// if it is, find the next non-whitespace char
1159
if (!ftp.getToken().getImage().startsWith("{")) // NOI18N
1160
return ftp;
1161
1162        // if the left brace is closed on the same line - "{ foo(); }"
1163
// it must be ignored. otherwise next statement is incorrectly indented
1164
// under the "f" and not under the "{" as expected
1165
FormatTokenPosition eolp = findNextEOL(ftp);
1166        TokenItem rbmt = findMatchingToken(ftp.getToken(),
1167            eolp != null ? eolp.getToken() : null, JavaTokenContext.RBRACE, false);
1168        if (rbmt != null)
1169            return ftp;
1170        
1171        FormatTokenPosition ftp_next = getNextPosition(ftp);
1172        if (ftp_next == null)
1173            return ftp;
1174        
1175        FormatTokenPosition ftp2 = findImportant(ftp_next, null, true, false);
1176        if (ftp2 != null)
1177            return ftp2;
1178        else
1179            return ftp;
1180    }
1181
1182}
1183
Popular Tags