1
2
3
4 package net.sourceforge.pmd.lang.java.rule.design;
5
6 import net.sourceforge.pmd.lang.ast.Node;
7 import net.sourceforge.pmd.lang.java.ast.ASTConditionalAndExpression;
8 import net.sourceforge.pmd.lang.java.ast.ASTConditionalExpression;
9 import net.sourceforge.pmd.lang.java.ast.ASTConditionalOrExpression;
10 import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression;
11 import net.sourceforge.pmd.lang.java.ast.ASTExpression;
12 import net.sourceforge.pmd.lang.java.ast.ASTIfStatement;
13 import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
14 import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
15 import net.sourceforge.pmd.lang.java.ast.ASTUnaryExpressionNotPlusMinus;
16 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
17 import net.sourceforge.pmd.lang.rule.properties.BooleanProperty;
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 public class ConfusingTernaryRule extends AbstractJavaRule {
53 private static BooleanProperty ignoreElseIfProperty = new BooleanProperty("ignoreElseIf",
54 "Ignore conditions with an else-if case",
55 Boolean.FALSE, 0);
56
57 public ConfusingTernaryRule() {
58 super();
59 definePropertyDescriptor(ignoreElseIfProperty);
60 }
61
62 public Object visit(ASTIfStatement node, Object data) {
63
64 if (node.jjtGetNumChildren() == 3) {
65 Node inode = node.jjtGetChild(0);
66 if (inode instanceof ASTExpression && inode.jjtGetNumChildren() == 1) {
67 Node jnode = inode.jjtGetChild(0);
68 if (isMatch(jnode)) {
69
70 if (!getProperty(ignoreElseIfProperty)
71 || !(node.jjtGetChild(2).jjtGetChild(0) instanceof ASTIfStatement)) {
72 addViolation(data, node);
73 }
74 }
75 }
76 }
77 return super.visit(node, data);
78 }
79
80 public Object visit(ASTConditionalExpression node, Object data) {
81
82 if (node.jjtGetNumChildren() > 0) {
83 Node inode = node.jjtGetChild(0);
84 if (isMatch(inode)) {
85 addViolation(data, node);
86 }
87 }
88 return super.visit(node, data);
89 }
90
91
92 private static boolean isMatch(Node node) {
93 return
94 isUnaryNot(node) ||
95 isNotEquals(node) ||
96 isConditionalWithAllMatches(node) ||
97 isParenthesisAroundMatch(node);
98 }
99
100 private static boolean isUnaryNot(Node node) {
101
102 return
103 node instanceof ASTUnaryExpressionNotPlusMinus &&
104 "!".equals(node.getImage());
105 }
106
107 private static boolean isNotEquals(Node node) {
108
109 return
110 node instanceof ASTEqualityExpression &&
111 "!=".equals(node.getImage());
112 }
113
114 private static boolean isConditionalWithAllMatches(Node node) {
115
116 if (!(node instanceof ASTConditionalAndExpression) &&
117 !(node instanceof ASTConditionalOrExpression)) {
118 return false;
119 }
120 int n = node.jjtGetNumChildren();
121 if (n <= 0) {
122 return false;
123 }
124 for (int i = 0; i < n; i++) {
125 Node inode = node.jjtGetChild(i);
126
127 if (!isMatch(inode)) {
128 return false;
129 }
130 }
131
132 return true;
133 }
134
135 private static boolean isParenthesisAroundMatch(Node node) {
136
137 if (!(node instanceof ASTPrimaryExpression) ||
138 (node.jjtGetNumChildren() != 1)) {
139 return false;
140 }
141 Node inode = node.jjtGetChild(0);
142 if (!(inode instanceof ASTPrimaryPrefix) ||
143 (inode.jjtGetNumChildren() != 1)) {
144 return false;
145 }
146 Node jnode = inode.jjtGetChild(0);
147 if (!(jnode instanceof ASTExpression) ||
148 (jnode.jjtGetNumChildren() != 1)) {
149 return false;
150 }
151 Node knode = jnode.jjtGetChild(0);
152
153 return isMatch(knode);
154 }
155 }