1 /*
2 * Copyright (c) 2011-2024 Qulice.com
3 *
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met: 1) Redistributions of source code must retain the above
9 * copyright notice, this list of conditions and the following
10 * disclaimer. 2) Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following
12 * disclaimer in the documentation and/or other materials provided
13 * with the distribution. 3) Neither the name of the Qulice.com nor
14 * the names of its contributors may be used to endorse or promote
15 * products derived from this software without specific prior written
16 * permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
20 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22 * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29 * OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31 package com.qulice.checkstyle;
32
33 import com.google.common.base.Optional;
34 import com.google.common.collect.ImmutableMap;
35 import com.google.common.collect.Lists;
36 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
37 import com.puppycrawl.tools.checkstyle.api.DetailAST;
38 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
39 import java.util.List;
40 import java.util.Map;
41
42 /**
43 * Checks the order of methods declaration.
44 *
45 * Right order is: public, protected and private
46 * @since 0.6
47 */
48 public final class MethodsOrderCheck extends AbstractCheck {
49
50 @Override
51 public int[] getDefaultTokens() {
52 return new int[]{
53 TokenTypes.CLASS_DEF,
54 TokenTypes.ENUM_DEF,
55 };
56 }
57
58 @Override
59 public int[] getAcceptableTokens() {
60 return this.getDefaultTokens();
61 }
62
63 @Override
64 public int[] getRequiredTokens() {
65 return this.getDefaultTokens();
66 }
67
68 @Override
69 public void visitToken(final DetailAST ast) {
70 if (ast.getType() == TokenTypes.CLASS_DEF
71 || ast.getType() == TokenTypes.ENUM_DEF) {
72 this.checkClass(ast);
73 }
74 }
75
76 /**
77 * Checks class definition to satisfy the rule.
78 * @param node Tree node, containing class definition (CLASS_DEF).
79 */
80 private void checkClass(final DetailAST node) {
81 final DetailAST obj = node.findFirstToken(TokenTypes.OBJBLOCK);
82 if (obj != null) {
83 this.checkOrder(
84 MethodsOrderCheck.findAllChildren(
85 obj, TokenTypes.METHOD_DEF
86 )
87 );
88 }
89 }
90
91 /**
92 * Checks order of methods.
93 *
94 * @param methods Nodes representing class methods
95 */
96 private void checkOrder(final Iterable<DetailAST> methods) {
97 MethodsOrderCheck.Modifiers prev = MethodsOrderCheck.Modifiers.PUB;
98 for (final DetailAST method : methods) {
99 final MethodsOrderCheck.Modifiers mtype =
100 MethodsOrderCheck.getModifierType(method);
101 if (mtype.getOrder() < prev.getOrder()) {
102 this.log(
103 method.getLineNo(),
104 "Wrong method declaration order"
105 );
106 } else {
107 prev = mtype;
108 }
109 }
110 }
111
112 /**
113 * Get method modifier as enum {@code Modifiers}.
114 * @param method DetailAST of method
115 * @return Element of {@code Modifiers} enum
116 */
117 private static MethodsOrderCheck.Modifiers getModifierType(
118 final DetailAST method
119 ) {
120 final DetailAST modifiers = method.findFirstToken(TokenTypes.MODIFIERS);
121 final DetailAST modifier = Optional.fromNullable(
122 modifiers.findFirstToken(
123 MethodsOrderCheck.Modifiers.PUB.getType()
124 )
125 ).or(
126 Optional.fromNullable(
127 modifiers.findFirstToken(
128 MethodsOrderCheck.Modifiers.PROT.getType()
129 )
130 )
131 ).or(
132 Optional.fromNullable(
133 modifiers.findFirstToken(
134 MethodsOrderCheck.Modifiers.PRIV.getType()
135 )
136 )
137 ).orNull();
138 final MethodsOrderCheck.Modifiers mod;
139 if (modifier == null) {
140 mod = MethodsOrderCheck.Modifiers.DEF;
141 } else {
142 mod = getByType(modifier.getType());
143 }
144 return mod;
145 }
146
147 /**
148 * Search for all children of given type.
149 * @param base Parent node to start from
150 * @param type Node type
151 * @return Iterable
152 */
153 private static Iterable<DetailAST> findAllChildren(final DetailAST base,
154 final int type) {
155 final List<DetailAST> children = Lists.newArrayList();
156 DetailAST child = base.getFirstChild();
157 while (child != null) {
158 if (child.getType() == type) {
159 children.add(child);
160 }
161 child = child.getNextSibling();
162 }
163 return children;
164 }
165
166 /**
167 * Get Modifiers enum constant by TokenType id.
168 * @param type TokenType
169 * @return Modifiers constant
170 */
171 private static MethodsOrderCheck.Modifiers getByType(final int type) {
172 return MethodsOrderCheck.Modifiers.mdos.get(type);
173 }
174
175 /**
176 * Enumeration for constants of method modifiers.
177 */
178 private enum Modifiers {
179 /**
180 * PUBLIC method modifier.
181 */
182 PUB(TokenTypes.LITERAL_PUBLIC, 1),
183
184 /**
185 * PROTECTED method modifier.
186 */
187 PROT(TokenTypes.LITERAL_PROTECTED, 2),
188
189 /**
190 * DEFAULT method modifier.
191 * No correspondent constant in TokenType.
192 */
193 DEF(-1, 3),
194
195 /**
196 * PRIVATE method modifier.
197 */
198 PRIV(TokenTypes.LITERAL_PRIVATE, 4);
199
200 /**
201 * Convenient map of {@code TokenType} on {@code Modifiers}.
202 */
203 private static Map<Integer, MethodsOrderCheck.Modifiers> mdos;
204
205 static {
206 MethodsOrderCheck.Modifiers.mdos =
207 ImmutableMap.<Integer, MethodsOrderCheck.Modifiers>builder()
208 .put(PUB.getType(), PUB)
209 .put(PROT.getType(), PROT)
210 .put(-1, DEF)
211 .put(PRIV.getType(), PRIV)
212 .build();
213 }
214
215 /**
216 * TokenType.
217 */
218 private final Integer type;
219
220 /**
221 * Order of modifier.
222 */
223 private final int order;
224
225 /**
226 * Constructor.
227 * @param typ TokenType of DetailAST which represents modifier
228 * @param ord Order of the modifier in class definition
229 */
230 Modifiers(final Integer typ, final Integer ord) {
231 this.type = typ;
232 this.order = ord;
233 }
234
235 /**
236 * TokenType.
237 * @return TokenType
238 */
239 public int getType() {
240 return this.type;
241 }
242
243 /**
244 * Order of modifier.
245 * @return Order number
246 */
247 public int getOrder() {
248 return this.order;
249 }
250 }
251 }