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}