View Javadoc
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 }