View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2017 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.indentation;
21  
22  import com.puppycrawl.tools.checkstyle.api.DetailAST;
23  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
24  
25  /**
26   * Handler for method definitions.
27   *
28   * @author jrichard
29   * @author Maikel Steneker
30   */
31  public class MethodDefHandler extends BlockParentHandler {
32      /**
33       * Construct an instance of this handler with the given indentation check,
34       * abstract syntax tree, and parent handler.
35       *
36       * @param indentCheck   the indentation check
37       * @param ast           the abstract syntax tree
38       * @param parent        the parent handler
39       */
40      public MethodDefHandler(IndentationCheck indentCheck,
41          DetailAST ast, AbstractExpressionHandler parent) {
42          super(indentCheck, getHandlerName(ast), ast, parent);
43      }
44  
45      @Override
46      protected DetailAST getTopLevelAst() {
47          // we check this stuff ourselves below
48          return null;
49      }
50  
51      @Override
52      protected void checkModifiers() {
53          final DetailAST modifier = getMainAst().findFirstToken(TokenTypes.MODIFIERS);
54          if (isOnStartOfLine(modifier)
55              && !getIndent().isAcceptable(expandedTabsColumnNo(modifier))) {
56              logError(modifier, "modifier", expandedTabsColumnNo(modifier));
57          }
58      }
59  
60      /**
61       * Check the indentation level of the throws clause.
62       */
63      private void checkThrows() {
64          final DetailAST throwsAst = getMainAst().findFirstToken(TokenTypes.LITERAL_THROWS);
65  
66          if (throwsAst != null) {
67              checkWrappingIndentation(throwsAst, throwsAst.getNextSibling(), getIndentCheck()
68                      .getThrowsIndent(), getLineStart(getMethodDefLineStart(getMainAst())),
69                      !isOnStartOfLine(throwsAst));
70          }
71      }
72  
73      /**
74       * Gets the start line of the method, excluding any annotations. This is required because the
75       * current {@link TokenTypes#METHOD_DEF} may not always be the start as seen in
76       * https://github.com/checkstyle/checkstyle/issues/3145.
77       *
78       * @param mainAst
79       *            The method definition ast.
80       * @return The start column position of the method.
81       */
82      private static int getMethodDefLineStart(DetailAST mainAst) {
83          // get first type position
84          int lineStart = mainAst.findFirstToken(TokenTypes.IDENT).getLineNo();
85  
86          // check if there is a type before the indent
87          final DetailAST typeNode = mainAst.findFirstToken(TokenTypes.TYPE);
88          if (typeNode != null) {
89              lineStart = getFirstLine(lineStart, typeNode);
90          }
91  
92          // check if there is a modifier before the type
93          for (DetailAST node = mainAst.findFirstToken(TokenTypes.MODIFIERS).getFirstChild();
94                  node != null;
95                  node = node.getNextSibling()) {
96              // skip annotations as we check them else where as outside the method
97              if (node.getType() == TokenTypes.ANNOTATION) {
98                  continue;
99              }
100 
101             if (node.getLineNo() < lineStart) {
102                 lineStart = node.getLineNo();
103             }
104         }
105 
106         return lineStart;
107     }
108 
109     @Override
110     public void checkIndentation() {
111         checkModifiers();
112         checkThrows();
113 
114         checkWrappingIndentation(getMainAst(), getMethodDefParamRightParen(getMainAst()));
115         // abstract method def -- no body
116         if (getLeftCurly() != null) {
117             super.checkIndentation();
118         }
119     }
120 
121     /**
122      * Returns right parenthesis of method definition parameter list.
123      * @param methodDefAst
124      *          method definition ast node(TokenTypes.LITERAL_IF)
125      * @return right parenthesis of method definition parameter list.
126      */
127     private static DetailAST getMethodDefParamRightParen(DetailAST methodDefAst) {
128         return methodDefAst.findFirstToken(TokenTypes.RPAREN);
129     }
130 
131     /**
132      * Creates a handler name for this class according to ast type.
133      *
134      * @param ast the abstract syntax tree.
135      * @return handler name for this class.
136      */
137     private static String getHandlerName(DetailAST ast) {
138         final String name;
139 
140         if (ast.getType() == TokenTypes.CTOR_DEF) {
141             name = "ctor def";
142         }
143         else if (ast.getType() == TokenTypes.ANNOTATION_FIELD_DEF) {
144             name = "annotation field def";
145         }
146         else {
147             name = "method def";
148         }
149         return name;
150     }
151 }