<commit_before>/*******************************************************************************
 * Copyright 2005, 2009 CHISEL Group, University of Victoria, Victoria, BC,
 * Canada. All rights reserved. This program and the accompanying materials are
 * made available under the terms of the Eclipse Public License v1.0 which
 * accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors: The Chisel Group, University of Victoria IBM CAS, IBM Toronto
 * Lab
 ******************************************************************************/
/*******************************************************************************
 * Modified work Copyright 2013 Temenos Holdings N.V.
 * The example code for XText visualisation has been modified to visualise 
 * the IRIS RIMDSL.
 ******************************************************************************/
package com.temenos.interaction.rimdsl.visualisation.providers;

/*
 * #%L
 * com.temenos.interaction.rimdsl.RimDsl - Visualisation
 * %%
 * Copyright (C) 2012 - 2013 Temenos Holdings N.V.
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */


import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.zest.core.viewers.IGraphEntityContentProvider;

import com.google.common.collect.Iterators;
import com.temenos.interaction.rimdsl.rim.DomainModel;
import com.temenos.interaction.rimdsl.rim.Expression;
import com.temenos.interaction.rimdsl.rim.Function;
import com.temenos.interaction.rimdsl.rim.NotFoundFunction;
import com.temenos.interaction.rimdsl.rim.OKFunction;
import com.temenos.interaction.rimdsl.rim.ResourceInteractionModel;
import com.temenos.interaction.rimdsl.rim.State;
import com.temenos.interaction.rimdsl.rim.Transition;
import com.temenos.interaction.rimdsl.rim.TransitionAuto;
import com.temenos.interaction.rimdsl.rim.TransitionForEach;


/**
 * Content provider for a ZEST graph viewer
 * @author aphethean 
 */
public class ResourceInteractionContentProvider implements IGraphEntityContentProvider {

	private boolean showIncomingRelations;
	private boolean showOutgoingRelations;
	
	private List<TransitionDescription> transitions;
	private Set<State> states;
	
	private EObject input;
	
	
	/**
	 * A utility method called from the label provider 
	 * that is used to get the description of a relation 
	 * @param from The source entity of the relation
	 * @param to The target entity of the relation
	 * @return The description of the relation connecting the source entity to the target entity
	 */
	public List<String> getTransitionDescription(Object from, Object to) {
		List<String> result = new ArrayList<String>();
		
		for (TransitionDescription transition : transitions) {
			if (transition.getFromState() == from && transition.getToState() == to) {
				if (transition.getTitle() != null) {
					result.add(transition.getEvent()+" "+transition.getTitle()
							+(transition.getConditions().length() > 0 ? " IF "+transition.getConditions() : ""));
				}
			}
		}
		return result;
	}
	
	/**
	 * Constructor
	 * @param showIncomingRelations whether incoming relations are shown
	 * @param showOutgoingRelations whether outgoing relations are shown
	 */
	public ResourceInteractionContentProvider(boolean showIncomingRelations, boolean showOutgoingRelations) {
		super();
		this.showIncomingRelations = showIncomingRelations;
		this.showOutgoingRelations = showOutgoingRelations;
		
		transitions = new ArrayList<TransitionDescription>();
		states = new HashSet<State>();
		
		input = null;
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.zest.core.viewers.IGraphEntityContentProvider#getConnectedTo(java.lang.Object)
	 */
	public Object[] getConnectedTo(Object o) {
		if (o instanceof State) {
			State state = (State)o;
			List<State> result = new ArrayList<State>();
			for (TransitionDescription transition : transitions) {
				if (transition.getFromState() == state) {
					result.add(transition.getToState());
				}
			}
			return result.toArray();
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.zest.core.viewers.IGraphEntityContentProvider#getElements(java.lang.Object)
	 */
	public Object[] getElements(Object inputElement) {
		List<EObject> result = new ArrayList<EObject>();
		result.addAll(states);
		return result.toArray();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
	 */
	public void dispose() {
		input = null;
		states.clear();
		transitions.clear();
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
	 */
	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
		if (newInput == null) {
			input = null;
			
			rebuildTransitions();
		} else {
			if (newInput instanceof State) {
				input = (EObject) newInput;
				rebuildTransitions();
			} else {
				throw new RuntimeException("Input element not supported! Need to be " + State.class.getCanonicalName());
			}
		}
	}

	/**
	 * Setter, shows or hides incoming relations
	 * @param enable
	 */
	public void setShowIncomingRelations(boolean enable) {
		this.showIncomingRelations = enable;
		rebuildTransitions();
	}

	/**
	 * Setter, shows or hides outgoing relations
	 * @param enable
	 */
	public void setShowOutgoingRelations(boolean enable) {
		this.showOutgoingRelations = enable;
		rebuildTransitions();
	}

	
	/**
	 * Used internally to build up all transitions and caches. 
	 * Must be called after the visualisation options have been changed
	 */
	private void rebuildTransitions() {
		states.clear();
		transitions.clear();
		
		if (input != null) {
			// Some temporary buffers
			Set<State> allStates = new HashSet<State>();
			Map<State, Set<TransitionDescription>> incomingTransitions = 
				new HashMap<State, Set<TransitionDescription>>();
			Map<State, Set<TransitionDescription>> outgoingTransitions = 
				new HashMap<State, Set<TransitionDescription>>();
			
			if (input instanceof State) {
				State state = (State)input;
				
				// Iterate over all elements in model
				Iterator<EObject> iter = state.eResource().getAllContents();
				while (iter.hasNext()) {
					EObject rootContent = iter.next();
					if (rootContent instanceof DomainModel) {
						DomainModel model = (DomainModel) rootContent;
						Iterator<ResourceInteractionModel> rims = Iterators.filter(model.eAllContents(), ResourceInteractionModel.class);
						while (rims.hasNext()) {
							ResourceInteractionModel rim = rims.next();
							processRIM(rim, allStates, incomingTransitions, outgoingTransitions);
						}
					}
				}

				// Then build the visual representation of the part of 
				// the network that is to be shown
				if (showIncomingRelations) {
					retrieveStatesTransitions(state, incomingTransitions, true);
				}
				
				if (showOutgoingRelations) {
					retrieveStatesTransitions(state, outgoingTransitions, false);
				}
			}
		}
	}

	private void processRIM(ResourceInteractionModel model, Set<State> allStates, Map<State, Set<TransitionDescription>> incomingTransitions, Map<State, Set<TransitionDescription>> outgoingTransitions) {
		Iterator<State> states = Iterators.filter(model.eAllContents(), State.class);
		while (states.hasNext()) {
			State state = states.next();
			System.out.println("Adding state " + state.getName());
			allStates.add((State) state);
			Iterator<Transition> transitions = Iterators.filter(state.eAllContents(), Transition.class);
			while (transitions.hasNext()) {
				Transition transition = transitions.next();
				String title = (transition.getTitle() != null ? transition.getTitle().getName() : "");
				assert(transition.eContainer() instanceof State);
				State fromState = (State)transition.eContainer();
				State toState = transition.getState();
				if (fromState != null && toState != null) {
					TransitionDescription t = new TransitionDescription.Builder()
						.title(title)
						.event(transition.getEvent().getName())
						.conditions(createConditionsStr(transition.getEval()))
						.fromState(fromState)
						.toState(toState)
						.build();
					addInOut(t, incomingTransitions, outgoingTransitions);
				}
			}
			Iterator<TransitionForEach> transitionsForEach = Iterators.filter(state.eAllContents(), TransitionForEach.class);
			while (transitionsForEach.hasNext()) {
				TransitionForEach transition = transitionsForEach.next();
				String title = (transition.getTitle() != null ? transition.getTitle().getName() : "");
				assert(transition.eContainer() instanceof State);
				State fromState = (State)transition.eContainer();
				State toState = transition.getState();
				if (fromState != null && toState != null) {
					TransitionDescription t = new TransitionDescription.Builder()
						.title(title)
						.event(transition.getEvent().getName())
						.conditions(createConditionsStr(transition.getEval()))
						.fromState(fromState)
						.toState(toState)
						.build();
					addInOut(t, incomingTransitions, outgoingTransitions);
				}
			}
			Iterator<TransitionAuto> transitionsAuto = Iterators.filter(state.eAllContents(), TransitionAuto.class);
			while (transitionsAuto.hasNext()) {
				TransitionAuto transition = transitionsAuto.next();
				String title = "-->";
				assert(transition.eContainer() instanceof State);
				State fromState = (State)transition.eContainer();
				State toState = transition.getState();
				if (fromState != null && toState != null) {
					TransitionDescription t = new TransitionDescription.Builder()
						.title(title)
						.event(transition.getEvent().getName())
						.conditions(createConditionsStr(transition.getEval()))
						.fromState(fromState)
						.toState(toState)
						.build();
					addInOut(t, incomingTransitions, outgoingTransitions);
				}
			}
		}
		
	}
	
	private String createConditionsStr(Expression expression) {
		String result = "";
		if (expression != null) {
			for (Function func : expression.getExpressions()) {
				if (result.length() > 0) {
					result += " ";
				}
				if (func instanceof NotFoundFunction) {
					result += "NOT_FOUND(" + ((NotFoundFunction)func).getState().getName()+ ")";
				}
				if (func instanceof OKFunction) {
					result += "OK(" + ((OKFunction)func).getState().getName()+ ")";
				}
			}
		}
		return result;
	}
	
	private void addInOut(TransitionDescription t, Map<State, Set<TransitionDescription>> incomingTransitions, Map<State, Set<TransitionDescription>> outgoingTransitions) {
		State toState = t.getToState();
		Set<TransitionDescription> incomingTransitionsForState = incomingTransitions.get(toState);
		if (incomingTransitionsForState == null) {
			incomingTransitionsForState = new HashSet<TransitionDescription>();
			incomingTransitions.put(toState, incomingTransitionsForState);
		}
		incomingTransitionsForState.add(t);
		
		State fromState = t.getFromState();
		assert(fromState instanceof State);
		Set<TransitionDescription> outgoingTransitionsForState = outgoingTransitions.get(fromState);
		if (outgoingTransitionsForState == null) {
			outgoingTransitionsForState = new HashSet<TransitionDescription>();
			outgoingTransitions.put(fromState, outgoingTransitionsForState);
		}
		outgoingTransitionsForState.add(t);
	}
	
	private void retrieveStatesTransitions(State root, Map<State, Set<TransitionDescription>> allTransistions, boolean backwards) {
		Set<State> visitedStates = new HashSet<State>();
		visitedStates.clear();
		internalRetrieveStatesTransitions(root, visitedStates, allTransistions, backwards);
	}

	private void internalRetrieveStatesTransitions(State root, Set<State> visitedStates, Map<State, Set<TransitionDescription>> allTransitions, boolean backwards) {
		states.add(root);
		Set<TransitionDescription> transitionsForState = allTransitions.get(root);
		if (transitionsForState != null) {
			for (TransitionDescription t : transitionsForState) {
				transitions.add(t);
			
				State next = null;
				if (backwards) {
					next = t.getFromState();
				} else {
					next = t.getToState();
				}
				
				// Recursive call if this element has not been in focus before
				if (!visitedStates.contains(next)) {
					visitedStates.add(next);
					internalRetrieveStatesTransitions(next, visitedStates, allTransitions, backwards);
				} 				<commit_msg>Correct a minor issue with the duplication of RIM transition descriptions<commit_after>/*******************************************************************************
 * Copyright 2005, 2009 CHISEL Group, University of Victoria, Victoria, BC,
 * Canada. All rights reserved. This program and the accompanying materials are
 * made available under the terms of the Eclipse Public License v1.0 which
 * accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors: The Chisel Group, University of Victoria IBM CAS, IBM Toronto
 * Lab
 ******************************************************************************/
/*******************************************************************************
 * Modified work Copyright 2013 Temenos Holdings N.V.
 * The example code for XText visualisation has been modified to visualise 
 * the IRIS RIMDSL.
 ******************************************************************************/
package com.temenos.interaction.rimdsl.visualisation.providers;

/*
 * #%L
 * com.temenos.interaction.rimdsl.RimDsl - Visualisation
 * %%
 * Copyright (C) 2012 - 2013 Temenos Holdings N.V.
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */


import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.zest.core.viewers.IGraphEntityContentProvider;

import com.google.common.collect.Iterators;
import com.temenos.interaction.rimdsl.rim.DomainModel;
import com.temenos.interaction.rimdsl.rim.Expression;
import com.temenos.interaction.rimdsl.rim.Function;
import com.temenos.interaction.rimdsl.rim.NotFoundFunction;
import com.temenos.interaction.rimdsl.rim.OKFunction;
import com.temenos.interaction.rimdsl.rim.ResourceInteractionModel;
import com.temenos.interaction.rimdsl.rim.State;
import com.temenos.interaction.rimdsl.rim.Transition;
import com.temenos.interaction.rimdsl.rim.TransitionAuto;
import com.temenos.interaction.rimdsl.rim.TransitionForEach;


/**
 * Content provider for a ZEST graph viewer
 * @author aphethean 
 */
public class ResourceInteractionContentProvider implements IGraphEntityContentProvider {

	private boolean showIncomingRelations;
	private boolean showOutgoingRelations;
	
	private List<TransitionDescription> transitions;
	private Set<State> states;
	
	private EObject input;
	
	
	/**
	 * A utility method called from the label provider 
	 * that is used to get the description of a relation 
	 * @param from The source entity of the relation
	 * @param to The target entity of the relation
	 * @return The description of the relation connecting the source entity to the target entity
	 */
	public List<String> getTransitionDescription(Object from, Object to) {
		List<String> result = new ArrayList<String>();
		
		for (TransitionDescription transition : transitions) {
			if (transition.getFromState() == from && transition.getToState() == to) {
				if (transition.getTitle() != null) {
					result.add(transition.getEvent()+" "+transition.getTitle());
					if (transition.getConditions().length() > 0) {
						result.add(transition.getConditions());
					}
				}
			}
		}
		return result;
	}
	
	/**
	 * Constructor
	 * @param showIncomingRelations whether incoming relations are shown
	 * @param showOutgoingRelations whether outgoing relations are shown
	 */
	public ResourceInteractionContentProvider(boolean showIncomingRelations, boolean showOutgoingRelations) {
		super();
		this.showIncomingRelations = showIncomingRelations;
		this.showOutgoingRelations = showOutgoingRelations;
		
		transitions = new ArrayList<TransitionDescription>();
		states = new HashSet<State>();
		
		input = null;
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.zest.core.viewers.IGraphEntityContentProvider#getConnectedTo(java.lang.Object)
	 */
	public Object[] getConnectedTo(Object o) {
		if (o instanceof State) {
			State state = (State)o;
			List<State> result = new ArrayList<State>();
			for (TransitionDescription transition : transitions) {
				if (transition.getFromState() == state) {
					result.add(transition.getToState());
				}
			}
			return result.toArray();
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.zest.core.viewers.IGraphEntityContentProvider#getElements(java.lang.Object)
	 */
	public Object[] getElements(Object inputElement) {
		List<EObject> result = new ArrayList<EObject>();
		result.addAll(states);
		return result.toArray();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
	 */
	public void dispose() {
		input = null;
		states.clear();
		transitions.clear();
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
	 */
	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
		if (newInput == null) {
			input = null;
			
			rebuildTransitions();
		} else {
			if (newInput instanceof State) {
				input = (EObject) newInput;
				rebuildTransitions();
			} else {
				throw new RuntimeException("Input element not supported! Need to be " + State.class.getCanonicalName());
			}
		}
	}

	/**
	 * Setter, shows or hides incoming relations
	 * @param enable
	 */
	public void setShowIncomingRelations(boolean enable) {
		this.showIncomingRelations = enable;
		rebuildTransitions();
	}

	/**
	 * Setter, shows or hides outgoing relations
	 * @param enable
	 */
	public void setShowOutgoingRelations(boolean enable) {
		this.showOutgoingRelations = enable;
		rebuildTransitions();
	}

	
	/**
	 * Used internally to build up all transitions and caches. 
	 * Must be called after the visualisation options have been changed
	 */
	private void rebuildTransitions() {
		states.clear();
		transitions.clear();
		
		if (input != null) {
			// Some temporary buffers
			Set<State> allStates = new HashSet<State>();
			Map<State, Set<TransitionDescription>> incomingTransitions = 
				new HashMap<State, Set<TransitionDescription>>();
			Map<State, Set<TransitionDescription>> outgoingTransitions = 
				new HashMap<State, Set<TransitionDescription>>();
			
			if (input instanceof State) {
				State state = (State)input;
				
				// Iterate over all elements in model
				Iterator<EObject> iter = state.eResource().getAllContents();
				while (iter.hasNext()) {
					EObject rootContent = iter.next();
					if (rootContent instanceof DomainModel) {
						DomainModel model = (DomainModel) rootContent;
						Iterator<ResourceInteractionModel> rims = Iterators.filter(model.eAllContents(), ResourceInteractionModel.class);
						while (rims.hasNext()) {
							ResourceInteractionModel rim = rims.next();
							processRIM(rim, allStates, incomingTransitions, outgoingTransitions);
						}
					}
				}

				// Then build the visual representation of the part of 
				// the network that is to be shown
				if (showIncomingRelations) {
					retrieveStatesTransitions(state, incomingTransitions, true);
				}
				
				if (showOutgoingRelations) {
					retrieveStatesTransitions(state, outgoingTransitions, false);
				}
			}
		}
	}

	private void processRIM(ResourceInteractionModel model, Set<State> allStates, Map<State, Set<TransitionDescription>> incomingTransitions, Map<State, Set<TransitionDescription>> outgoingTransitions) {
		Iterator<State> states = Iterators.filter(model.eAllContents(), State.class);
		while (states.hasNext()) {
			State state = states.next();
			System.out.println("Adding state " + state.getName());
			allStates.add((State) state);
			Iterator<Transition> transitions = Iterators.filter(state.eAllContents(), Transition.class);
			while (transitions.hasNext()) {
				Transition transition = transitions.next();
				String title = (transition.getTitle() != null ? transition.getTitle().getName() : "");
				assert(transition.eContainer() instanceof State);
				State fromState = (State)transition.eContainer();
				State toState = transition.getState();
				if (fromState != null && toState != null) {
					TransitionDescription t = new TransitionDescription.Builder()
						.title(title)
						.event(transition.getEvent().getName())
						.conditions(createConditionsStr(transition.getEval()))
						.fromState(fromState)
						.toState(toState)
						.build();
					addInOut(t, incomingTransitions, outgoingTransitions);
				}
			}
			Iterator<TransitionForEach> transitionsForEach = Iterators.filter(state.eAllContents(), TransitionForEach.class);
			while (transitionsForEach.hasNext()) {
				TransitionForEach transition = transitionsForEach.next();
				String title = (transition.getTitle() != null ? transition.getTitle().getName() : "");
				assert(transition.eContainer() instanceof State);
				State fromState = (State)transition.eContainer();
				State toState = transition.getState();
				if (fromState != null && toState != null) {
					TransitionDescription t = new TransitionDescription.Builder()
						.title(title)
						.event(transition.getEvent().getName())
						.conditions(createConditionsStr(transition.getEval()))
						.fromState(fromState)
						.toState(toState)
						.build();
					addInOut(t, incomingTransitions, outgoingTransitions);
				}
			}
			Iterator<TransitionAuto> transitionsAuto = Iterators.filter(state.eAllContents(), TransitionAuto.class);
			while (transitionsAuto.hasNext()) {
				TransitionAuto transition = transitionsAuto.next();
				String title = "-->";
				assert(transition.eContainer() instanceof State);
				State fromState = (State)transition.eContainer();
				State toState = transition.getState();
				if (fromState != null && toState != null) {
					TransitionDescription t = new TransitionDescription.Builder()
						.title(title)
						.event(transition.getEvent().getName())
						.conditions(createConditionsStr(transition.getEval()))
						.fromState(fromState)
						.toState(toState)
						.build();
					addInOut(t, incomingTransitions, outgoingTransitions);
				}
			}
		}
		
	}
	
	private String createConditionsStr(Expression expression) {
		String result = "";
		if (expression != null) {
			for (Function func : expression.getExpressions()) {
				if (result.length() > 0) {
					result += " ";
				}
				if (func instanceof NotFoundFunction) {
					result += "NOT_FOUND(" + ((NotFoundFunction)func).getState().getName()+ ")";
				}
				if (func instanceof OKFunction) {
					result += "OK(" + ((OKFunction)func).getState().getName()+ ")";
				}
			}
		}
		return result;
	}
	
	private void addInOut(TransitionDescription t, Map<State, Set<TransitionDescription>> incomingTransitions, Map<State, Set<TransitionDescription>> outgoingTransitions) {
		State toState = t.getToState();
		Set<TransitionDescription> incomingTransitionsForState = incomingTransitions.get(toState);
		if (incomingTransitionsForState == null) {
			incomingTransitionsForState = new HashSet<TransitionDescription>();
			incomingTransitions.put(toState, incomingTransitionsForState);
		}
		incomingTransitionsForState.add(t);
		
		State fromState = t.getFromState();
		assert(fromState instanceof State);
		Set<TransitionDescription> outgoingTransitionsForState = outgoingTransitions.get(fromState);
		if (outgoingTransitionsForState == null) {
			outgoingTransitionsForState = new HashSet<TransitionDescription>();
			outgoingTransitions.put(fromState, outgoingTransitionsForState);
		}
		outgoingTransitionsForState.add(t);
	}
	
	private void retrieveStatesTransitions(State root, Map<State, Set<TransitionDescription>> allTransistions, boolean backwards) {
		Set<State> visitedStates = new HashSet<State>();
		visitedStates.clear();
		internalRetrieveStatesTransitions(root, visitedStates, allTransistions, backwards);
	}

	private void internalRetrieveStatesTransitions(State root, Set<State> visitedStates, Map<State, Set<TransitionDescription>> allTransitions, boolean backwards) {
		states.add(root);
		Set<TransitionDescription> transitionsForState = allTransitions.get(root);
		if (transitionsForState != null) {
			for (TransitionDescription t : transitionsForState) {
				if (!transitions.contains(t))
					transitions.add(t);
			
				State next = null;
				if (backwards) {
					next = t.getFromState();
				} else {
					next = t.getToState();
				}
				
				// Recursive call if this element has not been in focus before
				if (!visitedStates.contains(next)) {
					visitedStates.add(next);
					internalRetrieveStatesTransitions(next, visitedStates, allTransitions, backwards);
				} 				
{'fraction_non_alphanumeric': 0.13154596893631565, 'fraction_numerical': 0.002050658869099609, 'mean_word_length': 4.392996108949417, 'pattern_counts': {'":': 0, '<': 101, '<?xml version=': 0, '>': 107, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, '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': '24485', 'n_tokens_mistral': 8583, 'n_tokens_neox': 7144, 'n_words': 1810}