001: /*
002: * Sun Public License Notice
003: *
004: * The contents of this file are subject to the Sun Public License
005: * Version 1.0 (the "License"). You may not use this file except in
006: * compliance with the License. A copy of the License is available at
007: * http://www.sun.com/
008: *
009: * The Original Code is NetBeans. The Initial Developer of the Original
010: * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
011: * Microsystems, Inc. All Rights Reserved.
012: */
013:
014: package org.netbeans.editor.ext.java;
015:
016: import org.netbeans.editor.TokenContextPath;
017: import org.netbeans.editor.TokenID;
018: import org.netbeans.editor.TokenItem;
019: import org.netbeans.editor.ext.ExtFormatSupport;
020: import org.netbeans.editor.ext.FormatTokenPosition;
021: import org.netbeans.editor.ext.FormatWriter;
022:
023: /**
024: * Java indentation services are located here
025: *
026: * @author Miloslav Metelka
027: * @version 1.00
028: */
029:
030: public class JavaFormatSupport extends ExtFormatSupport {
031:
032: private TokenContextPath tokenContextPath;
033:
034: public JavaFormatSupport(FormatWriter formatWriter) {
035: this (formatWriter, JavaTokenContext.contextPath);
036: }
037:
038: public JavaFormatSupport(FormatWriter formatWriter,
039: TokenContextPath tokenContextPath) {
040: super (formatWriter);
041: this .tokenContextPath = tokenContextPath;
042: }
043:
044: public TokenContextPath getTokenContextPath() {
045: return tokenContextPath;
046: }
047:
048: public boolean isComment(TokenItem token, int offset) {
049: TokenID tokenID = token.getTokenID();
050: return (token.getTokenContextPath() == tokenContextPath && (tokenID == JavaTokenContext.LINE_COMMENT || tokenID == JavaTokenContext.BLOCK_COMMENT));
051: }
052:
053: public boolean isMultiLineComment(TokenItem token) {
054: return (token.getTokenID() == JavaTokenContext.BLOCK_COMMENT);
055: }
056:
057: public boolean isMultiLineComment(FormatTokenPosition pos) {
058: TokenItem token = pos.getToken();
059: return (token == null) ? false : isMultiLineComment(token);
060: }
061:
062: /**
063: * Check whether the given token is multi-line comment that starts with
064: * slash and two stars.
065: */
066: public boolean isJavaDocComment(TokenItem token) {
067: return isMultiLineComment(token)
068: && token.getImage().startsWith("/**");
069: }
070:
071: public TokenID getWhitespaceTokenID() {
072: return JavaTokenContext.WHITESPACE;
073: }
074:
075: public TokenContextPath getWhitespaceTokenContextPath() {
076: return tokenContextPath;
077: }
078:
079: public boolean canModifyWhitespace(TokenItem inToken) {
080: if (inToken.getTokenContextPath() == JavaTokenContext.contextPath) {
081: switch (inToken.getTokenID().getNumericID()) {
082: case JavaTokenContext.BLOCK_COMMENT_ID:
083: case JavaTokenContext.WHITESPACE_ID:
084: return true;
085: }
086: }
087:
088: return false;
089: }
090:
091: /**
092: * Find the starting token of the statement before the given position and
093: * also return all the command delimiters. It searches in the backward
094: * direction for all the delimiters and statement starts and return all the
095: * tokens that are either command starts or delimiters. As the first step it
096: * uses <code>getPreviousToken()</code> so it ignores the initial token.
097: *
098: * @param token
099: * token before which the statement-start and delimiter is being
100: * searched.
101: * @return token that is start of the given statement or command delimiter.
102: * If the start of the statement is not found, null is retrurned.
103: */
104: public TokenItem findStatement(TokenItem token) {
105: TokenItem lit = null; // last important token
106: TokenItem t = getPreviousToken(token);
107:
108: while (t != null) {
109: if (t.getTokenContextPath() == tokenContextPath) {
110:
111: switch (t.getTokenID().getNumericID()) {
112: case JavaTokenContext.SEMICOLON_ID:
113: if (!isForLoopSemicolon(t)) {
114: return (lit != null) ? lit : t;
115: }
116: break;
117:
118: case JavaTokenContext.LBRACE_ID:
119: case JavaTokenContext.RBRACE_ID:
120: case JavaTokenContext.COLON_ID:
121: case JavaTokenContext.ELSE_ID:
122: return (lit != null) ? lit : t;
123:
124: case JavaTokenContext.DO_ID:
125: case JavaTokenContext.SWITCH_ID:
126: case JavaTokenContext.CASE_ID:
127: case JavaTokenContext.DEFAULT_ID:
128: return t;
129:
130: case JavaTokenContext.FOR_ID:
131: case JavaTokenContext.IF_ID:
132: case JavaTokenContext.WHILE_ID:
133: /*
134: * Try to find the statement after ( ... ) If it exists,
135: * then the first important token after it is the stmt
136: * start. Otherwise it's this token.
137: */
138: if (lit != null
139: && lit.getTokenID() == JavaTokenContext.LPAREN) {
140: // Find matching right paren in fwd dir
141: TokenItem mt = findMatchingToken(lit, token,
142: JavaTokenContext.RPAREN, false);
143: if (mt != null && mt.getNext() != null) {
144: mt = findImportantToken(mt.getNext(),
145: token, false);
146: if (mt != null) {
147: return mt;
148: }
149: }
150: }
151:
152: // No further stmt found, return this one
153: return t;
154:
155: }
156:
157: // Remember last important token
158: if (isImportant(t, 0)) {
159: lit = t;
160: }
161:
162: }
163:
164: t = t.getPrevious();
165: }
166:
167: return lit;
168: }
169:
170: /**
171: * Find the 'if' when the 'else' is provided.
172: *
173: * @param elseToken
174: * the token with the 'else' command for which the 'if' is being
175: * searched.
176: * @return corresponding 'if' token or null if there's no corresponding 'if'
177: * statement.
178: */
179: public TokenItem findIf(TokenItem elseToken) {
180: if (elseToken == null
181: || !tokenEquals(elseToken, JavaTokenContext.ELSE,
182: tokenContextPath)) {
183: throw new IllegalArgumentException("Only accept 'else'.");
184: }
185:
186: int braceDepth = 0; // depth of the braces
187: int elseDepth = 0; // depth of multiple else stmts
188: while (true) {
189: elseToken = findStatement(elseToken);
190: if (elseToken == null) {
191: return null;
192: }
193:
194: switch (elseToken.getTokenID().getNumericID()) {
195: case JavaTokenContext.LBRACE_ID:
196: if (--braceDepth < 0) {
197: return null; // no corresponding right brace
198: }
199: break;
200:
201: case JavaTokenContext.RBRACE_ID:
202: braceDepth++;
203: break;
204:
205: case JavaTokenContext.ELSE_ID:
206: if (braceDepth == 0) {
207: elseDepth++;
208: }
209: break;
210:
211: case JavaTokenContext.SEMICOLON_ID:
212: case JavaTokenContext.COLON_ID:
213: case JavaTokenContext.DO_ID:
214: case JavaTokenContext.CASE_ID:
215: case JavaTokenContext.DEFAULT_ID:
216: case JavaTokenContext.FOR_ID:
217: case JavaTokenContext.WHILE_ID:
218: break;
219:
220: case JavaTokenContext.IF_ID:
221: if (braceDepth == 0) {
222: if (elseDepth-- == 0) {
223: return elseToken; // successful search
224: }
225: }
226: break;
227: }
228: }
229: }
230:
231: /**
232: * Find the 'switch' when the 'case' is provided.
233: *
234: * @param caseToken
235: * the token with the 'case' command for which the 'switch' is
236: * being searched.
237: * @return corresponding 'switch' token or null if there's no corresponding
238: * 'switch' statement.
239: */
240: public TokenItem findSwitch(TokenItem caseToken) {
241: if (caseToken == null
242: || (!tokenEquals(caseToken, JavaTokenContext.CASE,
243: tokenContextPath) && !tokenEquals(caseToken,
244: JavaTokenContext.DEFAULT, tokenContextPath))) {
245: throw new IllegalArgumentException(
246: "Only accept 'case' or 'default'.");
247: }
248:
249: int braceDepth = 1; // depth of the braces - need one more left
250: while (true) {
251: caseToken = findStatement(caseToken);
252: if (caseToken == null) {
253: return null;
254: }
255:
256: switch (caseToken.getTokenID().getNumericID()) {
257: case JavaTokenContext.LBRACE_ID:
258: if (--braceDepth < 0) {
259: return null; // no corresponding right brace
260: }
261: break;
262:
263: case JavaTokenContext.RBRACE_ID:
264: braceDepth++;
265: break;
266:
267: case JavaTokenContext.SWITCH_ID:
268: case JavaTokenContext.DEFAULT_ID:
269: if (braceDepth == 0) {
270: return caseToken;
271: }
272: break;
273: }
274: }
275: }
276:
277: /**
278: * Find the 'try' when the 'catch' is provided.
279: *
280: * @param catchToken
281: * the token with the 'catch' command for which the 'try' is
282: * being searched.
283: * @return corresponding 'try' token or null if there's no corresponding
284: * 'try' statement.
285: */
286: public TokenItem findTry(TokenItem catchToken) {
287: if (catchToken == null
288: || (!tokenEquals(catchToken, JavaTokenContext.CATCH,
289: tokenContextPath))) {
290: throw new IllegalArgumentException("Only accept 'catch'.");
291: }
292:
293: int braceDepth = 0; // depth of the braces
294: while (true) {
295: catchToken = findStatement(catchToken);
296: if (catchToken == null) {
297: return null;
298: }
299:
300: switch (catchToken.getTokenID().getNumericID()) {
301: case JavaTokenContext.LBRACE_ID:
302: if (--braceDepth < 0) {
303: return null; // no corresponding right brace
304: }
305: break;
306:
307: case JavaTokenContext.RBRACE_ID:
308: braceDepth++;
309: break;
310:
311: case JavaTokenContext.TRY_ID:
312: if (braceDepth == 0) {
313: return catchToken;
314: }
315: break;
316: }
317: }
318: }
319:
320: /**
321: * Find the start of the statement.
322: *
323: * @param token
324: * token from which to start. It searches backward using
325: * <code>findStatement()</code> so it ignores the given token.
326: * @return the statement start token (outer statement start for nested
327: * statements). It returns the same token if there is '{' before the
328: * given token.
329: */
330: public TokenItem findStatementStart(TokenItem token) {
331: TokenItem t = findStatement(token);
332: if (t != null) {
333: switch (t.getTokenID().getNumericID()) {
334: case JavaTokenContext.SEMICOLON_ID: // ';' found
335: TokenItem scss = findStatement(t);
336:
337: // fix for issue 14274
338: if (scss == null)
339: return token;
340:
341: switch (scss.getTokenID().getNumericID()) {
342: case JavaTokenContext.LBRACE_ID: // '{' then ';'
343: case JavaTokenContext.RBRACE_ID: // '}' then ';'
344: case JavaTokenContext.COLON_ID: // ':' then ';'
345: case JavaTokenContext.CASE_ID: // 'case' then ';'
346: case JavaTokenContext.DEFAULT_ID:
347: case JavaTokenContext.SEMICOLON_ID: // ';' then ';'
348: return t; // return ';'
349:
350: case JavaTokenContext.DO_ID:
351: case JavaTokenContext.FOR_ID:
352: case JavaTokenContext.IF_ID:
353: case JavaTokenContext.WHILE_ID:
354: return findStatementStart(t);
355:
356: case JavaTokenContext.ELSE_ID: // 'else' then ';'
357: // Find the corresponding 'if'
358: TokenItem ifss = findIf(scss);
359: if (ifss != null) { // 'if' ... 'else' then ';'
360: return findStatementStart(ifss);
361:
362: } else { // no valid starting 'if'
363: return scss; // return 'else'
364: }
365:
366: default: // something usual then ';'
367: TokenItem bscss = findStatement(scss);
368: if (bscss != null) {
369: switch (bscss.getTokenID().getNumericID()) {
370: case JavaTokenContext.SEMICOLON_ID: // ';' then stmt
371: // ending with ';'
372: case JavaTokenContext.LBRACE_ID:
373: case JavaTokenContext.RBRACE_ID:
374: case JavaTokenContext.COLON_ID:
375: return scss; //
376:
377: case JavaTokenContext.DO_ID:
378: case JavaTokenContext.FOR_ID:
379: case JavaTokenContext.IF_ID:
380: case JavaTokenContext.WHILE_ID:
381: return findStatementStart(bscss);
382:
383: case JavaTokenContext.ELSE_ID:
384: // Find the corresponding 'if'
385: ifss = findIf(bscss);
386: if (ifss != null) { // 'if' ... 'else' ... ';'
387: return findStatementStart(ifss);
388:
389: } else { // no valid starting 'if'
390: return bscss; // return 'else'
391: }
392: }
393: }
394:
395: return scss;
396: } // semicolon servicing end
397:
398: case JavaTokenContext.LBRACE_ID: // '{' found
399: return token; // return original token
400:
401: case JavaTokenContext.RBRACE_ID: // '}' found
402: TokenItem lb = findMatchingToken(t, null,
403: JavaTokenContext.LBRACE, true);
404: if (lb != null) { // valid matching left-brace
405: // Find a stmt-start of the '{'
406: TokenItem lbss = findStatement(lb);
407: if (lbss != null) {
408: switch (lbss.getTokenID().getNumericID()) {
409: case JavaTokenContext.ELSE_ID: // 'else {'
410: // Find the corresponding 'if'
411: TokenItem ifss = findIf(lbss);
412: if (ifss != null) { // valid 'if'
413: return findStatementStart(ifss);
414: } else {
415: return lbss; // return 'else'
416: }
417:
418: case JavaTokenContext.CATCH_ID: // 'catch (...) {'
419: // Find the corresponding 'try'
420: TokenItem tryss = findTry(lbss);
421: if (tryss != null) { // valid 'try'
422: return findStatementStart(tryss);
423: } else {
424: return lbss; // return 'catch'
425: }
426:
427: case JavaTokenContext.DO_ID:
428: case JavaTokenContext.FOR_ID:
429: case JavaTokenContext.IF_ID:
430: case JavaTokenContext.WHILE_ID:
431: return findStatementStart(lbss);
432:
433: }
434:
435: // another hack to prevent problem described in issue
436: // 17033
437: if (lbss.getTokenID().getNumericID() == JavaTokenContext.LBRACE_ID) {
438: return t; // return right brace
439: }
440:
441: return lbss;
442: }
443:
444: }
445: return t; // return right brace
446:
447: case JavaTokenContext.COLON_ID:
448: case JavaTokenContext.CASE_ID:
449: case JavaTokenContext.DEFAULT_ID:
450: return token;
451:
452: case JavaTokenContext.ELSE_ID:
453: // Find the corresponding 'if'
454: TokenItem ifss = findIf(t);
455: return (ifss != null) ? findStatementStart(ifss) : t;
456:
457: case JavaTokenContext.DO_ID:
458: case JavaTokenContext.FOR_ID:
459: case JavaTokenContext.IF_ID:
460: case JavaTokenContext.WHILE_ID:
461: return findStatementStart(t);
462: }
463: }
464:
465: return token; // return original token
466: }
467:
468: /**
469: * Get the indentation for the given token. It first searches whether
470: * there's an non-whitespace and a non-leftbrace character on the line with
471: * the token and if so, it takes indent of the non-ws char instead.
472: *
473: * @param token
474: * token for which the indent is being searched. The token itself
475: * is ignored and the previous token is used as a base for the
476: * search.
477: * @param forceFirstNonWhitespace
478: * set true to ignore leftbrace and search directly for first
479: * non-whitespace
480: */
481: public int getTokenIndent(TokenItem token,
482: boolean forceFirstNonWhitespace) {
483: FormatTokenPosition tp = getPosition(token, 0);
484: // this is fix for bugs: 7980 and 9111
485: // see the findLineFirstNonWhitespaceAndNonLeftBrace definition
486: // for more info about the fix
487: FormatTokenPosition fnw;
488: if (forceFirstNonWhitespace)
489: fnw = findLineFirstNonWhitespace(tp);
490: else
491: fnw = findLineFirstNonWhitespaceAndNonLeftBrace(tp);
492:
493: if (fnw != null) { // valid first non-whitespace
494: tp = fnw;
495: }
496: return getVisualColumnOffset(tp);
497: }
498:
499: public int getTokenIndent(TokenItem token) {
500: return getTokenIndent(token, false);
501: }
502:
503: /**
504: * Find the indentation for the first token on the line. The given token is
505: * also examined in some cases.
506: */
507: public int findIndent(TokenItem token) {
508: int indent = -1; // assign invalid indent
509:
510: // First check the given token
511: if (token != null) {
512: switch (token.getTokenID().getNumericID()) {
513: case JavaTokenContext.ELSE_ID:
514: TokenItem ifss = findIf(token);
515: if (ifss != null) {
516: indent = getTokenIndent(ifss);
517: }
518: break;
519:
520: case JavaTokenContext.LBRACE_ID:
521: TokenItem stmt = findStatement(token);
522: if (stmt == null) {
523: indent = 0;
524:
525: } else {
526: switch (stmt.getTokenID().getNumericID()) {
527: case JavaTokenContext.DO_ID:
528: case JavaTokenContext.FOR_ID:
529: case JavaTokenContext.IF_ID:
530: case JavaTokenContext.WHILE_ID:
531: case JavaTokenContext.ELSE_ID:
532: indent = getTokenIndent(stmt);
533: break;
534:
535: case JavaTokenContext.LBRACE_ID:
536: indent = getTokenIndent(stmt) + getShiftWidth();
537: break;
538:
539: default:
540: stmt = findStatementStart(token);
541: if (stmt == null) {
542: indent = 0;
543:
544: } else if (stmt == token) {
545: stmt = findStatement(token); // search for
546: // delimiter
547: indent = (stmt != null) ? indent = getTokenIndent(stmt)
548: : 0;
549:
550: } else { // valid statement
551: indent = getTokenIndent(stmt);
552: switch (stmt.getTokenID().getNumericID()) {
553: case JavaTokenContext.LBRACE_ID:
554: indent += getShiftWidth();
555: break;
556: }
557: }
558: }
559: }
560: break;
561:
562: case JavaTokenContext.RBRACE_ID:
563: TokenItem rbmt = findMatchingToken(token, null,
564: JavaTokenContext.LBRACE, true);
565: if (rbmt != null) { // valid matching left-brace
566: TokenItem t = findStatement(rbmt);
567: boolean forceFirstNonWhitespace = false;
568: if (t == null) {
569: t = rbmt; // will get indent of the matching brace
570:
571: } else {
572: switch (t.getTokenID().getNumericID()) {
573: case JavaTokenContext.SEMICOLON_ID:
574: case JavaTokenContext.LBRACE_ID:
575: case JavaTokenContext.RBRACE_ID: {
576: t = rbmt;
577: forceFirstNonWhitespace = true;
578: }
579: }
580: }
581: // the right brace must be indented to the first
582: // non-whitespace char - forceFirstNonWhitespace=true
583: indent = getTokenIndent(t, forceFirstNonWhitespace);
584:
585: } else { // no matching left brace
586: indent = getTokenIndent(token); // leave as is
587: }
588: break;
589:
590: case JavaTokenContext.CASE_ID:
591: case JavaTokenContext.DEFAULT_ID:
592: TokenItem swss = findSwitch(token);
593: if (swss != null) {
594: indent = getTokenIndent(swss) + getShiftWidth();
595: }
596: break;
597:
598: }
599: }
600:
601: // If indent not found, search back for the first important token
602: if (indent < 0) { // if not yet resolved
603: TokenItem t = findImportantToken(token, null, true);
604: if (t != null) { // valid important token
605: switch (t.getTokenID().getNumericID()) {
606: case JavaTokenContext.SEMICOLON_ID: // semicolon found
607: TokenItem tt = findStatementStart(token);
608: indent = getTokenIndent(tt);
609:
610: break;
611:
612: case JavaTokenContext.LBRACE_ID:
613: TokenItem lbss = findStatementStart(t);
614: indent = getTokenIndent(t) + getShiftWidth();
615: break;
616:
617: case JavaTokenContext.RBRACE_ID:
618: if (true) {
619: TokenItem t3 = findStatementStart(token);
620: indent = getTokenIndent(t3);
621: break;
622: }
623:
624: /**
625: * Check whether the following situation occurs: if (t1) if
626: * (t2) { ... }
627: *
628: * In this case the indentation must be shifted one level
629: * back.
630: */
631: TokenItem rbmt = findMatchingToken(t, null,
632: JavaTokenContext.LBRACE, true);
633: if (rbmt != null) { // valid matching left-brace
634: // Check whether there's a indent stmt
635: TokenItem t6 = findStatement(rbmt);
636: if (t6 != null) {
637: switch (t6.getTokenID().getNumericID()) {
638: case JavaTokenContext.ELSE_ID:
639: /*
640: * Check the following situation: if (t1) if
641: * (t2) c1(); else { c2(); }
642: */
643:
644: // Find the corresponding 'if'
645: t6 = findIf(t6);
646: if (t6 != null) { // valid 'if'
647: TokenItem t7 = findStatement(t6);
648: if (t7 != null) {
649: switch (t7.getTokenID()
650: .getNumericID()) {
651: case JavaTokenContext.DO_ID:
652: case JavaTokenContext.FOR_ID:
653: case JavaTokenContext.IF_ID:
654: case JavaTokenContext.WHILE_ID:
655: indent = getTokenIndent(t7);
656: break;
657:
658: case JavaTokenContext.ELSE_ID:
659: indent = getTokenIndent(findStatementStart(t6));
660: }
661: }
662: }
663: break;
664:
665: case JavaTokenContext.DO_ID:
666: case JavaTokenContext.FOR_ID:
667: case JavaTokenContext.IF_ID:
668: case JavaTokenContext.WHILE_ID:
669: /*
670: * Check the following: if (t1) if (t2) { c1(); }
671: */
672: TokenItem t7 = findStatement(t6);
673: if (t7 != null) {
674: switch (t7.getTokenID()
675: .getNumericID()) {
676: case JavaTokenContext.DO_ID:
677: case JavaTokenContext.FOR_ID:
678: case JavaTokenContext.IF_ID:
679: case JavaTokenContext.WHILE_ID:
680: indent = getTokenIndent(t7);
681: break;
682:
683: case JavaTokenContext.ELSE_ID:
684: indent = getTokenIndent(findStatementStart(t6));
685:
686: }
687: }
688: break;
689:
690: case JavaTokenContext.LBRACE_ID: // '{' ... '{'
691: indent = getTokenIndent(rbmt);
692: break;
693:
694: }
695:
696: }
697:
698: if (indent < 0) {
699: indent = getTokenIndent(t); // indent of original
700: // rbrace
701: }
702:
703: } else { // no matching left-brace
704: indent = getTokenIndent(t); // return indent of '}'
705: }
706: break;
707:
708: case JavaTokenContext.RPAREN_ID:
709: // Try to find the matching left paren
710: TokenItem rpmt = findMatchingToken(t, null,
711: JavaTokenContext.LPAREN, true);
712: if (rpmt != null) {
713: rpmt = findImportantToken(rpmt, null, true);
714: // Check whether there are the indent changing kwds
715: if (rpmt != null
716: && rpmt.getTokenContextPath() == tokenContextPath) {
717: switch (rpmt.getTokenID().getNumericID()) {
718: case JavaTokenContext.FOR_ID:
719: case JavaTokenContext.IF_ID:
720: case JavaTokenContext.WHILE_ID:
721: // Indent one level
722: indent = getTokenIndent(rpmt)
723: + getShiftWidth();
724: break;
725: }
726: }
727: }
728: break;
729:
730: case JavaTokenContext.COLON_ID:
731: // Indent of line with ':' plus one indent level
732: indent = getTokenIndent(t) + getShiftWidth();
733: break;
734:
735: case JavaTokenContext.DO_ID:
736: case JavaTokenContext.ELSE_ID:
737: indent = getTokenIndent(t) + getShiftWidth();
738: break;
739:
740: }
741:
742: if (indent < 0) { // no indent found yet
743: indent = getTokenIndent(t);
744: }
745: }
746: }
747:
748: if (indent < 0) { // no important token found
749: indent = 0;
750: }
751:
752: return indent;
753: }
754:
755: public FormatTokenPosition indentLine(FormatTokenPosition pos) {
756: int indent = 0; // Desired indent
757:
758: // Get the first non-whitespace position on the line
759: FormatTokenPosition firstNWS = findLineFirstNonWhitespace(pos);
760: if (firstNWS != null) { // some non-WS on the line
761: if (isComment(firstNWS)) { // comment is first on the line
762: if (isMultiLineComment(firstNWS)
763: && firstNWS.getOffset() != 0) {
764:
765: // Indent the inner lines of the multi-line comment by one
766: indent = getLineIndent(getPosition(firstNWS
767: .getToken(), 0), true) + 1;
768:
769: // If the line is inside multi-line comment and doesn't
770: // contain '*'
771: if (!isIndentOnly() && getChar(firstNWS) != '*') {
772: if (isJavaDocComment(firstNWS.getToken())) {
773: if (getFormatLeadingStarInComment()) {
774: // For java-doc it should be OK to add the star
775: insertString(firstNWS, "* ");
776: }
777:
778: } else {
779: // For non-java-doc not because it can be commented
780: // code
781: indent = getLineIndent(pos, true);
782: }
783: }
784:
785: } else if (!isMultiLineComment(firstNWS)) { // line-comment
786: indent = findIndent(firstNWS.getToken());
787: } else { // multi-line comment
788: if (isJavaDocComment(firstNWS.getToken())) {
789: indent = findIndent(firstNWS.getToken());
790: } else {
791: // check whether the multiline comment isn't finished on
792: // the same line (see issue 12821)
793: if (firstNWS.getToken().getImage()
794: .indexOf('\n') == -1)
795: indent = findIndent(firstNWS.getToken());
796: else
797: indent = getLineIndent(firstNWS, true);
798: }
799: }
800:
801: } else { // first non-WS char is not comment
802: indent = findIndent(firstNWS.getToken());
803: }
804:
805: } else { // whole line is WS
806: // Can be empty line inside multi-line comment
807: TokenItem token = pos.getToken();
808: if (token == null) {
809: token = findLineStart(pos).getToken();
810: if (token == null) { // empty line
811: token = getLastToken();
812: }
813: }
814:
815: if (token != null && isMultiLineComment(token)) {
816: if (getFormatLeadingStarInComment()
817: && (isIndentOnly() || isJavaDocComment(token))) {
818: // Insert initial '*'
819: insertString(pos, "*");
820: setIndentShift(1);
821: }
822:
823: // Indent the multi-comment by one more space
824: indent = getVisualColumnOffset(getPosition(token, 0)) + 1;
825:
826: } else { // non-multi-line comment
827: indent = findIndent(pos.getToken());
828: }
829: }
830:
831: // For indent-only always indent
832: return changeLineIndent(pos, indent);
833: }
834:
835: /**
836: * Check whether the given semicolon is inside the for() statement.
837: *
838: * @param token
839: * token to check. It must be a semicolon.
840: * @return true if the given semicolon is inside the for() statement, or
841: * false otherwise.
842: */
843: public boolean isForLoopSemicolon(TokenItem token) {
844: if (token == null
845: || !tokenEquals(token, JavaTokenContext.SEMICOLON,
846: tokenContextPath)) {
847: throw new IllegalArgumentException("Only accept ';'.");
848: }
849:
850: int parDepth = 0; // parenthesis depth
851: int braceDepth = 0; // brace depth
852: boolean semicolonFound = false; // next semicolon
853: token = token.getPrevious(); // ignore this semicolon
854: while (token != null) {
855: if (tokenEquals(token, JavaTokenContext.LPAREN,
856: tokenContextPath)) {
857: if (parDepth == 0) { // could be a 'for ('
858: FormatTokenPosition tp = getPosition(token, 0);
859: tp = findImportant(tp, null, false, true);
860: if (tp != null
861: && tokenEquals(tp.getToken(),
862: JavaTokenContext.FOR,
863: tokenContextPath)) {
864: return true;
865: }
866: return false;
867:
868: } else { // non-zero depth
869: parDepth--;
870: }
871:
872: } else if (tokenEquals(token, JavaTokenContext.RPAREN,
873: tokenContextPath)) {
874: parDepth++;
875:
876: } else if (tokenEquals(token, JavaTokenContext.LBRACE,
877: tokenContextPath)) {
878: if (braceDepth == 0) { // unclosed left brace
879: return false;
880: }
881: braceDepth--;
882:
883: } else if (tokenEquals(token, JavaTokenContext.RBRACE,
884: tokenContextPath)) {
885: braceDepth++;
886:
887: } else if (tokenEquals(token, JavaTokenContext.SEMICOLON,
888: tokenContextPath)) {
889: if (semicolonFound) { // one semicolon already found
890: return false;
891: }
892: semicolonFound = true;
893: }
894:
895: token = token.getPrevious();
896: }
897:
898: return false;
899: }
900:
901: public boolean getFormatSpaceBeforeParenthesis() {
902: return getSettingBoolean(
903: JavaSettingsNames.JAVA_FORMAT_SPACE_BEFORE_PARENTHESIS,
904: JavaSettingsDefaults.defaultJavaFormatSpaceBeforeParenthesis);
905: }
906:
907: public boolean getFormatSpaceAfterComma() {
908: return getSettingBoolean(
909: JavaSettingsNames.JAVA_FORMAT_SPACE_AFTER_COMMA,
910: JavaSettingsDefaults.defaultJavaFormatSpaceAfterComma);
911: }
912:
913: public boolean getFormatNewlineBeforeBrace() {
914: return getSettingBoolean(
915: JavaSettingsNames.JAVA_FORMAT_NEWLINE_BEFORE_BRACE,
916: JavaSettingsDefaults.defaultJavaFormatNewlineBeforeBrace);
917: }
918:
919: public boolean getFormatLeadingSpaceInComment() {
920: return getSettingBoolean(
921: JavaSettingsNames.JAVA_FORMAT_LEADING_SPACE_IN_COMMENT,
922: JavaSettingsDefaults.defaultJavaFormatLeadingSpaceInComment);
923: }
924:
925: public boolean getFormatLeadingStarInComment() {
926: return getSettingBoolean(
927: JavaSettingsNames.JAVA_FORMAT_LEADING_STAR_IN_COMMENT,
928: JavaSettingsDefaults.defaultJavaFormatLeadingStarInComment);
929: }
930:
931: /*
932: * this is fix for bugs: 7980 and 9111. if user enters { foo(); and press
933: * enter at the end of the line, she wants to be indented just under "f" in
934: * "foo();" and not under the "{" as it happens now. and this is what
935: * findLineFirstNonWhitespaceAndNonLeftBrace checks
936: */
937: public FormatTokenPosition findLineFirstNonWhitespaceAndNonLeftBrace(
938: FormatTokenPosition pos) {
939: // first call the findLineFirstNonWhitespace
940: FormatTokenPosition ftp = super .findLineFirstNonWhitespace(pos);
941: if (ftp == null) { // no line start, no WS
942: return null;
943: }
944:
945: // now checks if the first non-whitespace char is "{"
946: // if it is, find the next non-whitespace char
947: if (!ftp.getToken().getImage().startsWith("{"))
948: return ftp;
949:
950: // if the left brace is closed on the same line - "{ foo(); }"
951: // it must be ignored. otherwise next statement is incorrectly indented
952: // under the "f" and not under the "{" as expected
953: FormatTokenPosition eolp = findNextEOL(ftp);
954: TokenItem rbmt = findMatchingToken(ftp.getToken(),
955: eolp != null ? eolp.getToken() : null,
956: JavaTokenContext.RBRACE, false);
957: if (rbmt != null)
958: return ftp;
959:
960: FormatTokenPosition ftp_next = getNextPosition(ftp);
961: if (ftp_next == null)
962: return ftp;
963:
964: FormatTokenPosition ftp2 = findImportant(ftp_next, null, true,
965: false);
966: if (ftp2 != null)
967: return ftp2;
968: else
969: return ftp;
970: }
971:
972: }
|