001package com.streamconverter.path; 002 003import java.util.ArrayList; 004import java.util.Collections; 005import java.util.List; 006 007/** 008 * 最小限のCSVPath実装 009 * 010 * <p>CSV列選択のパス一致判定のみに特化したシンプルな設計 011 * 012 * <p>複数の列セレクターをOR条件で判定する機能を提供 013 */ 014public class CSVPath extends AbstractPath<Integer> { 015 016 private final List<String> selectors; 017 018 /** 019 * 単一セレクターでCSVPathを作成 020 * 021 * @param selector 列選択子(列名または数値インデックス) 022 * @throws IllegalArgumentException セレクターが不正な場合 023 */ 024 public CSVPath(String selector) { 025 super(selector); 026 this.selectors = Collections.singletonList(selector.trim()); 027 } 028 029 /** 030 * 複数セレクターでCSVPathを作成(OR条件) 031 * 032 * @param selectorList 列選択子のリスト 033 * @throws IllegalArgumentException セレクターが不正な場合 034 */ 035 public CSVPath(List<String> selectorList) { 036 super(String.join(",", selectorList)); 037 if (selectorList == null || selectorList.isEmpty()) { 038 throw new IllegalArgumentException("Selector list cannot be null or empty"); 039 } 040 List<String> temp = new ArrayList<>(); 041 for (String sel : selectorList) { 042 temp.add(sel.trim()); 043 } 044 this.selectors = Collections.unmodifiableList(temp); 045 } 046 047 @Override 048 protected void validateAndNormalize(String rawSelector) { 049 if (isNullOrEmpty(rawSelector)) { 050 throw new IllegalArgumentException("CSV column selector cannot be null or empty"); 051 } 052 } 053 054 @Override 055 public boolean matches(Integer columnIndex) { 056 if (columnIndex == null || columnIndex < 0) { 057 return false; 058 } 059 060 // OR条件:いずれかのセレクターがマッチすればtrue 061 for (String selector : selectors) { 062 if (matchesSingleSelector(selector, columnIndex)) { 063 return true; 064 } 065 } 066 return false; 067 } 068 069 /** 070 * 列ヘッダー配列との一致判定(OR条件) 071 * 072 * @param headers CSV列ヘッダー配列 073 * @param targetIndex 対象列のインデックス 074 * @return いずれかのセレクターが一致する場合true 075 */ 076 public boolean matches(String[] headers, int targetIndex) { 077 if (headers == null || targetIndex < 0 || targetIndex >= headers.length) { 078 return false; 079 } 080 081 // OR条件:いずれかのセレクターがマッチすればtrue 082 for (String selector : selectors) { 083 if (matchesSingleSelector(selector, headers, targetIndex)) { 084 return true; 085 } 086 } 087 return false; 088 } 089 090 /** 091 * マッチするすべての列インデックスを取得(Don't Ask Tell準拠) 092 * 093 * @param headers CSV列ヘッダー配列 094 * @return マッチした列インデックスのリスト 095 */ 096 public List<Integer> findMatchingIndices(String[] headers) { 097 List<Integer> matchingIndices = new ArrayList<>(); 098 if (headers == null) { 099 return matchingIndices; 100 } 101 102 for (int i = 0; i < headers.length; i++) { 103 if (matches(headers, i)) { 104 matchingIndices.add(i); 105 } 106 } 107 return matchingIndices; 108 } 109 110 /** 111 * マッチするすべての列インデックスを取得(ヘッダーなしの場合) 112 * 113 * @param totalColumns 総列数 114 * @return マッチした列インデックスのリスト 115 */ 116 public List<Integer> findMatchingIndices(int totalColumns) { 117 List<Integer> matchingIndices = new ArrayList<>(); 118 119 for (int i = 0; i < totalColumns; i++) { 120 if (matches(i)) { 121 matchingIndices.add(i); 122 } 123 } 124 return matchingIndices; 125 } 126 127 /** 単一セレクターの列インデックス一致判定 */ 128 private boolean matchesSingleSelector(String selector, Integer columnIndex) { 129 int parsedIndex = parseAsIndex(selector); 130 if (parsedIndex >= 0) { 131 return parsedIndex == columnIndex; 132 } 133 return false; // 列名指定はヘッダー情報が必要 134 } 135 136 /** 単一セレクターのヘッダー一致判定 */ 137 private boolean matchesSingleSelector(String selector, String[] headers, int targetIndex) { 138 int parsedIndex = parseAsIndex(selector); 139 if (parsedIndex >= 0) { 140 // インデックス指定の場合 141 return parsedIndex == targetIndex; 142 } else { 143 // 列名指定の場合 144 return headers[targetIndex].trim().equalsIgnoreCase(selector.trim()); 145 } 146 } 147 148 /** 文字列が数値インデックスかどうかを判定 */ 149 private static int parseAsIndex(String selector) { 150 if (selector == null || selector.isEmpty()) { 151 return -1; 152 } 153 try { 154 int index = Integer.parseInt(selector.trim()); 155 return index >= 0 ? index : -1; 156 } catch (NumberFormatException e) { 157 return -1; 158 } 159 } 160 161 @Override 162 public String toString() { 163 if (selectors.size() == 1) { 164 return selectors.get(0); 165 } else { 166 return String.join(",", selectors); 167 } 168 } 169}