1
2
3
4
5 package com.qulice.checkstyle;
6
7 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
8 import com.puppycrawl.tools.checkstyle.api.DetailAST;
9 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
10
11
12
13
14
15
16
17
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 public final class DiamondOperatorCheck extends AbstractCheck {
50
51
52
53
54 public DiamondOperatorCheck() {
55
56 }
57
58 @Override
59 public int[] getDefaultTokens() {
60 return new int[]{TokenTypes.VARIABLE_DEF};
61 }
62
63 @Override
64 public int[] getAcceptableTokens() {
65 return this.getDefaultTokens();
66 }
67
68 @Override
69 public int[] getRequiredTokens() {
70 return this.getDefaultTokens();
71 }
72
73 @Override
74 public void visitToken(final DetailAST node) {
75 final DetailAST generic = DiamondOperatorCheck.findFirstChildNodeOfType(
76 node.findFirstToken(TokenTypes.TYPE), TokenTypes.TYPE_ARGUMENTS
77 );
78 final DetailAST assign = node.findFirstToken(TokenTypes.ASSIGN);
79 final DetailAST instance;
80 if (assign == null || generic == null) {
81 instance = null;
82 } else {
83 instance = assign.getFirstChild().getFirstChild();
84 }
85 if (instance != null && instance.getType() == TokenTypes.LITERAL_NEW
86 && DiamondOperatorCheck.validUsage(instance)) {
87 final DetailAST type =
88 DiamondOperatorCheck.findFirstChildNodeOfType(
89 instance, TokenTypes.TYPE_ARGUMENTS
90 );
91 if (type != null && !DiamondOperatorCheck.isDiamondOperatorUsed(type)) {
92 log(type, "Use diamond operator");
93 }
94 }
95 }
96
97 private static boolean validUsage(final DetailAST node) {
98 return DiamondOperatorCheck.isNotObjectBlock(node)
99 && DiamondOperatorCheck.isNotArray(node)
100 && !DiamondOperatorCheck.isInitUsingDiamond(node);
101 }
102
103 private static boolean isNotArray(final DetailAST node) {
104 return node.findFirstToken(TokenTypes.ARRAY_DECLARATOR) == null;
105 }
106
107 private static boolean isNotObjectBlock(final DetailAST node) {
108 return node.getLastChild().getType() != TokenTypes.OBJBLOCK;
109 }
110
111 private static boolean isInitUsingDiamond(final DetailAST node) {
112 final DetailAST init = node.findFirstToken(TokenTypes.ELIST);
113 boolean typed = false;
114 if (init != null) {
115 final DetailAST inst = DiamondOperatorCheck.secondChild(init);
116 if (inst != null && inst.getType() == TokenTypes.LITERAL_NEW) {
117 typed =
118 DiamondOperatorCheck.isDiamondOperatorUsed(
119 inst.findFirstToken(TokenTypes.TYPE_ARGUMENTS)
120 );
121 }
122 }
123 return typed;
124 }
125
126 private static DetailAST secondChild(final DetailAST node) {
127 DetailAST result = null;
128 if (node != null) {
129 final DetailAST first = node.getFirstChild();
130 if (first != null) {
131 result = first.getFirstChild();
132 }
133 }
134 return result;
135 }
136
137 private static boolean isDiamondOperatorUsed(final DetailAST node) {
138 return node != null && node.getChildCount() == 2
139 && node.getFirstChild().getType() == TokenTypes.GENERIC_START
140 && node.getLastChild().getType() == TokenTypes.GENERIC_END;
141 }
142
143 private static DetailAST findFirstChildNodeOfType(
144 final DetailAST node, final int type
145 ) {
146 DetailAST result = node.findFirstToken(type);
147 if (result == null) {
148 final DetailAST child = node.getFirstChild();
149 if (child != null) {
150 result = DiamondOperatorCheck
151 .findFirstChildNodeOfType(child, type);
152 }
153 }
154 return result;
155 }
156 }