Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
MutableExceptionCheck |
|
| 1.8888888888888888;1.889 |
1 | //////////////////////////////////////////////////////////////////////////////// | |
2 | // checkstyle: Checks Java source code for adherence to a set of rules. | |
3 | // Copyright (C) 2001-2014 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.FastStack; | |
23 | import com.puppycrawl.tools.checkstyle.api.TokenTypes; | |
24 | import com.puppycrawl.tools.checkstyle.checks.AbstractFormatCheck; | |
25 | ||
26 | /** | |
27 | * <p> Ensures that exceptions (defined as any class name conforming | |
28 | * to some regular expression) are immutable. That is, have only final | |
29 | * fields.</p> | |
30 | * <p> Rationale: Exception instances should represent an error | |
31 | * condition. Having non final fields not only allows the state to be | |
32 | * modified by accident and therefore mask the original condition but | |
33 | * also allows developers to accidentally forget to initialise state | |
34 | * thereby leading to code catching the exception to draw incorrect | |
35 | * conclusions based on the state.</p> | |
36 | * | |
37 | * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a> | |
38 | */ | |
39 | public final class MutableExceptionCheck extends AbstractFormatCheck | |
40 | { | |
41 | /** Default value for format property. */ | |
42 | private static final String DEFAULT_FORMAT = "^.*Exception$|^.*Error$"; | |
43 | /** Stack of checking information for classes. */ | |
44 | 1 | private final FastStack<Boolean> mCheckingStack = FastStack.newInstance(); |
45 | /** Should we check current class or not. */ | |
46 | private boolean mChecking; | |
47 | ||
48 | /** Creates new instance of the check. */ | |
49 | public MutableExceptionCheck() | |
50 | { | |
51 | 1 | super(DEFAULT_FORMAT); |
52 | 1 | } |
53 | ||
54 | @Override | |
55 | public int[] getDefaultTokens() | |
56 | { | |
57 | 1 | return new int[] {TokenTypes.CLASS_DEF, TokenTypes.VARIABLE_DEF}; |
58 | } | |
59 | ||
60 | @Override | |
61 | public int[] getRequiredTokens() | |
62 | { | |
63 | 0 | return getDefaultTokens(); |
64 | } | |
65 | ||
66 | @Override | |
67 | public void visitToken(DetailAST aAST) | |
68 | { | |
69 | 9 | switch (aAST.getType()) { |
70 | case TokenTypes.CLASS_DEF: | |
71 | 4 | visitClassDef(aAST); |
72 | 4 | break; |
73 | case TokenTypes.VARIABLE_DEF: | |
74 | 5 | visitVariableDef(aAST); |
75 | 5 | break; |
76 | default: | |
77 | 0 | throw new IllegalStateException(aAST.toString()); |
78 | } | |
79 | 9 | } |
80 | ||
81 | @Override | |
82 | public void leaveToken(DetailAST aAST) | |
83 | { | |
84 | 9 | switch (aAST.getType()) { |
85 | case TokenTypes.CLASS_DEF: | |
86 | 4 | leaveClassDef(); |
87 | 4 | break; |
88 | default: | |
89 | // Do nothing | |
90 | } | |
91 | 9 | } |
92 | ||
93 | /** | |
94 | * Called when we start processing class definition. | |
95 | * @param aAST class definition node | |
96 | */ | |
97 | private void visitClassDef(DetailAST aAST) | |
98 | { | |
99 | 4 | mCheckingStack.push(mChecking ? Boolean.TRUE : Boolean.FALSE); |
100 | 4 | mChecking = |
101 | isExceptionClass(aAST.findFirstToken(TokenTypes.IDENT).getText()); | |
102 | 4 | } |
103 | ||
104 | /** Called when we leave class definition. */ | |
105 | private void leaveClassDef() | |
106 | { | |
107 | 4 | mChecking = (mCheckingStack.pop()).booleanValue(); |
108 | 4 | } |
109 | ||
110 | /** | |
111 | * Checks variable definition. | |
112 | * @param aAST variable def node for check. | |
113 | */ | |
114 | private void visitVariableDef(DetailAST aAST) | |
115 | { | |
116 | 5 | if (mChecking && (aAST.getParent().getType() == TokenTypes.OBJBLOCK)) { |
117 | 3 | final DetailAST modifiersAST = |
118 | aAST.findFirstToken(TokenTypes.MODIFIERS); | |
119 | ||
120 | 3 | if (!(modifiersAST.findFirstToken(TokenTypes.FINAL) != null)) { |
121 | 2 | log(aAST.getLineNo(), aAST.getColumnNo(), "mutable.exception", |
122 | aAST.findFirstToken(TokenTypes.IDENT).getText()); | |
123 | } | |
124 | } | |
125 | 5 | } |
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 | 4 | return getRegexp().matcher(aClassName).find(); |
134 | } | |
135 | } |