1
2
3
4
5 package com.qulice.pmd.rules;
6
7 import net.sourceforge.pmd.lang.java.ast.ASTExpression;
8 import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression;
9 import net.sourceforge.pmd.lang.java.ast.ASTMethodCall;
10 import net.sourceforge.pmd.lang.java.ast.ASTNumericLiteral;
11 import net.sourceforge.pmd.lang.java.ast.BinaryOp;
12 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule;
13 import net.sourceforge.pmd.lang.java.types.JTypeMirror;
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 public final class UseStringIsEmptyRule extends AbstractJavaRulechainRule {
30
31
32
33
34 public UseStringIsEmptyRule() {
35 super(ASTInfixExpression.class);
36 }
37
38 @Override
39 public Object visit(final ASTInfixExpression expr, final Object data) {
40 final ASTExpression left = expr.getLeftOperand();
41 final ASTExpression right = expr.getRightOperand();
42 if (isEmptyEquivalent(expr.getOperator(), left, right)
43 || isEmptyEquivalent(mirror(expr.getOperator()), right, left)
44 ) {
45 asCtx(data).addViolation(expr);
46 }
47 return data;
48 }
49
50 private static boolean isEmptyEquivalent(
51 final BinaryOp operator,
52 final ASTExpression length,
53 final ASTExpression literal
54 ) {
55 return isStringLength(length) && isWhitelisted(operator, literal);
56 }
57
58 private static boolean isWhitelisted(
59 final BinaryOp operator,
60 final ASTExpression literal
61 ) {
62 boolean result = false;
63 if (literal instanceof ASTNumericLiteral lit) {
64 final String image = lit.getImage();
65 result = switch (operator) {
66 case EQ, NE, GT, LE -> "0".equals(image);
67 case LT, GE -> "1".equals(image);
68 default -> false;
69 };
70 }
71 return result;
72 }
73
74 private static BinaryOp mirror(final BinaryOp operator) {
75 return switch (operator) {
76 case GT -> BinaryOp.LT;
77 case LT -> BinaryOp.GT;
78 case GE -> BinaryOp.LE;
79 case LE -> BinaryOp.GE;
80 default -> operator;
81 };
82 }
83
84 private static boolean isStringLength(final ASTExpression expr) {
85 boolean result = false;
86 if (expr instanceof ASTMethodCall call && call.getQualifier() != null) {
87 result = "length".equals(call.getMethodName())
88 && call.getArguments().isEmpty()
89 && isStringExpression(call.getQualifier());
90 }
91 return result;
92 }
93
94 private static boolean isStringExpression(final ASTExpression expr) {
95 final JTypeMirror type = expr.getTypeMirror();
96 return type.isClassOrInterface()
97 && String.class.getName().equals(type.toString());
98 }
99 }