Coverage Report - com.puppycrawl.tools.checkstyle.checks.indentation.MethodCallHandler
 
Classes in this File Line Coverage Branch Coverage Complexity
MethodCallHandler
100%
64/64
100%
26/26
2.333
 
 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 calls.
 27  
  *
 28  
  * @author jrichard
 29  
  */
 30  
 public class MethodCallHandler extends AbstractExpressionHandler {
 31  
     /**
 32  
      * Construct an instance of this handler with the given indentation check,
 33  
      * abstract syntax tree, and parent handler.
 34  
      *
 35  
      * @param indentCheck   the indentation check
 36  
      * @param ast           the abstract syntax tree
 37  
      * @param parent        the parent handler
 38  
      */
 39  
     public MethodCallHandler(IndentationCheck indentCheck,
 40  
         DetailAST ast, AbstractExpressionHandler parent) {
 41  556
         super(indentCheck, "method call", ast, parent);
 42  556
     }
 43  
 
 44  
     @Override
 45  
     protected IndentLevel getIndentImpl() {
 46  
         final IndentLevel indentLevel;
 47  
         // if inside a method call's params, this could be part of
 48  
         // an expression, so get the previous line's start
 49  294
         if (getParent() instanceof MethodCallHandler) {
 50  25
             final MethodCallHandler container =
 51  25
                     (MethodCallHandler) getParent();
 52  25
             if (areOnSameLine(container.getMainAst(), getMainAst())
 53  24
                     || isChainedMethodCallWrapped()
 54  12
                     || areMethodsChained(container.getMainAst(), getMainAst())) {
 55  14
                 indentLevel = container.getIndent();
 56  
             }
 57  
             // we should increase indentation only if this is the first
 58  
             // chained method call which was moved to the next line
 59  
             else {
 60  11
                 indentLevel = new IndentLevel(container.getIndent(), getBasicOffset());
 61  
             }
 62  25
         }
 63  
         else {
 64  
             // if our expression isn't first on the line, just use the start
 65  
             // of the line
 66  269
             final LineSet lines = new LineSet();
 67  269
             findSubtreeLines(lines, getMainAst().getFirstChild(), true);
 68  269
             final int firstCol = lines.firstLineCol();
 69  269
             final int lineStart = getLineStart(getFirstAst(getMainAst()));
 70  269
             if (lineStart == firstCol) {
 71  254
                 indentLevel = super.getIndentImpl();
 72  
             }
 73  
             else {
 74  15
                 indentLevel = new IndentLevel(lineStart);
 75  
             }
 76  
         }
 77  294
         return indentLevel;
 78  
     }
 79  
 
 80  
     /**
 81  
      * Checks if ast2 is a chained method call that starts on the same level as ast1 ends.
 82  
      * In other words, if the right paren of ast1 is on the same level as the lparen of ast2:
 83  
      *
 84  
      * {@code
 85  
      *     value.methodOne(
 86  
      *         argument1
 87  
      *     ).methodTwo(
 88  
      *         argument2
 89  
      *     );
 90  
      * }
 91  
      *
 92  
      * @param ast1 Ast1
 93  
      * @param ast2 Ast2
 94  
      * @return True if ast2 begins on the same level that ast1 ends
 95  
      */
 96  
     private static boolean areMethodsChained(DetailAST ast1, DetailAST ast2) {
 97  12
         final DetailAST rparen = ast1.findFirstToken(TokenTypes.RPAREN);
 98  12
         return rparen.getLineNo() == ast2.getLineNo();
 99  
     }
 100  
 
 101  
     /**
 102  
      * If this is the first chained method call which was moved to the next line.
 103  
      * @return true if chained class are wrapped
 104  
      */
 105  
     private boolean isChainedMethodCallWrapped() {
 106  24
         boolean result = false;
 107  24
         final DetailAST main = getMainAst();
 108  24
         final DetailAST dot = main.getFirstChild();
 109  24
         final DetailAST target = dot.getFirstChild();
 110  
 
 111  24
         final DetailAST dot1 = target.getFirstChild();
 112  24
         final DetailAST target1 = dot1.getFirstChild();
 113  
 
 114  24
         if (dot1.getType() == TokenTypes.DOT
 115  23
             && target1.getType() == TokenTypes.METHOD_CALL) {
 116  12
             result = true;
 117  
         }
 118  24
         return result;
 119  
     }
 120  
 
 121  
     /**
 122  
      * Get the first AST of the specified method call.
 123  
      *
 124  
      * @param ast
 125  
      *            the method call
 126  
      *
 127  
      * @return the first AST of the specified method call
 128  
      */
 129  
     private static DetailAST getFirstAst(DetailAST ast) {
 130  
         // walk down the first child part of the dots that make up a method
 131  
         // call name
 132  
 
 133  269
         DetailAST astNode = ast.getFirstChild();
 134  522
         while (astNode.getType() == TokenTypes.DOT) {
 135  253
             astNode = astNode.getFirstChild();
 136  
         }
 137  269
         return astNode;
 138  
     }
 139  
 
 140  
     @Override
 141  
     public IndentLevel getSuggestedChildIndent(AbstractExpressionHandler child) {
 142  
         // for whatever reason a method that crosses lines, like asList
 143  
         // here:
 144  
         //            System.out.println("methods are: " + Arrays.asList(
 145  
         //                new String[] {"method"}).toString());
 146  
         // will not have the right line num, so just get the child name
 147  
 
 148  46
         final DetailAST first = getMainAst().getFirstChild();
 149  46
         IndentLevel suggestedLevel = new IndentLevel(getLineStart(first));
 150  92
         if (!areOnSameLine(child.getMainAst().getFirstChild(),
 151  46
                            getMainAst().getFirstChild())) {
 152  34
             suggestedLevel = new IndentLevel(suggestedLevel,
 153  34
                     getBasicOffset(),
 154  34
                     getIndentCheck().getLineWrappingIndentation());
 155  
         }
 156  
 
 157  
         // If the right parenthesis is at the start of a line;
 158  
         // include line wrapping in suggested indent level.
 159  46
         final DetailAST rparen = getMainAst().findFirstToken(TokenTypes.RPAREN);
 160  46
         if (getLineStart(rparen) == rparen.getColumnNo()) {
 161  18
             suggestedLevel.addAcceptedIndent(new IndentLevel(
 162  9
                     getParent().getSuggestedChildIndent(this),
 163  9
                     getIndentCheck().getLineWrappingIndentation()
 164  
             ));
 165  
         }
 166  
 
 167  46
         return suggestedLevel;
 168  
     }
 169  
 
 170  
     @Override
 171  
     public void checkIndentation() {
 172  556
         final DetailAST exprNode = getMainAst().getParent();
 173  556
         if (exprNode.getParent().getType() == TokenTypes.SLIST) {
 174  269
             final DetailAST methodName = getMainAst().getFirstChild();
 175  269
             checkExpressionSubtree(methodName, getIndent(), false, false);
 176  
 
 177  269
             final DetailAST lparen = getMainAst();
 178  269
             final DetailAST rparen = getMainAst().findFirstToken(TokenTypes.RPAREN);
 179  269
             checkLeftParen(lparen);
 180  
 
 181  269
             if (rparen.getLineNo() != lparen.getLineNo()) {
 182  88
                 checkExpressionSubtree(
 183  44
                     getMainAst().findFirstToken(TokenTypes.ELIST),
 184  44
                     new IndentLevel(getIndent(), getBasicOffset()),
 185  
                     false, true);
 186  
 
 187  44
                 checkRightParen(lparen, rparen);
 188  44
                 checkWrappingIndentation(getMainAst(), getMethodCallLastNode(getMainAst()));
 189  
             }
 190  
         }
 191  556
     }
 192  
 
 193  
     @Override
 194  
     protected boolean shouldIncreaseIndent() {
 195  8
         return false;
 196  
     }
 197  
 
 198  
     /**
 199  
      * Returns method call right paren.
 200  
      * @param firstNode
 201  
      *          method call ast(TokenTypes.METHOD_CALL)
 202  
      * @return ast node containing right paren for specified method call. If
 203  
      *     method calls are chained returns right paren for last call.
 204  
      */
 205  
     private static DetailAST getMethodCallLastNode(DetailAST firstNode) {
 206  44
         return firstNode.getLastChild();
 207  
     }
 208  
 }