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 public final class DiamondOperatorCheck extends AbstractCheck {
48
49 @Override
50 public int[] getDefaultTokens() {
51 return new int[]{TokenTypes.VARIABLE_DEF};
52 }
53
54 @Override
55 public int[] getAcceptableTokens() {
56 return this.getDefaultTokens();
57 }
58
59 @Override
60 public int[] getRequiredTokens() {
61 return this.getDefaultTokens();
62 }
63
64 @Override
65 public void visitToken(final DetailAST node) {
66 final DetailAST generic = DiamondOperatorCheck
67 .findFirstChildNodeOfType(
68 node.findFirstToken(TokenTypes.TYPE), TokenTypes.TYPE_ARGUMENTS
69 );
70 final DetailAST assign = node.findFirstToken(TokenTypes.ASSIGN);
71 final DetailAST instance;
72 if (assign == null || generic == null) {
73 instance = null;
74 } else {
75 instance = assign.getFirstChild().getFirstChild();
76 }
77 if (instance != null && instance.getType() == TokenTypes.LITERAL_NEW
78 && DiamondOperatorCheck.validUsage(instance)) {
79 final DetailAST type =
80 DiamondOperatorCheck.findFirstChildNodeOfType(
81 instance, TokenTypes.TYPE_ARGUMENTS
82 );
83 if (type != null && !DiamondOperatorCheck.isDiamondOperatorUsed(type)) {
84 log(type, "Use diamond operator");
85 }
86 }
87 }
88
89
90
91
92
93
94
95 private static boolean validUsage(final DetailAST node) {
96 return DiamondOperatorCheck.isNotObjectBlock(node)
97 && DiamondOperatorCheck.isNotArray(node)
98 && !DiamondOperatorCheck.isInitUsingDiamond(node);
99 }
100
101
102
103
104
105
106
107 private static boolean isNotArray(final DetailAST node) {
108 return node.findFirstToken(TokenTypes.ARRAY_DECLARATOR) == null;
109 }
110
111
112
113
114
115
116
117 private static boolean isNotObjectBlock(final DetailAST node) {
118 return node.getLastChild().getType() != TokenTypes.OBJBLOCK;
119 }
120
121
122
123
124
125
126
127 private static boolean isInitUsingDiamond(final DetailAST node) {
128 final DetailAST init = node.findFirstToken(TokenTypes.ELIST);
129 boolean typed = false;
130 if (init != null) {
131 final DetailAST inst = DiamondOperatorCheck.secondChild(init);
132 if (inst != null && inst.getType() == TokenTypes.LITERAL_NEW) {
133 typed =
134 DiamondOperatorCheck.isDiamondOperatorUsed(
135 inst.findFirstToken(TokenTypes.TYPE_ARGUMENTS)
136 );
137 }
138 }
139 return typed;
140 }
141
142
143
144
145
146
147
148 private static DetailAST secondChild(final DetailAST node) {
149 DetailAST result = null;
150 if (node != null) {
151 final DetailAST first = node.getFirstChild();
152 if (first != null) {
153 result = first.getFirstChild();
154 }
155 }
156 return result;
157 }
158
159
160
161
162
163
164
165 private static boolean isDiamondOperatorUsed(final DetailAST node) {
166 return node != null && node.getChildCount() == 2
167 && node.getFirstChild().getType() == TokenTypes.GENERIC_START
168 && node.getLastChild().getType() == TokenTypes.GENERIC_END;
169 }
170
171
172
173
174
175
176
177
178 private static DetailAST findFirstChildNodeOfType(
179 final DetailAST node, final int type
180 ) {
181 DetailAST result = node.findFirstToken(type);
182 if (result == null) {
183 final DetailAST child = node.getFirstChild();
184 if (child != null) {
185 result = DiamondOperatorCheck
186 .findFirstChildNodeOfType(child, type);
187 }
188 }
189 return result;
190 }
191 }