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.filters;
21
22 import java.util.List;
23 import java.util.Objects;
24 import java.util.Optional;
25 import java.util.regex.Pattern;
26
27 import com.puppycrawl.tools.checkstyle.TreeWalkerAuditEvent;
28 import com.puppycrawl.tools.checkstyle.TreeWalkerFilter;
29 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
30 import com.puppycrawl.tools.checkstyle.xpath.AbstractNode;
31 import com.puppycrawl.tools.checkstyle.xpath.RootNode;
32 import net.sf.saxon.Configuration;
33 import net.sf.saxon.om.Item;
34 import net.sf.saxon.sxpath.XPathDynamicContext;
35 import net.sf.saxon.sxpath.XPathEvaluator;
36 import net.sf.saxon.sxpath.XPathExpression;
37 import net.sf.saxon.trans.XPathException;
38
39
40
41
42
43
44 public class XpathFilterElement implements TreeWalkerFilter {
45
46
47 private final Pattern fileRegexp;
48
49
50 private final Pattern checkRegexp;
51
52
53 private final Pattern messageRegexp;
54
55
56 private final String moduleId;
57
58
59 private final XPathExpression xpathExpression;
60
61
62 private final String xpathQuery;
63
64
65 private final boolean isEmptyConfig;
66
67
68
69
70
71
72
73
74
75
76
77 public XpathFilterElement(String files, String checks,
78 String message, String moduleId, String query) {
79 this(Optional.ofNullable(files).map(Pattern::compile).orElse(null),
80 Optional.ofNullable(checks).map(CommonUtil::createPattern).orElse(null),
81 Optional.ofNullable(message).map(Pattern::compile).orElse(null),
82 moduleId,
83 query);
84 }
85
86
87
88
89
90
91
92
93
94
95
96 public XpathFilterElement(Pattern files, Pattern checks, Pattern message,
97 String moduleId, String query) {
98 fileRegexp = files;
99 checkRegexp = checks;
100 messageRegexp = message;
101 this.moduleId = moduleId;
102 xpathQuery = query;
103 if (xpathQuery == null) {
104 xpathExpression = null;
105 }
106 else {
107 final XPathEvaluator xpathEvaluator = new XPathEvaluator(
108 Configuration.newConfiguration());
109 try {
110 xpathExpression = xpathEvaluator.createExpression(xpathQuery);
111 }
112 catch (XPathException exc) {
113 throw new IllegalArgumentException("Incorrect xpath query: " + xpathQuery, exc);
114 }
115 }
116 isEmptyConfig = fileRegexp == null
117 && checkRegexp == null
118 && messageRegexp == null
119 && moduleId == null
120 && xpathExpression == null;
121 }
122
123 @Override
124 public boolean accept(TreeWalkerAuditEvent event) {
125 return isEmptyConfig
126 || !isFileNameAndModuleAndModuleNameMatching(event)
127 || !isMessageNameMatching(event)
128 || !isXpathQueryMatching(event);
129 }
130
131
132
133
134
135
136
137 private boolean isFileNameAndModuleAndModuleNameMatching(TreeWalkerAuditEvent event) {
138 return event.getFileName() != null
139 && (fileRegexp == null || fileRegexp.matcher(event.getFileName()).find())
140 && event.getViolation() != null
141 && (moduleId == null || moduleId.equals(event.getModuleId()))
142 && (checkRegexp == null || checkRegexp.matcher(event.getSourceName()).find());
143 }
144
145
146
147
148
149
150
151 private boolean isMessageNameMatching(TreeWalkerAuditEvent event) {
152 return messageRegexp == null || messageRegexp.matcher(event.getMessage()).find();
153 }
154
155
156
157
158
159
160
161 private boolean isXpathQueryMatching(TreeWalkerAuditEvent event) {
162 boolean isMatching;
163 if (xpathExpression == null) {
164 isMatching = true;
165 }
166 else {
167 isMatching = false;
168 final List<AbstractNode> nodes = getItems(event)
169 .stream().map(AbstractNode.class::cast)
170 .toList();
171 for (AbstractNode abstractNode : nodes) {
172 isMatching = abstractNode.getTokenType() == event.getTokenType()
173 && abstractNode.getLineNumber() == event.getLine()
174 && abstractNode.getColumnNumber() == event.getColumnCharIndex();
175 if (isMatching) {
176 break;
177 }
178 }
179 }
180 return isMatching;
181 }
182
183
184
185
186
187
188
189
190 private List<Item> getItems(TreeWalkerAuditEvent event) {
191 final RootNode rootNode;
192 if (event.getRootAst() == null) {
193 rootNode = null;
194 }
195 else {
196 rootNode = new RootNode(event.getRootAst());
197 }
198 final List<Item> items;
199 try {
200 final XPathDynamicContext xpathDynamicContext =
201 xpathExpression.createDynamicContext(rootNode);
202 items = xpathExpression.evaluate(xpathDynamicContext);
203 }
204 catch (XPathException exc) {
205 throw new IllegalStateException("Cannot initialize context and evaluate query: "
206 + xpathQuery, exc);
207 }
208 return items;
209 }
210
211 @Override
212 public int hashCode() {
213 return Objects.hash(getPatternSafely(fileRegexp), getPatternSafely(checkRegexp),
214 getPatternSafely(messageRegexp), moduleId, xpathQuery);
215 }
216
217 @Override
218 public boolean equals(Object other) {
219 if (this == other) {
220 return true;
221 }
222 if (other == null || getClass() != other.getClass()) {
223 return false;
224 }
225 final XpathFilterElement xpathFilter = (XpathFilterElement) other;
226 return Objects.equals(getPatternSafely(fileRegexp),
227 getPatternSafely(xpathFilter.fileRegexp))
228 && Objects.equals(getPatternSafely(checkRegexp),
229 getPatternSafely(xpathFilter.checkRegexp))
230 && Objects.equals(getPatternSafely(messageRegexp),
231 getPatternSafely(xpathFilter.messageRegexp))
232 && Objects.equals(moduleId, xpathFilter.moduleId)
233 && Objects.equals(xpathQuery, xpathFilter.xpathQuery);
234 }
235
236
237
238
239
240
241
242
243 private static String getPatternSafely(Pattern pattern) {
244 String result = null;
245 if (pattern != null) {
246 result = pattern.pattern();
247 }
248 return result;
249 }
250 }