package org.eclim.checkstyle; import com.puppycrawl.tools.checkstyle.api.TokenTypes; import com.puppycrawl.tools.checkstyle.api.DetailAST; import com.puppycrawl.tools.checkstyle.api.Utils; import com.puppycrawl.tools.checkstyle.checks.blocks.LeftCurlyCheck; import com.puppycrawl.tools.checkstyle.checks.blocks.LeftCurlyOption; /** * Extension to default LeftCurlyCheck which allows not requiring curly on a new * line for anonymous classes. */ public class LeftCurly extends LeftCurlyCheck { // copied from LeftCurlyCheck private static final int DEFAULT_MAX_LINE_LENGTH = 80; private int mMaxLineLength = DEFAULT_MAX_LINE_LENGTH; private boolean ignoreAnonymous; /** * Sets whether or not this instance should ignore anonymous class methods. * * @param ignoreAnonymous true to ignore anonymouse class methods, false * otherwise. */ public void setIgnoreAnonymousClassMethods(boolean ignoreAnonymous) { this.ignoreAnonymous = ignoreAnonymous; } // straight copy from LeftCurlyCheck @Override public void visitToken(DetailAST aAST) { final DetailAST startToken; final DetailAST brace; switch (aAST.getType()) { case TokenTypes.CTOR_DEF : case TokenTypes.METHOD_DEF : startToken = skipAnnotationOnlyLines(aAST); brace = aAST.findFirstToken(TokenTypes.SLIST); break; case TokenTypes.INTERFACE_DEF : case TokenTypes.CLASS_DEF : case TokenTypes.ANNOTATION_DEF : case TokenTypes.ENUM_DEF : case TokenTypes.ENUM_CONSTANT_DEF : startToken = skipAnnotationOnlyLines(aAST); final DetailAST objBlock = aAST.findFirstToken(TokenTypes.OBJBLOCK); brace = (objBlock == null) ? null : (DetailAST) objBlock.getFirstChild(); break; case TokenTypes.LITERAL_WHILE: case TokenTypes.LITERAL_CATCH: case TokenTypes.LITERAL_SYNCHRONIZED: case TokenTypes.LITERAL_FOR: case TokenTypes.LITERAL_TRY: case TokenTypes.LITERAL_FINALLY: case TokenTypes.LITERAL_DO: case TokenTypes.LITERAL_IF : startToken = aAST; brace = aAST.findFirstToken(TokenTypes.SLIST); break; case TokenTypes.LITERAL_ELSE : startToken = aAST; final DetailAST candidate = aAST.getFirstChild(); brace = (candidate.getType() == TokenTypes.SLIST) ? candidate : null; // silently ignore break; case TokenTypes.LITERAL_SWITCH : startToken = aAST; brace = aAST.findFirstToken(TokenTypes.LCURLY); break; default : startToken = null; brace = null; } if ((brace != null) && (startToken != null)) { verifyBrace(brace, startToken); } } // straight copy from LeftCurlyCheck private DetailAST skipAnnotationOnlyLines(DetailAST aAST) { final DetailAST modifiers = aAST.findFirstToken(TokenTypes.MODIFIERS); if (modifiers == null) { return aAST; } DetailAST lastAnnot = findLastAnnotation(modifiers); if (lastAnnot == null) { // There are no annotations. return aAST; } final DetailAST tokenAfterLast = lastAnnot.getNextSibling() != null ? lastAnnot.getNextSibling() : modifiers.getNextSibling(); if (tokenAfterLast.getLineNo() > lastAnnot.getLineNo()) { return tokenAfterLast; } final int lastAnnotLineNumber = lastAnnot.getLineNo(); while (lastAnnot.getPreviousSibling() != null && (lastAnnot.getPreviousSibling().getLineNo() == lastAnnotLineNumber)) { lastAnnot = lastAnnot.getPreviousSibling(); } return lastAnnot; } // straight copy from LeftCurlyCheck private DetailAST findLastAnnotation(DetailAST aModifiers) { DetailAST aAnnot = aModifiers.findFirstToken(TokenTypes.ANNOTATION); while (aAnnot != null && aAnnot.getNextSibling() != null && aAnnot.getNextSibling().getType() == TokenTypes.ANNOTATION) { aAnnot = aAnnot.getNextSibling(); } return aAnnot; } // copied from LeftCurlyCheck, but updated to ignore anonymous class methods // if configured to do so. private void verifyBrace(final DetailAST aBrace, final DetailAST aStartToken) { final String braceLine = getLines()[aBrace.getLineNo() - 1]; // calculate the previous line length without trailing whitespace. Need // to handle the case where there is no previous line, cause the line // being check is the first line in the file. final int prevLineLen = (aBrace.getLineNo() == 1) ? mMaxLineLength : Utils.lengthMinusTrailingWhitespace( getLines()[aBrace.getLineNo() - 2]); // Check for being told to ignore, or have '{}' which is a special case if ((braceLine.length() > (aBrace.getColumnNo() + 1)) && (braceLine.charAt(aBrace.getColumnNo() + 1) == '}')) { ; // ignore } else if (getAbstractOption() == LeftCurlyOption.NL) { if (!Utils.whitespaceBefore(aBrace.getColumnNo(), braceLine)) { if (ignoreAnonymous && aStartToken.getType() == TokenTypes.METHOD_DEF){ DetailAST parent = aStartToken.getParent(); if (parent != null){ parent = parent.getParent(); } if (parent != null && parent.getType() == TokenTypes.LITERAL_NEW){ return; } } log(aBrace.getLineNo(), aBrace.getColumnNo(), "line.new", "{"); } } else if (getAbstractOption() == LeftCurlyOption.EOL) { if (Utils.whitespaceBefore(aBrace.getColumnNo(), braceLine) && ((prevLineLen + 2) <= mMaxLineLength)) { log(aBrace.getLineNo(), aBrace.getColumnNo(), "line.previous", "{"); } } else if (getAbstractOption() == LeftCurlyOption.NLOW) { if (aStartToken.getLineNo() == aBrace.getLineNo()) { ; // all ok as on the same line } else if ((aStartToken.getLineNo() + 1) == aBrace.getLineNo()) { if (!Utils.whitespaceBefore(aBrace.getColumnNo(), braceLine)) { log(aBrace.getLineNo(), aBrace.getColumnNo(), "line.new", "{"); } else if ((prevLineLen + 2) <= mMaxLineLength) { log(aBrace.getLineNo(), aBrace.getColumnNo(), "line.previous", "{"); } } else if (!Utils.whitespaceBefore(aBrace.getColumnNo(), braceLine)) { log(aBrace.getLineNo(), aBrace.getColumnNo(), "line.new", "{"); } } } /** * {@inheritDoc} * @see com.puppycrawl.tools.checkstyle.api.AbstractViolationReporter#getMessageBundle() */ @Override protected String getMessageBundle() { String className = getClass().getSuperclass().getName(); String packageName = className.substring(0, className.lastIndexOf('.')); return packageName + ".messages"; } }