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 /** @see #setJavaFiveCompatibility(boolean) */ 090 private boolean mJavaFiveCompatibility; 091 092 /** 093 * Sets Java 5 compatibility mode. 094 * 095 * <p> 096 * In Java 5, this check could flag code that is not valid for the Override 097 * annotation even though it is a proper override. See the class 098 * documentation for more information. 099 * </p> 100 * 101 * <p> 102 * Set this to true to turn on Java 5 compatibility mode. Set this to 103 * false to turn off Java 5 compatibility mode. 104 * </p> 105 * 106 * @param aCompatibility compatibility or not 107 */ 108 public void setJavaFiveCompatibility(final boolean aCompatibility) 109 { 110 this.mJavaFiveCompatibility = aCompatibility; 111 } 112 113 /** {@inheritDoc} */ 114 @Override 115 public int[] getDefaultTokens() 116 { 117 return this.getRequiredTokens(); 118 } 119 120 /** {@inheritDoc} */ 121 @Override 122 public int[] getAcceptableTokens() 123 { 124 return this.getRequiredTokens(); 125 } 126 127 /** {@inheritDoc} */ 128 @Override 129 public int[] getRequiredTokens() 130 { 131 return new int[] 132 {TokenTypes.METHOD_DEF, }; 133 } 134 135 /** {@inheritDoc} */ 136 @Override 137 public void visitToken(final DetailAST aAST) 138 { 139 final TextBlock javadoc = 140 this.getFileContents().getJavadocBefore(aAST.getLineNo()); 141 142 143 final boolean containsTag = this.containsJavadocTag(javadoc); 144 if (containsTag && !JavadocTagInfo.INHERIT_DOC.isValidOn(aAST)) { 145 this.log(aAST.getLineNo(), "tag.not.valid.on", 146 JavadocTagInfo.INHERIT_DOC.getText()); 147 return; 148 } 149 150 if (this.mJavaFiveCompatibility) { 151 final DetailAST defOrNew = aAST.getParent().getParent(); 152 153 if (defOrNew.branchContains(TokenTypes.EXTENDS_CLAUSE) 154 || defOrNew.branchContains(TokenTypes.IMPLEMENTS_CLAUSE) 155 || defOrNew.getType() == TokenTypes.LITERAL_NEW) 156 { 157 return; 158 } 159 } 160 161 if (containsTag 162 && (!AnnotationUtility.containsAnnotation(aAST, OVERRIDE) 163 && !AnnotationUtility.containsAnnotation(aAST, FQ_OVERRIDE))) 164 { 165 this.log(aAST.getLineNo(), "annotation.missing.override"); 166 } 167 } 168 169 /** 170 * Checks to see if the text block contains a inheritDoc tag. 171 * 172 * @param aJavadoc the javadoc of the AST 173 * @return true if contains the tag 174 */ 175 private boolean containsJavadocTag(final TextBlock aJavadoc) 176 { 177 if (aJavadoc == null) { 178 return false; 179 } 180 181 final String[] lines = aJavadoc.getText(); 182 183 for (final String line : lines) { 184 final Matcher matchInheritDoc = 185 MissingOverrideCheck.MATCH_INHERITDOC.matcher(line); 186 187 if (matchInheritDoc.find()) { 188 return true; 189 } 190 } 191 return false; 192 } 193}