diff --git a/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/RedundantModifierCheck.java b/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/RedundantModifierCheck.java new file mode 100644 index 000000000..7545f034d --- /dev/null +++ b/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/RedundantModifierCheck.java @@ -0,0 +1,85 @@ +package com.puppycrawl.tools.checkstyle.checks; + +import java.util.Stack; + +import com.puppycrawl.tools.checkstyle.api.Check; +import com.puppycrawl.tools.checkstyle.api.DetailAST; +import com.puppycrawl.tools.checkstyle.JavaTokenTypes; + +public class RedundantModifierCheck extends Check implements JavaTokenTypes +{ + private final Stack mInInterface = new Stack(); + + public void beginTree() + { + super.beginTree(); + mInInterface.clear(); + } + + public int[] getDefaultTokens() + { + return new int[] {MODIFIERS, INTERFACE_DEF, CLASS_DEF}; + } + + public void visitToken(DetailAST aAST) + { + switch (aAST.getType()) + { + case INTERFACE_DEF: + mInInterface.push(Boolean.TRUE); + break; + case CLASS_DEF: + mInInterface.push(Boolean.FALSE); + break; + case MODIFIERS: + + // modifiers of the interface itself (public interface X) + // will be below the INTERFACE_DEF node. Example: + + // public interface X {void y();} + + // INTERFACE_DEF + // + MODUFIERS + // + public + // + OBJ_BLOCK + // + ... + + if (inInterfaceBlock(aAST)) { + DetailAST ast = (DetailAST) aAST.getFirstChild(); + while (ast != null) { + String modifier = ast.getText(); + if ("public".equals(modifier) + || "abstract".equals(modifier)) + { + log(ast.getLineNo(), + ast.getColumnNo(), + "redundantModifier", + new String[] {modifier}); + } + ast = (DetailAST) ast.getNextSibling(); + } + } + break; + default: + return; + } + } + + /** @return whether currently in an interface block, + * i.e. in an OBJ_BLOCK of an INTERFACE_DEF + */ + private boolean inInterfaceBlock(DetailAST aAST) + { + if (mInInterface.empty()) { + return false; + } + if (aAST.getParent().getType() == INTERFACE_DEF) { + int size = mInInterface.size(); + return size > 1 && Boolean.TRUE.equals(mInInterface.get(size - 2)); + } + else { + return Boolean.TRUE.equals(mInInterface.peek()); + } + } + +} diff --git a/src/tests/com/puppycrawl/tools/checkstyle/RedundantModifierTest.java b/src/tests/com/puppycrawl/tools/checkstyle/RedundantModifierTest.java new file mode 100644 index 000000000..d2766f1a7 --- /dev/null +++ b/src/tests/com/puppycrawl/tools/checkstyle/RedundantModifierTest.java @@ -0,0 +1,32 @@ +package com.puppycrawl.tools.checkstyle; + +import com.puppycrawl.tools.checkstyle.api.LocalizedMessages; +import com.puppycrawl.tools.checkstyle.api.Check; +import com.puppycrawl.tools.checkstyle.checks.RedundantModifierCheck; + +public class RedundantModifierTest extends AbstractCheckTestCase +{ + public RedundantModifierTest(String aName) + { + super(aName); + } + + public void testIt() + throws Exception + { + final LocalizedMessages msgs = new LocalizedMessages(8); + final TreeWalker walker = new TreeWalker(msgs); + final CheckConfiguration config = new CheckConfiguration(); + config.setClassname(RedundantModifierCheck.class.getName()); + final Check c = config.createInstance( + Thread.currentThread().getContextClassLoader()); + walker.registerCheck(c, config); + final String fname = CheckerTest.getPath("InputModifier.java"); + final String[] lines = getLines(fname); + walker.walk(getAST(fname, lines), lines, fname); + assertEquals(2, msgs.getMessages().length); + int idx = 0; + verifyMessage(msgs, idx++, 32, 9, "Redundant 'public' modifier."); + verifyMessage(msgs, idx++, 38, 9, "Redundant 'abstract' modifier."); + } +}