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 final 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.fileName() != null
139 && (fileRegexp == null || fileRegexp.matcher(event.fileName()).find())
140 && event.violation() != 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<Item> nodes = getItems(event)
169 .stream()
170 .toList();
171 for (Item item : nodes) {
172 final AbstractNode abstractNode = (AbstractNode) item;
173 isMatching = abstractNode.getTokenType() == event.getTokenType()
174 && abstractNode.getLineNumber() == event.getLine()
175 && abstractNode.getColumnNumber() == event.getColumnCharIndex();
176 if (isMatching) {
177 break;
178 }
179 }
180 }
181 return isMatching;
182 }
183
184
185
186
187
188
189
190
191 private List<Item> getItems(TreeWalkerAuditEvent event) {
192 final RootNode rootNode;
193 if (event.rootAst() == null) {
194 rootNode = null;
195 }
196 else {
197 rootNode = new RootNode(event.rootAst());
198 }
199 final List<Item> items;
200 try {
201 final XPathDynamicContext xpathDynamicContext =
202 xpathExpression.createDynamicContext(rootNode);
203 items = xpathExpression.evaluate(xpathDynamicContext);
204 }
205 catch (XPathException exc) {
206 throw new IllegalStateException("Cannot initialize context and evaluate query: "
207 + xpathQuery, exc);
208 }
209 return items;
210 }
211
212 @Override
213 public int hashCode() {
214 return Objects.hash(getPatternSafely(fileRegexp), getPatternSafely(checkRegexp),
215 getPatternSafely(messageRegexp), moduleId, xpathQuery);
216 }
217
218 @Override
219 public boolean equals(Object other) {
220 if (this == other) {
221 return true;
222 }
223 if (other == null || getClass() != other.getClass()) {
224 return false;
225 }
226 final XpathFilterElement xpathFilter = (XpathFilterElement) other;
227 return Objects.equals(getPatternSafely(fileRegexp),
228 getPatternSafely(xpathFilter.fileRegexp))
229 && Objects.equals(getPatternSafely(checkRegexp),
230 getPatternSafely(xpathFilter.checkRegexp))
231 && Objects.equals(getPatternSafely(messageRegexp),
232 getPatternSafely(xpathFilter.messageRegexp))
233 && Objects.equals(moduleId, xpathFilter.moduleId)
234 && Objects.equals(xpathQuery, xpathFilter.xpathQuery);
235 }
236
237
238
239
240
241
242
243
244 private static String getPatternSafely(Pattern pattern) {
245 String result = null;
246 if (pattern != null) {
247 result = pattern.pattern();
248 }
249 return result;
250 }
251 }