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