001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2022 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.api; 021 022import java.io.File; 023import java.util.Arrays; 024import java.util.SortedSet; 025import java.util.TreeSet; 026 027import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 028 029/** 030 * Provides common functionality for many FileSetChecks. 031 * 032 * @noinspection NoopMethodInAbstractClass 033 * @noinspectionreason NoopMethodInAbstractClass - we allow each 034 * check to define these methods, as needed. They 035 * should be overridden only by demand in subclasses 036 */ 037public abstract class AbstractFileSetCheck 038 extends AbstractViolationReporter 039 implements FileSetCheck { 040 041 /** 042 * The check context. 043 * 044 * @noinspection ThreadLocalNotStaticFinal 045 * @noinspectionreason ThreadLocalNotStaticFinal - static context is 046 * problematic for multithreading 047 */ 048 private final ThreadLocal<FileContext> context = ThreadLocal.withInitial(FileContext::new); 049 050 /** The dispatcher errors are fired to. */ 051 private MessageDispatcher messageDispatcher; 052 053 /** Specify the file type extension of files to process. */ 054 private String[] fileExtensions = CommonUtil.EMPTY_STRING_ARRAY; 055 056 /** The tab width for column reporting. */ 057 private int tabWidth = CommonUtil.DEFAULT_TAB_WIDTH; 058 059 /** 060 * Called to process a file that matches the specified file extensions. 061 * 062 * @param file the file to be processed 063 * @param fileText the contents of the file. 064 * @throws CheckstyleException if error condition within Checkstyle occurs. 065 */ 066 protected abstract void processFiltered(File file, FileText fileText) 067 throws CheckstyleException; 068 069 @Override 070 public void init() { 071 // No code by default, should be overridden only by demand at subclasses 072 } 073 074 @Override 075 public void destroy() { 076 context.remove(); 077 } 078 079 @Override 080 public void beginProcessing(String charset) { 081 // No code by default, should be overridden only by demand at subclasses 082 } 083 084 @Override 085 public final SortedSet<Violation> process(File file, FileText fileText) 086 throws CheckstyleException { 087 final FileContext fileContext = context.get(); 088 fileContext.fileContents = new FileContents(fileText); 089 fileContext.violations.clear(); 090 // Process only what interested in 091 if (CommonUtil.matchesFileExtension(file, fileExtensions)) { 092 processFiltered(file, fileText); 093 } 094 final SortedSet<Violation> result = new TreeSet<>(fileContext.violations); 095 fileContext.violations.clear(); 096 return result; 097 } 098 099 @Override 100 public void finishProcessing() { 101 // No code by default, should be overridden only by demand at subclasses 102 } 103 104 @Override 105 public final void setMessageDispatcher(MessageDispatcher messageDispatcher) { 106 this.messageDispatcher = messageDispatcher; 107 } 108 109 /** 110 * A message dispatcher is used to fire violations to 111 * interested audit listeners. 112 * 113 * @return the current MessageDispatcher. 114 */ 115 protected final MessageDispatcher getMessageDispatcher() { 116 return messageDispatcher; 117 } 118 119 /** 120 * Returns the sorted set of {@link Violation}. 121 * 122 * @return the sorted set of {@link Violation}. 123 */ 124 public SortedSet<Violation> getViolations() { 125 return new TreeSet<>(context.get().violations); 126 } 127 128 /** 129 * Set the file contents associated with the tree. 130 * 131 * @param contents the manager 132 */ 133 public final void setFileContents(FileContents contents) { 134 context.get().fileContents = contents; 135 } 136 137 /** 138 * Returns the file contents associated with the file. 139 * 140 * @return the file contents 141 */ 142 protected final FileContents getFileContents() { 143 return context.get().fileContents; 144 } 145 146 /** 147 * Makes copy of file extensions and returns them. 148 * 149 * @return file extensions that identify the files that pass the 150 * filter of this FileSetCheck. 151 */ 152 public String[] getFileExtensions() { 153 return Arrays.copyOf(fileExtensions, fileExtensions.length); 154 } 155 156 /** 157 * Setter to specify the file type extension of files to process. 158 * 159 * @param extensions the set of file extensions. A missing 160 * initial '.' character of an extension is automatically added. 161 * @throws IllegalArgumentException is argument is null 162 */ 163 public final void setFileExtensions(String... extensions) { 164 if (extensions == null) { 165 throw new IllegalArgumentException("Extensions array can not be null"); 166 } 167 168 fileExtensions = new String[extensions.length]; 169 for (int i = 0; i < extensions.length; i++) { 170 final String extension = extensions[i]; 171 if (CommonUtil.startsWithChar(extension, '.')) { 172 fileExtensions[i] = extension; 173 } 174 else { 175 fileExtensions[i] = "." + extension; 176 } 177 } 178 } 179 180 /** 181 * Get tab width to report audit events with. 182 * 183 * @return the tab width to report audit events with 184 */ 185 protected final int getTabWidth() { 186 return tabWidth; 187 } 188 189 /** 190 * Set the tab width to report audit events with. 191 * 192 * @param tabWidth an {@code int} value 193 */ 194 public final void setTabWidth(int tabWidth) { 195 this.tabWidth = tabWidth; 196 } 197 198 /** 199 * Adds the sorted set of {@link Violation} to the message collector. 200 * 201 * @param violations the sorted set of {@link Violation}. 202 */ 203 protected void addViolations(SortedSet<Violation> violations) { 204 context.get().violations.addAll(violations); 205 } 206 207 @Override 208 public final void log(int line, String key, Object... args) { 209 context.get().violations.add( 210 new Violation(line, 211 getMessageBundle(), 212 key, 213 args, 214 getSeverityLevel(), 215 getId(), 216 getClass(), 217 getCustomMessages().get(key))); 218 } 219 220 @Override 221 public final void log(int lineNo, int colNo, String key, 222 Object... args) { 223 final FileContext fileContext = context.get(); 224 final int col = 1 + CommonUtil.lengthExpandedTabs( 225 fileContext.fileContents.getLine(lineNo - 1), colNo, tabWidth); 226 fileContext.violations.add( 227 new Violation(lineNo, 228 col, 229 getMessageBundle(), 230 key, 231 args, 232 getSeverityLevel(), 233 getId(), 234 getClass(), 235 getCustomMessages().get(key))); 236 } 237 238 /** 239 * Notify all listeners about the errors in a file. 240 * Calls {@code MessageDispatcher.fireErrors()} with 241 * all logged errors and then clears errors' list. 242 * 243 * @param fileName the audited file 244 */ 245 protected final void fireErrors(String fileName) { 246 final FileContext fileContext = context.get(); 247 final SortedSet<Violation> errors = new TreeSet<>(fileContext.violations); 248 fileContext.violations.clear(); 249 messageDispatcher.fireErrors(fileName, errors); 250 } 251 252 /** 253 * The actual context holder. 254 */ 255 private static class FileContext { 256 257 /** The sorted set for collecting violations. */ 258 private final SortedSet<Violation> violations = new TreeSet<>(); 259 260 /** The current file contents. */ 261 private FileContents fileContents; 262 263 } 264 265}