<commit_before>/*******************************************************************************
 * Caleydo - Visualization for Molecular Biology - http://caleydo.org
 * Copyright (c) The Caleydo Team. All rights reserved.
 * Licensed under the new BSD license, available at http://caleydo.org/license
 ******************************************************************************/
package org.caleydo.view.relationshipexplorer.ui;

import gleem.linalg.Vec2f;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;

import org.caleydo.core.data.collection.EDimension;
import org.caleydo.core.data.perspective.table.TablePerspective;
import org.caleydo.core.id.IDType;
import org.caleydo.core.util.base.ILabeled;
import org.caleydo.core.util.color.Color;
import org.caleydo.core.util.color.ColorBrewer;
import org.caleydo.core.view.opengl.canvas.IGLCanvas;
import org.caleydo.core.view.opengl.layout2.AGLElementView;
import org.caleydo.core.view.opengl.layout2.GLElement;
import org.caleydo.core.view.opengl.layout2.GLGraphics;
import org.caleydo.core.view.opengl.layout2.PickableGLElement;
import org.caleydo.core.view.opengl.layout2.animation.AnimatedGLElementContainer;
import org.caleydo.core.view.opengl.layout2.basic.ScrollBar;
import org.caleydo.core.view.opengl.layout2.basic.ScrollingDecorator;
import org.caleydo.core.view.opengl.layout2.dnd.EDnDType;
import org.caleydo.core.view.opengl.layout2.dnd.IDnDItem;
import org.caleydo.core.view.opengl.layout2.dnd.IDragInfo;
import org.caleydo.core.view.opengl.layout2.dnd.IDropGLTarget;
import org.caleydo.core.view.opengl.layout2.layout.GLLayoutDatas;
import org.caleydo.core.view.opengl.layout2.layout.GLMinSizeProviders;
import org.caleydo.core.view.opengl.layout2.layout.GLPadding;
import org.caleydo.core.view.opengl.layout2.layout.GLSizeRestrictiveFlowLayout;
import org.caleydo.core.view.opengl.layout2.layout.GLSizeRestrictiveFlowLayout2;
import org.caleydo.core.view.opengl.layout2.util.GLElementWindow;
import org.caleydo.core.view.opengl.layout2.util.GLElementWindow.ICloseWindowListener;
import org.caleydo.core.view.opengl.picking.Pick;
import org.caleydo.view.relationshipexplorer.internal.Activator;
import org.caleydo.view.relationshipexplorer.ui.collection.EnrichmentScores;
import org.caleydo.view.relationshipexplorer.ui.collection.IEntityCollection;
import org.caleydo.view.relationshipexplorer.ui.column.operation.AMappingUpdateOperation;
import org.caleydo.view.relationshipexplorer.ui.column.operation.AddColumnTreeCommand;
import org.caleydo.view.relationshipexplorer.ui.column.operation.HideDetailCommand;
import org.caleydo.view.relationshipexplorer.ui.column.operation.RemoveColumnCommand;
import org.caleydo.view.relationshipexplorer.ui.filter.FilterPipeline;
import org.caleydo.view.relationshipexplorer.ui.list.ColumnTree;
import org.caleydo.view.relationshipexplorer.ui.list.DragAndDropHeader.ColumnDragInfo;
import org.caleydo.view.relationshipexplorer.ui.list.EUpdateCause;

import com.google.common.base.Predicates;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.Iterables;

/**
 *
 *
 * @author Christian
 *
 */
public class RelationshipExplorerElement extends AnimatedGLElementContainer {

	protected final static int MIN_HISTORY_HEIGHT = 30;
	protected final static int MIN_FILTER_PIPELINE_HEIGHT = 16;
	protected final static int MIN_COLUMN_HEIGHT = 200;

	// ------------
	protected List<ColumnTree> cols = new ArrayList<>();

	// ------------
	protected EnrichmentScores enrichmentScores = new EnrichmentScores(this);

	// protected List<AEntityColumn> columns = new ArrayList<>();
	protected AnimatedGLElementContainer columnContainer;
	protected AnimatedGLElementContainer detailContainer;
	protected History history;
	protected FilterPipeline filterPipeline;
	protected BiMap<IEntityCollection, GLElementWindow> detailMap = HashBiMap.create(2);
	protected Queue<GLElementWindow> detailWindowQueue = new LinkedList<>();

	protected Set<IEntityCollection> entityCollections = new LinkedHashSet<>();

	public interface IIDMappingUpdateHandler {
		public void handleIDMappingUpdate(AMappingUpdateOperation operation);
	}

	// public interface ISelectionMappingUpdateListener {
	// public void updateSelectionMappings(AEntityColumn srcColumn);
	// }

	protected IIDMappingUpdateHandler idMappingUpdateHandler = new IIDMappingUpdateHandler() {

		@Override
		public void handleIDMappingUpdate(AMappingUpdateOperation operation) {
			executeMappingUpdateOperation(operation);
			updateMappings(operation);
		}

	};

	protected class ColumnSeparator extends PickableGLElement implements IDropGLTarget {

		protected boolean isDraggedOver = false;
		protected boolean isOver = false;

		@Override
		protected void renderImpl(GLGraphics g, float w, float h) {
			g.color(isDraggedOver ? Color.DARK_GRAY : Color.LIGHT_GRAY);
			g.lineWidth(isDraggedOver ? 4 : 2);
			g.drawLine(w / 2.0f, 0, w / 2.0f, h);
			g.lineWidth(1);
		}

		@Override
		protected void onMouseOver(Pick pick) {
			context.getMouseLayer().addDropTarget(this);
			isOver = true;
		}

		@Override
		protected void onMouseOut(Pick pick) {
			context.getMouseLayer().removeDropTarget(this);
			if (isDraggedOver) {
				isDraggedOver = false;
				isOver = false;
				repaint();
			}
		}

		@Override
		public boolean canSWTDrop(IDnDItem item) {
			return item.getInfo() instanceof ColumnDragInfo;
		}

		@Override
		public void onDrop(IDnDItem item) {
			IDragInfo i = item.getInfo();
			if (!(i instanceof ColumnDragInfo))
				return;
			ColumnDragInfo info = (ColumnDragInfo) i;

			if (item.getType() == EDnDType.COPY) {
				AddColumnTreeCommand c = new AddColumnTreeCommand(info.getModel().getCollection(),
						RelationshipExplorerElement.this);
				c.setIndex(columnContainer.indexOf(this));
				c.execute();
				history.addHistoryCommand(c, Color.DARK_BLUE);
			} else {
				AddColumnTreeCommand c = new AddColumnTreeCommand(info.getModel().getCollection(),
						RelationshipExplorerElement.this);
				c.setIndex(columnContainer.indexOf(this));
				c.execute();
				history.addHistoryCommand(c, Color.DARK_BLUE);

				RemoveColumnCommand rc = new RemoveColumnCommand(info.getModel(), history);
				rc.execute();
				history.addHistoryCommand(rc, Color.DARK_BLUE);
			}
		}

		@Override
		public void onItemChanged(IDnDItem item) {
			if (!isDraggedOver && isOver) {
				isDraggedOver = true;
				repaint();
			}

		}

		@Override
		public EDnDType defaultSWTDnDType(IDnDItem item) {
			return EDnDType.MOVE;
		}

		@Override
		protected void takeDown() {
			context.getMouseLayer().removeDropTarget(this);
			super.takeDown();
		}

	}

	// private class DetailConnector extends GLElement {
	//
	// @Override
	// protected void renderImpl(GLGraphics g, float w, float h) {
	// for (GLElement detailWindow : detailContainer) {
	// AEntityColumn column = detailMap.inverse().get(detailWindow);
	// Vec2f winLoc1 = new Vec2f(detailWindow.getLocation().x(), 0);
	// Vec2f winLoc2 = new Vec2f(winLoc1.x() + detailWindow.getSize().x(), 0);
	// Vec2f colLoc1 = new Vec2f(column.getLocation().x(), h);
	// Vec2f colLoc2 = new Vec2f(colLoc1.x() + column.getSize().x(), h);
	// g.color(Color.RED).drawPath(true, winLoc1, winLoc2, colLoc2, colLoc1);
	// }
	// }
	// }

	public RelationshipExplorerElement() {
		super(new GLSizeRestrictiveFlowLayout(false, 5, GLPadding.ZERO));
		columnContainer = new AnimatedGLElementContainer(new GLSizeRestrictiveFlowLayout2(true, 0, GLPadding.ZERO));
		detailContainer = new AnimatedGLElementContainer(new GLSizeRestrictiveFlowLayout2(true, 5, GLPadding.ZERO));
		detailContainer.setSize(Float.NaN, 0);
		add(detailContainer);
		// columnContainer.setLayoutData(0.9f);
		add(columnContainer);

		filterPipeline = new FilterPipeline(this);
		ScrollingDecorator scrollingDecorator = new ScrollingDecorator(filterPipeline, new ScrollBar(true),
				new ScrollBar(false), 8, EDimension.RECORD);
		scrollingDecorator.setMinSizeProvider(filterPipeline);
		scrollingDecorator.setSize(Float.NaN, MIN_FILTER_PIPELINE_HEIGHT);
		add(scrollingDecorator);

		history = new History(this);
		scrollingDecorator = new ScrollingDecorator(history, new ScrollBar(true), new ScrollBar(false), 8,
				EDimension.RECORD);
		scrollingDecorator.setMinSizeProvider(history);
		scrollingDecorator.setSize(Float.NaN, MIN_HISTORY_HEIGHT);
		add(scrollingDecorator);

	}

	/**
	 * @return the filterPipeline, see {@link #filterPipeline}
	 */
	public FilterPipeline getFilterPipeline() {
		return filterPipeline;
	}

	// public void addEntityColumn(AEntityColumn column) {
	// if (columns.size() > 0) {
	// GLElement columnSpacer = new GLElement(GLRenderers.fillRect(Color.LIGHT_GRAY));
	// columnSpacer.setSize(2, Float.NaN);
	// columnContainer.add(columnSpacer);
	// }
	// // columnContainer.add(column);
	// columns.add(column);
	// registerEntityCollection(column);
	// }

	public void addColumn(ColumnTree column) {
		if (cols.size() > 0) {
			ColumnSeparator columnSeparator = new ColumnSeparator();
			columnSeparator.setSize(12, Float.NaN);
			columnContainer.add(columnSeparator);
		}
		columnContainer.add(column);
		cols.add(column);
		// registerEntityCollection(column);
	}

	public void addColumn(int index, ColumnTree column) {
		if (cols.isEmpty())
			addColumn(column);
		if (index > columnContainer.size())
			index = columnContainer.size() - 1;
		if (index < 0)
			index = 0;

		GLElement element = columnContainer.get(index);
		if (element instanceof ColumnSeparator) {

			columnContainer.add(index, column);
			cols.add(column);
			ColumnSeparator columnSeparator = new ColumnSeparator();
			columnSeparator.setSize(12, Float.NaN);
			columnContainer.add(index, columnSeparator);
		} else {

			ColumnSeparator columnSeparator = new ColumnSeparator();
			columnSeparator.setSize(12, Float.NaN);
			columnContainer.add(index, columnSeparator);
			columnContainer.add(index, column);
			cols.add(column);
		}
	}

	public void removeColumn(ColumnTree column) {
		int index = columnContainer.indexOf(column);
		if (index == -1)
			return;

		if (index == 0) {<commit_msg>improved reordering of root columns<commit_after>/*******************************************************************************
 * Caleydo - Visualization for Molecular Biology - http://caleydo.org
 * Copyright (c) The Caleydo Team. All rights reserved.
 * Licensed under the new BSD license, available at http://caleydo.org/license
 ******************************************************************************/
package org.caleydo.view.relationshipexplorer.ui;

import gleem.linalg.Vec2f;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;

import org.caleydo.core.data.collection.EDimension;
import org.caleydo.core.data.perspective.table.TablePerspective;
import org.caleydo.core.id.IDType;
import org.caleydo.core.util.base.ILabeled;
import org.caleydo.core.util.color.Color;
import org.caleydo.core.util.color.ColorBrewer;
import org.caleydo.core.view.opengl.canvas.IGLCanvas;
import org.caleydo.core.view.opengl.layout2.AGLElementView;
import org.caleydo.core.view.opengl.layout2.GLElement;
import org.caleydo.core.view.opengl.layout2.GLGraphics;
import org.caleydo.core.view.opengl.layout2.PickableGLElement;
import org.caleydo.core.view.opengl.layout2.animation.AnimatedGLElementContainer;
import org.caleydo.core.view.opengl.layout2.basic.ScrollBar;
import org.caleydo.core.view.opengl.layout2.basic.ScrollingDecorator;
import org.caleydo.core.view.opengl.layout2.dnd.EDnDType;
import org.caleydo.core.view.opengl.layout2.dnd.IDnDItem;
import org.caleydo.core.view.opengl.layout2.dnd.IDragInfo;
import org.caleydo.core.view.opengl.layout2.dnd.IDropGLTarget;
import org.caleydo.core.view.opengl.layout2.layout.GLLayoutDatas;
import org.caleydo.core.view.opengl.layout2.layout.GLMinSizeProviders;
import org.caleydo.core.view.opengl.layout2.layout.GLPadding;
import org.caleydo.core.view.opengl.layout2.layout.GLSizeRestrictiveFlowLayout;
import org.caleydo.core.view.opengl.layout2.layout.GLSizeRestrictiveFlowLayout2;
import org.caleydo.core.view.opengl.layout2.util.GLElementWindow;
import org.caleydo.core.view.opengl.layout2.util.GLElementWindow.ICloseWindowListener;
import org.caleydo.core.view.opengl.picking.Pick;
import org.caleydo.view.relationshipexplorer.internal.Activator;
import org.caleydo.view.relationshipexplorer.ui.History.IHistoryCommand;
import org.caleydo.view.relationshipexplorer.ui.History.IHistoryIDOwner;
import org.caleydo.view.relationshipexplorer.ui.collection.EnrichmentScores;
import org.caleydo.view.relationshipexplorer.ui.collection.IEntityCollection;
import org.caleydo.view.relationshipexplorer.ui.column.operation.AMappingUpdateOperation;
import org.caleydo.view.relationshipexplorer.ui.column.operation.AddColumnTreeCommand;
import org.caleydo.view.relationshipexplorer.ui.column.operation.HideDetailCommand;
import org.caleydo.view.relationshipexplorer.ui.column.operation.RemoveColumnCommand;
import org.caleydo.view.relationshipexplorer.ui.filter.FilterPipeline;
import org.caleydo.view.relationshipexplorer.ui.list.ColumnTree;
import org.caleydo.view.relationshipexplorer.ui.list.DragAndDropHeader.ColumnDragInfo;
import org.caleydo.view.relationshipexplorer.ui.list.EUpdateCause;
import org.caleydo.view.relationshipexplorer.ui.list.IColumnModel;

import com.google.common.base.Predicates;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.Iterables;

/**
 *
 *
 * @author Christian
 *
 */
public class RelationshipExplorerElement extends AnimatedGLElementContainer {

	protected final static int MIN_HISTORY_HEIGHT = 30;
	protected final static int MIN_FILTER_PIPELINE_HEIGHT = 16;
	protected final static int MIN_COLUMN_HEIGHT = 200;

	// ------------
	protected List<ColumnTree> cols = new ArrayList<>();

	// ------------
	protected EnrichmentScores enrichmentScores = new EnrichmentScores(this);

	// protected List<AEntityColumn> columns = new ArrayList<>();
	protected AnimatedGLElementContainer columnContainer;
	protected AnimatedGLElementContainer detailContainer;
	protected History history;
	protected FilterPipeline filterPipeline;
	protected BiMap<IEntityCollection, GLElementWindow> detailMap = HashBiMap.create(2);
	protected Queue<GLElementWindow> detailWindowQueue = new LinkedList<>();

	protected Set<IEntityCollection> entityCollections = new LinkedHashSet<>();

	public interface IIDMappingUpdateHandler {
		public void handleIDMappingUpdate(AMappingUpdateOperation operation);
	}

	// public interface ISelectionMappingUpdateListener {
	// public void updateSelectionMappings(AEntityColumn srcColumn);
	// }

	protected IIDMappingUpdateHandler idMappingUpdateHandler = new IIDMappingUpdateHandler() {

		@Override
		public void handleIDMappingUpdate(AMappingUpdateOperation operation) {
			executeMappingUpdateOperation(operation);
			updateMappings(operation);
		}

	};

	protected class ReorderColumnTreesCommand implements IHistoryCommand {

		protected final int columnModelHistoryID;
		protected final int separatorHistoryID;

		public ReorderColumnTreesCommand(IColumnModel model, ColumnSeparator separator) {
			this.columnModelHistoryID = model.getHistoryID();
			this.separatorHistoryID = separator.getHistoryID();
		}

		@Override
		public Object execute() {
			final List<ColumnTree> l = new ArrayList<>();
			List<ColumnSeparator> separatorsToRemove = new ArrayList<>();
			IColumnModel model = history.getHistoryObjectAs(IColumnModel.class, columnModelHistoryID);
			ColumnSeparator separator = history.getHistoryObjectAs(ColumnSeparator.class, separatorHistoryID);

			ColumnTree columnTree = model.getColumn().getColumnTree();
			for (GLElement e : columnContainer) {
				if (e instanceof ColumnTree && e != columnTree) {
					l.add((ColumnTree) e);
				} else {
					if (e == separator) {
						l.add(columnTree);
					}
				}
				if (e instanceof ColumnSeparator) {
					separatorsToRemove.add((ColumnSeparator) e);
				}
			}

			for (ColumnSeparator cs : separatorsToRemove) {
				columnContainer.remove(cs, 0);
			}

			columnContainer.sortBy(new Comparator<GLElement>() {
				@Override
				public int compare(GLElement o1, GLElement o2) {
					return l.indexOf(o1) - l.indexOf(o2);
				}
			});

			for (int j = 0; j < l.size(); j++) {
				ColumnTree ct = l.get(j);

				columnContainer.add(columnContainer.indexOf(ct), new ColumnSeparator(), 0);

			}
			columnContainer.add(new ColumnSeparator(), 0);

			return null;
		}

		@Override
		public String getDescription() {
			IColumnModel model = history.getHistoryObjectAs(IColumnModel.class, columnModelHistoryID);
			return "Moved column " + model.getLabel();
		}
	}

	protected class ColumnSeparator extends PickableGLElement implements IDropGLTarget, IHistoryIDOwner {

		protected boolean isDraggedOver = false;
		protected boolean isOver = false;
		protected int historyID;

		public ColumnSeparator() {
			setSize(12, Float.NaN);
			historyID = history.registerHistoryObject(this);
		}

		@Override
		protected void renderImpl(GLGraphics g, float w, float h) {
			g.color(isDraggedOver ? Color.DARK_GRAY : Color.LIGHT_GRAY);
			g.lineWidth(isDraggedOver ? 4 : 2);
			g.drawLine(w / 2.0f, 0, w / 2.0f, h);
			g.lineWidth(1);
		}

		@Override
		protected void onMouseOver(Pick pick) {
			context.getMouseLayer().addDropTarget(this);
			isOver = true;
		}

		@Override
		protected void onMouseOut(Pick pick) {
			context.getMouseLayer().removeDropTarget(this);
			if (isDraggedOver) {
				isDraggedOver = false;
				isOver = false;
				repaint();
			}
		}

		@Override
		public boolean canSWTDrop(IDnDItem item) {
			return item.getInfo() instanceof ColumnDragInfo;
		}

		@Override
		public void onDrop(IDnDItem item) {
			IDragInfo i = item.getInfo();
			if (!(i instanceof ColumnDragInfo))
				return;
			ColumnDragInfo info = (ColumnDragInfo) i;

			if (item.getType() == EDnDType.COPY) {

				AddColumnTreeCommand c = new AddColumnTreeCommand(info.getModel().getCollection(),
						RelationshipExplorerElement.this);
				c.setIndex(columnContainer.indexOf(this));
				c.execute();
				history.addHistoryCommand(c, Color.DARK_BLUE);

			} else {
				if (info.getModel().getColumn().isRoot()) {
					ReorderColumnTreesCommand c = new ReorderColumnTreesCommand(info.getModel(), this);
					c.execute();
					history.addHistoryCommand(c, Color.DARK_BLUE);
				} else {
					AddColumnTreeCommand c = new AddColumnTreeCommand(info.getModel().getCollection(),
							RelationshipExplorerElement.this);
					c.setIndex(columnContainer.indexOf(this));
					c.execute();
					history.addHistoryCommand(c, Color.DARK_BLUE);

					RemoveColumnCommand rc = new RemoveColumnCommand(info.getModel(), history);
					rc.execute();
					history.addHistoryCommand(rc, Color.DARK_BLUE);
				}
			}
		}

		@Override
		public void onItemChanged(IDnDItem item) {
			if (!isDraggedOver && isOver) {
				isDraggedOver = true;
				repaint();
			}

		}

		@Override
		public EDnDType defaultSWTDnDType(IDnDItem item) {
			return EDnDType.MOVE;
		}

		@Override
		protected void takeDown() {
			context.getMouseLayer().removeDropTarget(this);
			super.takeDown();
		}

		@Override
		public int getHistoryID() {
			return historyID;
		}

	}

	// private class DetailConnector extends GLElement {
	//
	// @Override
	// protected void renderImpl(GLGraphics g, float w, float h) {
	// for (GLElement detailWindow : detailContainer) {
	// AEntityColumn column = detailMap.inverse().get(detailWindow);
	// Vec2f winLoc1 = new Vec2f(detailWindow.getLocation().x(), 0);
	// Vec2f winLoc2 = new Vec2f(winLoc1.x() + detailWindow.getSize().x(), 0);
	// Vec2f colLoc1 = new Vec2f(column.getLocation().x(), h);
	// Vec2f colLoc2 = new Vec2f(colLoc1.x() + column.getSize().x(), h);
	// g.color(Color.RED).drawPath(true, winLoc1, winLoc2, colLoc2, colLoc1);
	// }
	// }
	// }

	public RelationshipExplorerElement() {
		super(new GLSizeRestrictiveFlowLayout(false, 5, GLPadding.ZERO));
		columnContainer = new AnimatedGLElementContainer(new GLSizeRestrictiveFlowLayout2(true, 0, GLPadding.ZERO));
		detailContainer = new AnimatedGLElementContainer(new GLSizeRestrictiveFlowLayout2(true, 5, GLPadding.ZERO));
		detailContainer.setSize(Float.NaN, 0);
		add(detailContainer);
		// columnContainer.setLayoutData(0.9f);
		add(columnContainer);
		history = new History(this);

		columnContainer.add(new ColumnSeparator());
		columnContainer.add(new ColumnSeparator());

		filterPipeline = new FilterPipeline(this);
		ScrollingDecorator scrollingDecorator = new ScrollingDecorator(filterPipeline, new ScrollBar(true),
				new ScrollBar(false), 8, EDimension.RECORD);
		scrollingDecorator.setMinSizeProvider(filterPipeline);
		scrollingDecorator.setSize(Float.NaN, MIN_FILTER_PIPELINE_HEIGHT);
		add(scrollingDecorator);


		scrollingDecorator = new ScrollingDecorator(history, new ScrollBar(true), new ScrollBar(false), 8,
				EDimension.RECORD);
		scrollingDecorator.setMinSizeProvider(history);
		scrollingDecorator.setSize(Float.NaN, MIN_HISTORY_HEIGHT);
		add(scrollingDecorator);

	}

	/**
	 * @return the filterPipeline, see {@link #filterPipeline}
	 */
	public FilterPipeline getFilterPipeline() {
		return filterPipeline;
	}

	// public void addEntityColumn(AEntityColumn column) {
	// if (columns.size() > 0) {
	// GLElement columnSpacer = new GLElement(GLRenderers.fillRect(Color.LIGHT_GRAY));
	// columnSpacer.setSize(2, Float.NaN);
	// columnContainer.add(columnSpacer);
	// }
	// // columnContainer.add(column);
	// columns.add(column);
	// registerEntityCollection(column);
	// }

	public void addColumn(ColumnTree column) {
		if (cols.size() > 0) {
			ColumnSeparator columnSeparator = new ColumnSeparator();
			columnContainer.add(columnContainer.size() - 1, columnSeparator);
		}
		columnContainer.add(columnContainer.size() - 1, column);
		cols.add(column);
		// registerEntityCollection(column);
	}

	public void addColumn(int index, ColumnTree column) {
		if (cols.isEmpty())
			addColumn(column);
		if (index > columnContainer.size())
			index = columnContainer.size() - 1;
		if (index < 0)
			index = 0;

		GLElement element = columnContainer.get(index);
		if (element instanceof ColumnSeparator) {

			columnContainer.add(index, column);
			cols.add(column);
			ColumnSeparator columnSeparator = new ColumnSeparator();
			columnContainer.add(index, columnSeparator);
		} else {

			ColumnSeparator columnSeparator = new ColumnSeparator();
			columnContainer.add(index, columnSeparator);
			columnContainer.add(index, column);
			cols.add(column);
		}
	}

	public void removeColumn(ColumnTree column) {
		int index = columnContainer.indexOf(column);
		if (index == -1)
			return;

		if (index == 0) {
{'fraction_non_alphanumeric': 0.12645424552873763, 'fraction_numerical': 0.007206112172252127, 'mean_word_length': 5.013312451057165, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 32, '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': '6038846', 'n_tokens_mistral': 8184, 'n_tokens_neox': 7309, 'n_words': 1297}