View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2017 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.filters;
21  
22  import java.util.List;
23  import java.util.Objects;
24  import java.util.regex.Pattern;
25  
26  import com.puppycrawl.tools.checkstyle.TreeWalkerAuditEvent;
27  import com.puppycrawl.tools.checkstyle.TreeWalkerFilter;
28  import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
29  import com.puppycrawl.tools.checkstyle.xpath.AbstractNode;
30  import com.puppycrawl.tools.checkstyle.xpath.RootNode;
31  import net.sf.saxon.om.Item;
32  import net.sf.saxon.sxpath.XPathDynamicContext;
33  import net.sf.saxon.sxpath.XPathEvaluator;
34  import net.sf.saxon.sxpath.XPathExpression;
35  import net.sf.saxon.trans.XPathException;
36  
37  /**
38   * This filter processes {@link TreeWalkerAuditEvent}
39   * objects based on the criteria of file, check, module id, xpathQuery.
40   *
41   * @author Timur Tibeyev.
42   */
43  public class XpathFilter implements TreeWalkerFilter {
44      /** The regexp to match file names against. */
45      private final Pattern fileRegexp;
46  
47      /** The pattern for file names. */
48      private final String filePattern;
49  
50      /** The regexp to match check names against. */
51      private final Pattern checkRegexp;
52  
53      /** The pattern for check class names. */
54      private final String checkPattern;
55  
56      /** The regexp to match message names against. */
57      private final Pattern messageRegexp;
58  
59      /** The pattern for message names. */
60      private final String messagePattern;
61  
62      /** Module id filter. */
63      private final String moduleId;
64  
65      /** Xpath expression. */
66      private final XPathExpression xpathExpression;
67  
68      /** Xpath query. */
69      private final String xpathQuery;
70  
71      /**
72       * Creates a {@code XpathElement} instance.
73       * @param files regular expression for names of filtered files
74       * @param checks regular expression for filtered check classes
75       * @param message regular expression for messages.
76       * @param moduleId the module id
77       * @param query the xpath query
78       */
79      public XpathFilter(String files, String checks,
80                         String message, String moduleId, String query) {
81          filePattern = files;
82          if (files == null) {
83              fileRegexp = null;
84          }
85          else {
86              fileRegexp = Pattern.compile(files);
87          }
88          checkPattern = checks;
89          if (checks == null) {
90              checkRegexp = null;
91          }
92          else {
93              checkRegexp = CommonUtils.createPattern(checks);
94          }
95          messagePattern = message;
96          if (message == null) {
97              messageRegexp = null;
98          }
99          else {
100             messageRegexp = Pattern.compile(message);
101         }
102         this.moduleId = moduleId;
103         xpathQuery = query;
104         if (xpathQuery == null) {
105             xpathExpression = null;
106         }
107         else {
108             final XPathEvaluator xpathEvaluator = new XPathEvaluator();
109             try {
110                 xpathExpression = xpathEvaluator.createExpression(xpathQuery);
111             }
112             catch (XPathException ex) {
113                 throw new IllegalStateException("Unexpected xpath query: " + xpathQuery, ex);
114             }
115         }
116     }
117 
118     @Override
119     public boolean accept(TreeWalkerAuditEvent event) {
120         return !isFileNameAndModuleAndCheckNameMatching(event)
121                 || !isMessageNameMatching(event)
122                 || !isXpathQueryMatching(event);
123     }
124 
125     /**
126      * Is matching by file name, moduleId and Check name.
127      * @param event event
128      * @return true if it is matching
129      */
130     private boolean isFileNameAndModuleAndCheckNameMatching(TreeWalkerAuditEvent event) {
131         return event.getFileName() != null
132                 && (fileRegexp == null || fileRegexp.matcher(event.getFileName()).find())
133                 && event.getLocalizedMessage() != null
134                 && (moduleId == null || moduleId.equals(event.getModuleId()))
135                 && (checkRegexp == null || checkRegexp.matcher(event.getSourceName()).find());
136     }
137 
138     /**
139      * Is matching by message.
140      * @param event event
141      * @return true is matching or not set.
142      */
143     private boolean isMessageNameMatching(TreeWalkerAuditEvent event) {
144         return messageRegexp == null || messageRegexp.matcher(event.getMessage()).find();
145     }
146 
147     /**
148      * Is matching by xpath query.
149      * @param event event
150      * @return true is matching
151      */
152     private boolean isXpathQueryMatching(TreeWalkerAuditEvent event) {
153         boolean isMatching;
154         if (xpathExpression == null) {
155             isMatching = true;
156         }
157         else {
158             isMatching = false;
159             final List<Item> items = getItems(event);
160             for (Item item : items) {
161                 final AbstractNode abstractNode = (AbstractNode) item;
162                 isMatching = abstractNode.getTokenType() == event.getTokenType()
163                         && abstractNode.getLineNumber() == event.getLine()
164                         && abstractNode.getColumnNumber() == event.getColumnCharIndex();
165                 if (isMatching) {
166                     break;
167                 }
168             }
169         }
170         return isMatching;
171     }
172 
173     /**
174      * Returns list of nodes matching xpath expression given event.
175      * @param event {@code TreeWalkerAuditEvent} object
176      * @return list of nodes matching xpath expression given event
177      */
178     private List<Item> getItems(TreeWalkerAuditEvent event) {
179         final RootNode rootNode;
180         if (event.getRootAst() == null) {
181             rootNode = null;
182         }
183         else {
184             rootNode = new RootNode(event.getRootAst());
185         }
186         final List<Item> items;
187         try {
188             final XPathDynamicContext xpathDynamicContext =
189                     xpathExpression.createDynamicContext(rootNode);
190             items = xpathExpression.evaluate(xpathDynamicContext);
191         }
192         catch (XPathException ex) {
193             throw new IllegalStateException("Cannot initialize context and evaluate query: "
194                     + xpathQuery, ex);
195         }
196         return items;
197     }
198 
199     @Override
200     public int hashCode() {
201         return Objects.hash(filePattern, checkPattern, messagePattern, moduleId, xpathQuery);
202     }
203 
204     @Override
205     public boolean equals(Object other) {
206         if (this == other) {
207             return true;
208         }
209         if (other == null || getClass() != other.getClass()) {
210             return false;
211         }
212         final XpathFilter xpathFilter = (XpathFilter) other;
213         return Objects.equals(filePattern, xpathFilter.filePattern)
214                 && Objects.equals(checkPattern, xpathFilter.checkPattern)
215                 && Objects.equals(messagePattern, xpathFilter.messagePattern)
216                 && Objects.equals(moduleId, xpathFilter.moduleId)
217                 && Objects.equals(xpathQuery, xpathFilter.xpathQuery);
218     }
219 }