1 /* 2 * Copyright (c) 2011-2025 Yegor Bugayenko 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.AbstractFileSetCheck; 34 import com.puppycrawl.tools.checkstyle.api.FileText; 35 import java.io.File; 36 import org.cactoos.text.Joined; 37 38 /** 39 * Make sure each line indentation is either: 40 * <ul> 41 * <li>the same as previous one or less 42 * <li>bigger than previous by exactly 4 43 * </ul> 44 * All other cases must cause a failure. 45 * 46 * @since 0.3 47 */ 48 public final class CascadeIndentationCheck extends AbstractFileSetCheck { 49 /** 50 * Exact indentation increase difference. 51 */ 52 private static final int LINE_INDENT_DIFF = 4; 53 54 @Override 55 public void processFiltered(final File file, final FileText lines) { 56 int previous = 0; 57 for (int pos = 0; pos < lines.size(); pos += 1) { 58 final String line = lines.get(pos); 59 final int current = CascadeIndentationCheck.indentation(line); 60 if (CascadeIndentationCheck.inCommentBlock(line) 61 || line.isEmpty()) { 62 continue; 63 } 64 if (current > previous 65 && current != previous 66 + CascadeIndentationCheck.LINE_INDENT_DIFF) { 67 this.log( 68 pos + 1, 69 String.format( 70 new Joined( 71 "", 72 "Indentation (%d) must be same or ", 73 "less than previous line (%d), or ", 74 "bigger by exactly 4" 75 ).toString(), 76 current, 77 previous 78 ) 79 ); 80 } 81 previous = current; 82 } 83 } 84 85 /** 86 * Checks if the line belongs to a comment block. 87 * @param line Input. 88 * @return True if the line belongs to a comment block. 89 */ 90 private static boolean inCommentBlock(final String line) { 91 final String trimmed = line.trim(); 92 return !trimmed.isEmpty() 93 && (trimmed.charAt(0) == '*' 94 || trimmed.startsWith("/*") 95 || trimmed.startsWith("*/") 96 ); 97 } 98 99 /** 100 * Calculates indentation of a line. 101 * @param line Input line 102 * @return Indentation of the given line. 103 */ 104 private static int indentation(final String line) { 105 int result = 0; 106 for (int pos = 0; pos < line.length(); pos += 1) { 107 if (!Character.isWhitespace(line.charAt(pos))) { 108 break; 109 } 110 result += 1; 111 } 112 return result; 113 } 114 115 }