1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.checks.annotation;
21
22 import java.util.Objects;
23 import java.util.Optional;
24 import java.util.regex.Pattern;
25 import java.util.stream.Stream;
26
27 import com.puppycrawl.tools.checkstyle.StatelessCheck;
28 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
29 import com.puppycrawl.tools.checkstyle.api.DetailAST;
30 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
31 import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTagInfo;
32 import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil;
33 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
34 import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95 @StatelessCheck
96 public final class MissingOverrideCheck extends AbstractCheck {
97
98
99
100
101
102 public static final String MSG_KEY_TAG_NOT_VALID_ON = "tag.not.valid.on";
103
104
105
106
107
108 public static final String MSG_KEY_ANNOTATION_MISSING_OVERRIDE =
109 "annotation.missing.override";
110
111
112 private static final Pattern MATCH_INHERIT_DOC =
113 CommonUtil.createPattern("\\{\\s*@(inheritDoc)\\s*\\}");
114
115
116
117
118 private boolean javaFiveCompatibility;
119
120
121
122
123
124
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 boolean isCommentNodesRequired() {
142 return true;
143 }
144
145 @Override
146 public int[] getRequiredTokens() {
147 return new int[]
148 {TokenTypes.METHOD_DEF, };
149 }
150
151 @Override
152 public void visitToken(final DetailAST ast) {
153 final boolean containsTag = containsInheritDocTag(ast);
154 if (containsTag && !JavadocTagInfo.INHERIT_DOC.isValidOn(ast)) {
155 log(ast, MSG_KEY_TAG_NOT_VALID_ON,
156 JavadocTagInfo.INHERIT_DOC.getText());
157 }
158 else {
159 boolean check = true;
160
161 if (javaFiveCompatibility) {
162 final DetailAST defOrNew = ast.getParent().getParent();
163
164 if (defOrNew.findFirstToken(TokenTypes.EXTENDS_CLAUSE) != null
165 || defOrNew.findFirstToken(TokenTypes.IMPLEMENTS_CLAUSE) != null
166 || defOrNew.getType() == TokenTypes.LITERAL_NEW) {
167 check = false;
168 }
169 }
170
171 if (check
172 && containsTag
173 && !AnnotationUtil.hasOverrideAnnotation(ast)) {
174 log(ast, MSG_KEY_ANNOTATION_MISSING_OVERRIDE);
175 }
176 }
177 }
178
179
180
181
182
183
184
185 private static boolean containsInheritDocTag(DetailAST ast) {
186 final DetailAST modifiers = ast.getFirstChild();
187 final DetailAST startNode;
188 if (modifiers.hasChildren()) {
189 startNode = Optional.ofNullable(ast.getFirstChild()
190 .findFirstToken(TokenTypes.ANNOTATION))
191 .orElse(modifiers);
192 }
193 else {
194 startNode = ast.findFirstToken(TokenTypes.TYPE);
195 }
196 final Optional<String> javadoc =
197 Stream.iterate(startNode.getLastChild(), Objects::nonNull,
198 DetailAST::getPreviousSibling)
199 .filter(node -> node.getType() == TokenTypes.BLOCK_COMMENT_BEGIN)
200 .map(DetailAST::getFirstChild)
201 .map(DetailAST::getText)
202 .filter(JavadocUtil::isJavadocComment)
203 .findFirst();
204 return javadoc.isPresent()
205 && MATCH_INHERIT_DOC.matcher(javadoc.orElseThrow()).find();
206 }
207
208 }