001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2019 the original author or authors. 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//////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.checks.annotation; 021 022import java.util.regex.Matcher; 023import java.util.regex.Pattern; 024 025import com.puppycrawl.tools.checkstyle.StatelessCheck; 026import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 027import com.puppycrawl.tools.checkstyle.api.DetailAST; 028import com.puppycrawl.tools.checkstyle.api.TextBlock; 029import com.puppycrawl.tools.checkstyle.api.TokenTypes; 030import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTagInfo; 031import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil; 032import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 033 034/** 035 * <p> 036 * This class is used to verify that the {@link Override Override} 037 * annotation is present when the inheritDoc javadoc tag is present. 038 * </p> 039 * 040 * <p> 041 * Rationale: The {@link Override Override} annotation helps 042 * compiler tools ensure that an override is actually occurring. It is 043 * quite easy to accidentally overload a method or hide a static method 044 * and using the {@link Override Override} annotation points 045 * out these problems. 046 * </p> 047 * 048 * <p> 049 * This check will log a violation if using the inheritDoc tag on a method that 050 * is not valid (ex: private, or static method). 051 * </p> 052 * 053 * <p> 054 * There is a slight difference between the Override annotation in Java 5 versus 055 * Java 6 and above. In Java 5, any method overridden from an interface cannot 056 * be annotated with Override. In Java 6 this behavior is allowed. 057 * </p> 058 * 059 * <p> 060 * As a result of the aforementioned difference between Java 5 and Java 6, a 061 * property called {@code javaFiveCompatibility } is available. This 062 * property will only check classes, interfaces, etc. that do not contain the 063 * extends or implements keyword or are not anonymous classes. This means it 064 * only checks methods overridden from {@code java.lang.Object} 065 * 066 * <b>Java 5 Compatibility mode severely limits this check. It is recommended to 067 * only use it on Java 5 source</b> 068 * </p> 069 * 070 * <pre> 071 * <module name="MissingOverride"> 072 * <property name="javaFiveCompatibility" 073 * value="true"/> 074 * </module> 075 * </pre> 076 * 077 */ 078@StatelessCheck 079public final class MissingOverrideCheck extends AbstractCheck { 080 081 /** 082 * A key is pointing to the warning message text in "messages.properties" 083 * file. 084 */ 085 public static final String MSG_KEY_TAG_NOT_VALID_ON = "tag.not.valid.on"; 086 087 /** 088 * A key is pointing to the warning message text in "messages.properties" 089 * file. 090 */ 091 public static final String MSG_KEY_ANNOTATION_MISSING_OVERRIDE = 092 "annotation.missing.override"; 093 094 /** {@link Override Override} annotation name. */ 095 private static final String OVERRIDE = "Override"; 096 097 /** Fully-qualified {@link Override Override} annotation name. */ 098 private static final String FQ_OVERRIDE = "java.lang." + OVERRIDE; 099 100 /** Compiled regexp to match Javadoc tags with no argument and {}. */ 101 private static final Pattern MATCH_INHERIT_DOC = 102 CommonUtil.createPattern("\\{\\s*@(inheritDoc)\\s*\\}"); 103 104 /** 105 * Java 5 compatibility option. 106 * @see #setJavaFiveCompatibility(boolean) 107 */ 108 private boolean javaFiveCompatibility; 109 110 /** 111 * Sets Java 5 compatibility mode. 112 * 113 * <p> 114 * In Java 5, this check could flag code that is not valid for the Override 115 * annotation even though it is a proper override. See the class 116 * documentation for more information. 117 * </p> 118 * 119 * <p> 120 * Set this to true to turn on Java 5 compatibility mode. Set this to 121 * false to turn off Java 5 compatibility mode. 122 * </p> 123 * 124 * @param compatibility compatibility or not 125 */ 126 public void setJavaFiveCompatibility(final boolean compatibility) { 127 javaFiveCompatibility = compatibility; 128 } 129 130 @Override 131 public int[] getDefaultTokens() { 132 return getRequiredTokens(); 133 } 134 135 @Override 136 public int[] getAcceptableTokens() { 137 return getRequiredTokens(); 138 } 139 140 @Override 141 public int[] getRequiredTokens() { 142 return new int[] 143 {TokenTypes.METHOD_DEF, }; 144 } 145 146 // -@cs[CyclomaticComplexity] Too complex to break apart. 147 @Override 148 public void visitToken(final DetailAST ast) { 149 final TextBlock javadoc = 150 getFileContents().getJavadocBefore(ast.getLineNo()); 151 152 final boolean containsTag = containsJavadocTag(javadoc); 153 if (containsTag && !JavadocTagInfo.INHERIT_DOC.isValidOn(ast)) { 154 log(ast.getLineNo(), MSG_KEY_TAG_NOT_VALID_ON, 155 JavadocTagInfo.INHERIT_DOC.getText()); 156 } 157 else { 158 boolean check = true; 159 160 if (javaFiveCompatibility) { 161 final DetailAST defOrNew = ast.getParent().getParent(); 162 163 if (defOrNew.findFirstToken(TokenTypes.EXTENDS_CLAUSE) != null 164 || defOrNew.findFirstToken(TokenTypes.IMPLEMENTS_CLAUSE) != null 165 || defOrNew.getType() == TokenTypes.LITERAL_NEW) { 166 check = false; 167 } 168 } 169 170 if (check 171 && containsTag 172 && !AnnotationUtil.containsAnnotation(ast, OVERRIDE) 173 && !AnnotationUtil.containsAnnotation(ast, FQ_OVERRIDE)) { 174 log(ast.getLineNo(), MSG_KEY_ANNOTATION_MISSING_OVERRIDE); 175 } 176 } 177 } 178 179 /** 180 * Checks to see if the text block contains a inheritDoc tag. 181 * 182 * @param javadoc the javadoc of the AST 183 * @return true if contains the tag 184 */ 185 private static boolean containsJavadocTag(final TextBlock javadoc) { 186 boolean javadocTag = false; 187 188 if (javadoc != null) { 189 final String[] lines = javadoc.getText(); 190 191 for (final String line : lines) { 192 final Matcher matchInheritDoc = 193 MATCH_INHERIT_DOC.matcher(line); 194 195 if (matchInheritDoc.find()) { 196 javadocTag = true; 197 break; 198 } 199 } 200 } 201 return javadocTag; 202 } 203 204}