LoopTreeNode.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.tree;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.sandbox.functional.core.model.LoopModel;

/**
 * Represents a node in the loop tree.
 * 
 * <p>Each node represents a loop in the source code and maintains information
 * about its scope, children (nested loops), conversion decision, and optional
 * AST node reference for integration with Eclipse JDT.</p>
 * 
 * @since 1.0.0
 */
public class LoopTreeNode {
    private final LoopKind kind;
    private final ScopeInfo scopeInfo;
    private final List<LoopTreeNode> children = new ArrayList<>();
    private LoopTreeNode parent;
    private ConversionDecision decision = ConversionDecision.PENDING;
    private LoopModel loopModel;
    private Object astNodeReference;
    
    /**
     * Creates a new loop tree node.
     * 
     * @param kind the kind of loop
     * @param scopeInfo the scope information for this loop
     */
    public LoopTreeNode(LoopKind kind, ScopeInfo scopeInfo) {
        this.kind = kind;
        this.scopeInfo = scopeInfo;
    }
    
    /**
     * Adds a child node to this node.
     * 
     * @param child the child node to add
     */
    public void addChild(LoopTreeNode child) {
        children.add(child);
        child.parent = this;
    }
    
    /**
     * Checks if any descendant node is convertible.
     * 
     * <p>This is used to determine if a parent loop should be skipped
     * because an inner loop can be converted.</p>
     * 
     * @return true if any descendant has a CONVERTIBLE decision
     */
    public boolean hasConvertibleDescendant() {
        for (LoopTreeNode child : children) {
            if (child.decision == ConversionDecision.CONVERTIBLE || child.hasConvertibleDescendant()) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * Checks if all children are non-convertible.
     * 
     * <p>Children with NOT_CONVERTIBLE or SKIPPED_INNER_CONVERTED
     * decisions are considered non-convertible.</p>
     * 
     * @return true if all children are non-convertible
     */
    public boolean allChildrenNonConvertible() {
        return children.stream()
            .allMatch(c -> c.decision == ConversionDecision.NOT_CONVERTIBLE 
                        || c.decision == ConversionDecision.SKIPPED_INNER_CONVERTED);
    }
    
    /**
     * Gets the kind of loop.
     * 
     * @return the loop kind
     */
    public LoopKind getKind() { 
        return kind; 
    }
    
    /**
     * Gets the scope information for this loop.
     * 
     * @return the scope info
     */
    public ScopeInfo getScopeInfo() { 
        return scopeInfo; 
    }
    
    /**
     * Gets an unmodifiable view of child nodes.
     * 
     * @return unmodifiable list of children
     */
    public List<LoopTreeNode> getChildren() { 
        return Collections.unmodifiableList(children); 
    }
    
    /**
     * Gets the parent node.
     * 
     * @return the parent node, or null if this is a root
     */
    public LoopTreeNode getParent() { 
        return parent; 
    }
    
    /**
     * Gets the conversion decision for this loop.
     * 
     * @return the conversion decision
     */
    public ConversionDecision getDecision() { 
        return decision; 
    }
    
    /**
     * Sets the conversion decision for this loop.
     * 
     * @param decision the conversion decision
     */
    public void setDecision(ConversionDecision decision) { 
        this.decision = decision; 
    }
    
    /**
     * Gets the loop model if this loop was analyzed.
     * 
     * @return the loop model, or null if not analyzed
     */
    public LoopModel getLoopModel() { 
        return loopModel; 
    }
    
    /**
     * Sets the loop model for this node.
     * 
     * @param loopModel the loop model
     */
    public void setLoopModel(LoopModel loopModel) { 
        this.loopModel = loopModel; 
    }
    
    /**
     * Gets the AST node reference.
     * 
     * <p>This is used by the Eclipse JDT integration to link tree nodes
     * back to their source AST nodes for rewriting.</p>
     * 
     * @return the AST node reference, or null if not set
     */
    public Object getAstNodeReference() { 
        return astNodeReference; 
    }
    
    /**
     * Sets the AST node reference.
     * 
     * @param astNodeReference the AST node reference
     */
    public void setAstNodeReference(Object astNodeReference) { 
        this.astNodeReference = astNodeReference; 
    }
}