001package com.streamconverter.validation; 002 003import java.time.Instant; 004import java.util.ArrayList; 005import java.util.Collections; 006import java.util.List; 007import java.util.Objects; 008 009/** 010 * バリデーション結果を統一的に表現するクラス 011 * 012 * <p>JSON、XML、CSVなど、すべてのバリデーション処理の結果を一貫した形式で提供します。 バリデーションの成功/失敗、エラーメッセージ、実行時間などの情報を含みます。 013 * 014 * <p>使用例: 015 * 016 * <pre> 017 * ValidationResult result = ValidationResult.builder() 018 * .validationType("JSON") 019 * .schemaPath("schema/user.json") 020 * .success(false) 021 * .addError("Field 'email' is required") 022 * .addError("Field 'age' must be a number") 023 * .build(); 024 * </pre> 025 */ 026public final class ValidationResult { 027 028 private final String validationType; 029 private final String schemaPath; 030 private final boolean valid; 031 private final List<String> errors; 032 private final List<String> warnings; 033 private final Instant validationTime; 034 private final long execTimeMillis; 035 private final String dataSource; 036 037 private ValidationResult( 038 final String validationType, 039 final String schemaPath, 040 final boolean valid, 041 final List<String> errors, 042 final List<String> warnings, 043 final Instant validationTime, 044 final long execTimeMillis, 045 final String dataSource) { 046 this.validationType = validationType; 047 this.schemaPath = schemaPath; 048 this.valid = valid; 049 this.errors = Collections.unmodifiableList(new ArrayList<>(errors)); 050 this.warnings = Collections.unmodifiableList(new ArrayList<>(warnings)); 051 this.validationTime = validationTime; 052 this.execTimeMillis = execTimeMillis; 053 this.dataSource = dataSource; 054 } 055 056 /** 057 * 成功した場合のバリデーション結果を作成 058 * 059 * @param validationType バリデーションタイプ("JSON", "XML", "CSV"など) 060 * @param schemaPath スキーマファイルのパス 061 * @param execTimeMillis 実行時間(ミリ秒) 062 * @return 成功を表すValidationResult 063 */ 064 public static ValidationResult success( 065 final String validationType, final String schemaPath, final long execTimeMillis) { 066 return builder() 067 .validationType(validationType) 068 .schemaPath(schemaPath) 069 .success(true) 070 .executionTimeMillis(execTimeMillis) 071 .build(); 072 } 073 074 /** 075 * 失敗した場合のバリデーション結果を作成 076 * 077 * @param validationType バリデーションタイプ("JSON", "XML", "CSV"など) 078 * @param schemaPath スキーマファイルのパス 079 * @param errors エラーメッセージのリスト 080 * @param execTimeMillis 実行時間(ミリ秒) 081 * @return 失敗を表すValidationResult 082 */ 083 public static ValidationResult failure( 084 final String validationType, 085 final String schemaPath, 086 final List<String> errors, 087 final long execTimeMillis) { 088 final Builder builder = 089 builder() 090 .validationType(validationType) 091 .schemaPath(schemaPath) 092 .success(false) 093 .executionTimeMillis(execTimeMillis); 094 095 if (errors != null) { 096 for (final String error : errors) { 097 builder.addError(error); 098 } 099 } 100 101 return builder.build(); 102 } 103 104 /** 105 * Builderインスタンスを作成 106 * 107 * @return 新しいBuilderインスタンス 108 */ 109 public static Builder builder() { 110 return new Builder(); 111 } 112 113 // Getters 114 115 /** 116 * バリデーションタイプを取得 117 * 118 * @return バリデーションタイプ 119 */ 120 public String getValidationType() { 121 return validationType; 122 } 123 124 /** 125 * スキーマパスを取得 126 * 127 * @return スキーマファイルのパス 128 */ 129 public String getSchemaPath() { 130 return schemaPath; 131 } 132 133 /** 134 * バリデーション結果が有効かどうかを取得 135 * 136 * @return 有効な場合true、無効な場合false 137 */ 138 public boolean isValid() { 139 return valid; 140 } 141 142 /** 143 * エラーメッセージのリストを取得 144 * 145 * @return エラーメッセージのリスト(読み取り専用) 146 */ 147 public List<String> getErrors() { 148 return errors; 149 } 150 151 /** 152 * 警告メッセージのリストを取得 153 * 154 * @return 警告メッセージのリスト(読み取り専用) 155 */ 156 public List<String> getWarnings() { 157 return warnings; 158 } 159 160 /** 161 * バリデーション実行時刻を取得 162 * 163 * @return 実行時刻 164 */ 165 public Instant getValidationTime() { 166 return validationTime; 167 } 168 169 /** 170 * 実行時間を取得 171 * 172 * @return 実行時間(ミリ秒) 173 */ 174 public long getExecutionTimeMillis() { 175 return execTimeMillis; 176 } 177 178 /** 179 * データソース情報を取得 180 * 181 * @return データソース情報 182 */ 183 public String getDataSource() { 184 return dataSource; 185 } 186 187 @Override 188 public String toString() { 189 return String.format( 190 "ValidationResult{type=%s, schema=%s, valid=%s, errors=%d, warnings=%d, executionTimeMs=%d}", 191 validationType, schemaPath, valid, errors.size(), warnings.size(), execTimeMillis); 192 } 193 194 @Override 195 public boolean equals(final Object other) { 196 boolean result = false; 197 if (this == other) { 198 result = true; 199 } else if (other instanceof ValidationResult) { 200 final ValidationResult that = (ValidationResult) other; 201 result = 202 valid == that.valid 203 && execTimeMillis == that.execTimeMillis 204 && Objects.equals(validationType, that.validationType) 205 && Objects.equals(schemaPath, that.schemaPath) 206 && Objects.equals(errors, that.errors) 207 && Objects.equals(warnings, that.warnings) 208 && Objects.equals(validationTime, that.validationTime) 209 && Objects.equals(dataSource, that.dataSource); 210 } 211 return result; 212 } 213 214 @Override 215 public int hashCode() { 216 return Objects.hash( 217 validationType, 218 schemaPath, 219 valid, 220 errors, 221 warnings, 222 validationTime, 223 execTimeMillis, 224 dataSource); 225 } 226 227 /** ValidationResult作成用のBuilderクラス */ 228 public static class Builder { 229 private String typeVal; 230 private String schemaPathVal; 231 private boolean valid; 232 private final List<String> errors = new ArrayList<>(); 233 private final List<String> warnings = new ArrayList<>(); 234 private Instant timeVal = Instant.now(); 235 private long execTimeMillis; 236 private String dataSourceVal; 237 238 /** 239 * バリデーションタイプを設定 240 * 241 * @param validationType バリデーションタイプ 242 * @return Builder 243 */ 244 public Builder validationType(final String validationType) { 245 this.typeVal = validationType; 246 return this; 247 } 248 249 /** 250 * スキーマパスを設定 251 * 252 * @param schemaPath スキーマファイルのパス 253 * @return Builder 254 */ 255 public Builder schemaPath(final String schemaPath) { 256 this.schemaPathVal = schemaPath; 257 return this; 258 } 259 260 /** 261 * バリデーション成功/失敗フラグを設定 262 * 263 * @param success 成功の場合true 264 * @return Builder 265 */ 266 public Builder success(final boolean success) { 267 this.valid = success; 268 return this; 269 } 270 271 /** 272 * エラーメッセージを追加 273 * 274 * @param error エラーメッセージ 275 * @return Builder 276 */ 277 public Builder addError(final String error) { 278 if (error != null && !error.isBlank()) { 279 this.errors.add(error.trim()); 280 } 281 return this; 282 } 283 284 /** 285 * 警告メッセージを追加 286 * 287 * @param warning 警告メッセージ 288 * @return Builder 289 */ 290 public Builder addWarning(final String warning) { 291 if (warning != null && !warning.isBlank()) { 292 this.warnings.add(warning.trim()); 293 } 294 return this; 295 } 296 297 /** 298 * バリデーション実行時刻を設定 299 * 300 * @param validationTime 実行時刻 301 * @return Builder 302 */ 303 public Builder validationTime(final Instant validationTime) { 304 this.timeVal = validationTime; 305 return this; 306 } 307 308 /** 309 * 実行時間を設定 310 * 311 * @param execTimeMillis 実行時間(ミリ秒) 312 * @return Builder 313 */ 314 public Builder executionTimeMillis(final long execTimeMillis) { 315 this.execTimeMillis = execTimeMillis; 316 return this; 317 } 318 319 /** 320 * データソース情報を設定 321 * 322 * @param dataSource データソース情報 323 * @return Builder 324 */ 325 public Builder dataSource(final String dataSource) { 326 this.dataSourceVal = dataSource; 327 return this; 328 } 329 330 /** 331 * ValidationResultインスタンスを構築 332 * 333 * @return ValidationResult 334 * @throws IllegalStateException 必須フィールドが設定されていない場合 335 */ 336 public ValidationResult build() { 337 requireNonBlank(typeVal, "Validation type"); 338 requireNonBlank(schemaPathVal, "Schema path"); 339 ensureConsistency(); 340 return new ValidationResult( 341 typeVal, schemaPathVal, valid, errors, warnings, timeVal, execTimeMillis, dataSourceVal); 342 } 343 344 private static void requireNonBlank(final String value, final String field) { 345 if (value == null || value.isBlank()) { 346 throw new IllegalArgumentException(field + " cannot be null or empty"); 347 } 348 } 349 350 private void ensureConsistency() { 351 if (valid && !errors.isEmpty()) { 352 throw new IllegalStateException( 353 "ValidationResult cannot be marked as valid when errors are present"); 354 } 355 if (!valid && errors.isEmpty()) { 356 addError("Validation failed (no specific error message)"); 357 } 358 } 359 } 360}