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   * <p>Right order is: public, protected and private</p>
20   *
21   * @since 0.6
22   */
23  public final class MethodsOrderCheck extends AbstractCheck {
24  
25      /**
26       * Default constructor.
27       */
28      public MethodsOrderCheck() {
29          // nothing to initialize
30      }
31  
32      @Override
33      public int[] getDefaultTokens() {
34          return new int[]{
35              TokenTypes.CLASS_DEF,
36              TokenTypes.ENUM_DEF,
37          };
38      }
39  
40      @Override
41      public int[] getAcceptableTokens() {
42          return this.getDefaultTokens();
43      }
44  
45      @Override
46      public int[] getRequiredTokens() {
47          return this.getDefaultTokens();
48      }
49  
50      @Override
51      public void visitToken(final DetailAST ast) {
52          if (ast.getType() == TokenTypes.CLASS_DEF
53              || ast.getType() == TokenTypes.ENUM_DEF) {
54              this.checkClass(ast);
55          }
56      }
57  
58      private void checkClass(final DetailAST node) {
59          final DetailAST obj = node.findFirstToken(TokenTypes.OBJBLOCK);
60          if (obj != null) {
61              this.checkOrder(
62                  MethodsOrderCheck.findAllChildren(
63                      obj, TokenTypes.METHOD_DEF
64                  )
65              );
66          }
67      }
68  
69      private void checkOrder(final Iterable<DetailAST> methods) {
70          MethodsOrderCheck.Modifiers prev = MethodsOrderCheck.Modifiers.PUB;
71          for (final DetailAST method : methods) {
72              final MethodsOrderCheck.Modifiers mtype =
73                  MethodsOrderCheck.getModifierType(method);
74              if (mtype.getOrder() < prev.getOrder()) {
75                  this.log(
76                      method.getLineNo(),
77                      "Wrong method declaration order"
78                  );
79              } else {
80                  prev = mtype;
81              }
82          }
83      }
84  
85      private static MethodsOrderCheck.Modifiers getModifierType(
86          final DetailAST method
87      ) {
88          final DetailAST modifiers = method.findFirstToken(TokenTypes.MODIFIERS);
89          final DetailAST modifier = Optional.fromNullable(
90              modifiers.findFirstToken(
91                  MethodsOrderCheck.Modifiers.PUB.getType()
92              )
93          ).or(
94              Optional.fromNullable(
95                  modifiers.findFirstToken(
96                      MethodsOrderCheck.Modifiers.PROT.getType()
97                  )
98              )
99          ).or(
100             Optional.fromNullable(
101                 modifiers.findFirstToken(
102                     MethodsOrderCheck.Modifiers.PRIV.getType()
103                 )
104             )
105         ).orNull();
106         final MethodsOrderCheck.Modifiers mod;
107         if (modifier == null) {
108             mod = MethodsOrderCheck.Modifiers.DEF;
109         } else {
110             mod = getByType(modifier.getType());
111         }
112         return mod;
113     }
114 
115     private static Iterable<DetailAST> findAllChildren(final DetailAST base,
116         final int type) {
117         final List<DetailAST> children = new ArrayList<>(base.getChildCount());
118         DetailAST child = base.getFirstChild();
119         while (child != null) {
120             if (child.getType() == type) {
121                 children.add(child);
122             }
123             child = child.getNextSibling();
124         }
125         return children;
126     }
127 
128     private static MethodsOrderCheck.Modifiers getByType(final int type) {
129         return MethodsOrderCheck.Modifiers.mdos.get(type);
130     }
131 
132     /**
133      * Enumeration for constants of method modifiers.
134      */
135     private enum Modifiers {
136 
137         /**
138          * PUBLIC method modifier.
139          */
140         PUB(TokenTypes.LITERAL_PUBLIC, 1),
141 
142         /**
143          * PROTECTED method modifier.
144          */
145         PROT(TokenTypes.LITERAL_PROTECTED, 2),
146 
147         /**
148          * DEFAULT method modifier.
149          * No correspondent constant in TokenType.
150          */
151         DEF(-1, 3),
152 
153         /**
154          * PRIVATE method modifier.
155          */
156         PRIV(TokenTypes.LITERAL_PRIVATE, 4);
157 
158         /**
159          * Convenient map of {@code TokenType} on {@code Modifiers}.
160          */
161         private static Map<Integer, MethodsOrderCheck.Modifiers> mdos;
162 
163         static {
164             MethodsOrderCheck.Modifiers.mdos =
165                 ImmutableMap.<Integer, MethodsOrderCheck.Modifiers>builder()
166                     .put(PUB.getType(), PUB)
167                     .put(PROT.getType(), PROT)
168                     .put(-1, DEF)
169                     .put(PRIV.getType(), PRIV)
170                     .build();
171         }
172 
173         /**
174          * TokenType.
175          */
176         private final Integer type;
177 
178         /**
179          * Order of modifier.
180          */
181         private final int order;
182 
183         /**
184          * Constructor.
185          *
186          * @param type TokenType of DetailAST which represents modifier
187          * @param ord Order of the modifier in class definition
188          */
189         Modifiers(final Integer type, final Integer ord) {
190             this.type = type;
191             this.order = ord;
192         }
193 
194         /**
195          * TokenType.
196          *
197          * @return TokenType
198          */
199         int getType() {
200             return this.type;
201         }
202 
203         /**
204          * Order of modifier.
205          *
206          * @return Order number
207          */
208         int getOrder() {
209             return this.order;
210         }
211     }
212 }