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