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.AbstractCheck; 34 import com.puppycrawl.tools.checkstyle.api.DetailAST; 35 import com.puppycrawl.tools.checkstyle.api.TokenTypes; 36 import org.cactoos.text.Sub; 37 38 /** 39 * Check indents in multi line JavaDoc tags. 40 * 41 * <p>This is how you should format javadoc tags that need a few lines: 42 * 43 * <pre> 44 * /** 45 * * This is my new method. 46 * * @param text Some text information, provided to the 47 * * method by another class 48 * * @todo #123 I will implement it later, when more information 49 * * come to light and I have documentation supplied by 50 * * AAA team in the office accross the street 51 * */ 52 * public void func() { 53 * // ... 54 * } 55 * </pre> 56 * 57 * <p>Keep in mind that all free-text information should go <b>before</b> 58 * javadoc tags, or else it will treated as part of the latest tag and 59 * qulice will complain. 60 * 61 * @since 0.3 62 */ 63 public final class MultilineJavadocTagsCheck extends AbstractCheck { 64 65 @Override 66 public int[] getDefaultTokens() { 67 return new int[] { 68 TokenTypes.METHOD_DEF, 69 TokenTypes.CTOR_DEF, 70 TokenTypes.PACKAGE_DEF, 71 }; 72 } 73 74 @Override 75 public int[] getAcceptableTokens() { 76 return this.getDefaultTokens(); 77 } 78 79 @Override 80 public int[] getRequiredTokens() { 81 return this.getDefaultTokens(); 82 } 83 84 @Override 85 public void visitToken(final DetailAST ast) { 86 final String[] lines = this.getLines(); 87 final int start = ast.getLineNo(); 88 final int cstart = 89 MultilineJavadocTagsCheck.findCommentStart(lines, start) + 1; 90 final int cend = 91 MultilineJavadocTagsCheck.findCommentEnd(lines, start) - 1; 92 if (cend >= cstart && cstart >= 0) { 93 this.checkJavaDoc(lines, cstart, cend); 94 } else { 95 this.log(0, "Can't find method comment"); 96 } 97 } 98 99 /** 100 * Checks method's Java Doc for satisfy indentation rules. 101 * @param lines Code of the whole class. 102 * @param start Start line of the Java Doc. 103 * @param end End line of the Java Doc. 104 */ 105 @SuppressWarnings("PMD.InefficientEmptyStringCheck") 106 private void checkJavaDoc(final String[] lines, final int start, 107 final int end) { 108 boolean tagged = false; 109 int index = -1; 110 for (int current = start; current <= end; current += 1) { 111 final String line = lines[current]; 112 if (line.contains("* @")) { 113 tagged = true; 114 index = line.indexOf('@'); 115 } else { 116 if (tagged) { 117 final int comment = line.indexOf('*'); 118 final String sub = new Sub( 119 line, comment + 1, index + 1 120 ).toString(); 121 final String ext = new Sub( 122 line, comment + 1, index + 2 123 ).toString(); 124 if (!sub.trim().isEmpty() || ext.trim().isEmpty()) { 125 this.log( 126 current + 1, 127 "Should contain one indentation space" 128 ); 129 } 130 } 131 } 132 } 133 } 134 135 /** 136 * Find javadoc starting comment. 137 * @param lines List of lines to check. 138 * @param start Start searching from this line number. 139 * @return Line number with found starting comment or -1 otherwise. 140 */ 141 private static int findCommentStart(final String[] lines, final int start) { 142 return MultilineJavadocTagsCheck.findTrimmedTextUp(lines, start, "/**"); 143 } 144 145 /** 146 * Find javadoc ending comment. 147 * @param lines List of lines to check. 148 * @param start Start searching from this line number. 149 * @return Line number with found ending comment, or -1 if it wasn't found. 150 */ 151 private static int findCommentEnd(final String[] lines, final int start) { 152 return MultilineJavadocTagsCheck.findTrimmedTextUp(lines, start, "*/"); 153 } 154 155 /** 156 * Find a text in lines, by going up. 157 * @param lines List of lines to check. 158 * @param start Start searching from this line number. 159 * @param text Text to find. 160 * @return Line number with found text, or -1 if it wasn't found. 161 */ 162 private static int findTrimmedTextUp(final String[] lines, 163 final int start, final String text) { 164 int found = -1; 165 for (int pos = start - 1; pos >= 0; pos -= 1) { 166 if (lines[pos].trim().equals(text)) { 167 found = pos; 168 break; 169 } 170 } 171 return found; 172 } 173 }