1 /**
2 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3 */
4 package net.sourceforge.pmd.rules.design;
5
6 import java.util.ArrayList;
7 import java.util.List;
8 import java.util.Map;
9
10 import net.sourceforge.pmd.AbstractJavaRule;
11 import net.sourceforge.pmd.RuleContext;
12 import net.sourceforge.pmd.ast.ASTArgumentList;
13 import net.sourceforge.pmd.ast.ASTCastExpression;
14 import net.sourceforge.pmd.ast.ASTCatchStatement;
15 import net.sourceforge.pmd.ast.ASTClassOrInterfaceType;
16 import net.sourceforge.pmd.ast.ASTName;
17 import net.sourceforge.pmd.ast.ASTPrimaryExpression;
18 import net.sourceforge.pmd.ast.ASTPrimaryPrefix;
19 import net.sourceforge.pmd.ast.ASTThrowStatement;
20 import net.sourceforge.pmd.ast.ASTVariableDeclarator;
21 import net.sourceforge.pmd.ast.Node;
22 import net.sourceforge.pmd.ast.SimpleNode;
23 import net.sourceforge.pmd.symboltable.NameOccurrence;
24 import net.sourceforge.pmd.symboltable.VariableNameDeclaration;
25
26 import org.jaxen.JaxenException;
27
28 /**
29 *
30 * @author Unknown,
31 * @author Romain PELISSE, belaran@gmail.com, fix for bug 1808110
32 *
33 */
34 public class PreserveStackTrace extends AbstractJavaRule {
35
36 private List<ASTName> nameNodes = new ArrayList<ASTName>();
37
38
39
40 private static final String FIND_THROWABLE_INSTANCE =
41 "./VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/AllocationExpression" +
42 "[ClassOrInterfaceType[contains(@Image,'Exception')] and Arguments[count(*)=0]]";
43
44 private static final String ILLEGAL_STATE_EXCEPTION = "IllegalStateException";
45 private static final String FILL_IN_STACKTRACE = ".fillInStackTrace";
46
47 @Override
48 public Object visit(ASTCatchStatement catchStmt, Object data) {
49 String target = ((SimpleNode)catchStmt.jjtGetChild(0).jjtGetChild(1)).getImage();
50
51 List<ASTThrowStatement> lstThrowStatements = catchStmt.findChildrenOfType(ASTThrowStatement.class);
52 for (ASTThrowStatement throwStatement : lstThrowStatements) {
53 Node n = throwStatement.jjtGetChild(0).jjtGetChild(0);
54 if (n.getClass().equals(ASTCastExpression.class)) {
55 ASTPrimaryExpression expr = (ASTPrimaryExpression) n.jjtGetChild(1);
56 if (expr.jjtGetNumChildren() > 1 && expr.jjtGetChild(1).getClass().equals(ASTPrimaryPrefix.class)) {
57 RuleContext ctx = (RuleContext) data;
58 addViolation(ctx, throwStatement);
59 }
60 continue;
61 }
62
63 if ( ! isThrownExceptionOfType(throwStatement,ILLEGAL_STATE_EXCEPTION) ) {
64
65 ASTArgumentList args = throwStatement.getFirstChildOfType(ASTArgumentList.class);
66
67 if (args != null) {
68 ck(data, target, throwStatement, args);
69 }
70 else {
71 SimpleNode child = (SimpleNode)throwStatement.jjtGetChild(0);
72 while (child != null && child.jjtGetNumChildren() > 0
73 && !child.getClass().equals(ASTName.class)) {
74 child = (SimpleNode)child.jjtGetChild(0);
75 }
76 if (child != null){
77 if( child.getClass().equals(ASTName.class) && !target.equals(child.getImage()) && !child.hasImageEqualTo(target + FILL_IN_STACKTRACE)) {
78 Map<VariableNameDeclaration, List<NameOccurrence>> vars = ((ASTName) child).getScope().getVariableDeclarations();
79 for (VariableNameDeclaration decl: vars.keySet()) {
80 args = ((SimpleNode)decl.getNode().jjtGetParent())
81 .getFirstChildOfType(ASTArgumentList.class);
82 if (args != null) {
83 ck(data, target, throwStatement, args);
84 }
85 }
86 } else if(child.getClass().equals(ASTClassOrInterfaceType.class)){
87 addViolation(data, throwStatement);
88 }
89 }
90 }
91 }
92
93 }
94 return super.visit(catchStmt, data);
95 }
96
97 @Override
98 public Object visit(ASTVariableDeclarator node, Object data) {
99
100 try {
101 List<Node> nodes = node.findChildNodesWithXPath(FIND_THROWABLE_INSTANCE);
102 if (nodes.size() > 0) {
103 String variableName = ((SimpleNode)node.jjtGetChild(0)).getImage();
104 ASTCatchStatement catchStmt = node.getFirstParentOfType(ASTCatchStatement.class);
105
106 while (catchStmt != null) {
107 List<SimpleNode> violations = catchStmt.findChildNodesWithXPath("//Expression/PrimaryExpression/PrimaryPrefix/Name[@Image = '" + variableName + "']");
108 if (violations != null && violations.size() > 0) {
109
110
111 if (!useInitCause(violations.get(0), catchStmt)) {
112 addViolation(data, node);
113 }
114 }
115
116
117 catchStmt = catchStmt.getFirstParentOfType(ASTCatchStatement.class);
118 }
119 }
120 return super.visit(node, data);
121 } catch (JaxenException e) {
122
123 throw new IllegalStateException(e);
124 }
125 }
126
127 private boolean useInitCause(SimpleNode node, ASTCatchStatement catchStmt) throws JaxenException {
128
129 if ( node != null && node.getImage() != null )
130 {
131 List <Node> nodes = catchStmt.findChildNodesWithXPath("descendant::StatementExpression/PrimaryExpression/PrimaryPrefix/Name[@Image = '" + node.getImage() + ".initCause']");
132 if ( nodes != null && nodes.size() > 0 )
133 {
134 return true;
135 }
136 }
137 return false;
138 }
139
140 private boolean isThrownExceptionOfType(ASTThrowStatement throwStatement,String type) {
141 boolean status = false;
142 try {
143 List<Node> results = throwStatement.findChildNodesWithXPath("Expression/PrimaryExpression/PrimaryPrefix/AllocationExpression/ClassOrInterfaceType[@Image = '" + type + "']");
144
145 if ( results != null && results.size() == 1 ) {
146 status = true;
147 }
148 } catch (JaxenException e) {
149
150 e.printStackTrace();
151 }
152 return status;
153 }
154
155 private void ck(Object data, String target, ASTThrowStatement throwStatement,
156 ASTArgumentList args) {
157 boolean match = false;
158 nameNodes.clear();
159 args.findChildrenOfType(ASTName.class, nameNodes);
160 for (ASTName nameNode : nameNodes) {
161 if (target.equals(nameNode.getImage())) {
162 match = true;
163 break;
164 }
165 }
166 if ( ! match) {
167 RuleContext ctx = (RuleContext) data;
168 addViolation(ctx, throwStatement);
169 }
170 }
171 }