View Javadoc
1   /*
2    * SPDX-FileCopyrightText: Copyright (c) 2011-2026 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.puppycrawl.tools.checkstyle.api.AbstractCheck;
10  import com.puppycrawl.tools.checkstyle.api.DetailAST;
11  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
12  import java.util.ArrayList;
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   *
21   * @since 0.6
22   */
23  public final class MethodsOrderCheck extends AbstractCheck {
24  
25      @Override
26      public int[] getDefaultTokens() {
27          return new int[]{
28              TokenTypes.CLASS_DEF,
29              TokenTypes.ENUM_DEF,
30          };
31      }
32  
33      @Override
34      public int[] getAcceptableTokens() {
35          return this.getDefaultTokens();
36      }
37  
38      @Override
39      public int[] getRequiredTokens() {
40          return this.getDefaultTokens();
41      }
42  
43      @Override
44      public void visitToken(final DetailAST ast) {
45          if (ast.getType() == TokenTypes.CLASS_DEF
46              || ast.getType() == TokenTypes.ENUM_DEF) {
47              this.checkClass(ast);
48          }
49      }
50  
51      /**
52       * Checks class definition to satisfy the rule.
53       * @param node Tree node, containing class definition (CLASS_DEF)
54       */
55      private void checkClass(final DetailAST node) {
56          final DetailAST obj = node.findFirstToken(TokenTypes.OBJBLOCK);
57          if (obj != null) {
58              this.checkOrder(
59                  MethodsOrderCheck.findAllChildren(
60                      obj, TokenTypes.METHOD_DEF
61                  )
62              );
63          }
64      }
65  
66      /**
67       * Checks order of methods.
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 = new ArrayList<>(base.getChildCount());
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         /**
155          * PUBLIC method modifier.
156          */
157         PUB(TokenTypes.LITERAL_PUBLIC, 1),
158 
159         /**
160          * PROTECTED method modifier.
161          */
162         PROT(TokenTypes.LITERAL_PROTECTED, 2),
163 
164         /**
165          * DEFAULT method modifier.
166          * No correspondent constant in TokenType.
167          */
168         DEF(-1, 3),
169 
170         /**
171          * PRIVATE method modifier.
172          */
173         PRIV(TokenTypes.LITERAL_PRIVATE, 4);
174 
175         /**
176          * Convenient map of {@code TokenType} on {@code Modifiers}.
177          */
178         private static Map<Integer, MethodsOrderCheck.Modifiers> mdos;
179 
180         static {
181             MethodsOrderCheck.Modifiers.mdos =
182                 ImmutableMap.<Integer, MethodsOrderCheck.Modifiers>builder()
183                     .put(PUB.getType(), PUB)
184                     .put(PROT.getType(), PROT)
185                     .put(-1, DEF)
186                     .put(PRIV.getType(), PRIV)
187                     .build();
188         }
189 
190         /**
191          * TokenType.
192          */
193         private final Integer type;
194 
195         /**
196          * Order of modifier.
197          */
198         private final int order;
199 
200         /**
201          * Constructor.
202          * @param type TokenType of DetailAST which represents modifier
203          * @param ord Order of the modifier in class definition
204          */
205         Modifiers(final Integer type, final Integer ord) {
206             this.type = type;
207             this.order = ord;
208         }
209 
210         /**
211          * TokenType.
212          * @return TokenType
213          */
214         int getType() {
215             return this.type;
216         }
217 
218         /**
219          * Order of modifier.
220          * @return Order number
221          */
222         int getOrder() {
223             return this.order;
224         }
225     }
226 }