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