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 import java.util.stream.Collectors;
27
28 import com.puppycrawl.tools.checkstyle.TreeWalkerAuditEvent;
29 import com.puppycrawl.tools.checkstyle.TreeWalkerFilter;
30 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
31 import com.puppycrawl.tools.checkstyle.xpath.AbstractNode;
32 import com.puppycrawl.tools.checkstyle.xpath.RootNode;
33 import net.sf.saxon.Configuration;
34 import net.sf.saxon.om.Item;
35 import net.sf.saxon.sxpath.XPathDynamicContext;
36 import net.sf.saxon.sxpath.XPathEvaluator;
37 import net.sf.saxon.sxpath.XPathExpression;
38 import net.sf.saxon.trans.XPathException;
39
40
41
42
43
44
45 public class XpathFilterElement implements TreeWalkerFilter {
46
47
48 private final Pattern fileRegexp;
49
50
51 private final Pattern checkRegexp;
52
53
54 private final Pattern messageRegexp;
55
56
57 private final String moduleId;
58
59
60 private final XPathExpression xpathExpression;
61
62
63 private final String xpathQuery;
64
65
66 private final boolean isEmptyConfig;
67
68
69
70
71
72
73
74
75
76
77
78 public XpathFilterElement(String files, String checks,
79 String message, String moduleId, String query) {
80 this(Optional.ofNullable(files).map(Pattern::compile).orElse(null),
81 Optional.ofNullable(checks).map(CommonUtil::createPattern).orElse(null),
82 Optional.ofNullable(message).map(Pattern::compile).orElse(null),
83 moduleId,
84 query);
85 }
86
87
88
89
90
91
92
93
94
95
96
97 public XpathFilterElement(Pattern files, Pattern checks, Pattern message,
98 String moduleId, String query) {
99 fileRegexp = files;
100 checkRegexp = checks;
101 messageRegexp = message;
102 this.moduleId = moduleId;
103 xpathQuery = query;
104 if (xpathQuery == null) {
105 xpathExpression = null;
106 }
107 else {
108 final XPathEvaluator xpathEvaluator = new XPathEvaluator(
109 Configuration.newConfiguration());
110 try {
111 xpathExpression = xpathEvaluator.createExpression(xpathQuery);
112 }
113 catch (XPathException ex) {
114 throw new IllegalArgumentException("Incorrect xpath query: " + xpathQuery, ex);
115 }
116 }
117 isEmptyConfig = fileRegexp == null
118 && checkRegexp == null
119 && messageRegexp == null
120 && moduleId == null
121 && xpathExpression == null;
122 }
123
124 @Override
125 public boolean accept(TreeWalkerAuditEvent event) {
126 return isEmptyConfig
127 || !isFileNameAndModuleAndModuleNameMatching(event)
128 || !isMessageNameMatching(event)
129 || !isXpathQueryMatching(event);
130 }
131
132
133
134
135
136
137
138 private boolean isFileNameAndModuleAndModuleNameMatching(TreeWalkerAuditEvent event) {
139 return event.getFileName() != null
140 && (fileRegexp == null || fileRegexp.matcher(event.getFileName()).find())
141 && event.getViolation() != null
142 && (moduleId == null || moduleId.equals(event.getModuleId()))
143 && (checkRegexp == null || checkRegexp.matcher(event.getSourceName()).find());
144 }
145
146
147
148
149
150
151
152 private boolean isMessageNameMatching(TreeWalkerAuditEvent event) {
153 return messageRegexp == null || messageRegexp.matcher(event.getMessage()).find();
154 }
155
156
157
158
159
160
161
162 private boolean isXpathQueryMatching(TreeWalkerAuditEvent event) {
163 boolean isMatching;
164 if (xpathExpression == null) {
165 isMatching = true;
166 }
167 else {
168 isMatching = false;
169 final List<AbstractNode> nodes = getItems(event)
170 .stream().map(AbstractNode.class::cast)
171 .collect(Collectors.toUnmodifiableList());
172 for (AbstractNode abstractNode : nodes) {
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.getRootAst() == null) {
194 rootNode = null;
195 }
196 else {
197 rootNode = new RootNode(event.getRootAst());
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 ex) {
206 throw new IllegalStateException("Cannot initialize context and evaluate query: "
207 + xpathQuery, ex);
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 }