<commit_before>package nl.utwente.viskell.ui.components;

import com.google.common.collect.ImmutableMap;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.geometry.Point2D;
import javafx.scene.transform.Transform;
import nl.utwente.viskell.ui.CustomUIPane;
import nl.utwente.viskell.ui.serialize.Bundleable;

import java.util.Map;
import java.util.Optional;


/**
 * This is a Connection that represents a flow between an {@link InputAnchor}
 * and {@link OutputAnchor}. Both anchors are stored referenced respectively as
 * startAnchor and endAnchor {@link Optional} within this class.
 * <p>
 * It is possible for a connection to exist without both anchors being present,
 * whenever the position of either the start or end anchor changes the
 * {@link #invalidateAnchorPositions()} should be called to refresh the visual
 * representation of the connection.
 * </p>
 * 
 * Connection is also a changeListener for a Transform, in order to be able to
 * update the Line's position when the anchor's positions change.
 */
public class Connection extends ConnectionLine implements
        ChangeListener<Transform>, Bundleable {
    /** Starting point of this Line that can be Anchored onto other objects. */
    private Optional<OutputAnchor> startAnchor = Optional.empty();
    /** Ending point of this Line that can be Anchored onto other objects. */
    private Optional<InputAnchor> endAnchor = Optional.empty();

    /** The Pane this Connection is on. */
    private CustomUIPane pane;
    
    /** Property describing the error state. */
    private BooleanProperty errorState;

    /** 
     * Construct a new Connection.
     * @param pane The Pane this Connection is on.
     */
    private Connection(CustomUIPane pane) {
        this.pane = pane;
        pane.getChildren().add(0, this);
        this.errorState = new SimpleBooleanProperty(false);
        this.errorState.addListener(this::checkErrorListener);
    }

    /** 
     * Construct a new Connection.
     * @param pane The Pane this Connection is on.
     * @param anchor A ConnectionAnchor of this Connection.
     */
    public Connection(CustomUIPane pane, ConnectionAnchor anchor) {
        this(pane);
        Point2D initPos = pane.sceneToLocal(anchor.localToScene(anchor.getLocalCenter()));
        setStartPositionParent(initPos);
        setEndPositionParent(initPos);
        connectTo(anchor);
    }
    
    /** Set a new error state. */
    public void setErrorState(boolean error) {
        errorState.set(error);
    }
    
    /* GETTERS */
    
    /** @return this connection's end anchor, if any. */
    public final Optional<InputAnchor> getInputAnchor() {
        return endAnchor;
    }

    /** @return this connection's start anchor, if any. */
    public final Optional<OutputAnchor> getOutputAnchor() {
        return startAnchor;
    }

    /**
     * Get the optional output anchor on the other side of
     * the provided input anchor in this Connection.
     * 
     * @param anchor on this side of the connection  
     * @return the output anchor if it exists in this connection.
     */
    public Optional<OutputAnchor> getOppositeAnchorOf(InputAnchor anchor) {
        return this.endAnchor.filter(ia -> ia == anchor).flatMap(x -> this.startAnchor);
    }

    /**
     * Get the optional output anchor on the other side of
     * the provided input anchor in this Connection.
     * 
     * @param anchor on this side of the connection  
     * @return the input anchor if it exists in this connection.
     */
    public Optional<InputAnchor> getOppositeAnchorOf(OutputAnchor anchor) {
        return this.startAnchor.filter(oa -> oa == anchor).flatMap(x -> this.endAnchor);
    }
    
    /* SETTERS */

    /**
     * Sets an OutputAnchor or InputAnchor for this line.
     * After setting the line will update accordingly to the possible state change.
     */
    public void connectTo(ConnectionAnchor newAnchor) {
        // Add the anchor.
        if (newAnchor instanceof OutputAnchor && !startAnchor.isPresent()) {
            startAnchor = Optional.of((OutputAnchor) newAnchor);
        } else if (newAnchor instanceof InputAnchor && !endAnchor.isPresent()) {
            endAnchor = Optional.of((InputAnchor) newAnchor);
        } else {
            return;
        }
        
        // Add this to the anchor.
        newAnchor.addConnection(this);
        this.addListeners(newAnchor);
        
        this.handleConnectionChanges();
    }

    /**<commit_msg>Avoid double updates in connection creation<commit_after>package nl.utwente.viskell.ui.components;

import com.google.common.collect.ImmutableMap;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.geometry.Point2D;
import javafx.scene.transform.Transform;
import nl.utwente.viskell.ui.CustomUIPane;
import nl.utwente.viskell.ui.serialize.Bundleable;

import java.util.Map;
import java.util.Optional;


/**
 * This is a Connection that represents a flow between an {@link InputAnchor}
 * and {@link OutputAnchor}. Both anchors are stored referenced respectively as
 * startAnchor and endAnchor {@link Optional} within this class.
 * <p>
 * It is possible for a connection to exist without both anchors being present,
 * whenever the position of either the start or end anchor changes the
 * {@link #invalidateAnchorPositions()} should be called to refresh the visual
 * representation of the connection.
 * </p>
 * 
 * Connection is also a changeListener for a Transform, in order to be able to
 * update the Line's position when the anchor's positions change.
 */
public class Connection extends ConnectionLine implements
        ChangeListener<Transform>, Bundleable {
    /** Starting point of this Line that can be Anchored onto other objects. */
    private Optional<OutputAnchor> startAnchor = Optional.empty();
    /** Ending point of this Line that can be Anchored onto other objects. */
    private Optional<InputAnchor> endAnchor = Optional.empty();

    /** The Pane this Connection is on. */
    private CustomUIPane pane;
    
    /** Property describing the error state. */
    private BooleanProperty errorState;

    /** 
     * Construct a new Connection.
     * @param pane The Pane this Connection is on.
     */
    private Connection(CustomUIPane pane) {
        this.pane = pane;
        pane.getChildren().add(0, this);
        this.errorState = new SimpleBooleanProperty(false);
        this.errorState.addListener(this::checkErrorListener);
    }

    /** 
     * Construct a new Connection.
     * @param pane The Pane this Connection is on.
     * @param anchor A ConnectionAnchor of this Connection.
     */
    public Connection(CustomUIPane pane, ConnectionAnchor anchor) {
        this(pane);
        Point2D initPos = pane.sceneToLocal(anchor.localToScene(anchor.getLocalCenter()));
        setStartPositionParent(initPos);
        setEndPositionParent(initPos);
        connectTo(anchor);
    }
    
    /** Set a new error state. */
    public void setErrorState(boolean error) {
        errorState.set(error);
    }
    
    /* GETTERS */
    
    /** @return this connection's end anchor, if any. */
    public final Optional<InputAnchor> getInputAnchor() {
        return endAnchor;
    }

    /** @return this connection's start anchor, if any. */
    public final Optional<OutputAnchor> getOutputAnchor() {
        return startAnchor;
    }

    /**
     * Get the optional output anchor on the other side of
     * the provided input anchor in this Connection.
     * 
     * @param anchor on this side of the connection  
     * @return the output anchor if it exists in this connection.
     */
    public Optional<OutputAnchor> getOppositeAnchorOf(InputAnchor anchor) {
        return this.endAnchor.filter(ia -> ia == anchor).flatMap(x -> this.startAnchor);
    }

    /**
     * Get the optional output anchor on the other side of
     * the provided input anchor in this Connection.
     * 
     * @param anchor on this side of the connection  
     * @return the input anchor if it exists in this connection.
     */
    public Optional<InputAnchor> getOppositeAnchorOf(OutputAnchor anchor) {
        return this.startAnchor.filter(oa -> oa == anchor).flatMap(x -> this.endAnchor);
    }
    
    /* SETTERS */

    /**
     * Sets an OutputAnchor or InputAnchor for this line.
     * After setting the line will update accordingly to the possible state change.
     */
    public void connectTo(ConnectionAnchor newAnchor) {
        // Add the anchor.
        if (newAnchor instanceof OutputAnchor && !startAnchor.isPresent()) {
            startAnchor = Optional.of((OutputAnchor) newAnchor);
        } else if (newAnchor instanceof InputAnchor && !endAnchor.isPresent()) {
            endAnchor = Optional.of((InputAnchor) newAnchor);
        } else {
            return;
        }
        
        // Add this to the anchor.
        newAnchor.addConnection(this);
        this.addListeners(newAnchor);
        
        newAnchor.handleConnectionChanges();
    }

    /**
{'fraction_non_alphanumeric': 0.08909730363423213, 'fraction_numerical': 0.0006394543323031014, 'mean_word_length': 2.9165275459098496, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6202882', 'n_tokens_mistral': 2490, 'n_tokens_neox': 2431, 'n_words': 825}