0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 2001-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.cnd.editor.cplusplus;
0043:
0044: import org.netbeans.editor.TokenID;
0045: import org.netbeans.editor.TokenItem;
0046: import org.netbeans.editor.TokenContextPath;
0047: import org.netbeans.editor.ext.FormatTokenPosition;
0048: import org.netbeans.editor.ext.ExtFormatSupport;
0049: import org.netbeans.editor.ext.FormatWriter;
0050: import org.netbeans.modules.cnd.editor.api.CodeStyle;
0051: import org.openide.util.NbBundle;
0052:
0053: /**
0054: * CC indentation services are located here
0055: *
0056: * duped from editor/libsrc/org/netbeans/editor/ext/java/JavaFormatSupport.java
0057: */
0058: public class CCFormatSupport extends ExtFormatSupport {
0059:
0060: private TokenContextPath tokenContextPath;
0061: private CodeStyle.Language language;
0062:
0063: public CCFormatSupport(CodeStyle.Language language,
0064: FormatWriter formatWriter) {
0065: this (language, formatWriter, CCTokenContext.contextPath);
0066: }
0067:
0068: private CCFormatSupport(CodeStyle.Language language,
0069: FormatWriter formatWriter, TokenContextPath tokenContextPath) {
0070: super (formatWriter);
0071: this .tokenContextPath = tokenContextPath;
0072: this .language = language;
0073: }
0074:
0075: public TokenContextPath getTokenContextPath() {
0076: return tokenContextPath;
0077: }
0078:
0079: @Override
0080: public boolean isComment(TokenItem token, int offset) {
0081: TokenID tokenID = token.getTokenID();
0082: return (token.getTokenContextPath() == tokenContextPath && (tokenID == CCTokenContext.LINE_COMMENT || tokenID == CCTokenContext.BLOCK_COMMENT));
0083: }
0084:
0085: /** Is given token a preprocessor **/
0086: public boolean isPreprocessorAtLineStart(TokenItem token) {
0087: if (getFormatSpaceBeforeMethodCallParenthesis())
0088: return false;
0089: if (token != null) {
0090: FormatTokenPosition ft = findLineFirstNonWhitespace(getPosition(
0091: token, 0));
0092: if (ft != null) {
0093: token = ft.getToken();
0094: }
0095: }
0096: return (token != null && token.getImage().startsWith("#")); // NOI18N
0097: //&& getVisualColumnOffset(getPosition(token,0)) == 0);
0098: }
0099:
0100: /** Is given token a preprocessor **/
0101: public boolean isPreprocessorLine(TokenItem token) {
0102: if (token != null) {
0103: FormatTokenPosition ft = findLineFirstNonWhitespace(getPosition(
0104: token, 0));
0105: if (ft != null) {
0106: if (isEndBackSlashedLine(ft)) {
0107: FormatTokenPosition eol = findPreviousEOL(ft);
0108: ft = eol;
0109: while (eol != null) {
0110: if (isBackSlashEndLine(eol)) {
0111: ft = eol;
0112: eol = findPreviousEOL(eol);
0113: } else {
0114: break;
0115: }
0116: }
0117: ft = findLineFirstNonWhitespace(ft);
0118: } else if (isBackSlashEndLine(ft)) {
0119: FormatTokenPosition eol = ft;
0120: eol = findPreviousEOL(eol);
0121: while (eol != null) {
0122: if (isBackSlashEndLine(eol)) {
0123: ft = eol;
0124: eol = findPreviousEOL(eol);
0125: } else {
0126: break;
0127: }
0128: }
0129: ft = findLineFirstNonWhitespace(ft);
0130: }
0131: if (ft != null) {
0132: token = ft.getToken();
0133: }
0134: }
0135: }
0136: return (token != null && token.getImage().startsWith("#")); // NOI18N
0137: }
0138:
0139: /** Return the ending non whitespace on the line or null
0140: * if there's no such token on the given line.
0141: */
0142: public boolean isEndBackSlashedLine(FormatTokenPosition pos) {
0143: FormatTokenPosition eol = findPreviousEOL(pos);
0144: if (eol != null) {
0145: TokenItem token = eol.getToken();
0146: if (token != null) {
0147: eol = findLineFirstNonWhitespace(getPosition(token, 0));
0148: if (eol != null) {
0149: return isBackSlashEndLine(eol);
0150: }
0151: }
0152: }
0153: return false;
0154: }
0155:
0156: /** Return the ending non whitespace on the line or null
0157: * if there's no such token on the given line.
0158: */
0159: public boolean isBackSlashEndLine(FormatTokenPosition pos) {
0160: FormatTokenPosition eol = findLineEndNonImportant(pos);
0161: if (eol != null) {
0162: TokenItem token = eol.getToken();
0163: if (token != null) {
0164: if (token.getTokenID() == CCTokenContext.BACKSLASH
0165: || token.getTokenID() == CCTokenContext.INVALID_BACKSLASH) {
0166: return true;
0167: }
0168: token = token.getPrevious();
0169: return token != null
0170: && (token.getTokenID() == CCTokenContext.BACKSLASH || token
0171: .getTokenID() == CCTokenContext.INVALID_BACKSLASH);
0172: }
0173: }
0174: return false;
0175: }
0176:
0177: public boolean isMultiLineComment(TokenItem token) {
0178: return (token.getTokenID() == CCTokenContext.BLOCK_COMMENT);
0179: }
0180:
0181: public boolean isMultiLineComment(FormatTokenPosition pos) {
0182: TokenItem token = pos.getToken();
0183: return (token == null) ? false : isMultiLineComment(token);
0184: }
0185:
0186: /** Check whether the given token is multi-line comment
0187: * that starts with a slash and an asterisk.
0188: */
0189: public boolean isCCDocComment(TokenItem token) {
0190: return isMultiLineComment(token)
0191: && token.getImage().startsWith("/*");
0192: }
0193:
0194: @Override
0195: public TokenID getWhitespaceTokenID() {
0196: return CCTokenContext.WHITESPACE;
0197: }
0198:
0199: @Override
0200: public TokenContextPath getWhitespaceTokenContextPath() {
0201: return tokenContextPath;
0202: }
0203:
0204: @Override
0205: public boolean canModifyWhitespace(TokenItem inToken) {
0206: if (inToken.getTokenContextPath() == CCTokenContext.contextPath) {
0207: switch (inToken.getTokenID().getNumericID()) {
0208: case CCTokenContext.BLOCK_COMMENT_ID:
0209: case CCTokenContext.WHITESPACE_ID:
0210: return true;
0211: }
0212: }
0213: return false;
0214: }
0215:
0216: /** Find the starting token of the statement before
0217: * the given position and also return all the command
0218: * delimiters. It searches in the backward direction
0219: * for all the delimiters and statement starts and
0220: * return all the tokens that are either command starts
0221: * or delimiters. As the first step it uses
0222: * <code>getPreviousToken()</code> so it ignores the initial token.
0223: * @param token token before which the statement-start
0224: * and delimiter is being searched.
0225: * @return token that is start of the given statement
0226: * or command delimiter.
0227: * If the start of the statement is not found, null is returned.
0228: */
0229: public TokenItem findStatement(TokenItem token) {
0230: TokenItem lit = null; // last important token
0231: boolean firstColon = true;
0232: TokenItem t = getPreviousToken(token);
0233:
0234: while (t != null) {
0235: if (t.getTokenContextPath() == tokenContextPath) {
0236:
0237: switch (t.getTokenID().getNumericID()) {
0238: case CCTokenContext.SEMICOLON_ID:
0239: if (!isForLoopSemicolon(t)) {
0240: return (lit != null) ? lit : t;
0241: }
0242: break;
0243:
0244: case CCTokenContext.LBRACE_ID:
0245: case CCTokenContext.ELSE_ID:
0246: return (lit != null) ? lit : t;
0247:
0248: case CCTokenContext.RBRACE_ID:
0249: // Check whether this is an array initialization block
0250: if (!isArrayInitializationBraceBlock(t, null)) {
0251: return (lit != null) ? lit : t;
0252: } else { // skip the array initialization block
0253: t = findMatchingToken(t, null,
0254: CCTokenContext.LBRACE, true);
0255: }
0256: break;
0257:
0258: case CCTokenContext.COLON_ID:
0259: TokenItem tt = findAnyToken(t, null, new TokenID[] {
0260: CCTokenContext.CASE,
0261: CCTokenContext.DEFAULT,
0262: CCTokenContext.QUESTION }, t
0263: .getTokenContextPath(), true);
0264: if (tt != null) {
0265: switch (tt.getTokenID().getNumericID()) {
0266: case CCTokenContext.CASE_ID:
0267: case CCTokenContext.DEFAULT_ID:
0268: return (lit != null) ? lit : t;
0269: }
0270: }
0271: TokenItem prev = findImportantToken(t, null, true,
0272: true);
0273: if (prev != null
0274: && prev.getTokenID().getNumericID() == CCTokenContext.RPAREN_ID) {
0275: t = prev;
0276: break;
0277: }
0278: if (lit != null && firstColon && tt == null) {
0279: return lit;
0280: }
0281: firstColon = false;
0282: break;
0283:
0284: case CCTokenContext.DO_ID:
0285: case CCTokenContext.SWITCH_ID:
0286: case CCTokenContext.CASE_ID:
0287: case CCTokenContext.DEFAULT_ID:
0288: return t;
0289:
0290: case CCTokenContext.FOR_ID:
0291: case CCTokenContext.IF_ID:
0292: case CCTokenContext.WHILE_ID:
0293: /* Try to find the statement after ( ... )
0294: * If it exists, then the first important
0295: * token after it is the stmt start. Otherwise
0296: * it's this token.
0297: */
0298: if (lit != null
0299: && lit.getTokenID() == CCTokenContext.LPAREN) {
0300: // Find matching right paren in fwd dir
0301: TokenItem mt = findMatchingToken(lit, token,
0302: CCTokenContext.RPAREN, false);
0303: if (mt != null && mt.getNext() != null) {
0304: mt = findImportantToken(mt.getNext(),
0305: token, false, true);
0306: if (mt != null) {
0307: return mt;
0308: }
0309: }
0310: }
0311: // No further stmt found, return this one
0312: return t;
0313: }
0314: // Remember last important token (preprocessor token are not important (?) (4922370))
0315: if (!isPreprocessorLine(t) && isImportant(t, 0)) {
0316: lit = t;
0317: }
0318: }
0319: t = t.getPrevious();
0320: }
0321: return lit;
0322: }
0323:
0324: /**
0325: * Find the 'if' when the 'else' is provided.
0326: * @param elseToken the token with the 'else' command
0327: * for which the 'if' is being searched.
0328: * @return corresponding 'if' token or null if there's
0329: * no corresponding 'if' statement.
0330: */
0331: public TokenItem findIf(TokenItem elseToken) {
0332: if (elseToken == null
0333: || !tokenEquals(elseToken, CCTokenContext.ELSE,
0334: tokenContextPath)) {
0335: throw new IllegalArgumentException(NbBundle.getBundle(
0336: CCFormatSupport.class).getString(
0337: "MSG_AcceptOnlyElse")); //NOI18N
0338: }
0339:
0340: int braceDepth = 0; // depth of the braces
0341: int elseDepth = 0; // depth of multiple else stmts
0342: while (true) {
0343: elseToken = findStatement(elseToken);
0344: if (elseToken == null) {
0345: return null;
0346: }
0347:
0348: switch (elseToken.getTokenID().getNumericID()) {
0349: case CCTokenContext.LBRACE_ID:
0350: if (--braceDepth < 0) {
0351: return null; // no corresponding right brace
0352: }
0353: break;
0354:
0355: case CCTokenContext.RBRACE_ID:
0356: braceDepth++;
0357: break;
0358:
0359: case CCTokenContext.ELSE_ID:
0360: if (braceDepth == 0) {
0361: elseDepth++;
0362: }
0363: break;
0364:
0365: case CCTokenContext.SEMICOLON_ID:
0366: case CCTokenContext.COLON_ID:
0367: case CCTokenContext.DO_ID:
0368: case CCTokenContext.CASE_ID:
0369: case CCTokenContext.DEFAULT_ID:
0370: case CCTokenContext.FOR_ID:
0371: case CCTokenContext.WHILE_ID:
0372: break;
0373:
0374: case CCTokenContext.IF_ID:
0375: if (braceDepth == 0) {
0376: if (elseDepth-- == 0) {
0377: return elseToken; // successful search
0378: }
0379: }
0380: break;
0381: }
0382: }
0383: }
0384:
0385: /** Find the 'switch' when the 'case' is provided.
0386: * @param caseToken the token with the 'case' command
0387: * for which the 'switch' is being searched.
0388: * @return corresponding 'switch' token or null if there's
0389: * no corresponding 'switch' statement.
0390: */
0391: public TokenItem findSwitch(TokenItem caseToken) {
0392: if (caseToken == null
0393: || (!tokenEquals(caseToken, CCTokenContext.CASE,
0394: tokenContextPath) && !tokenEquals(caseToken,
0395: CCTokenContext.DEFAULT, tokenContextPath))) {
0396: throw new IllegalArgumentException(NbBundle.getBundle(
0397: CCFormatSupport.class).getString(
0398: "MSG_AcceptOlyCaseDefault")); //NOI18N
0399: }
0400:
0401: int braceDepth = 1; // depth of the braces - need one more left
0402: while (true) {
0403: caseToken = findStatement(caseToken);
0404: if (caseToken == null) {
0405: return null;
0406: }
0407:
0408: switch (caseToken.getTokenID().getNumericID()) {
0409: case CCTokenContext.LBRACE_ID:
0410: if (--braceDepth < 0) {
0411: return null; // no corresponding right brace
0412: }
0413: break;
0414:
0415: case CCTokenContext.RBRACE_ID:
0416: braceDepth++;
0417: break;
0418:
0419: case CCTokenContext.SWITCH_ID:
0420: case CCTokenContext.DEFAULT_ID:
0421: if (braceDepth == 0) {
0422: return caseToken;
0423: }
0424: break;
0425: }
0426: }
0427: }
0428:
0429: /** Find the 'class' or 'struct'
0430: * @param visibilityToken the token.
0431: */
0432: private TokenItem findClassifier(TokenItem visibilityToken) {
0433: int braceDepth = 1; // depth of the braces - need one more left
0434: TokenItem classifierToken = visibilityToken;
0435: while (true) {
0436: classifierToken = findStatement(classifierToken);
0437: if (classifierToken == null) {
0438: return null;
0439: }
0440:
0441: switch (classifierToken.getTokenID().getNumericID()) {
0442: case CCTokenContext.LBRACE_ID:
0443: if (--braceDepth < 0) {
0444: return null; // no corresponding right brace
0445: }
0446: if (braceDepth == 0) {
0447: while ((classifierToken = classifierToken
0448: .getPrevious()) != null) {
0449: switch (classifierToken.getTokenID()
0450: .getNumericID()) {
0451: case CCTokenContext.CLASS_ID:
0452: case CCTokenContext.STRUCT_ID:
0453: case CCTokenContext.ENUM_ID:
0454: case CCTokenContext.UNION_ID:
0455: return classifierToken;
0456: }
0457: }
0458: return null;
0459: }
0460: break;
0461:
0462: case CCTokenContext.RBRACE_ID:
0463: braceDepth++;
0464: break;
0465:
0466: }
0467: }
0468: }
0469:
0470: /** Find the 'try' when the 'catch' is provided.
0471: * @param catchToken the token with the 'catch' command
0472: * for which the 'try' is being searched.
0473: * @return corresponding 'try' token or null if there's
0474: * no corresponding 'try' statement.
0475: */
0476: public TokenItem findTry(TokenItem catchToken) {
0477: if (catchToken == null
0478: || (!tokenEquals(catchToken, CCTokenContext.CATCH,
0479: tokenContextPath))) {
0480: throw new IllegalArgumentException(NbBundle.getBundle(
0481: CCFormatSupport.class).getString(
0482: "MSG_AcceptOnlyCatch")); //NOI18N
0483: }
0484:
0485: int braceDepth = 0; // depth of the braces
0486: while (true) {
0487: catchToken = findStatement(catchToken);
0488: if (catchToken == null) {
0489: return null;
0490: }
0491:
0492: switch (catchToken.getTokenID().getNumericID()) {
0493: case CCTokenContext.LBRACE_ID:
0494: if (--braceDepth < 0) {
0495: return null; // no corresponding right brace
0496: }
0497: break;
0498:
0499: case CCTokenContext.RBRACE_ID:
0500: braceDepth++;
0501: break;
0502:
0503: case CCTokenContext.TRY_ID:
0504: if (braceDepth == 0) {
0505: return catchToken;
0506: }
0507: break;
0508: }
0509: }
0510: }
0511:
0512: /** Find the start of the statement.
0513: * @param token token from which to start. It searches
0514: * backward using <code>findStatement()</code> so it ignores
0515: * the given token.
0516: * @return the statement start token (outer statement start for nested
0517: * statements).
0518: * It returns the same token if there is '{' before
0519: * the given token.
0520: */
0521: public TokenItem findStatementStart(TokenItem token) {
0522: TokenItem t = findStatementStart(token, true);
0523: // preprocessor tokens are not important (bug#22570)
0524: while (t != null && isPreprocessorLine(t)) {
0525: TokenItem current = t.getPrevious();
0526: if (current == null) {
0527: return null;
0528: }
0529: t = findStatementStart(current, true);
0530: }
0531: return t;
0532: }
0533:
0534: public TokenItem findStatementStart(TokenItem token,
0535: boolean outermost) {
0536: TokenItem t = findStatement(token);
0537: if (t != null) {
0538: switch (t.getTokenID().getNumericID()) {
0539: case CCTokenContext.SEMICOLON_ID: // ';' found
0540: TokenItem scss = findStatement(t);
0541: if (scss == null) {
0542: return token;
0543: }
0544: switch (scss.getTokenID().getNumericID()) {
0545: case CCTokenContext.LBRACE_ID: // '{' then ';'
0546: case CCTokenContext.RBRACE_ID: // '}' then ';'
0547: case CCTokenContext.COLON_ID: // ':' then ';'
0548: case CCTokenContext.CASE_ID: // 'case' then ';'
0549: case CCTokenContext.DEFAULT_ID:
0550: case CCTokenContext.SEMICOLON_ID: // ';' then ';'
0551: return t; // return ';'
0552:
0553: case CCTokenContext.PRIVATE_ID:
0554: case CCTokenContext.PROTECTED_ID:
0555: case CCTokenContext.PUBLIC_ID:
0556:
0557: case CCTokenContext.DO_ID:
0558: case CCTokenContext.FOR_ID:
0559: case CCTokenContext.IF_ID:
0560: case CCTokenContext.WHILE_ID:
0561: return findStatementStart(t, outermost);
0562:
0563: case CCTokenContext.ELSE_ID: // 'else' then ';'
0564: // Find the corresponding 'if'
0565: TokenItem ifss = findIf(scss);
0566: if (ifss != null) { // 'if' ... 'else' then ';'
0567: return findStatementStart(ifss, outermost);
0568: } else { // no valid starting 'if'
0569: return scss; // return 'else'
0570: }
0571:
0572: default: // something usual then ';'
0573: TokenItem bscss = findStatement(scss);
0574: if (bscss != null) {
0575: switch (bscss.getTokenID().getNumericID()) {
0576: case CCTokenContext.SEMICOLON_ID: // ';' then stmt ending with ';'
0577: case CCTokenContext.LBRACE_ID:
0578: case CCTokenContext.RBRACE_ID:
0579: case CCTokenContext.COLON_ID:
0580: return scss; //
0581:
0582: case CCTokenContext.DO_ID:
0583: case CCTokenContext.FOR_ID:
0584: case CCTokenContext.IF_ID:
0585: case CCTokenContext.WHILE_ID:
0586: return findStatementStart(bscss, outermost);
0587:
0588: case CCTokenContext.ELSE_ID:
0589: // Find the corresponding 'if'
0590: ifss = findIf(bscss);
0591: if (ifss != null) { // 'if' ... 'else' ... ';'
0592: return findStatementStart(ifss,
0593: outermost);
0594: } else { // no valid starting 'if'
0595: return bscss; // return 'else'
0596: }
0597: }
0598: }
0599:
0600: return scss;
0601: } // semicolon servicing end
0602:
0603: case CCTokenContext.LBRACE_ID: // '{' found
0604: return token; // return original token
0605:
0606: case CCTokenContext.RBRACE_ID: // '}' found
0607: TokenItem lb = findMatchingToken(t, null,
0608: CCTokenContext.LBRACE, true);
0609: if (lb != null) { // valid matching left-brace
0610: // Find a stmt-start of the '{'
0611: TokenItem lbss = findStatement(lb);
0612: if (lbss != null) {
0613: switch (lbss.getTokenID().getNumericID()) {
0614: case CCTokenContext.ELSE_ID: // 'else {'
0615: // Find the corresponding 'if'
0616: TokenItem ifss = findIf(lbss);
0617: if (ifss != null) { // valid 'if'
0618: return findStatementStart(ifss,
0619: outermost);
0620: } else {
0621: return lbss; // return 'else'
0622: }
0623:
0624: case CCTokenContext.CATCH_ID: // 'catch (...) {'
0625: // I'm not sure what to do if this isn't C++...
0626: // Find the corresponding 'try'
0627: TokenItem tryss = findTry(lbss);
0628: if (tryss != null) { // valid 'try'
0629: return findStatementStart(tryss,
0630: outermost);
0631: } else {
0632: return lbss; // return 'catch'
0633: }
0634:
0635: case CCTokenContext.DO_ID:
0636: case CCTokenContext.FOR_ID:
0637: case CCTokenContext.IF_ID:
0638: case CCTokenContext.WHILE_ID:
0639: return findStatementStart(lbss, outermost);
0640: }
0641: // I copied the next if from JavaFormatSupport. But I'm not 100% certain it
0642: // applies...
0643: if (lbss.getTokenID().getNumericID() == CCTokenContext.LBRACE_ID) {
0644: return t; // return right brace
0645: }
0646: return lbss;
0647: }
0648: }
0649: return t; // return right brace
0650:
0651: case CCTokenContext.COLON_ID:
0652: case CCTokenContext.CASE_ID:
0653: case CCTokenContext.DEFAULT_ID:
0654: return token;
0655:
0656: case CCTokenContext.ELSE_ID:
0657: // Find the corresponding 'if'
0658: TokenItem ifss = findIf(t);
0659: return (ifss != null) ? findStatementStart(ifss,
0660: outermost) : t;
0661:
0662: case CCTokenContext.DO_ID:
0663: case CCTokenContext.FOR_ID:
0664: case CCTokenContext.IF_ID:
0665: case CCTokenContext.WHILE_ID:
0666: if (!outermost) {
0667: return t;
0668: } else {
0669: return findStatementStart(t, outermost);
0670: }
0671:
0672: case CCTokenContext.IDENTIFIER_ID:
0673: return t;
0674: default:
0675: return t;
0676: }
0677: }
0678: return token; // return original token
0679: }
0680:
0681: /**
0682: * Get the indentation for the given token. It first searches whether there's an
0683: * non-whitespace and a non-leftbrace character on the line with the token and if so,
0684: * it takes indent of the non-ws char instead.
0685: *
0686: * @param token token for which the indent is being searched.
0687: * The token itself is ignored and the previous token is used as a base for the search.
0688: * @param forceFirstNonWhitespace set true to ignore leftbrace and search
0689: * directly for first non-whitespace
0690: */
0691: public int getTokenIndent(TokenItem token,
0692: boolean forceFirstNonWhitespace) {
0693: FormatTokenPosition tp = getPosition(token, 0);
0694: // this is fix for bugs: 7980 and 9111
0695: // see the findLineFirstNonWhitespaceAndNonLeftBrace definition
0696: // for more info about the fix
0697: FormatTokenPosition fnw;
0698: if (forceFirstNonWhitespace)
0699: fnw = findLineFirstNonWhitespace(tp);
0700: else
0701: fnw = findLineFirstNonWhitespaceAndNonLeftBrace(tp);
0702:
0703: if (fnw != null) { // valid first non-whitespace
0704: tp = fnw;
0705: }
0706: return getVisualColumnOffset(tp);
0707: }
0708:
0709: public int getTokenIndent(TokenItem token) {
0710: return getTokenIndent(token, false);
0711: }
0712:
0713: /**
0714: * Find the indentation for the first token on the line.
0715: * The given token is also examined in some cases.
0716: */
0717: public int findIndent(TokenItem token) {
0718: int indent = -1; // assign invalid indent
0719:
0720: // First check the given token
0721: if (token != null) {
0722: switch (token.getTokenID().getNumericID()) {
0723: case CCTokenContext.ELSE_ID:
0724: TokenItem ifss = findIf(token);
0725: if (ifss != null) {
0726: indent = getTokenIndent(ifss);
0727: }
0728: break;
0729:
0730: case CCTokenContext.LBRACE_ID:
0731: TokenItem stmt = findStatement(token);
0732: if (stmt == null) {
0733: indent = 0;
0734: } else {
0735: switch (stmt.getTokenID().getNumericID()) {
0736: case CCTokenContext.DO_ID:
0737: case CCTokenContext.FOR_ID:
0738: case CCTokenContext.IF_ID:
0739: case CCTokenContext.WHILE_ID:
0740: case CCTokenContext.ELSE_ID:
0741: indent = getTokenIndent(stmt);
0742: break;
0743:
0744: case CCTokenContext.LBRACE_ID:
0745: indent = getTokenIndent(stmt) + getShiftWidth();
0746: break;
0747:
0748: default:
0749: stmt = findStatementStart(token);
0750: if (stmt == null) {
0751: indent = 0;
0752: } else if (stmt == token) {
0753: stmt = findStatement(token); // search for delimiter
0754: indent = (stmt != null) ? indent = getTokenIndent(stmt)
0755: : 0;
0756: } else { // valid statement
0757: indent = getTokenIndent(stmt);
0758: switch (stmt.getTokenID().getNumericID()) {
0759: case CCTokenContext.LBRACE_ID:
0760: indent += getShiftWidth();
0761: break;
0762: }
0763: }
0764: }
0765: }
0766: break;
0767:
0768: case CCTokenContext.RBRACE_ID:
0769: TokenItem rbmt = findMatchingToken(token, null,
0770: CCTokenContext.LBRACE, true);
0771: if (rbmt != null) { // valid matching left-brace
0772: TokenItem t = findStatement(rbmt);
0773: boolean forceFirstNonWhitespace = false;
0774: if (t == null) {
0775: t = rbmt; // will get indent of the matching brace
0776: } else {
0777: switch (t.getTokenID().getNumericID()) {
0778: case CCTokenContext.SEMICOLON_ID:
0779: case CCTokenContext.LBRACE_ID:
0780: case CCTokenContext.RBRACE_ID: {
0781: t = rbmt;
0782: forceFirstNonWhitespace = true;
0783: }
0784: }
0785: }
0786: // the right brace must be indented to the first
0787: // non-whitespace char - forceFirstNonWhitespace=true
0788: indent = getTokenIndent(t, forceFirstNonWhitespace);
0789: } else { // no matching left brace
0790: indent = getTokenIndent(token); // leave as is
0791: }
0792: break;
0793:
0794: case CCTokenContext.CASE_ID:
0795: case CCTokenContext.DEFAULT_ID:
0796: TokenItem swss = findSwitch(token);
0797: if (swss != null) {
0798: indent = getTokenIndent(swss);
0799: if (indentCasesFromSwitch()) {
0800: indent += getShiftWidth();
0801: }
0802: }
0803: break;
0804: case CCTokenContext.PUBLIC_ID:
0805: case CCTokenContext.PRIVATE_ID:
0806: case CCTokenContext.PROTECTED_ID:
0807: TokenItem cls = findClassifier(token);
0808: if (cls != null) {
0809: indent = getTokenIndent(cls);
0810: }
0811: break;
0812: case CCTokenContext.CLASS_ID:
0813: case CCTokenContext.STRUCT_ID:
0814: TokenItem clsTemplate = findClassifierStart(token);
0815: if (clsTemplate != null) {
0816: indent = getTokenIndent(clsTemplate);
0817: }
0818: break;
0819: }
0820: }
0821:
0822: // If indent not found, search back for the first important token
0823: if (indent < 0) { // if not yet resolved
0824: TokenItem t = findImportantToken(token, null, true, true);
0825: if (t != null) { // valid important token
0826: switch (t.getTokenID().getNumericID()) {
0827: case CCTokenContext.SEMICOLON_ID: // semicolon found
0828: TokenItem tt = findStatementStart(token);
0829: // preprocessor tokens are not important (bug#22570)
0830: while (tt != null
0831: && (isPreprocessorLine(tt) || tt.getImage()
0832: .startsWith("\n"))) { // NOI18N
0833: tt = findStatementStart(tt.getPrevious());
0834: }
0835: if (tt != null) {
0836: switch (tt.getTokenID().getNumericID()) {
0837: case CCTokenContext.PUBLIC_ID:
0838: case CCTokenContext.PRIVATE_ID:
0839: case CCTokenContext.PROTECTED_ID:
0840: indent = getTokenIndent(tt)
0841: + getShiftWidth();
0842: break;
0843: default:
0844: indent = getTokenIndent(tt);
0845: break;
0846: }
0847: } else {
0848: indent = getTokenIndent(tt);
0849: }
0850: break;
0851:
0852: case CCTokenContext.LBRACE_ID:
0853: TokenItem lbss = findStatementStart(t, false);
0854: if (lbss == null) {
0855: lbss = t;
0856: }
0857: indent = getTokenIndent(lbss) + getShiftWidth();
0858: break;
0859:
0860: case CCTokenContext.RBRACE_ID:
0861: TokenItem t3 = findStatementStart(token);
0862: indent = getTokenIndent(t3);
0863: break;
0864:
0865: case CCTokenContext.COLON_ID:
0866: TokenItem ttt = getVisibility(t);
0867: if (ttt != null) {
0868: indent = getTokenIndent(ttt) + getShiftWidth();
0869: } else {
0870: ttt = findAnyToken(t, null, new TokenID[] {
0871: CCTokenContext.CASE,
0872: CCTokenContext.DEFAULT,
0873: CCTokenContext.QUESTION,
0874: CCTokenContext.PRIVATE,
0875: CCTokenContext.PROTECTED,
0876: CCTokenContext.PUBLIC }, t
0877: .getTokenContextPath(), true);
0878: if (ttt != null) {
0879: int id = ttt.getTokenID().getNumericID();
0880: if (id == CCTokenContext.QUESTION_ID) {
0881: indent = getTokenIndent(ttt)
0882: + getShiftWidth();
0883: } else if (id == CCTokenContext.CASE_ID
0884: || id == CCTokenContext.DEFAULT_ID) {
0885: indent = getTokenIndent(ttt)
0886: + getShiftWidth();
0887: } else {
0888: indent = getTokenIndent(t);// + getShiftWidth();
0889: }
0890: } else {
0891: // Indent of line with ':' plus one indent level
0892: indent = getTokenIndent(t);// + getShiftWidth();
0893: }
0894: }
0895: break;
0896:
0897: case CCTokenContext.QUESTION_ID:
0898: case CCTokenContext.DO_ID:
0899: case CCTokenContext.ELSE_ID:
0900: indent = getTokenIndent(t) + getShiftWidth();
0901: break;
0902:
0903: case CCTokenContext.RPAREN_ID:
0904: // Try to find the matching left paren
0905: TokenItem rpmt = findMatchingToken(t, null,
0906: CCTokenContext.LPAREN, true);
0907: if (rpmt != null) {
0908: rpmt = findImportantToken(rpmt, null, true,
0909: true);
0910: // Check whether there are the indent changing kwds
0911: if (rpmt != null
0912: && rpmt.getTokenContextPath() == tokenContextPath) {
0913: switch (rpmt.getTokenID().getNumericID()) {
0914: case CCTokenContext.FOR_ID:
0915: case CCTokenContext.IF_ID:
0916: case CCTokenContext.WHILE_ID:
0917: // Indent one level
0918: indent = getTokenIndent(rpmt)
0919: + getShiftWidth();
0920: break;
0921: }
0922: }
0923: }
0924: if (indent < 0) {
0925: indent = computeStatementIndent(t);
0926: }
0927: break;
0928:
0929: case CCTokenContext.COMMA_ID:
0930: if (isEnumComma(t)) {
0931: indent = getTokenIndent(t);
0932: break;
0933: } // else continue to default
0934:
0935: default:
0936: indent = computeStatementIndent(t);
0937: break;
0938: }
0939:
0940: if (indent < 0) { // no indent found yet
0941: indent = getTokenIndent(t);
0942: }
0943: }
0944: }
0945:
0946: if (indent < 0) { // no important token found
0947: indent = 0;
0948: }
0949: return indent;
0950: }
0951:
0952: private int computeStatementIndent(final TokenItem t) {
0953: int indent;
0954: // Find stmt start and add continuation indent
0955: TokenItem stmtStart = findStatementStart(t);
0956: indent = getTokenIndent(stmtStart);
0957: //int tindent = getTokenIndent(t);
0958: //if (tindent > indent)
0959: // return tindent;
0960:
0961: if (stmtStart != null) {
0962: // Check whether there is a comma on the previous line end
0963: // and if so then also check whether the present
0964: // statement is inside array initialization statement
0965: // and not inside parents and if so then do not indent
0966: // statement continuation
0967: if (t != null
0968: && tokenEquals(t, CCTokenContext.COMMA,
0969: tokenContextPath)) {
0970: if (isArrayInitializationBraceBlock(t, null)
0971: && getLeftParen(t, stmtStart) == null) {
0972: return indent;
0973: }
0974: TokenItem lparen = getLeftParen(t, stmtStart);
0975: if (lparen != null) {
0976: TokenItem prev = findImportantToken(lparen, null,
0977: true, true);
0978: if (prev != null
0979: && prev.getTokenID().getNumericID() == CCTokenContext.IDENTIFIER_ID) {
0980: if (isStatement(stmtStart)) {
0981: if (alignMultilineCallArgs()) {
0982: return getVisualColumnOffset(getPosition(
0983: lparen, 0)) + 1;
0984: }
0985: } else {
0986: if (alignMultilineMethodParams()) {
0987: return getVisualColumnOffset(getPosition(
0988: lparen, 0)) + 1;
0989: }
0990: }
0991: }
0992: }
0993: } else if (!isStatement(stmtStart)) {
0994: return indent;
0995: }
0996: indent += getFormatStatementContinuationIndent();
0997: }
0998: return indent;
0999: }
1000:
1001: private boolean isStatement(TokenItem t) {
1002: boolean likeDeclaration = false;
1003: boolean findLParen = false;
1004: boolean findQuestion = false;
1005: int identifiers = 0;
1006: while (t != null) {
1007: if (t.getTokenContextPath() == tokenContextPath) {
1008: switch (t.getTokenID().getNumericID()) {
1009: case CCTokenContext.EQ_ID:
1010: case CCTokenContext.AND_EQ_ID:
1011: case CCTokenContext.OR_EQ_ID:
1012: case CCTokenContext.MUL_EQ_ID:
1013: case CCTokenContext.DIV_EQ_ID:
1014: case CCTokenContext.XOR_EQ_ID:
1015: case CCTokenContext.MOD_EQ_ID:
1016: case CCTokenContext.DELETE_ID:
1017: case CCTokenContext.RETURN_ID:
1018: case CCTokenContext.BREAK_ID:
1019: case CCTokenContext.CASE_ID:
1020: case CCTokenContext.CATCH_ID:
1021: case CCTokenContext.CONTINUE_ID:
1022: case CCTokenContext.DEFAULT_ID:
1023: case CCTokenContext.DO_ID:
1024: case CCTokenContext.ELSE_ID:
1025: case CCTokenContext.FOR_ID:
1026: case CCTokenContext.GOTO_ID:
1027: case CCTokenContext.IF_ID:
1028: case CCTokenContext.SIZEOF_ID:
1029: case CCTokenContext.SWITCH_ID:
1030: case CCTokenContext.THIS_ID:
1031: case CCTokenContext.THROW_ID:
1032: case CCTokenContext.TRY_ID:
1033: case CCTokenContext.USING_ID:
1034: case CCTokenContext.WHILE_ID:
1035: return true;
1036: case CCTokenContext.SEMICOLON_ID:
1037: if (likeDeclaration) {
1038: return false;
1039: }
1040: return true;
1041: case CCTokenContext.FRIEND_ID:
1042: case CCTokenContext.EXPLICIT_ID:
1043: case CCTokenContext.EXTERN_ID:
1044: case CCTokenContext.CLASS_ID:
1045: case CCTokenContext.STATIC_ID:
1046: case CCTokenContext.OPERATOR_ID:
1047: case CCTokenContext.PRIVATE_ID:
1048: case CCTokenContext.PROTECTED_ID:
1049: case CCTokenContext.PUBLIC_ID:
1050: case CCTokenContext.NAMESPACE_ID:
1051: case CCTokenContext.TEMPLATE_ID:
1052: case CCTokenContext.UNION_ID:
1053: case CCTokenContext.ENUM_ID:
1054: case CCTokenContext.VIRTUAL_ID:
1055: case CCTokenContext.INLINE_ID:
1056: case CCTokenContext.LBRACE_ID:
1057: return false;
1058: case CCTokenContext.COLON_ID:
1059: if (!findQuestion) {
1060: return false;
1061: }
1062: break;
1063: case CCTokenContext.QUESTION_ID:
1064: findQuestion = true;
1065: break;
1066: case CCTokenContext.SCOPE_ID:
1067: if (!findLParen && identifiers == 1) {
1068: likeDeclaration = true;
1069: }
1070: break;
1071: case CCTokenContext.RPAREN_ID:
1072: break;
1073: case CCTokenContext.LPAREN_ID:
1074: if (!findLParen && identifiers > 1) {
1075: likeDeclaration = true;
1076: }
1077: findLParen = true;
1078: break;
1079: case CCTokenContext.ASM_ID:
1080: case CCTokenContext.AUTO_ID:
1081: case CCTokenContext.BOOLEAN_ID:
1082: case CCTokenContext.CHAR_ID:
1083: case CCTokenContext.DOUBLE_ID:
1084: case CCTokenContext.EXPORT_ID:
1085: case CCTokenContext.FLOAT_ID:
1086: case CCTokenContext.INT_ID:
1087: case CCTokenContext.LONG_ID:
1088: case CCTokenContext.MUTABLE_ID:
1089: case CCTokenContext.REGISTER_ID:
1090: case CCTokenContext.SHORT_ID:
1091: case CCTokenContext.SIGNED_ID:
1092: case CCTokenContext.STRUCT_ID:
1093: case CCTokenContext.TYPEDEF_ID:
1094: case CCTokenContext.TYPENAME_ID:
1095: case CCTokenContext.UNSIGNED_ID:
1096: case CCTokenContext.VOID_ID:
1097: case CCTokenContext.WCHAR_T_ID:
1098: case CCTokenContext.VOLATILE_ID:
1099: case CCTokenContext.CONST_ID:
1100: if (!findLParen) {
1101: return false;
1102: }
1103: break;
1104: case CCTokenContext.IDENTIFIER_ID:
1105: identifiers++;
1106: break;
1107: }
1108: }
1109: t = t.getNext();
1110: }
1111: return true;
1112: }
1113:
1114: public FormatTokenPosition indentLine(FormatTokenPosition pos) {
1115: int indent = 0; // Desired indent
1116:
1117: // Get the first non-whitespace position on the line
1118: FormatTokenPosition firstNWS = findLineFirstNonWhitespace(pos);
1119: if (firstNWS != null) { // some non-WS on the line
1120: if (isPreprocessorLine(firstNWS.getToken())) {
1121: // leave untouched for now, (bug#22570)
1122: if (!isPreprocessorAtLineStart(firstNWS.getToken())) {
1123: return pos;
1124: }
1125: } else if (isComment(firstNWS)) { // comment is first on the line
1126: if (isMultiLineComment(firstNWS)
1127: && firstNWS.getOffset() != 0) {
1128:
1129: // Indent the inner lines of the multi-line comment by one
1130: indent = getLineIndent(getPosition(firstNWS
1131: .getToken(), 0), true) + 1;
1132:
1133: // If the line is inside multi-line comment and doesn't contain '*'
1134: if (!isIndentOnly()) {
1135: if (getChar(firstNWS) != '*') {
1136: if (isCCDocComment(firstNWS.getToken())) {
1137: if (getFormatLeadingStarInComment()) {
1138: insertString(firstNWS, "* "); // NOI18N
1139: }
1140: } else {
1141: // For non-java-doc not because it can be commented code
1142: indent = getLineIndent(pos, true);
1143: }
1144: }
1145: } else {
1146: if (getChar(firstNWS) != '*') { // e.g. not for '*/'
1147: if (isCCDocComment(firstNWS.getToken())) {
1148: if (getFormatLeadingStarInComment()) {
1149: insertString(firstNWS, "* "); // NOI18N
1150: setIndentShift(2);
1151: }
1152: }
1153: }
1154: }
1155: } else if (!isMultiLineComment(firstNWS)) { // line-comment
1156: indent = firstNWS.equals(findLineStart(firstNWS)) ? getLineIndent(
1157: firstNWS, true)
1158: : findIndent(firstNWS.getToken());
1159: } else { // multi-line comment
1160: if (isCCDocComment(firstNWS.getToken())) {
1161: indent = findIndent(firstNWS.getToken());
1162: } else {
1163: // check whether the multiline comment isn't finished on the same line (see issue 12821)
1164: if (firstNWS.getToken().getImage()
1165: .indexOf('\n') == -1) {
1166: indent = findIndent(firstNWS.getToken());
1167: } else {
1168: indent = getLineIndent(firstNWS, true);
1169: }
1170: }
1171: }
1172: } else { // first non-WS char is not comment
1173: indent = findIndent(firstNWS.getToken());
1174: }
1175: } else { // whole line is WS
1176: // Can be empty line inside multi-line comment
1177: TokenItem token = pos.getToken();
1178: if (token == null) {
1179: token = findLineStart(pos).getToken();
1180: if (token == null) { // empty line
1181: token = getLastToken();
1182: }
1183: }
1184:
1185: if (token != null && isMultiLineComment(token)) {
1186: if (getFormatLeadingStarInComment()
1187: && (isIndentOnly() || isCCDocComment(token))) {
1188: // Insert initial '*'
1189: insertString(pos, "*"); // NOI18N
1190: setIndentShift(1);
1191: }
1192:
1193: // Indent the multi-comment
1194: indent = getVisualColumnOffset(getPosition(token, 0)) + 1;
1195: } else { // non-multi-line comment
1196: indent = findIndent(pos.getToken());
1197: }
1198: }
1199:
1200: // For indent-only always indent
1201: return changeLineIndent(pos, indent);
1202: }
1203:
1204: /**
1205: * Check whether the given semicolon is inside the for() statement
1206: *
1207: * @param token token to check. It must be a semicolon
1208: * @return true if the given semicolon is inside the for() statement, or false otherwise
1209: */
1210: public boolean isForLoopSemicolon(TokenItem token) {
1211: if (token == null
1212: || !tokenEquals(token, CCTokenContext.SEMICOLON,
1213: tokenContextPath)) {
1214: throw new IllegalArgumentException("Only accept ';'."); // NOI18N
1215: }
1216:
1217: int parDepth = 0; // parenthesis depth
1218: int braceDepth = 0; // brace depth
1219: boolean semicolonFound = false; // next semicolon
1220: token = token.getPrevious(); // ignore this semicolon
1221: while (token != null) {
1222: if (tokenEquals(token, CCTokenContext.LPAREN,
1223: tokenContextPath)) {
1224: if (parDepth == 0) { // could be a 'for ('
1225: FormatTokenPosition tp = getPosition(token, 0);
1226: tp = findImportant(tp, null, false, true);
1227: if (tp != null
1228: && tokenEquals(tp.getToken(),
1229: CCTokenContext.FOR,
1230: tokenContextPath)) {
1231: return true;
1232: }
1233: return false;
1234: } else { // non-zero depth
1235: parDepth--;
1236: }
1237: } else if (tokenEquals(token, CCTokenContext.RPAREN,
1238: tokenContextPath)) {
1239: parDepth++;
1240: } else if (tokenEquals(token, CCTokenContext.LBRACE,
1241: tokenContextPath)) {
1242: if (braceDepth == 0) { // unclosed left brace
1243: return false;
1244: }
1245: braceDepth--;
1246: } else if (tokenEquals(token, CCTokenContext.RBRACE,
1247: tokenContextPath)) {
1248: braceDepth++;
1249: } else if (tokenEquals(token, CCTokenContext.SEMICOLON,
1250: tokenContextPath)) {
1251: if (semicolonFound) { // one semicolon already found
1252: return false;
1253: }
1254: semicolonFound = true;
1255: }
1256:
1257: token = token.getPrevious();
1258: }
1259:
1260: return false;
1261: }
1262:
1263: private TokenItem findClassifierStart(TokenItem token) {
1264: while (true) {
1265: token = findStatement(token);
1266: if (token == null) {
1267: return null;
1268: }
1269: switch (token.getTokenID().getNumericID()) {
1270: case CCTokenContext.LBRACE_ID:
1271: case CCTokenContext.RBRACE_ID:
1272: case CCTokenContext.SEMICOLON_ID:
1273: return null;
1274: case CCTokenContext.TEMPLATE_ID:
1275: return findStatementStart(token);
1276: }
1277: }
1278: }
1279:
1280: private TokenItem getVisibility(TokenItem token) {
1281: TokenItem t = token;
1282: if (t != null) {
1283: t = token.getPrevious();
1284: }
1285: while (t != null) {
1286: if (t.getTokenContextPath() == tokenContextPath) {
1287: switch (t.getTokenID().getNumericID()) {
1288: case CCTokenContext.SEMICOLON_ID:
1289: case CCTokenContext.LBRACE_ID:
1290: case CCTokenContext.RBRACE_ID:
1291: return null;
1292: case CCTokenContext.PRIVATE_ID:
1293: case CCTokenContext.PROTECTED_ID:
1294: case CCTokenContext.PUBLIC_ID:
1295: return t;
1296: }
1297: }
1298: t = t.getPrevious();
1299: }
1300: return null;
1301: }
1302:
1303: /**
1304: * Check whether there are left parenthesis before the given token
1305: * until the limit token.
1306: *
1307: * @param token non-null token from which to start searching back.
1308: * @param limitToken limit token when reached the search will stop
1309: * with returning false.
1310: * @return true if there is LPAREN token before the given token
1311: * (while respecting paren nesting).
1312: */
1313: private TokenItem getLeftParen(TokenItem token, TokenItem limitToken) {
1314: int depth = 0;
1315: token = token.getPrevious();
1316:
1317: while (token != null && token != limitToken) {
1318: if (tokenEquals(token, CCTokenContext.LPAREN,
1319: tokenContextPath)) {
1320: if (--depth < 0) {
1321: return token;
1322: }
1323:
1324: } else if (tokenEquals(token, CCTokenContext.RPAREN,
1325: tokenContextPath)) {
1326: depth++;
1327: }
1328: token = token.getPrevious();
1329: }
1330: return null;
1331: }
1332:
1333: /**
1334: * Check whether the given token is located in array initialization block.
1335: *
1336: * @param token non-null token from which to start searching back.
1337: * @param limitToken limit token when reached the search will stop
1338: * with returning false.
1339: * @return true if the token is located inside the brace block of array
1340: * initialization.
1341: */
1342: private boolean isArrayInitializationBraceBlock(TokenItem token,
1343: TokenItem limitToken) {
1344: int depth = 0;
1345: token = token.getPrevious();
1346:
1347: while (token != null && token != limitToken
1348: && token.getTokenContextPath() == tokenContextPath) {
1349: switch (token.getTokenID().getNumericID()) {
1350: case CCTokenContext.RBRACE_ID:
1351: depth++;
1352: break;
1353:
1354: case CCTokenContext.LBRACE_ID:
1355: depth--;
1356: if (depth < 0) {
1357: TokenItem prev = findImportantToken(token,
1358: limitToken, true, true);
1359: // Array initialization left brace should be preceded
1360: // by either '=' or ']' i.e.
1361: // either "String array = { "a", "b", ... }"
1362: // or "String array = new String[] { "a", "b", ... }"
1363: return (prev != null
1364: && prev.getTokenContextPath() == tokenContextPath && (CCTokenContext.RBRACKET
1365: .equals(prev.getTokenID()) || CCTokenContext.EQ
1366: .equals(prev.getTokenID())));
1367: }
1368: break;
1369:
1370: // Array initialization block should not contain statements or ';'
1371: case CCTokenContext.DO_ID:
1372: case CCTokenContext.FOR_ID:
1373: case CCTokenContext.IF_ID:
1374: case CCTokenContext.WHILE_ID:
1375: case CCTokenContext.SEMICOLON_ID:
1376: if (depth == 0) {
1377: return false;
1378: }
1379: }
1380: token = token.getPrevious();
1381: }
1382: return false;
1383: }
1384:
1385: public boolean isEnumComma(TokenItem token) {
1386: while (token != null
1387: && tokenEquals(token, CCTokenContext.COMMA,
1388: tokenContextPath)) {
1389: TokenItem itm = findStatementStart(token);
1390: if (itm == token) {
1391: break;
1392: }
1393: token = itm;
1394: }
1395: if (token != null
1396: && tokenEquals(token, CCTokenContext.IDENTIFIER,
1397: tokenContextPath)) {
1398: TokenItem itm = findImportantToken(token, null, true, true);
1399: if (itm != null
1400: && tokenEquals(itm, CCTokenContext.LBRACE,
1401: tokenContextPath)) {
1402: TokenItem startItem = findStatementStart(itm);
1403: if (startItem != null
1404: && findToken(startItem, itm,
1405: CCTokenContext.ENUM, tokenContextPath,
1406: null, false) != null)
1407: return true;
1408: }
1409: }
1410: return false;
1411: }
1412:
1413: private CodeStyle getCodeStyle() {
1414: return CodeStyle.getDefault(language);
1415: }
1416:
1417: public boolean getFormatSpaceBeforeMethodCallParenthesis() {
1418: //return getCodeStyle().getFormatSpaceBeforeParenthesis();
1419: return getCodeStyle().spaceBeforeMethodCallParen();
1420: }
1421:
1422: public boolean getFormatSpaceAfterComma() {
1423: return getCodeStyle().spaceAfterComma();
1424: }
1425:
1426: public boolean indentCasesFromSwitch() {
1427: return getCodeStyle().indentCasesFromSwitch();
1428: }
1429:
1430: public boolean getFormatNewlineBeforeBrace() {
1431: return getCodeStyle().getFormatNewlineBeforeBrace() == CodeStyle.BracePlacement.NEW_LINE;
1432: }
1433:
1434: public boolean getFormatNewlineBeforeBraceDeclaration() {
1435: return getCodeStyle().getFormatNewlineBeforeBraceDeclaration() == CodeStyle.BracePlacement.NEW_LINE;
1436: }
1437:
1438: public boolean getFormatLeadingStarInComment() {
1439: return getCodeStyle().getFormatLeadingStarInComment();
1440: }
1441:
1442: private int getFormatStatementContinuationIndent() {
1443: return getCodeStyle().getFormatStatementContinuationIndent();
1444: }
1445:
1446: private boolean alignMultilineCallArgs() {
1447: return getCodeStyle().alignMultilineCallArgs();
1448: }
1449:
1450: private boolean alignMultilineMethodParams() {
1451: return getCodeStyle().alignMultilineMethodParams();
1452: }
1453:
1454: /* this is fix for bugs: 7980 and 9111. if user enters
1455: * { foo();
1456: * and press enter at the end of the line, she wants
1457: * to be indented just under "f" in "foo();" and not under the "{"
1458: * as it happens now. and this is what findLineFirstNonWhitespaceAndNonLeftBrace checks
1459: */
1460: public FormatTokenPosition findLineFirstNonWhitespaceAndNonLeftBrace(
1461: FormatTokenPosition pos) {
1462: // first call the findLineFirstNonWhitespace
1463: FormatTokenPosition ftp = super .findLineFirstNonWhitespace(pos);
1464: if (ftp == null) { // no line start, no WS
1465: return null;
1466: }
1467:
1468: // now checks if the first non-whitespace char is "{"
1469: // if it is, find the next non-whitespace char
1470: if (!ftp.getToken().getImage().startsWith("{")) // NOI18N
1471: return ftp;
1472:
1473: // if the left brace is closed on the same line - "{ foo(); }"
1474: // it must be ignored. otherwise next statement is incorrectly indented
1475: // under the "f" and not under the "{" as expected
1476: FormatTokenPosition eolp = findNextEOL(ftp);
1477: TokenItem rbmt = findMatchingToken(ftp.getToken(),
1478: eolp != null ? eolp.getToken() : null,
1479: CCTokenContext.RBRACE, false);
1480: if (rbmt != null)
1481: return ftp;
1482:
1483: FormatTokenPosition ftp_next = getNextPosition(ftp);
1484: if (ftp_next == null)
1485: return ftp;
1486:
1487: FormatTokenPosition ftp2 = findImportant(ftp_next, null, true,
1488: false);
1489: if (ftp2 != null)
1490: return ftp2;
1491: else
1492: return ftp;
1493: }
1494:
1495: public TokenItem findImportantToken(TokenItem startToken,
1496: TokenItem limitToken, boolean backward,
1497: boolean ignorePreprocessor) {
1498: TokenItem t = findImportantToken(startToken, limitToken,
1499: backward);
1500: if (ignorePreprocessor) {
1501: // preprocessor tokens are not important (bug#22570)
1502: while (t != null && getPosition(t, 0) != null) {
1503: FormatTokenPosition ft = findLineFirstNonWhitespace(getPosition(
1504: t, 0));
1505: if (ft == null) {
1506: return null;
1507: }
1508: if (isPreprocessorLine(ft.getToken())) {
1509: t = backward ? t.getPrevious() : t.getNext();
1510: if (t != null) {
1511: t = findImportantToken(t, limitToken, backward);
1512: }
1513: } else {
1514: break;
1515: }
1516: }
1517: }
1518: return t;
1519: }
1520: }
|