001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2017 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018//////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.filters; 021 022import java.util.List; 023import java.util.Objects; 024import java.util.regex.Pattern; 025 026import com.puppycrawl.tools.checkstyle.TreeWalkerAuditEvent; 027import com.puppycrawl.tools.checkstyle.TreeWalkerFilter; 028import com.puppycrawl.tools.checkstyle.utils.CommonUtils; 029import com.puppycrawl.tools.checkstyle.xpath.AbstractNode; 030import com.puppycrawl.tools.checkstyle.xpath.RootNode; 031import net.sf.saxon.om.Item; 032import net.sf.saxon.sxpath.XPathDynamicContext; 033import net.sf.saxon.sxpath.XPathEvaluator; 034import net.sf.saxon.sxpath.XPathExpression; 035import net.sf.saxon.trans.XPathException; 036 037/** 038 * This filter processes {@link TreeWalkerAuditEvent} 039 * objects based on the criteria of file, check, module id, xpathQuery. 040 * 041 * @author Timur Tibeyev. 042 */ 043public class XpathFilter implements TreeWalkerFilter { 044 /** The regexp to match file names against. */ 045 private final Pattern fileRegexp; 046 047 /** The pattern for file names. */ 048 private final String filePattern; 049 050 /** The regexp to match check names against. */ 051 private final Pattern checkRegexp; 052 053 /** The pattern for check class names. */ 054 private final String checkPattern; 055 056 /** Module id filter. */ 057 private final String moduleId; 058 059 /** Xpath expression. */ 060 private final XPathExpression xpathExpression; 061 062 /** Xpath query. */ 063 private final String xpathQuery; 064 065 /** 066 * Creates a {@code XpathElement} instance. 067 * @param files regular expression for names of filtered files 068 * @param checks regular expression for filtered check classes 069 * @param moduleId the module id 070 * @param query the xpath query 071 */ 072 public XpathFilter(String files, String checks, 073 String moduleId, String query) { 074 filePattern = files; 075 fileRegexp = Pattern.compile(files); 076 checkPattern = checks; 077 if (checks == null) { 078 checkRegexp = null; 079 } 080 else { 081 checkRegexp = CommonUtils.createPattern(checks); 082 } 083 this.moduleId = moduleId; 084 xpathQuery = query; 085 if (xpathQuery == null) { 086 xpathExpression = null; 087 } 088 else { 089 final XPathEvaluator xpathEvaluator = new XPathEvaluator(); 090 try { 091 xpathExpression = xpathEvaluator.createExpression(xpathQuery); 092 } 093 catch (XPathException ex) { 094 throw new IllegalStateException("Unexpected xpath query: " + xpathQuery, ex); 095 } 096 } 097 } 098 099 @Override 100 public boolean accept(TreeWalkerAuditEvent event) { 101 return !isFileNameAndModuleAndCheckNameMatching(event) 102 || !isXpathQueryMatching(event); 103 } 104 105 /** 106 * Is matching by file name, moduleId and Check name. 107 * @param event event 108 * @return true if it is matching 109 */ 110 private boolean isFileNameAndModuleAndCheckNameMatching(TreeWalkerAuditEvent event) { 111 return event.getFileName() != null 112 && fileRegexp.matcher(event.getFileName()).find() 113 && event.getLocalizedMessage() != null 114 && (moduleId == null || moduleId.equals(event.getModuleId())) 115 && (checkRegexp == null || checkRegexp.matcher(event.getSourceName()).find()); 116 } 117 118 /** 119 * Is matching by xpath query. 120 * @param event event 121 * @return true is matching 122 */ 123 private boolean isXpathQueryMatching(TreeWalkerAuditEvent event) { 124 boolean isMatching = false; 125 if (xpathExpression != null) { 126 final List<Item> items = getItems(event); 127 for (Item item : items) { 128 final AbstractNode abstractNode = (AbstractNode) item; 129 isMatching = abstractNode.getTokenType() == event.getTokenType() 130 && abstractNode.getLineNumber() == event.getLine() 131 && abstractNode.getColumnNumber() == event.getColumn(); 132 if (isMatching) { 133 break; 134 } 135 } 136 } 137 return isMatching; 138 } 139 140 /** 141 * Returns list of nodes matching xpath expression given event. 142 * @param event {@code TreeWalkerAuditEvent} object 143 * @return list of nodes matching xpath expression given event 144 */ 145 private List<Item> getItems(TreeWalkerAuditEvent event) { 146 final RootNode rootNode; 147 if (event.getRootAst() == null) { 148 rootNode = null; 149 } 150 else { 151 rootNode = new RootNode(event.getRootAst()); 152 } 153 final List<Item> items; 154 try { 155 final XPathDynamicContext xpathDynamicContext = 156 xpathExpression.createDynamicContext(rootNode); 157 items = xpathExpression.evaluate(xpathDynamicContext); 158 } 159 catch (XPathException ex) { 160 throw new IllegalStateException("Cannot initialize context and evaluate query: " 161 + xpathQuery, ex); 162 } 163 return items; 164 } 165 166 @Override 167 public int hashCode() { 168 return Objects.hash(filePattern, checkPattern, moduleId, xpathQuery); 169 } 170 171 @Override 172 public boolean equals(Object other) { 173 if (this == other) { 174 return true; 175 } 176 if (other == null || getClass() != other.getClass()) { 177 return false; 178 } 179 final XpathFilter xpathFilter = (XpathFilter) other; 180 return Objects.equals(filePattern, xpathFilter.filePattern) 181 && Objects.equals(checkPattern, xpathFilter.checkPattern) 182 && Objects.equals(moduleId, xpathFilter.moduleId) 183 && Objects.equals(xpathQuery, xpathFilter.xpathQuery); 184 } 185}