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 @StatelessCheck
90 public final class MissingOverrideCheck extends AbstractCheck {
91
92
93
94
95
96 public static final String MSG_KEY_TAG_NOT_VALID_ON = "tag.not.valid.on";
97
98
99
100
101
102 public static final String MSG_KEY_ANNOTATION_MISSING_OVERRIDE =
103 "annotation.missing.override";
104
105
106 private static final Pattern MATCH_INHERIT_DOC =
107 CommonUtil.createPattern("\\{\\s*@(inheritDoc)\\s*\\}");
108
109
110
111
112 private boolean javaFiveCompatibility;
113
114
115
116
117
118
119
120 public void setJavaFiveCompatibility(final boolean compatibility) {
121 javaFiveCompatibility = compatibility;
122 }
123
124 @Override
125 public int[] getDefaultTokens() {
126 return getRequiredTokens();
127 }
128
129 @Override
130 public int[] getAcceptableTokens() {
131 return getRequiredTokens();
132 }
133
134 @Override
135 public boolean isCommentNodesRequired() {
136 return true;
137 }
138
139 @Override
140 public int[] getRequiredTokens() {
141 return new int[]
142 {TokenTypes.METHOD_DEF, };
143 }
144
145 @Override
146 public void visitToken(final DetailAST ast) {
147 final boolean containsTag = containsInheritDocTag(ast);
148 if (containsTag && !JavadocTagInfo.INHERIT_DOC.isValidOn(ast)) {
149 log(ast, MSG_KEY_TAG_NOT_VALID_ON,
150 JavadocTagInfo.INHERIT_DOC.getText());
151 }
152 else {
153 boolean check = true;
154
155 if (javaFiveCompatibility) {
156 final DetailAST defOrNew = ast.getParent().getParent();
157
158 if (defOrNew.findFirstToken(TokenTypes.EXTENDS_CLAUSE) != null
159 || defOrNew.findFirstToken(TokenTypes.IMPLEMENTS_CLAUSE) != null
160 || defOrNew.getType() == TokenTypes.LITERAL_NEW) {
161 check = false;
162 }
163 }
164
165 if (check
166 && containsTag
167 && !AnnotationUtil.hasOverrideAnnotation(ast)) {
168 log(ast, MSG_KEY_ANNOTATION_MISSING_OVERRIDE);
169 }
170 }
171 }
172
173
174
175
176
177
178
179 private static boolean containsInheritDocTag(DetailAST ast) {
180 final DetailAST modifiers = ast.getFirstChild();
181 final DetailAST startNode;
182 if (modifiers.hasChildren()) {
183 startNode = Optional.ofNullable(ast.getFirstChild()
184 .findFirstToken(TokenTypes.ANNOTATION))
185 .orElse(modifiers);
186 }
187 else {
188 startNode = ast.findFirstToken(TokenTypes.TYPE);
189 }
190 final Optional<String> javadoc =
191 Stream.iterate(startNode.getLastChild(), Objects::nonNull,
192 DetailAST::getPreviousSibling)
193 .filter(node -> node.getType() == TokenTypes.BLOCK_COMMENT_BEGIN)
194 .map(DetailAST::getFirstChild)
195 .map(DetailAST::getText)
196 .filter(JavadocUtil::isJavadocComment)
197 .findFirst();
198 return javadoc.isPresent()
199 && MATCH_INHERIT_DOC.matcher(javadoc.orElseThrow()).find();
200 }
201
202 }