LoopModelBuilder.java

/*******************************************************************************
 * Copyright (c) 2026 Carsten Hammer.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Carsten Hammer
 *******************************************************************************/
package org.sandbox.functional.core.builder;

import java.util.ArrayList;
import java.util.List;
import org.sandbox.functional.core.model.*;
import org.sandbox.functional.core.operation.*;
import org.sandbox.functional.core.terminal.*;

/**
 * Fluent builder for constructing LoopModel instances.
 * 
 * <p>This builder is AST-independent and works with abstract data.
 * JDT-specific extraction is handled by JdtLoopExtractor in the
 * sandbox_functional_converter module.</p>
 * 
 * <p>Example usage:</p>
 * <pre>
 * LoopModel model = new LoopModelBuilder()
 *     .source(SourceType.COLLECTION, "list", "String")
 *     .element("item", "String", false)
 *     .metadata(false, false, false, false, true)
 *     .filter("item != null")
 *     .map("item.toUpperCase()", "String")
 *     .forEach(List.of("System.out.println(item)"), false)
 *     .build();
 * </pre>
 */
public class LoopModelBuilder {
    
    private SourceDescriptor source;
    private ElementDescriptor element;
    private LoopMetadata metadata;
    private final List<Operation> operations = new ArrayList<>();
    private TerminalOperation terminal;
    
    public LoopModelBuilder() {}
    
    // Source configuration
    public LoopModelBuilder source(SourceDescriptor.SourceType type, 
                                   String expression, 
                                   String elementTypeName) {
        this.source = new SourceDescriptor(type, expression, elementTypeName);
        return this;
    }
    
    public LoopModelBuilder source(SourceDescriptor source) {
        this.source = source;
        return this;
    }
    
    // Element configuration
    public LoopModelBuilder element(String variableName, 
                                    String typeName, 
                                    boolean isFinal) {
        this.element = new ElementDescriptor(variableName, typeName, isFinal);
        return this;
    }
    
    public LoopModelBuilder element(ElementDescriptor element) {
        this.element = element;
        return this;
    }
    
    // Metadata configuration
    public LoopModelBuilder metadata(boolean hasBreak, 
                                     boolean hasContinue, 
                                     boolean hasReturn,
                                     boolean modifiesCollection, 
                                     boolean requiresOrdering) {
        this.metadata = new LoopMetadata(hasBreak, hasContinue, hasReturn, 
                                         modifiesCollection, requiresOrdering);
        return this;
    }
    
    public LoopModelBuilder metadata(LoopMetadata metadata) {
        this.metadata = metadata;
        return this;
    }
    
    // Operation shortcuts
    public LoopModelBuilder filter(String expression) {
        this.operations.add(new FilterOp(expression));
        return this;
    }
    
    public LoopModelBuilder map(String expression, String targetType) {
        this.operations.add(new MapOp(expression, targetType));
        return this;
    }
    
    public LoopModelBuilder map(String expression) {
        this.operations.add(new MapOp(expression, null));
        return this;
    }
    
    public LoopModelBuilder flatMap(String expression) {
        this.operations.add(new FlatMapOp(expression));
        return this;
    }
    
    public LoopModelBuilder peek(String expression) {
        this.operations.add(new PeekOp(expression));
        return this;
    }
    
    public LoopModelBuilder distinct() {
        this.operations.add(new DistinctOp());
        return this;
    }
    
    public LoopModelBuilder sorted() {
        this.operations.add(new SortOp(null));
        return this;
    }
    
    public LoopModelBuilder sorted(String comparatorExpression) {
        this.operations.add(new SortOp(comparatorExpression));
        return this;
    }
    
    public LoopModelBuilder limit(long maxSize) {
        this.operations.add(new LimitOp(maxSize));
        return this;
    }
    
    public LoopModelBuilder skip(long count) {
        this.operations.add(new SkipOp(count));
        return this;
    }
    
    public LoopModelBuilder operation(Operation op) {
        this.operations.add(op);
        return this;
    }
    
    // Terminal shortcuts
    public LoopModelBuilder forEach(List<String> bodyStatements, boolean ordered) {
        this.terminal = new ForEachTerminal(bodyStatements, ordered);
        return this;
    }
    
    public LoopModelBuilder forEach(List<String> bodyStatements) {
        return forEach(bodyStatements, false);
    }
    
    public LoopModelBuilder collect(CollectTerminal.CollectorType type, 
                                    String targetVariable) {
        this.terminal = new CollectTerminal(type, targetVariable);
        return this;
    }
    
    public LoopModelBuilder reduce(String identity, 
                                   String accumulator, 
                                   String combiner,
                                   ReduceTerminal.ReduceType type) {
        this.terminal = new ReduceTerminal(identity, accumulator, combiner, type);
        return this;
    }
    
    public LoopModelBuilder count() {
        this.terminal = new CountTerminal();
        return this;
    }
    
    public LoopModelBuilder findFirst() {
        this.terminal = new FindTerminal(true);
        return this;
    }
    
    public LoopModelBuilder findAny() {
        this.terminal = new FindTerminal(false);
        return this;
    }
    
    public LoopModelBuilder anyMatch(String predicate) {
        this.terminal = new MatchTerminal(MatchTerminal.MatchType.ANY_MATCH, predicate);
        return this;
    }
    
    public LoopModelBuilder allMatch(String predicate) {
        this.terminal = new MatchTerminal(MatchTerminal.MatchType.ALL_MATCH, predicate);
        return this;
    }
    
    public LoopModelBuilder noneMatch(String predicate) {
        this.terminal = new MatchTerminal(MatchTerminal.MatchType.NONE_MATCH, predicate);
        return this;
    }
    
    public LoopModelBuilder terminal(TerminalOperation terminal) {
        this.terminal = terminal;
        return this;
    }
    
    // Build
    public LoopModel build() {
        LoopModel model = new LoopModel(source, element, metadata);
        operations.forEach(model::addOperation);
        if (terminal != null) {
            model.withTerminal(terminal);
        }
        return model;
    }
    
    // Validation
    public boolean isValid() {
        return source != null && element != null;
    }
}