001package com.streamconverter.examples; 002 003import com.streamconverter.StreamConverter; 004import com.streamconverter.command.AbstractStreamCommand; 005import com.streamconverter.command.IStreamCommand; 006import com.streamconverter.command.impl.LineEndingNormalizeCommand; 007import java.io.ByteArrayInputStream; 008import java.io.ByteArrayOutputStream; 009import java.io.IOException; 010import java.io.InputStream; 011import java.io.OutputStream; 012import java.nio.charset.StandardCharsets; 013import org.slf4j.Logger; 014import org.slf4j.LoggerFactory; 015 016/** 017 * 例1: IStreamCommand と StreamConverter の仕組み 018 * 019 * <p>StreamConverter のパイプライン処理の基本を示す。 020 * 021 * <p><b>この例で学べること:</b> 022 * 023 * <ul> 024 * <li>{@link IStreamCommand} はラムダ式でも、{@link AbstractStreamCommand} を継承したクラスでも実装できる 025 * <li>{@link StreamConverter#create(IStreamCommand...)} に複数のコマンドを渡すと順番に接続されパイプラインになる 026 * <li>各コマンドは別の仮想スレッドで並列実行される(ログのスレッド名で確認できる) 027 * <li>前段コマンドの出力が後段コマンドの入力に自動的にバイト列として接続される 028 * </ul> 029 * 030 * <p><b>パイプライン構成(3段):</b> 031 * 032 * <pre> 033 * [コマンド1: ラムダ実装] 各行の前後空白をトリム 034 * ↓ 035 * [コマンド2: クラス実装] 各行を大文字に変換(AbstractStreamCommand 継承) 036 * ↓ 037 * [コマンド3: 組み込みコマンド] 行末コードを LF に統一(LineEndingNormalizeCommand) 038 * </pre> 039 */ 040public class PipelineBasicsExample { 041 042 private static final Logger log = LoggerFactory.getLogger(PipelineBasicsExample.class); 043 044 /** 045 * @param args コマンドライン引数(未使用) 046 * @throws IOException I/O エラー 047 */ 048 public static void main(String[] args) throws IOException { 049 log.info("=== 例1: IStreamCommand と StreamConverter の仕組み ==="); 050 051 String input = " hello world \r\n" + " stream converter \r\n" + " pipeline demo \r\n"; 052 053 log.info("入力データ(各行に前後スペース、行末 CRLF):\n{}", input); 054 055 // --- コマンド1: ラムダ実装 --- 056 // IStreamCommand は @FunctionalInterface なのでラムダで実装できる。 057 // ただしラムダはクラス名を持たないため、ログでは "IStreamCommand" と表示される。 058 IStreamCommand trimCommand = 059 (in, out) -> { 060 String text = new String(in.readAllBytes(), StandardCharsets.UTF_8); 061 StringBuilder sb = new StringBuilder(); 062 for (String line : text.split("\n")) { 063 sb.append(line.stripTrailing().stripLeading()).append("\n"); 064 } 065 out.write(sb.toString().getBytes(StandardCharsets.UTF_8)); 066 }; 067 068 // --- コマンド2: AbstractStreamCommand 継承クラス --- 069 // AbstractStreamCommand を継承することで: 070 // - クラス名がログに表示される(UpperCaseCommand) 071 // - protected final Logger log が自動的に用意される 072 IStreamCommand upperCaseCommand = new UpperCaseCommand(); 073 074 // --- コマンド3: 組み込みコマンド --- 075 // LineEndingNormalizeCommand は AbstractStreamCommand を継承した既製コマンド。 076 // 行末コードを UNIX (LF) / WINDOWS (CRLF) / CLASSIC_MAC (CR) に統一する。 077 IStreamCommand normalizeCommand = 078 new LineEndingNormalizeCommand(LineEndingNormalizeCommand.LineEndingType.UNIX); 079 080 // --- パイプライン実行 --- 081 // StreamConverter.create() に3つのコマンドを渡すと、 082 // 内部で各コマンドを仮想スレッドで並列起動し、AbortablePipedStream で接続する。 083 StreamConverter converter = 084 StreamConverter.create(trimCommand, upperCaseCommand, normalizeCommand); 085 086 ByteArrayOutputStream output = new ByteArrayOutputStream(); 087 try (InputStream inputStream = 088 new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8))) { 089 converter.run(inputStream, output); 090 } 091 092 String expected = "HELLO WORLD\n" + "STREAM CONVERTER\n" + "PIPELINE DEMO\n"; 093 094 String result = output.toString(StandardCharsets.UTF_8); 095 log.info("期待値(空白トリム→大文字変換→行末 LF 統一):\n{}", expected); 096 log.info("出力データ:\n{}", result); 097 } 098 099 /** 100 * AbstractStreamCommand を継承したクラス実装の例。 101 * 102 * <p>{@link AbstractStreamCommand} を継承することで: 103 * 104 * <ul> 105 * <li>クラス名(UpperCaseCommand)がログのコマンド名として自動的に使われる 106 * <li>{@code protected final Logger log} が利用可能になる 107 * <li>{@code execute()} メソッドを実装するだけでよい 108 * </ul> 109 */ 110 static class UpperCaseCommand extends AbstractStreamCommand { 111 112 @Override 113 public void execute(InputStream inputStream, OutputStream outputStream) throws IOException { 114 String text = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); 115 log.debug("UpperCaseCommand: {} 文字を大文字に変換", text.length()); 116 outputStream.write(text.toUpperCase().getBytes(StandardCharsets.UTF_8)); 117 } 118 } 119}