<commit_before>package de.uni_potsdam.hpi.bpt.bp2014.conversion.converter.activity_centric;

import de.uni_potsdam.hpi.bpt.bp2014.conversion.IConverter;
import de.uni_potsdam.hpi.bpt.bp2014.conversion.IEdge;
import de.uni_potsdam.hpi.bpt.bp2014.conversion.IModel;
import de.uni_potsdam.hpi.bpt.bp2014.conversion.INode;
import de.uni_potsdam.hpi.bpt.bp2014.conversion.activity_centric.*;
import de.uni_potsdam.hpi.bpt.bp2014.conversion.olc.DataObjectState;
import de.uni_potsdam.hpi.bpt.bp2014.conversion.olc.ObjectLifeCycle;
import de.uni_potsdam.hpi.bpt.bp2014.conversion.olc.StateTransition;
import de.uni_potsdam.hpi.bpt.bp2014.conversion.olc.synchronize.SynchronizedObjectLifeCycle;

import java.util.*;

/**
 * This class is a converter.
 * It offers methods to transform an {@link ActivityCentricProcessModel} into
 * a {@link SynchronizedObjectLifeCycle}.
 */
public class ActivityCentricToSynchronizedOLC implements IConverter {
    /**
     * This variable holds an instance of {@link ActivityCentricProcessModel}.
     * It is model which will be converted into an
     * {@link SynchronizedObjectLifeCycle}.
     * <p>
     * If changes happen to this model during the conversion, they might end
     * up in a corrupt state of the converter or synchronizedObjectLifeCycle.
     */
    private ActivityCentricProcessModel acpm;

    /**
     * Holds a Collection of all Object Life Cycles being a part of the
     * synchronized OLC.
     * There will be exactly one OLC for each DataClass.
     */
    private Collection<ObjectLifeCycle> olcs;

    /**
     * This map holds a collection of distinct data states
     * for each OLC.
     */
    private Map<ObjectLifeCycle, Collection<DataObjectState>> dataStatesPerOLC;

    /**
     * Checked nodes, contains all nodes which have already
     * been checked for their successors.
     */
    private Collection<INode> checkedNodes;
    /**
     * Contains all Nodes which are part of a loop condition
     * They will be only checked twice, which means, that their
     * will be only one trace with this loop
     */
    private Collection<INode> loopNodes;

    /**
     * This Collection hold Lists of iNodes.
     * Every list represents a possible trace inside the activity centric
     * process model.
     */
    private Collection<List<INode>> traces;

    /**
     * This map represent the synchronization edges between the different olcs.
     */
    private Map<StateTransition, List<StateTransition>> synchronisationEdges;

    /**
     * Creates a new Object of the class.
     * Sets and collections will be initialized (empty).
     */
    public ActivityCentricToSynchronizedOLC() {
        checkedNodes = new HashSet<>();
        loopNodes = new HashSet<>();
        synchronisationEdges = new HashMap<>();
    }

    /**
     * Generates an {@link SynchronizedObjectLifeCycle} from an given {@link ActivityCentricProcessModel}.
     * Activities will be transformed to transitions and there will be an OLC for each unique DataObject name.
     * For more details about the conversion algorithm {@see bpt.hpi.uni-potsdam.de/pub/Public/AndreasMeyer/Technical_Report_Activity-centric_and_Artifact-centric_Process_Model_Roundtrip.pdf}
     *
     * @param acpm Is the Activity Centric Process Model to be converted.
     * @return The generated model, an instance of {@link SynchronizedObjectLifeCycle}
     */
    public IModel convert(ActivityCentricProcessModel acpm) {
        assert null != acpm : "Null can not be converted into a process model";
        this.acpm = acpm;
        initOLCs();
        identifyDistinctDataStates();
        extractTraces();
        for (List<INode> trace : traces) {
            new HashMap<>();
            Map<String, Collection<DataObjectState>> stateCollections = getInitialStateCollections();
            for (INode node : trace) {
                if (node instanceof Activity) {
                    Map<String, Collection<DataObjectState>> currentStates = extractCurrentStates(node);
                    connectStatesForActivity(stateCollections, currentStates, null);
                    establishSynchronizationEdges(stateCollections, currentStates);
                    stateCollections.putAll(currentStates);
                    currentStates = getStatesAfterNode(node);
                    connectStatesForActivity(stateCollections, currentStates, (Activity) node);
                    establishSynchronizationEdges(stateCollections, currentStates);
                    stateCollections.putAll(currentStates);
                }
            }
        }
        detectFinalStates();

        return buildSynchronizedObjectLifeCycle();
    }

    /**
     * Creates an synchronized Object Life Cycle for all OLCs
     * created during the conversion.
     * @return The synchronized object life cycle which has been created.
     */
    private IModel buildSynchronizedObjectLifeCycle() {
        SynchronizedObjectLifeCycle synchOLC = new SynchronizedObjectLifeCycle();
        synchOLC.setObjectLifeCycles(new LinkedList<>(olcs));
        synchOLC.setSynchronisationEdges(synchronisationEdges);
        return synchOLC;
    }

    /**
     * This method detects all final states.
     * If a state has no outgoing edges it is considered final.
     * Such a state will be added to collection and than marked as
     * final for the specific object life cycle.
     */
    private void detectFinalStates() {
        for (Map.Entry<ObjectLifeCycle, Collection<DataObjectState>> entry
                : dataStatesPerOLC.entrySet()) {
            Collection<DataObjectState> finalStates = new HashSet<>();
            for (DataObjectState dataObjectState : entry.getValue()) {
                entry.getKey().addNode(dataObjectState);
                if (dataObjectState.getOutgoingEdges().isEmpty()) {
                    finalStates.add(dataObjectState);
                }
            }
            for (DataObjectState finalState : finalStates) {
                entry.getKey().addFinalNode(finalState);
            }
        }
    }


    /**
     * Establishes state transitions between the predecessor and successor states.
     * The transition represents an action taken by an activity.
     * If the activity is null the action will be a silent transition marked with "t".
     *
     * @param stateCollections The collection of all states mapped to the name of the OLC.
     * @param currentStates    The currently extracted States mapped.
     * @param node             The node representing the action.
     */
    private void connectStatesForActivity(Map<String, Collection<DataObjectState>> stateCollections,
                                          Map<String, Collection<DataObjectState>> currentStates,
                                          Activity node) {
        for (Map.Entry<String, Collection<DataObjectState>> entry
                : stateCollections.entrySet()) {
            for (DataObjectState predecessor : entry.getValue()) {
                if (currentStates.containsKey(entry.getKey())) {
                    for (DataObjectState successor : currentStates.get(entry.getKey())) {
                        StateTransition transition =
                                new StateTransition(predecessor,
                                        successor,
                                        (node == null) ? "t" : ((Activity) node).getName());
                        predecessor.addOutgoingEdge(transition);
                        successor.addIncomingEdge(transition);
                    }
                }
            }
        }

    }

    /**
     * Returns a map containing all data nodes created by a given node.
     * In general the node should be of type {@link Activity}.
     * In that case the map would contain the states of the data outputs.
     * In addition it maps the data object name to the data object states.
     * This indicates all possible states after the termination.
     *
     * @param node The node which outgoing dataflow edges will be checked.
     * @return Returns a map, containing the data object names (key) and a Collection of<commit_msg>Small bug fix Loop transitions will not be added<commit_after>package de.uni_potsdam.hpi.bpt.bp2014.conversion.converter.activity_centric;

import de.uni_potsdam.hpi.bpt.bp2014.conversion.IConverter;
import de.uni_potsdam.hpi.bpt.bp2014.conversion.IEdge;
import de.uni_potsdam.hpi.bpt.bp2014.conversion.IModel;
import de.uni_potsdam.hpi.bpt.bp2014.conversion.INode;
import de.uni_potsdam.hpi.bpt.bp2014.conversion.activity_centric.*;
import de.uni_potsdam.hpi.bpt.bp2014.conversion.olc.DataObjectState;
import de.uni_potsdam.hpi.bpt.bp2014.conversion.olc.ObjectLifeCycle;
import de.uni_potsdam.hpi.bpt.bp2014.conversion.olc.StateTransition;
import de.uni_potsdam.hpi.bpt.bp2014.conversion.olc.synchronize.SynchronizedObjectLifeCycle;

import java.util.*;

/**
 * This class is a converter.
 * It offers methods to transform an {@link ActivityCentricProcessModel} into
 * a {@link SynchronizedObjectLifeCycle}.
 */
public class ActivityCentricToSynchronizedOLC implements IConverter {
    /**
     * This variable holds an instance of {@link ActivityCentricProcessModel}.
     * It is model which will be converted into an
     * {@link SynchronizedObjectLifeCycle}.
     * <p>
     * If changes happen to this model during the conversion, they might end
     * up in a corrupt state of the converter or synchronizedObjectLifeCycle.
     */
    private ActivityCentricProcessModel acpm;

    /**
     * Holds a Collection of all Object Life Cycles being a part of the
     * synchronized OLC.
     * There will be exactly one OLC for each DataClass.
     */
    private Collection<ObjectLifeCycle> olcs;

    /**
     * This map holds a collection of distinct data states
     * for each OLC.
     */
    private Map<ObjectLifeCycle, Collection<DataObjectState>> dataStatesPerOLC;

    /**
     * Checked nodes, contains all nodes which have already
     * been checked for their successors.
     */
    private Collection<INode> checkedNodes;
    /**
     * Contains all Nodes which are part of a loop condition
     * They will be only checked twice, which means, that their
     * will be only one trace with this loop
     */
    private Collection<INode> loopNodes;

    /**
     * This Collection hold Lists of iNodes.
     * Every list represents a possible trace inside the activity centric
     * process model.
     */
    private Collection<List<INode>> traces;

    /**
     * This map represent the synchronization edges between the different olcs.
     */
    private Map<StateTransition, List<StateTransition>> synchronisationEdges;

    /**
     * Creates a new Object of the class.
     * Sets and collections will be initialized (empty).
     */
    public ActivityCentricToSynchronizedOLC() {
        checkedNodes = new HashSet<>();
        loopNodes = new HashSet<>();
        synchronisationEdges = new HashMap<>();
    }

    /**
     * Generates an {@link SynchronizedObjectLifeCycle} from an given {@link ActivityCentricProcessModel}.
     * Activities will be transformed to transitions and there will be an OLC for each unique DataObject name.
     * For more details about the conversion algorithm {@see bpt.hpi.uni-potsdam.de/pub/Public/AndreasMeyer/Technical_Report_Activity-centric_and_Artifact-centric_Process_Model_Roundtrip.pdf}
     *
     * @param acpm Is the Activity Centric Process Model to be converted.
     * @return The generated model, an instance of {@link SynchronizedObjectLifeCycle}
     */
    public IModel convert(ActivityCentricProcessModel acpm) {
        assert null != acpm : "Null can not be converted into a process model";
        this.acpm = acpm;
        initOLCs();
        identifyDistinctDataStates();
        extractTraces();
        for (List<INode> trace : traces) {
            new HashMap<>();
            Map<String, Collection<DataObjectState>> stateCollections = getInitialStateCollections();
            for (INode node : trace) {
                if (node instanceof Activity) {
                    Map<String, Collection<DataObjectState>> currentStates = extractCurrentStates(node);
                    connectStatesForActivity(stateCollections, currentStates, null);
                    establishSynchronizationEdges(stateCollections, currentStates);
                    stateCollections.putAll(currentStates);
                    currentStates = getStatesAfterNode(node);
                    connectStatesForActivity(stateCollections, currentStates, (Activity) node);
                    establishSynchronizationEdges(stateCollections, currentStates);
                    stateCollections.putAll(currentStates);
                }
            }
        }
        detectFinalStates();

        return buildSynchronizedObjectLifeCycle();
    }

    /**
     * Creates an synchronized Object Life Cycle for all OLCs
     * created during the conversion.
     * @return The synchronized object life cycle which has been created.
     */
    private IModel buildSynchronizedObjectLifeCycle() {
        SynchronizedObjectLifeCycle synchOLC = new SynchronizedObjectLifeCycle();
        synchOLC.setObjectLifeCycles(new LinkedList<>(olcs));
        synchOLC.setSynchronisationEdges(synchronisationEdges);
        return synchOLC;
    }

    /**
     * This method detects all final states.
     * If a state has no outgoing edges it is considered final.
     * Such a state will be added to collection and than marked as
     * final for the specific object life cycle.
     */
    private void detectFinalStates() {
        for (Map.Entry<ObjectLifeCycle, Collection<DataObjectState>> entry
                : dataStatesPerOLC.entrySet()) {
            Collection<DataObjectState> finalStates = new HashSet<>();
            for (DataObjectState dataObjectState : entry.getValue()) {
                entry.getKey().addNode(dataObjectState);
                if (dataObjectState.getOutgoingEdges().isEmpty()) {
                    finalStates.add(dataObjectState);
                }
            }
            for (DataObjectState finalState : finalStates) {
                entry.getKey().addFinalNode(finalState);
            }
        }
    }


    /**
     * Establishes state transitions between the predecessor and successor states.
     * The transition represents an action taken by an activity.
     * If the activity is null the action will be a silent transition marked with "t".
     *
     * @param stateCollections The collection of all states mapped to the name of the OLC.
     * @param currentStates    The currently extracted States mapped.
     * @param node             The node representing the action.
     */
    private void connectStatesForActivity(Map<String, Collection<DataObjectState>> stateCollections,
                                          Map<String, Collection<DataObjectState>> currentStates,
                                          Activity node) {
        for (Map.Entry<String, Collection<DataObjectState>> entry
                : stateCollections.entrySet()) {
            for (DataObjectState predecessor : entry.getValue()) {
                if (currentStates.containsKey(entry.getKey())) {
                    for (DataObjectState successor : currentStates.get(entry.getKey())) {
                        if (node != null || !predecessor.equals(successor)) {
                            StateTransition transition =
                                    new StateTransition(predecessor,
                                            successor,
                                            (node == null) ? "t" : ((Activity) node).getName());
                            predecessor.addOutgoingEdge(transition);
                            successor.addIncomingEdge(transition);
                        }
                    }
                }
            }
        }

    }

    /**
     * Returns a map containing all data nodes created by a given node.
     * In general the node should be of type {@link Activity}.
     * In that case the map would contain the states of the data outputs.
     * In addition it maps the data object name to the data object states.
     * This indicates all possible states after the termination.
     *
     * @param node The node which outgoing dataflow edges will be checked.
     * @return Returns a map, containing the data object names (key) and a Collection of
{'fraction_non_alphanumeric': 0.07674065065369413, 'fraction_numerical': 0.00486470051687443, 'mean_word_length': 2.452141057934509, 'pattern_counts': {'":': 0, '<': 63, '<?xml version=': 0, '>': 63, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1572262', 'n_tokens_mistral': 4250, 'n_tokens_neox': 4057, 'n_words': 1271}