Source code: com/puppycrawl/tools/checkstyle/checks/design/MutableExceptionCheck.java
1 ////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code for adherence to a set of rules.
3 // Copyright (C) 2001-2003 Oliver Burn
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 ////////////////////////////////////////////////////////////////////////////////
19 package com.puppycrawl.tools.checkstyle.checks.design;
20
21 import com.puppycrawl.tools.checkstyle.api.DetailAST;
22 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
23 import com.puppycrawl.tools.checkstyle.checks.AbstractFormatCheck;
24
25 import java.util.Stack;
26
27 /**
28 * <p> Ensures that exceptions (defined as any class name conforming
29 * to some regular expression) are immutable. That is, have only final
30 * fields.</p>
31 * <p> Rationale: Exception instances should represent an error
32 * condition. Having non final fields not only allows the state to be
33 * modified by accident and therefore mask the original condition but
34 * also allows developers to accidentally forget to initialise state
35 * thereby leading to code catching the exception to draw incorrect
36 * conclusions based on the state.</p>
37 *
38 * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a>
39 */
40 public final class MutableExceptionCheck extends AbstractFormatCheck
41 {
42 /** Default value for format property. */
43 private static final String DEFAULT_FORMAT = "^.*Exception$|^.*Error$";
44 /** Stack of checking information for classes. */
45 private final Stack mCheckingStack = new Stack();
46 /** Should we check current class or not. */
47 private boolean mChecking;
48
49 /** Creates new instance of the check. */
50 public MutableExceptionCheck()
51 {
52 super(DEFAULT_FORMAT);
53 }
54
55 /** @see com.puppycrawl.tools.checkstyle.api.Check */
56 public int[] getDefaultTokens()
57 {
58 return new int[] {TokenTypes.CLASS_DEF, TokenTypes.VARIABLE_DEF};
59 }
60
61 /** @see com.puppycrawl.tools.checkstyle.api.Check */
62 public int[] getRequiredTokens()
63 {
64 return getDefaultTokens();
65 }
66
67 /** @see com.puppycrawl.tools.checkstyle.api.Check */
68 public void visitToken(DetailAST aAST)
69 {
70 switch (aAST.getType()) {
71 case TokenTypes.CLASS_DEF:
72 visitClassDef(aAST);
73 break;
74 case TokenTypes.VARIABLE_DEF:
75 visitVariableDef(aAST);
76 break;
77 default:
78 throw new IllegalStateException(aAST.toString());
79 }
80 }
81
82 /** @see com.puppycrawl.tools.checkstyle.api.Check */
83 public void leaveToken(DetailAST aAST)
84 {
85 switch (aAST.getType()) {
86 case TokenTypes.CLASS_DEF:
87 leaveClassDef();
88 break;
89 default:
90 // Do nothing
91 }
92 }
93
94 /**
95 * Called when we start processing class definition.
96 * @param aAST class definition node
97 */
98 private void visitClassDef(DetailAST aAST)
99 {
100 mCheckingStack.push(Boolean.valueOf(mChecking));
101 mChecking =
102 isExceptionClass(aAST.findFirstToken(TokenTypes.IDENT).getText());
103 }
104
105 /** Called when we leave class definition. */
106 private void leaveClassDef()
107 {
108 mChecking = ((Boolean) mCheckingStack.pop()).booleanValue();
109 }
110
111 /**
112 * Checks variable definition.
113 * @param aAST variable def node for check.
114 */
115 private void visitVariableDef(DetailAST aAST)
116 {
117 if (mChecking && aAST.getParent().getType() == TokenTypes.OBJBLOCK) {
118 DetailAST modifiersAST = aAST.findFirstToken(TokenTypes.MODIFIERS);
119
120 if (!(modifiersAST.findFirstToken(TokenTypes.FINAL) != null)) {
121 log(aAST.getLineNo(), aAST.getColumnNo(), "mutable.exception",
122 aAST.findFirstToken(TokenTypes.IDENT).getText());
123 }
124 }
125 }
126
127 /**
128 * @param aClassName class name to check
129 * @return true if a given class name confirms specified format
130 */
131 private boolean isExceptionClass(String aClassName)
132 {
133 return getRegexp().match(aClassName);
134 }
135 }