001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2014 Oliver Burn 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018//////////////////////////////////////////////////////////////////////////////// 019package com.puppycrawl.tools.checkstyle.checks.javadoc; 020 021import com.puppycrawl.tools.checkstyle.api.DetailNode; 022import com.puppycrawl.tools.checkstyle.api.JavadocTokenTypes; 023 024/** 025 * Checks that: 026 * <ul> 027 * <li>There is one blank line between each of two paragraphs 028 * and one blank line before the at-clauses block if it is present.</li> 029 * <li>Each paragraph but the first has <p> immediately 030 * before the first word, with no space after.</li> 031 * </ul> 032 * 033 * <p> 034 * Default configuration: 035 * </p> 036 * <pre> 037 * <module name="JavadocParagraph"/> 038 * </pre> 039 * 040 * @author maxvetrenko 041 * 042 */ 043public class JavadocParagraphCheck extends AbstractJavadocCheck 044{ 045 046 @Override 047 public int[] getDefaultJavadocTokens() 048 { 049 return new int[] { 050 JavadocTokenTypes.NEWLINE, 051 JavadocTokenTypes.HTML_ELEMENT, 052 }; 053 } 054 055 @Override 056 public void visitJavadocToken(DetailNode ast) 057 { 058 if (ast.getType() == JavadocTokenTypes.NEWLINE && isEmptyLine(ast)) { 059 checkEmptyLine(ast); 060 } 061 else if (ast.getType() == JavadocTokenTypes.HTML_ELEMENT 062 && JavadocUtils.getFirstChild(ast).getType() == JavadocTokenTypes.P_TAG_OPEN) 063 { 064 checkParagraphTag(ast); 065 } 066 } 067 068 /** 069 * Determines whether or not the next line after empty line has paragraph tag in the beginning. 070 * @param newline NEWLINE node. 071 */ 072 private void checkEmptyLine(DetailNode newline) 073 { 074 final DetailNode nearestToken = getNearestNode(newline); 075 if (!isLastEmptyLine(newline) && nearestToken != null 076 && nearestToken.getType() == JavadocTokenTypes.TEXT 077 && nearestToken.getChildren().length > 1) 078 { 079 log(newline.getLineNumber(), "javadoc.paragraph.tag.after"); 080 } 081 } 082 083 /** 084 * Determines whether or not the line with paragraph tag has previous empty line. 085 * @param tag html tag. 086 */ 087 private void checkParagraphTag(DetailNode tag) 088 { 089 final DetailNode newLine = getNearestEmptyLine(tag); 090 if (isFirstParagraph(tag)) { 091 log(tag.getLineNumber(), "javadoc.paragraph.redundant.paragraph"); 092 } 093 else if (newLine == null || tag.getLineNumber() - newLine.getLineNumber() != 1) { 094 log(tag.getLineNumber(), "javadoc.paragraph.line.before"); 095 } 096 } 097 098 /** 099 * Returns nearest node. 100 * @param node DetailNode node. 101 * @return nearest node. 102 */ 103 private DetailNode getNearestNode(DetailNode node) 104 { 105 DetailNode tag = JavadocUtils.getNextSibling(node); 106 while (tag != null && (tag.getType() == JavadocTokenTypes.LEADING_ASTERISK 107 || tag.getType() == JavadocTokenTypes.NEWLINE)) 108 { 109 tag = JavadocUtils.getNextSibling(tag); 110 } 111 return tag; 112 } 113 114 /** 115 * Determines whether or not the line is empty line. 116 * @param newLine NEWLINE node. 117 * @return true, if line is empty line. 118 */ 119 private boolean isEmptyLine(DetailNode newLine) 120 { 121 DetailNode previousSibling = JavadocUtils.getPreviousSibling(newLine); 122 if (previousSibling == null 123 || previousSibling.getParent().getType() != JavadocTokenTypes.JAVADOC) 124 { 125 return false; 126 } 127 if (previousSibling.getType() == JavadocTokenTypes.TEXT 128 && previousSibling.getChildren().length == 1) 129 { 130 previousSibling = JavadocUtils.getPreviousSibling(previousSibling); 131 } 132 return previousSibling != null 133 && previousSibling.getType() == JavadocTokenTypes.LEADING_ASTERISK; 134 } 135 136 /** 137 * Determines whether or not the line with paragraph tag is first line in javadoc. 138 * @param paragraphTag paragraph tag. 139 * @return true, if line with paragraph tag is first line in javadoc. 140 */ 141 private boolean isFirstParagraph(DetailNode paragraphTag) 142 { 143 DetailNode previousNode = JavadocUtils.getPreviousSibling(paragraphTag); 144 while (previousNode != null) { 145 if (previousNode.getType() == JavadocTokenTypes.TEXT 146 && previousNode.getChildren().length > 1 147 || previousNode.getType() != JavadocTokenTypes.LEADING_ASTERISK 148 && previousNode.getType() != JavadocTokenTypes.NEWLINE 149 && previousNode.getType() != JavadocTokenTypes.TEXT) 150 { 151 return false; 152 } 153 previousNode = JavadocUtils.getPreviousSibling(previousNode); 154 } 155 return true; 156 } 157 158 /** 159 * Finds and returns nearest empty line in javadoc. 160 * @param node DetailNode node. 161 * @return Some nearest empty line in javadoc. 162 */ 163 private DetailNode getNearestEmptyLine(DetailNode node) 164 { 165 DetailNode newLine = JavadocUtils.getPreviousSibling(node); 166 while (newLine != null) { 167 final DetailNode previousSibling = JavadocUtils.getPreviousSibling(newLine); 168 if (newLine.getType() == JavadocTokenTypes.NEWLINE && isEmptyLine(newLine)) 169 { 170 break; 171 } 172 newLine = previousSibling; 173 } 174 return newLine; 175 } 176 177 /** 178 * Tests if NEWLINE node is a last node in javadoc. 179 * @param newLine NEWLINE node. 180 * @return true, if NEWLINE node is a last node in javadoc. 181 */ 182 private boolean isLastEmptyLine(DetailNode newLine) 183 { 184 DetailNode nextNode = JavadocUtils.getNextSibling(newLine); 185 while (nextNode != null && nextNode.getType() != JavadocTokenTypes.JAVADOC_TAG) { 186 if (nextNode.getType() == JavadocTokenTypes.TEXT 187 && nextNode.getChildren().length > 1 188 || nextNode.getType() == JavadocTokenTypes.HTML_ELEMENT) 189 { 190 return false; 191 } 192 nextNode = JavadocUtils.getNextSibling(nextNode); 193 } 194 return true; 195 } 196}