001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code 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; 021 022import java.io.File; 023import java.io.OutputStream; 024import java.io.OutputStreamWriter; 025import java.io.PrintWriter; 026import java.nio.charset.StandardCharsets; 027 028import com.puppycrawl.tools.checkstyle.api.AuditEvent; 029import com.puppycrawl.tools.checkstyle.api.AuditListener; 030import com.puppycrawl.tools.checkstyle.api.AutomaticBean; 031 032/** 033 * Generates <b>suppressions.xml</b> file, based on violations occurred. 034 * See issue #102 https://github.com/checkstyle/checkstyle/issues/102 035 */ 036public class XpathFileGeneratorAuditListener extends AutomaticBean implements AuditListener { 037 038 /** The " quote character. */ 039 private static final String QUOTE_CHAR = "\""; 040 041 /** 042 * Helper writer that allows easy encoding and printing. 043 */ 044 private final PrintWriter writer; 045 046 /** Close output stream in auditFinished. */ 047 private final boolean closeStream; 048 049 /** Determines if xml header is printed. */ 050 private boolean isXmlHeaderPrinted; 051 052 /** 053 * Creates a new {@code SuppressionFileGenerator} instance. 054 * Sets the output to a defined stream. 055 * 056 * @param out the output stream 057 * @param outputStreamOptions if {@code CLOSE} stream should be closed in auditFinished() 058 */ 059 public XpathFileGeneratorAuditListener(OutputStream out, 060 OutputStreamOptions outputStreamOptions) { 061 writer = new PrintWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)); 062 closeStream = outputStreamOptions == OutputStreamOptions.CLOSE; 063 } 064 065 @Override 066 public void auditStarted(AuditEvent event) { 067 // No code by default 068 } 069 070 @Override 071 public void auditFinished(AuditEvent event) { 072 if (isXmlHeaderPrinted) { 073 writer.println("</suppressions>"); 074 } 075 076 writer.flush(); 077 if (closeStream) { 078 writer.close(); 079 } 080 } 081 082 @Override 083 public void fileStarted(AuditEvent event) { 084 // No code by default 085 } 086 087 @Override 088 public void fileFinished(AuditEvent event) { 089 // No code by default 090 } 091 092 @Override 093 public void addError(AuditEvent event) { 094 final String xpathQuery = XpathFileGeneratorAstFilter.findCorrespondingXpathQuery(event); 095 if (xpathQuery != null) { 096 printXmlHeader(); 097 098 final File file = new File(event.getFileName()); 099 100 writer.println("<suppress-xpath"); 101 writer.print(" files=\""); 102 writer.print(file.getName()); 103 writer.println(QUOTE_CHAR); 104 105 if (event.getModuleId() == null) { 106 final String checkName = 107 PackageObjectFactory.getShortFromFullModuleNames(event.getSourceName()); 108 writer.print(" checks=\""); 109 writer.print(checkName); 110 } 111 else { 112 writer.print(" id=\""); 113 writer.print(event.getModuleId()); 114 } 115 writer.println(QUOTE_CHAR); 116 117 writer.print(" query=\""); 118 writer.print(xpathQuery); 119 120 writer.println("\"/>"); 121 } 122 } 123 124 @Override 125 public void addException(AuditEvent event, Throwable throwable) { 126 throw new UnsupportedOperationException("Operation is not supported"); 127 } 128 129 /** 130 * Prints XML header if only it was not printed before. 131 */ 132 private void printXmlHeader() { 133 if (!isXmlHeaderPrinted) { 134 writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); 135 writer.println("<!DOCTYPE suppressions PUBLIC"); 136 writer.println(" \"-//Checkstyle//DTD SuppressionXpathFilter Experimental " 137 + "Configuration 1.2//EN\""); 138 writer.println(" \"https://checkstyle.org/dtds/" 139 + "suppressions_1_2_xpath_experimental.dtd\">"); 140 writer.println("<suppressions>"); 141 isXmlHeaderPrinted = true; 142 } 143 } 144 145 @Override 146 protected void finishLocalSetup() { 147 // No code by default 148 } 149}