<commit_before>package pl.pwr.hiervis.core;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;

import org.apache.commons.lang3.tuple.Pair;

import basic_hierarchy.common.HierarchyBuilder;
import basic_hierarchy.interfaces.Hierarchy;
import pl.pwr.hiervis.prefuse.TableEx;
import pl.pwr.hiervis.prefuse.visualization.TreeLayoutData;
import prefuse.data.Tree;


/**
 * Container class used to associate a {@link Hierarchy} instance with various other
 * objects pertaining to that hierarchy, like loading options, and processed data used
 * for visualizations.
 * 
 * @author <NAME>
 *
 */
public class LoadedHierarchy
{
	public final Hierarchy data;
	public final LoadedHierarchy.Options options;

	private final Map<String, Object> computedMeasureMap;

	private Tree hierarchyTree;
	private TreeLayoutData hierarchyTreeLayout;
	private TableEx instanceTable;


	public LoadedHierarchy( Hierarchy h, LoadedHierarchy.Options o )
	{
		if ( h == null )
			throw new IllegalArgumentException( "Hierarchy must not be null!" );
		if ( o == null )
			throw new IllegalArgumentException( "Options must not be null!" );

		this.data = h;
		this.options = o;

		this.computedMeasureMap = new HashMap<>();
	}

	/**
	 * Processes the specified hierarchy, building hierarchy tree and creating instance table
	 * used in visualizations.
	 * 
	 * @param hierarchy
	 *            the hierarchy to process
	 */
	protected void processHierarchy( HVConfig config )
	{
		// TODO:
		// Might want to use some kind of algorithm to figure out optimal tree layout area?
		// 1024x1024 seems to work well enough for now.
		Pair<Tree, TreeLayoutData> treeData = HierarchyProcessor.buildHierarchyTree(
			data.getRoot(), 2048, 2048
		);
		hierarchyTree = treeData.getLeft();
		hierarchyTreeLayout = treeData.getRight();

		instanceTable = HierarchyProcessor.createInstanceTable(
			config, this, hierarchyTree
		);
	}

	/**
	 * @return true if the hierarchy has been processed, and is ready to be visualized.
	 */
	public boolean isProcessed()
	{
		return hierarchyTree != null && hierarchyTreeLayout != null && instanceTable != null;
	}

	/**
	 * @return tree structure representing relationships between groups (nodes) in the hierarchy
	 */
	public Tree getTree()
	{
		return hierarchyTree;
	}

	/**
	 * @return helper layout data for drawing the tree.
	 */
	public TreeLayoutData getTreeLayoutData()
	{
		return hierarchyTreeLayout;
	}

	/**
	 * @return table containing processed instance data
	 */
	public TableEx getInstanceTable()
	{
		return instanceTable;
	}

	/**
	 * Returns a set of measures that have been computed thus far for the currently loaded hierarchy.
	 * <p>
	 * This method is not particularly thread-safe, as the map of measures might be updated with new entries
	 * while you are processing the set, resulting in missed entries.
	 * </p>
	 * <p>
	 * For a thread-safe alternative, see {@link #forComputedMeasures(Consumer)}
	 * </p>
	 * 
	 * @see #forComputedMeasures(Consumer)
	 */
	public Set<Map.Entry<String, Object>> getComputedMeasures()
	{
		synchronized ( computedMeasureMap ) {
			return Collections.unmodifiableMap( computedMeasureMap ).entrySet();
		}
	}

	/**
	 * @param identifier
	 *            identifier of the task to look for
	 * @return true if the task is already computed, false otherwise
	 */
	public boolean isMeasureComputed( String identifier )
	{
		synchronized ( computedMeasureMap ) {
			return computedMeasureMap.containsKey( identifier );
		}
	}

	/**
	 * Performs the specified function on the set of measures that have been computed thus far for
	 * the currently loaded hierarchy.
	 * <p>
	 * This method executes the function inside of a synchronized block, preventing the set from
	 * being updated while this method is executing.
	 * </p>
	 */
	public void forComputedMeasures( Consumer<Set<Map.Entry<String, Object>>> function )
	{
		synchronized ( computedMeasureMap ) {
			function.accept( Collections.unmodifiableMap( computedMeasureMap ).entrySet() );
		}
	}

	public Object getMeasureResultOrDefault( String identifier, Object defaultValue )
	{
		synchronized ( computedMeasureMap ) {
			return computedMeasureMap.getOrDefault( identifier, defaultValue );
		}
	}

	public Object getMeasureResult( String identifier )
	{
		synchronized ( computedMeasureMap ) {
			return computedMeasureMap.get( identifier );
		}
	}

	protected void putMeasureResult( String identifier, Object value )
	{
		synchronized ( computedMeasureMap ) {
			computedMeasureMap.put( identifier, value );
		}
	}

	public void dispose()
	{
		computedMeasureMap.clear();

		hierarchyTree = null;
		hierarchyTreeLayout = null;

		if ( instanceTable != null ) {
			instanceTable.removeAllTableListeners();
			instanceTable.clear();
			instanceTable.dispose();
			instanceTable = null;
		}
	}


	/**
	 * Container class for load options for each {@link Hierarchy} object loaded in the program.
	 * 
	 * @author <NAME>
	 *
	 */
	public static class Options
	{
		public static final Options DEFAULT = new Options( false, false, false, false, false );<commit_msg>Added dispose of hierarchy tree graph when disposing a loaded hierarchy.<commit_after>package pl.pwr.hiervis.core;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;

import org.apache.commons.lang3.tuple.Pair;

import basic_hierarchy.common.HierarchyBuilder;
import basic_hierarchy.interfaces.Hierarchy;
import pl.pwr.hiervis.prefuse.TableEx;
import pl.pwr.hiervis.prefuse.visualization.TreeLayoutData;
import prefuse.data.Tree;


/**
 * Container class used to associate a {@link Hierarchy} instance with various other
 * objects pertaining to that hierarchy, like loading options, and processed data used
 * for visualizations.
 * 
 * @author <NAME>
 *
 */
public class LoadedHierarchy
{
	public final Hierarchy data;
	public final LoadedHierarchy.Options options;

	private final Map<String, Object> computedMeasureMap;

	private Tree hierarchyTree;
	private TreeLayoutData hierarchyTreeLayout;
	private TableEx instanceTable;


	public LoadedHierarchy( Hierarchy h, LoadedHierarchy.Options o )
	{
		if ( h == null )
			throw new IllegalArgumentException( "Hierarchy must not be null!" );
		if ( o == null )
			throw new IllegalArgumentException( "Options must not be null!" );

		this.data = h;
		this.options = o;

		this.computedMeasureMap = new HashMap<>();
	}

	/**
	 * Processes the specified hierarchy, building hierarchy tree and creating instance table
	 * used in visualizations.
	 * 
	 * @param hierarchy
	 *            the hierarchy to process
	 */
	protected void processHierarchy( HVConfig config )
	{
		// TODO:
		// Might want to use some kind of algorithm to figure out optimal tree layout area?
		// 1024x1024 seems to work well enough for now.
		Pair<Tree, TreeLayoutData> treeData = HierarchyProcessor.buildHierarchyTree(
			data.getRoot(), 2048, 2048
		);
		hierarchyTree = treeData.getLeft();
		hierarchyTreeLayout = treeData.getRight();

		instanceTable = HierarchyProcessor.createInstanceTable(
			config, this, hierarchyTree
		);
	}

	/**
	 * @return true if the hierarchy has been processed, and is ready to be visualized.
	 */
	public boolean isProcessed()
	{
		return hierarchyTree != null && hierarchyTreeLayout != null && instanceTable != null;
	}

	/**
	 * @return tree structure representing relationships between groups (nodes) in the hierarchy
	 */
	public Tree getTree()
	{
		return hierarchyTree;
	}

	/**
	 * @return helper layout data for drawing the tree.
	 */
	public TreeLayoutData getTreeLayoutData()
	{
		return hierarchyTreeLayout;
	}

	/**
	 * @return table containing processed instance data
	 */
	public TableEx getInstanceTable()
	{
		return instanceTable;
	}

	/**
	 * Returns a set of measures that have been computed thus far for the currently loaded hierarchy.
	 * <p>
	 * This method is not particularly thread-safe, as the map of measures might be updated with new entries
	 * while you are processing the set, resulting in missed entries.
	 * </p>
	 * <p>
	 * For a thread-safe alternative, see {@link #forComputedMeasures(Consumer)}
	 * </p>
	 * 
	 * @see #forComputedMeasures(Consumer)
	 */
	public Set<Map.Entry<String, Object>> getComputedMeasures()
	{
		synchronized ( computedMeasureMap ) {
			return Collections.unmodifiableMap( computedMeasureMap ).entrySet();
		}
	}

	/**
	 * @param identifier
	 *            identifier of the task to look for
	 * @return true if the task is already computed, false otherwise
	 */
	public boolean isMeasureComputed( String identifier )
	{
		synchronized ( computedMeasureMap ) {
			return computedMeasureMap.containsKey( identifier );
		}
	}

	/**
	 * Performs the specified function on the set of measures that have been computed thus far for
	 * the currently loaded hierarchy.
	 * <p>
	 * This method executes the function inside of a synchronized block, preventing the set from
	 * being updated while this method is executing.
	 * </p>
	 */
	public void forComputedMeasures( Consumer<Set<Map.Entry<String, Object>>> function )
	{
		synchronized ( computedMeasureMap ) {
			function.accept( Collections.unmodifiableMap( computedMeasureMap ).entrySet() );
		}
	}

	public Object getMeasureResultOrDefault( String identifier, Object defaultValue )
	{
		synchronized ( computedMeasureMap ) {
			return computedMeasureMap.getOrDefault( identifier, defaultValue );
		}
	}

	public Object getMeasureResult( String identifier )
	{
		synchronized ( computedMeasureMap ) {
			return computedMeasureMap.get( identifier );
		}
	}

	protected void putMeasureResult( String identifier, Object value )
	{
		synchronized ( computedMeasureMap ) {
			computedMeasureMap.put( identifier, value );
		}
	}

	public void dispose()
	{
		computedMeasureMap.clear();

		hierarchyTree.dispose();
		hierarchyTree.removeAllSets();
		hierarchyTree.clear();
		hierarchyTree = null;

		hierarchyTreeLayout = null;

		if ( instanceTable != null ) {
			instanceTable.removeAllTableListeners();
			instanceTable.clear();
			instanceTable.dispose();
			instanceTable = null;
		}
	}


	/**
	 * Container class for load options for each {@link Hierarchy} object loaded in the program.
	 * 
	 * @author <NAME>
	 *
	 */
	public static class Options
	{
		public static final Options DEFAULT = new Options( false, false, false, false, false );
{'fraction_non_alphanumeric': 0.09045659040872978, 'fraction_numerical': 0.0032545228295204364, 'mean_word_length': 3.9493131217432498, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 35, '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': '3621679', 'n_tokens_mistral': 3199, 'n_tokens_neox': 2891, 'n_words': 961}