001package com.streamconverter.controller; 002 003import com.streamconverter.command.CommandConfig; 004import com.streamconverter.command.impl.SampleStreamCommand; 005import com.streamconverter.command.impl.json.JsonNavigateCommand; 006import com.streamconverter.command.impl.json.JsonValidateCommand; 007 008/** 009 * Controller for JSON data processing operations. 010 * 011 * <p>This controller demonstrates advanced command pipeline configuration for JSON processing. It 012 * supports various JSON processing scenarios: 013 * 014 * <ul> 015 * <li>Property extraction using TreePath-like expressions 016 * <li>JSON formatting and pretty-printing 017 * <li>Data validation with custom rules 018 * <li>Transformation pipelines with multiple stages 019 * </ul> 020 * 021 * <p>Usage examples: 022 * 023 * <pre> 024 * // Extract property with validation 025 * JsonProcessingController controller = JsonProcessingController.forPropertyExtraction("user.name", true); 026 * controller.process(inputStream, outputStream); 027 * 028 * // Multi-stage transformation 029 * JsonProcessingController controller = JsonProcessingController.forTransformation( 030 * "data.items", "item-processor", "final-formatter" 031 * ); 032 * controller.process(inputStream, outputStream); 033 * </pre> 034 * 035 * @author StreamConverter Team 036 * @version 1.0 037 * @since 1.0 038 */ 039public class JsonProcessingController extends AbstractStreamController { 040 041 /** Default JSON schema path for validation */ 042 private static final String DEFAULT_SCHEMA_PATH = "schemas/default.json"; 043 044 /** Processing scenarios for JSON */ 045 public enum ProcessingScenario { 046 /** Extract specific JSON property values */ 047 PROPERTY_EXTRACTION, 048 /** Format JSON for readability only */ 049 FORMAT_ONLY, 050 /** Validate JSON and extract properties */ 051 VALIDATION_WITH_EXTRACTION, 052 /** Apply multiple transformation stages */ 053 MULTI_STAGE_TRANSFORMATION 054 } 055 056 /** The processing scenario */ 057 private final ProcessingScenario scenario; 058 059 /** TreePath-like expression for property extraction */ 060 private final String propertyPath; 061 062 /** Whether to enable validation */ 063 private final boolean enableValidation; 064 065 /** Processing stages for multi-stage transformation */ 066 private final String[] processingStages; 067 068 /** Private constructor for property extraction. */ 069 private JsonProcessingController(String propertyPath, boolean enableValidation) { 070 this.scenario = 071 enableValidation 072 ? ProcessingScenario.VALIDATION_WITH_EXTRACTION 073 : ProcessingScenario.PROPERTY_EXTRACTION; 074 this.propertyPath = propertyPath; 075 this.enableValidation = enableValidation; 076 this.processingStages = null; 077 } 078 079 /** Private constructor for format-only. */ 080 private JsonProcessingController() { 081 this.scenario = ProcessingScenario.FORMAT_ONLY; 082 this.propertyPath = null; 083 this.enableValidation = false; 084 this.processingStages = null; 085 } 086 087 /** Private constructor for multi-stage transformation. */ 088 private JsonProcessingController(String propertyPath, String... processingStages) { 089 this.scenario = ProcessingScenario.MULTI_STAGE_TRANSFORMATION; 090 this.propertyPath = propertyPath; 091 this.enableValidation = false; 092 this.processingStages = processingStages.clone(); 093 } 094 095 /** 096 * Factory method for property extraction. 097 * 098 * @param propertyPath TreePath-like expression (e.g., "user.name", "data[0].id") 099 * @param enableValidation whether to enable validation 100 * @return configured controller 101 */ 102 public static JsonProcessingController forPropertyExtraction( 103 String propertyPath, boolean enableValidation) { 104 if (propertyPath == null || propertyPath.trim().isEmpty()) { 105 throw new IllegalArgumentException("Property path cannot be null or empty"); 106 } 107 return new JsonProcessingController(propertyPath.trim(), enableValidation); 108 } 109 110 /** 111 * Factory method for JSON formatting only. 112 * 113 * @return configured controller that formats JSON without extraction 114 */ 115 public static JsonProcessingController forFormatting() { 116 return new JsonProcessingController(); 117 } 118 119 /** 120 * Factory method for multi-stage transformation. 121 * 122 * @param propertyPath TreePath-like expression for initial extraction 123 * @param processingStages array of processor IDs for transformation stages 124 * @return configured controller 125 */ 126 public static JsonProcessingController forTransformation( 127 String propertyPath, String... processingStages) { 128 if (propertyPath == null || propertyPath.trim().isEmpty()) { 129 throw new IllegalArgumentException("Property path cannot be null or empty"); 130 } 131 if (processingStages == null || processingStages.length == 0) { 132 throw new IllegalArgumentException("At least one processing stage must be specified"); 133 } 134 135 // Validate processing stages 136 for (int i = 0; i < processingStages.length; i++) { 137 if (processingStages[i] == null || processingStages[i].trim().isEmpty()) { 138 throw new IllegalArgumentException("Processing stage " + i + " cannot be null or empty"); 139 } 140 processingStages[i] = processingStages[i].trim(); 141 } 142 143 return new JsonProcessingController(propertyPath.trim(), processingStages); 144 } 145 146 @Override 147 protected CommandConfig[] configureCommands() { 148 switch (scenario) { 149 case PROPERTY_EXTRACTION: 150 return new CommandConfig[] { 151 new CommandConfig( 152 JsonNavigateCommand.class, "Extract JSON property: " + propertyPath, propertyPath) 153 }; 154 155 case FORMAT_ONLY: 156 return new CommandConfig[] {new CommandConfig(JsonNavigateCommand.class, "Format JSON")}; 157 158 case VALIDATION_WITH_EXTRACTION: 159 return new CommandConfig[] { 160 new CommandConfig( 161 JsonValidateCommand.class, "Validate JSON structure", DEFAULT_SCHEMA_PATH), 162 new CommandConfig( 163 JsonNavigateCommand.class, "Extract JSON property: " + propertyPath, propertyPath) 164 }; 165 166 case MULTI_STAGE_TRANSFORMATION: 167 // Build dynamic pipeline based on processing stages 168 CommandConfig[] configs = new CommandConfig[1 + processingStages.length]; 169 170 // First stage: JSON extraction 171 configs[0] = 172 new CommandConfig( 173 JsonNavigateCommand.class, "Extract JSON property: " + propertyPath, propertyPath); 174 175 // Additional processing stages 176 for (int i = 0; i < processingStages.length; i++) { 177 configs[i + 1] = 178 new CommandConfig( 179 SampleStreamCommand.class, 180 "Processing stage " + (i + 1) + ": " + processingStages[i], 181 processingStages[i]); 182 } 183 184 return configs; 185 186 default: 187 throw new IllegalStateException("Unknown processing scenario: " + scenario); 188 } 189 } 190 191 @Override 192 public String getInputDataType() { 193 return "JSON"; 194 } 195 196 @Override 197 public String getOutputDataType() { 198 switch (scenario) { 199 case PROPERTY_EXTRACTION: 200 return "JSON_PROPERTY"; 201 case FORMAT_ONLY: 202 return "JSON_FORMATTED"; 203 case VALIDATION_WITH_EXTRACTION: 204 return "VALIDATED_JSON_PROPERTY"; 205 case MULTI_STAGE_TRANSFORMATION: 206 return "TRANSFORMED_DATA"; 207 default: 208 return "UNKNOWN"; 209 } 210 } 211 212 /** 213 * Gets the processing scenario. 214 * 215 * @return the processing scenario 216 */ 217 public ProcessingScenario getProcessingScenario() { 218 return scenario; 219 } 220 221 /** 222 * Gets the property path for extraction. 223 * 224 * @return the property path, or null if not applicable 225 */ 226 public String getPropertyPath() { 227 return propertyPath; 228 } 229 230 /** 231 * Checks if validation is enabled. 232 * 233 * @return true if validation is enabled 234 */ 235 public boolean isValidationEnabled() { 236 return enableValidation; 237 } 238 239 /** 240 * Gets the processing stages for multi-stage transformation. 241 * 242 * @return array of processing stage IDs, or null if not applicable 243 */ 244 public String[] getProcessingStages() { 245 return processingStages == null ? null : processingStages.clone(); 246 } 247 248 @Override 249 public String getConfigurationDescription() { 250 StringBuilder desc = new StringBuilder(super.getConfigurationDescription()); 251 desc.append(" - Scenario: ").append(scenario); 252 253 if (propertyPath != null) { 254 desc.append(", Property: ").append(propertyPath); 255 } 256 257 if (enableValidation) { 258 desc.append(", Validation: enabled"); 259 } 260 261 if (processingStages != null) { 262 desc.append(", Stages: ").append(processingStages.length); 263 } 264 265 return desc.toString(); 266 } 267 268 @Override 269 protected void validateConfiguration() { 270 super.validateConfiguration(); 271 272 // Custom validation for JSON processing 273 if (scenario == ProcessingScenario.VALIDATION_WITH_EXTRACTION && propertyPath == null) { 274 throw new IllegalStateException("Property path is required for validation with extraction"); 275 } 276 277 if (scenario == ProcessingScenario.MULTI_STAGE_TRANSFORMATION) { 278 if (propertyPath == null || processingStages == null || processingStages.length == 0) { 279 throw new IllegalStateException( 280 "Multi-stage transformation requires property path and processing stages"); 281 } 282 } 283 } 284}