View Javadoc
1   /*
2    * SPDX-FileCopyrightText: Copyright (c) 2011-2025 Yegor Bugayenko
3    * SPDX-License-Identifier: MIT
4    */
5   package com.qulice.checkstyle;
6   
7   import com.google.common.base.Optional;
8   import com.google.common.collect.ImmutableMap;
9   import com.google.common.collect.Lists;
10  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
11  import com.puppycrawl.tools.checkstyle.api.DetailAST;
12  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
13  import java.util.List;
14  import java.util.Map;
15  
16  /**
17   * Checks the order of methods declaration.
18   *
19   * Right order is: public, protected and private
20   * @since 0.6
21   */
22  public final class MethodsOrderCheck extends AbstractCheck {
23  
24      @Override
25      public int[] getDefaultTokens() {
26          return new int[]{
27              TokenTypes.CLASS_DEF,
28              TokenTypes.ENUM_DEF,
29          };
30      }
31  
32      @Override
33      public int[] getAcceptableTokens() {
34          return this.getDefaultTokens();
35      }
36  
37      @Override
38      public int[] getRequiredTokens() {
39          return this.getDefaultTokens();
40      }
41  
42      @Override
43      public void visitToken(final DetailAST ast) {
44          if (ast.getType() == TokenTypes.CLASS_DEF
45              || ast.getType() == TokenTypes.ENUM_DEF) {
46              this.checkClass(ast);
47          }
48      }
49  
50      /**
51       * Checks class definition to satisfy the rule.
52       * @param node Tree node, containing class definition (CLASS_DEF).
53       */
54      private void checkClass(final DetailAST node) {
55          final DetailAST obj = node.findFirstToken(TokenTypes.OBJBLOCK);
56          if (obj != null) {
57              this.checkOrder(
58                  MethodsOrderCheck.findAllChildren(
59                      obj, TokenTypes.METHOD_DEF
60                  )
61              );
62          }
63      }
64  
65      /**
66       * Checks order of methods.
67       *
68       * @param methods Nodes representing class methods
69       */
70      private void checkOrder(final Iterable<DetailAST> methods) {
71          MethodsOrderCheck.Modifiers prev = MethodsOrderCheck.Modifiers.PUB;
72          for (final DetailAST method : methods) {
73              final MethodsOrderCheck.Modifiers mtype =
74                  MethodsOrderCheck.getModifierType(method);
75              if (mtype.getOrder() < prev.getOrder()) {
76                  this.log(
77                      method.getLineNo(),
78                      "Wrong method declaration order"
79                  );
80              } else {
81                  prev = mtype;
82              }
83          }
84      }
85  
86      /**
87       * Get method modifier as enum {@code Modifiers}.
88       * @param method DetailAST of method
89       * @return Element of {@code Modifiers} enum
90       */
91      private static MethodsOrderCheck.Modifiers getModifierType(
92          final DetailAST method
93      ) {
94          final DetailAST modifiers = method.findFirstToken(TokenTypes.MODIFIERS);
95          final DetailAST modifier = Optional.fromNullable(
96              modifiers.findFirstToken(
97                  MethodsOrderCheck.Modifiers.PUB.getType()
98              )
99          ).or(
100             Optional.fromNullable(
101                 modifiers.findFirstToken(
102                     MethodsOrderCheck.Modifiers.PROT.getType()
103                 )
104             )
105         ).or(
106             Optional.fromNullable(
107                 modifiers.findFirstToken(
108                     MethodsOrderCheck.Modifiers.PRIV.getType()
109                 )
110             )
111         ).orNull();
112         final MethodsOrderCheck.Modifiers mod;
113         if (modifier == null) {
114             mod = MethodsOrderCheck.Modifiers.DEF;
115         } else {
116             mod = getByType(modifier.getType());
117         }
118         return mod;
119     }
120 
121     /**
122      * Search for all children of given type.
123      * @param base Parent node to start from
124      * @param type Node type
125      * @return Iterable
126      */
127     private static Iterable<DetailAST> findAllChildren(final DetailAST base,
128         final int type) {
129         final List<DetailAST> children = Lists.newArrayList();
130         DetailAST child = base.getFirstChild();
131         while (child != null) {
132             if (child.getType() == type) {
133                 children.add(child);
134             }
135             child = child.getNextSibling();
136         }
137         return children;
138     }
139 
140     /**
141      * Get Modifiers enum constant by TokenType id.
142      * @param type TokenType
143      * @return Modifiers constant
144      */
145     private static MethodsOrderCheck.Modifiers getByType(final int type) {
146         return MethodsOrderCheck.Modifiers.mdos.get(type);
147     }
148 
149     /**
150      * Enumeration for constants of method modifiers.
151      */
152     private enum Modifiers {
153         /**
154          * PUBLIC method modifier.
155          */
156         PUB(TokenTypes.LITERAL_PUBLIC, 1),
157 
158         /**
159          * PROTECTED method modifier.
160          */
161         PROT(TokenTypes.LITERAL_PROTECTED, 2),
162 
163         /**
164          * DEFAULT method modifier.
165          * No correspondent constant in TokenType.
166          */
167         DEF(-1, 3),
168 
169         /**
170          * PRIVATE method modifier.
171          */
172         PRIV(TokenTypes.LITERAL_PRIVATE, 4);
173 
174         /**
175          * Convenient map of {@code TokenType} on {@code Modifiers}.
176          */
177         private static Map<Integer, MethodsOrderCheck.Modifiers> mdos;
178 
179         static {
180             MethodsOrderCheck.Modifiers.mdos =
181                 ImmutableMap.<Integer, MethodsOrderCheck.Modifiers>builder()
182                     .put(PUB.getType(), PUB)
183                     .put(PROT.getType(), PROT)
184                     .put(-1, DEF)
185                     .put(PRIV.getType(), PRIV)
186                     .build();
187         }
188 
189         /**
190          * TokenType.
191          */
192         private final Integer type;
193 
194         /**
195          * Order of modifier.
196          */
197         private final int order;
198 
199         /**
200          * Constructor.
201          * @param type TokenType of DetailAST which represents modifier
202          * @param ord Order of the modifier in class definition
203          */
204         Modifiers(final Integer type, final Integer ord) {
205             this.type = type;
206             this.order = ord;
207         }
208 
209         /**
210          * TokenType.
211          * @return TokenType
212          */
213         public int getType() {
214             return this.type;
215         }
216 
217         /**
218          * Order of modifier.
219          * @return Order number
220          */
221         public int getOrder() {
222             return this.order;
223         }
224     }
225 }