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