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