001package com.streamconverter.command.impl.analysis; 002 003import com.fasterxml.jackson.dataformat.csv.CsvMapper; 004import com.fasterxml.jackson.dataformat.csv.CsvSchema; 005import com.streamconverter.command.AbstractStreamCommand; 006import com.streamconverter.command.impl.analysis.PmdXmlToMarkdownCommand.PmdViolation; 007import java.io.IOException; 008import java.io.InputStream; 009import java.io.OutputStream; 010import java.util.*; 011import javax.xml.parsers.DocumentBuilder; 012import javax.xml.parsers.DocumentBuilderFactory; 013import org.w3c.dom.Document; 014import org.w3c.dom.Element; 015import org.w3c.dom.NodeList; 016 017/** 018 * PMD XML レポートを CSV 形式に変換するコマンド 019 * 020 * <p>StreamConverter アーキテクチャ準拠の実装例として、PMD XMLレポートを スプレッドシート分析に適したCSV形式に変換します。Jackson CSV 021 * mapperを使用した 型安全で効率的なCSV生成を実装しています。 022 * 023 * <p><strong>出力CSV形式:</strong> 024 * 025 * <pre> 026 * File,Line,Rule,Category,Priority,Description,Class,Method,Variable 027 * </pre> 028 * 029 * <p><strong>特徴:</strong> 030 * 031 * <ul> 032 * <li>Jackson CsvMapper による型安全なCSV生成 033 * <li>適切なCSVエスケープ処理 034 * <li>StreamConverter設計原則準拠 035 * <li>ストリーミング処理対応 036 * </ul> 037 * 038 * <p><strong>使用例:</strong> 039 * 040 * <pre> 041 * // パイプライン使用例 042 * StreamConverter converter = new StreamConverter( 043 * new PmdXmlToCsvCommand() 044 * ); 045 * converter.run(pmdXmlInputStream, csvOutputStream); 046 * </pre> 047 */ 048public class PmdXmlToCsvCommand extends AbstractStreamCommand { 049 050 private final CsvMapper csvMapper; 051 052 /** Creates a new command instance. */ 053 public PmdXmlToCsvCommand() { 054 this.csvMapper = new CsvMapper(); 055 } 056 057 /** 058 * PMD XML InputStream を CSV OutputStream に変換 059 * 060 * @param input PMD XML レポートの入力ストリーム 061 * @param output CSV レポートの出力ストリーム 062 * @throws IOException XML解析エラーまたはI/O例外の場合 063 */ 064 @Override 065 protected void executeInternal(InputStream input, OutputStream output) throws IOException { 066 try { 067 // StreamConverter原則に従った変換処理 068 List<PmdViolation> violations = parseXmlStream(input); 069 generateCsvOutput(violations, output); 070 071 } catch (Exception e) { 072 throw new IOException("Failed to convert PMD XML to CSV: " + e.getMessage(), e); 073 } 074 } 075 076 @Override 077 protected String getCommandDetails() { 078 return "PmdXmlToCsvCommand: Converts PMD XML reports to CSV format using Jackson CsvMapper"; 079 } 080 081 /** 082 * PMD XML ストリームからバイオレーション情報を解析 083 * 084 * @param input PMD XML入力ストリーム 085 * @return 解析されたバイオレーションのリスト 086 * @throws Exception XML解析エラーの場合 087 */ 088 private List<PmdViolation> parseXmlStream(InputStream input) throws Exception { 089 DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); 090 Document doc = builder.parse(input); 091 092 NodeList fileNodes = doc.getElementsByTagName("file"); 093 List<PmdViolation> violations = new ArrayList<>(); 094 095 for (int i = 0; i < fileNodes.getLength(); i++) { 096 Element fileElement = (Element) fileNodes.item(i); 097 String fileName = fileElement.getAttribute("name"); 098 099 NodeList violationNodes = fileElement.getElementsByTagName("violation"); 100 for (int j = 0; j < violationNodes.getLength(); j++) { 101 Element violationElement = (Element) violationNodes.item(j); 102 103 PmdViolation violation = 104 new PmdViolation( 105 extractRelativePath(fileName), 106 Integer.parseInt(violationElement.getAttribute("beginline")), 107 violationElement.getAttribute("rule"), 108 violationElement.getAttribute("ruleset"), 109 Integer.parseInt(violationElement.getAttribute("priority")), 110 violationElement.getTextContent().trim(), 111 violationElement.getAttribute("class"), 112 violationElement.getAttribute("method"), 113 violationElement.getAttribute("variable")); 114 violations.add(violation); 115 } 116 } 117 118 return violations; 119 } 120 121 /** StreamConverterプロジェクト内の相対パスを抽出 */ 122 private String extractRelativePath(String fullPath) { 123 int index = fullPath.indexOf("streamconverter-"); 124 return index != -1 ? fullPath.substring(index) : fullPath; 125 } 126 127 /** 128 * Jackson CsvMapper を使用してCSV出力を生成 129 * 130 * @param violations バイオレーション情報 131 * @param output 出力ストリーム 132 * @throws IOException CSV生成エラーの場合 133 */ 134 private void generateCsvOutput(List<PmdViolation> violations, OutputStream output) 135 throws IOException { 136 // CSV スキーマ定義(ヘッダー付き) 137 CsvSchema schema = 138 csvMapper 139 .schemaFor(PmdViolation.class) 140 .withHeader() 141 .withColumnSeparator(',') 142 .withQuoteChar('"') 143 .withEscapeChar('\\') 144 .withLineSeparator("\n"); 145 146 // Jackson CsvMapper による型安全なCSV生成 - Listタイプ用のwriter使用 147 csvMapper.writer(schema).writeValue(output, violations); 148 } 149}