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