1 /* 2 * Copyright (c) 2011-2024 Qulice.com 3 * 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 1) Redistributions of source code must retain the above 9 * copyright notice, this list of conditions and the following 10 * disclaimer. 2) Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following 12 * disclaimer in the documentation and/or other materials provided 13 * with the distribution. 3) Neither the name of the Qulice.com nor 14 * the names of its contributors may be used to endorse or promote 15 * products derived from this software without specific prior written 16 * permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT 20 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 21 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 22 * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 27 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 29 * OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 package com.qulice.checkstyle; 32 33 import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 34 import com.puppycrawl.tools.checkstyle.api.DetailAST; 35 import com.puppycrawl.tools.checkstyle.api.TokenTypes; 36 37 /** 38 * Checks node/closing brackets to be the last symbols on the line. 39 * 40 * <p>This is how a correct bracket structure should look like: 41 * 42 * <pre> 43 * String text = String.format( 44 * "some text: %s", 45 * new Foo().with( 46 * "abc", 47 * "foo" 48 * ) 49 * ); 50 * </pre> 51 * 52 * <p>The motivation for such formatting is simple - we want to see the entire 53 * block as fast as possible. When you look at a block of code you should be 54 * able to see where it starts and where it ends. In exactly the same way 55 * we organize curled brackets. 56 * 57 * <p>In other words, when you open a bracket and can't close it at the same 58 * line - you should leave it as the last symbol at this line. 59 * 60 * @since 0.3 61 */ 62 public final class BracketsStructureCheck extends AbstractCheck { 63 64 @Override 65 public int[] getDefaultTokens() { 66 return new int[] { 67 TokenTypes.LITERAL_NEW, 68 TokenTypes.METHOD_CALL, 69 }; 70 } 71 72 @Override 73 public int[] getAcceptableTokens() { 74 return this.getDefaultTokens(); 75 } 76 77 @Override 78 public int[] getRequiredTokens() { 79 return this.getDefaultTokens(); 80 } 81 82 @Override 83 public void visitToken(final DetailAST ast) { 84 if (ast.getType() == TokenTypes.METHOD_CALL 85 || ast.getType() == TokenTypes.LITERAL_NEW) { 86 this.checkParams(ast); 87 } else { 88 final DetailAST brackets = ast.findFirstToken(TokenTypes.LPAREN); 89 if (brackets != null) { 90 this.checkParams(brackets); 91 } 92 } 93 } 94 95 /** 96 * Checks params statement to satisfy the rule. 97 * @param node Tree node, containing method call statement. 98 */ 99 private void checkParams(final DetailAST node) { 100 final DetailAST closing = node.findFirstToken(TokenTypes.RPAREN); 101 if (closing != null) { 102 this.checkLines(node, node.getLineNo(), closing.getLineNo()); 103 } 104 } 105 106 /** 107 * Checks params statement to satisfy the rule. 108 * @param node Tree node, containing method call statement. 109 * @param start First line 110 * @param end Final line 111 */ 112 private void checkLines(final DetailAST node, final int start, 113 final int end) { 114 if (start != end) { 115 final DetailAST elist = node.findFirstToken(TokenTypes.ELIST); 116 final int pline = elist.getLineNo(); 117 if (pline == start) { 118 this.log(pline, "Parameters should start on a new line"); 119 } 120 this.checkExpressionList(elist, end); 121 } 122 } 123 124 /** 125 * Checks expression list if closing bracket is on new line. 126 * @param elist Tree node, containing expression list 127 * @param end Final line 128 */ 129 private void checkExpressionList(final DetailAST elist, final int end) { 130 if (elist.getChildCount() > 0) { 131 final DetailAST last = elist.getLastChild(); 132 final int lline = last.getLineNo(); 133 if (lline == end) { 134 this.log(lline, "Closing bracket should be on a new line"); 135 } 136 } 137 } 138 139 }