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 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 }; 71 } 72 73 @Override 74 public int[] getAcceptableTokens() { 75 return this.getDefaultTokens(); 76 } 77 78 @Override 79 public int[] getRequiredTokens() { 80 return this.getDefaultTokens(); 81 } 82 83 @Override 84 public void visitToken(final DetailAST ast) { 85 final String[] lines = this.getLines(); 86 final int start = ast.getLineNo(); 87 final int cstart = 88 MultilineJavadocTagsCheck.findCommentStart(lines, start) + 1; 89 final int cend = 90 MultilineJavadocTagsCheck.findCommentEnd(lines, start) - 1; 91 if (cend >= cstart && cstart >= 0) { 92 this.checkJavaDoc(lines, cstart, cend); 93 } else { 94 this.log(0, "Can't find method comment"); 95 } 96 } 97 98 /** 99 * Checks method's Java Doc for satisfy indentation rules. 100 * @param lines Code of the whole class. 101 * @param start Start line of the Java Doc. 102 * @param end End line of the Java Doc. 103 */ 104 @SuppressWarnings("PMD.InefficientEmptyStringCheck") 105 private void checkJavaDoc(final String[] lines, final int start, 106 final int end) { 107 boolean tagged = false; 108 int index = -1; 109 for (int current = start; current <= end; current += 1) { 110 final String line = lines[current]; 111 if (line.contains("* @")) { 112 tagged = true; 113 index = line.indexOf('@'); 114 } else { 115 if (tagged) { 116 final int comment = line.indexOf('*'); 117 final String sub = new Sub( 118 line, comment + 1, index + 1 119 ).toString(); 120 final String ext = new Sub( 121 line, comment + 1, index + 2 122 ).toString(); 123 if (!sub.trim().isEmpty() || ext.trim().isEmpty()) { 124 this.log( 125 current + 1, 126 "Should contain one indentation space" 127 ); 128 } 129 } 130 } 131 } 132 } 133 134 /** 135 * Find javadoc starting comment. 136 * @param lines List of lines to check. 137 * @param start Start searching from this line number. 138 * @return Line number with found starting comment or -1 otherwise. 139 */ 140 private static int findCommentStart(final String[] lines, final int start) { 141 return MultilineJavadocTagsCheck.findTrimmedTextUp(lines, start, "/**"); 142 } 143 144 /** 145 * Find javadoc ending comment. 146 * @param lines List of lines to check. 147 * @param start Start searching from this line number. 148 * @return Line number with found ending comment, or -1 if it wasn't found. 149 */ 150 private static int findCommentEnd(final String[] lines, final int start) { 151 return MultilineJavadocTagsCheck.findTrimmedTextUp(lines, start, "*/"); 152 } 153 154 /** 155 * Find a text in lines, by going up. 156 * @param lines List of lines to check. 157 * @param start Start searching from this line number. 158 * @param text Text to find. 159 * @return Line number with found text, or -1 if it wasn't found. 160 */ 161 private static int findTrimmedTextUp(final String[] lines, 162 final int start, final String text) { 163 int found = -1; 164 for (int pos = start - 1; pos >= 0; pos -= 1) { 165 if (lines[pos].trim().equals(text)) { 166 found = pos; 167 break; 168 } 169 } 170 return found; 171 } 172 }