<commit_before>using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using NHibernate.Criterion;
using NHibernate.Engine;
using NHibernate.SqlCommand;
using NHibernate.Transform;
using NHibernate.Util;

namespace NHibernate.Impl
{
	/// <summary>
	/// Implementation of the <see cref="ICriteria"/> interface
	/// </summary>
	[Serializable]
	public class CriteriaImpl : ICriteria
	{
		private readonly System.Type persistentClass;
		private readonly List<CriterionEntry> criteria = new List<CriterionEntry>();
		private readonly List<OrderEntry> orderEntries = new List<OrderEntry>(10);
		private readonly Dictionary<string, FetchMode> fetchModes = new Dictionary<string, FetchMode>();
		private readonly Dictionary<string, LockMode> lockModes = new Dictionary<string, LockMode>();
		private int maxResults = RowSelection.NoValue;
		private int firstResult;
		private int timeout = RowSelection.NoValue;
		private int fetchSize = RowSelection.NoValue;
		private ISessionImplementor session;
		private IResultTransformer resultTransformer = CriteriaSpecification.RootEntity;
		private bool cacheable;
		private string cacheRegion;
		private CacheMode? cacheMode;
		private CacheMode? sessionCacheMode;
		private string comment;
		private FlushMode? flushMode;
		private FlushMode? sessionFlushMode;

		private readonly List<Subcriteria> subcriteriaList = new List<Subcriteria>();
		private readonly string rootAlias;

		private readonly Dictionary<string, ICriteria> subcriteriaByPath = new Dictionary<string, ICriteria>();
		private readonly Dictionary<string, ICriteria> subcriteriaByAlias = new Dictionary<string, ICriteria>();
		private readonly string entityOrClassName;

		// Projection Fields
		private IProjection projection;
		private ICriteria projectionCriteria;

		public CriteriaImpl(System.Type persistentClass, ISessionImplementor session)
			: this(persistentClass.FullName, CriteriaSpecification.RootAlias, session) 
		{
			this.persistentClass = persistentClass;
		}

		public CriteriaImpl(System.Type persistentClass, string alias, ISessionImplementor session)
			: this(persistentClass.FullName, alias, session)
		{
			this.persistentClass = persistentClass;
		}

		public CriteriaImpl(string entityOrClassName, ISessionImplementor session)
			: this(entityOrClassName, CriteriaSpecification.RootAlias, session) {}

		public CriteriaImpl(string entityOrClassName, string alias, ISessionImplementor session)
		{
			this.session = session;
			this.entityOrClassName = entityOrClassName;
			cacheable = false;
			rootAlias = alias;
			subcriteriaByAlias[alias] = this;
		}

		public ISessionImplementor Session
		{
			get { return session; }
			set { session = value; }
		}

		public string EntityOrClassName
		{
			get { return entityOrClassName; }
		}

		public IDictionary<string, LockMode> LockModes
		{
			get { return lockModes; }
		}

		public ICriteria ProjectionCriteria
		{
			get { return projectionCriteria; }
		}

		public bool LookupByNaturalKey
		{
			get
			{
				if (projection != null)
				{
					return false;
				}
				if (subcriteriaList.Count > 0)
				{
					return false;
				}
				if (criteria.Count != 1)
				{
					return false;
				}

				CriterionEntry ce = criteria[0];
				return ce.Criterion is NaturalIdentifier;
			}
		}

		public string Alias
		{
			get { return rootAlias; }
		}

		public IProjection Projection
		{
			get { return projection; }
		}

		public FetchMode GetFetchMode(string path)
		{
			FetchMode result;
			if (!fetchModes.TryGetValue(path, out result))
			{
				result = FetchMode.Default;
			}
			return result;
		}

		public IResultTransformer ResultTransformer
		{
			get { return resultTransformer; }
		}

		public int MaxResults
		{
			get { return maxResults; }
		}

		public int FirstResult
		{
			get { return firstResult; }
		}

		public int FetchSize
		{
			get { return fetchSize; }
		}

		public int Timeout
		{
			get { return timeout; }
		}

		public bool Cacheable
		{
			get { return cacheable; }
		}

		public string CacheRegion
		{
			get { return cacheRegion; }
		}

		public string Comment
		{
			get { return comment; }
		}

		protected internal void Before()
		{
			if (flushMode.HasValue)
			{
				sessionFlushMode = Session.FlushMode;
				Session.FlushMode = flushMode.Value;
			}
			if (cacheMode.HasValue)
			{
				sessionCacheMode = Session.CacheMode;
				Session.CacheMode = cacheMode.Value;
			}
		}

		protected internal void After()
		{
			if (sessionFlushMode.HasValue)
			{
				Session.FlushMode = sessionFlushMode.Value;
				sessionFlushMode = null;
			}
			if (sessionCacheMode.HasValue)
			{
				Session.CacheMode = sessionCacheMode.Value;
				sessionCacheMode = null;
			}
		}

		public ICriteria SetMaxResults(int maxResults)
		{
			this.maxResults = maxResults;
			return this;
		}

		public ICriteria SetFirstResult(int firstResult)
		{
			this.firstResult = firstResult;
			return this;
		}

		public ICriteria SetTimeout(int timeout)
		{
			this.timeout = timeout;
			return this;
		}

		public ICriteria SetFetchSize(int fetchSize)
		{
			this.fetchSize = fetchSize;
			return this;
		}

		public ICriteria Add(ICriterion expression)
		{
			Add(this, expression);
			return this;
		}

		public IList List()
		{
			ArrayList results = new ArrayList();
			List(results);
			return results;
		}

		public void List(IList results)
		{
			Before();
			try
			{
				session.List(this, results);
			}
			finally
			{
				After();
			}
		}

		public IList<T> List<T>()
		{
			List<T> results = new List<T>();
			List(results);
			return results;
		}

		public T UniqueResult<T>()
		{
			object result = UniqueResult();
			if (result == null && typeof (T).IsValueType)
			{
				return default(T);
			}
			else
			{
				return (T) result;
			}
		}

		public void ClearOrders()
		{
			orderEntries.Clear();
		}

		public IEnumerable<CriterionEntry> IterateExpressionEntries()
		{
			return criteria;
		}

		public IEnumerable<OrderEntry> IterateOrderings()
		{
			return orderEntries;
		}

		public IEnumerable<Subcriteria> IterateSubcriteria()
		{
			return subcriteriaList;
		}

		public override string ToString()
		{
			bool first = true;
			StringBuilder builder = new StringBuilder();
			foreach (CriterionEntry criterionEntry in criteria)
			{
				if (!first)
				{
					builder.Append(" and ");
				}
				builder.Append(criterionEntry.ToString());
				first = false;
			}
			if (orderEntries.Count != 0)
			{
				builder.Append(Environment.NewLine);
			}
			first = true;
			foreach (OrderEntry orderEntry in orderEntries)
			{
				if (!first)
				{
					builder.Append(" and ");
				}
				builder.Append(orderEntry.ToString());
				first = false;
			}
			return builder.ToString();
		}

		public ICriteria AddOrder(Order ordering)
		{
			orderEntries.Add(new OrderEntry(ordering, this));
			return this;
		}

		public ICriteria SetFetchMode(string associationPath, FetchMode mode)
		{
			fetchModes[associationPath] = mode;
			return this;
		}

		public ICriteria CreateAlias(string associationPath, string alias)
		{
			CreateAlias(associationPath, alias, JoinType.InnerJoin);
			return this;
		}

		public ICriteria CreateAlias(string associationPath, string alias, JoinType joinType)
		{
			new Subcriteria(this, this, associationPath, alias, joinType);
			return this;
		}

		public ICriteria Add(ICriteria criteriaInst, ICriterion expression)
		{
			criteria.Add(new CriterionEntry(expression, criteriaInst));
			return this;
		}

		public ICriteria CreateCriteria(string associationPath)
		{
			return CreateCriteria(associationPath, JoinType.InnerJoin);
		}

		public ICriteria CreateCriteria(string associationPath, JoinType joinType)
		{
			return new Subcriteria(this, this, associationPath, joinType);
		}

		public ICriteria CreateCriteria(string associationPath, string alias)
		{
			return CreateCriteria(associationPath, alias, JoinType.InnerJoin);
		}

		public ICriteria CreateCriteria(string associationPath, string alias, JoinType joinType)
		{
			return new Subcriteria(this, this, associationPath, alias, joinType);
		}

		public object UniqueResult()
		{
			return AbstractQueryImpl.UniqueElement(List());
		}

		public ICriteria SetLockMode(LockMode lockMode)
		{
			return SetLockMode(CriteriaSpecification.RootAlias, lockMode);
		}

		public ICriteria SetLockMode(string alias, LockMode lockMode)
		{
			lockModes[alias] = lockMode;
			return this;
		}

		public ICriteria SetResultTransformer(IResultTransformer tupleMapper)
		{
			resultTransformer = tupleMapper;
			return this;
		}

		public ICriteria SetCacheable(bool cacheable)
		{
			this.cacheable = cacheable;
			return this;
		}

		public ICriteria SetCacheRegion(string cacheRegion)
		{
			this.cacheRegion = cacheRegion.Trim();
			return this;
		}

		public ICriteria SetComment(string comment)
		{
			this.comment = comment;
			return this;
		}

		public ICriteria SetFlushMode(FlushMode flushMode)
		{
			this.flushMode = flushMode;
			return this;
		}

		public ICriteria SetProjection(IProjection projection)
		{
			this.projection = projection;
			projectionCriteria = this;
			SetResultTransformer(CriteriaSpecification.Projection);
			return this;
		}

		/// <summary> Override the cache mode for this particular query. </summary>
		/// <param name="cacheMode">The cache mode to use. </param>
		/// <returns> this (for method chaining) </returns>
		public ICriteria SetCacheMode(CacheMode cacheMode)
		{
			this.cacheMode = cacheMode;
			return this;
		}

		public object Clone()
		{
			CriteriaImpl clone = new CriteriaImpl(entityOrClassName, Alias, Session);
			CloneSubcriteria(clone);
			foreach (KeyValuePair<string, FetchMode> de in fetchModes)
			{
				clone.fetchModes.Add(de.Key, de.Value);
			}
			foreach (KeyValuePair<string, LockMode> de in lockModes)
			{
				clone.lockModes.Add(de.Key, de.Value);
			}
			clone.maxResults = maxResults;
			clone.firstResult = firstResult;
			clone.timeout = timeout;
			clone.fetchSize = fetchSize;
			clone.cacheable = cacheable;
			clone.cacheRegion = cacheRegion;
			clone.SetProjection(projection);
			CloneProjectCrtieria(clone);
			clone.SetResultTransformer(resultTransformer);
			clone.comment = comment;
			if (flushMode.HasValue)
			{
				clone.SetFlushMode(flushMode.Value);
			}
			if (cacheMode.HasValue)
			{
				clone.SetCacheMode(cacheMode.Value);
			}
			return clone;
		}

		private void CloneProjectCrtieria(CriteriaImpl clone)
		{
			if (projectionCriteria != null)
			{
				if (projectionCriteria == this)
				{
					clone.projectionCriteria = clone;
				}
				else
				{
					ICriteria clonedProjectionCriteria = (ICriteria) projectionCriteria.Clone();
					clone.projectionCriteria = clonedProjectionCriteria;
				}
			}
		}

		private void CloneSubcriteria(CriteriaImpl clone)
		{
			//we need to preserve the parent criteria, we rely on the orderring when creating the 
			//subcriterias initially here, so we don't need to make more than a single pass
			Dictionary<ICriteria, ICriteria> newParents = new Dictionary<ICriteria, ICriteria>();
			newParents[this] = clone;

			foreach (Subcriteria subcriteria in IterateSubcriteria())
			{
				ICriteria currentParent;
				if (!newParents.TryGetValue(subcriteria.Parent, out currentParent))
				{
					throw new AssertionFailure(
						"Could not find parent for subcriteria in the previous subcriteria. If you see this error, it is a bug");
				}
				Subcriteria clonedSubCriteria =
					new Subcriteria(clone, currentParent, subcriteria.Path, subcriteria.Alias, subcriteria.JoinType);
				clonedSubCriteria.SetLockMode(subcriteria.LockMode);
				newParents[subcriteria] = clonedSubCriteria;
			}

			// remap the orders
			foreach (OrderEntry orderEntry in IterateOrderings())
			{
				ICriteria currentParent = newParents[orderEntry.Criteria];
				if (currentParent == null)
				{
					throw new AssertionFailure(
						"Could not find parent for order in the previous criteria. If you see this error, it is a bug");
				}
				currentParent.AddOrder(orderEntry.Order);
			}

			// remap the restrictions to appropriate criterias
			foreach (CriterionEntry criterionEntry in criteria)
			{
				ICriteria currentParent;
				if (!newParents.TryGetValue(criterionEntry.Criteria, out currentParent))
				{
					throw new AssertionFailure(
						"Could not find parent for restriction in the previous criteria. If you see this error, it is a bug.");
				}

				currentParent.Add(criterionEntry.Criterion);
			}		
		}

		public ICriteria GetCriteriaByPath(string path)
		{
			ICriteria result;
			subcriteriaByPath.TryGetValue(path, out result);
			return result;
		}

		public ICriteria GetCriteriaByAlias(string alias)<commit_msg>Prevent unmanaged exception.<commit_after>using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using NHibernate.Criterion;
using NHibernate.Engine;
using NHibernate.SqlCommand;
using NHibernate.Transform;
using NHibernate.Util;

namespace NHibernate.Impl
{
	/// <summary>
	/// Implementation of the <see cref="ICriteria"/> interface
	/// </summary>
	[Serializable]
	public class CriteriaImpl : ICriteria
	{
		private readonly System.Type persistentClass;
		private readonly List<CriterionEntry> criteria = new List<CriterionEntry>();
		private readonly List<OrderEntry> orderEntries = new List<OrderEntry>(10);
		private readonly Dictionary<string, FetchMode> fetchModes = new Dictionary<string, FetchMode>();
		private readonly Dictionary<string, LockMode> lockModes = new Dictionary<string, LockMode>();
		private int maxResults = RowSelection.NoValue;
		private int firstResult;
		private int timeout = RowSelection.NoValue;
		private int fetchSize = RowSelection.NoValue;
		private ISessionImplementor session;
		private IResultTransformer resultTransformer = CriteriaSpecification.RootEntity;
		private bool cacheable;
		private string cacheRegion;
		private CacheMode? cacheMode;
		private CacheMode? sessionCacheMode;
		private string comment;
		private FlushMode? flushMode;
		private FlushMode? sessionFlushMode;

		private readonly List<Subcriteria> subcriteriaList = new List<Subcriteria>();
		private readonly string rootAlias;

		private readonly Dictionary<string, ICriteria> subcriteriaByPath = new Dictionary<string, ICriteria>();
		private readonly Dictionary<string, ICriteria> subcriteriaByAlias = new Dictionary<string, ICriteria>();
		private readonly string entityOrClassName;

		// Projection Fields
		private IProjection projection;
		private ICriteria projectionCriteria;

		public CriteriaImpl(System.Type persistentClass, ISessionImplementor session)
			: this(persistentClass.FullName, CriteriaSpecification.RootAlias, session) 
		{
			this.persistentClass = persistentClass;
		}

		public CriteriaImpl(System.Type persistentClass, string alias, ISessionImplementor session)
			: this(persistentClass.FullName, alias, session)
		{
			this.persistentClass = persistentClass;
		}

		public CriteriaImpl(string entityOrClassName, ISessionImplementor session)
			: this(entityOrClassName, CriteriaSpecification.RootAlias, session) {}

		public CriteriaImpl(string entityOrClassName, string alias, ISessionImplementor session)
		{
			this.session = session;
			this.entityOrClassName = entityOrClassName;
			cacheable = false;
			rootAlias = alias;
			subcriteriaByAlias[alias] = this;
		}

		public ISessionImplementor Session
		{
			get { return session; }
			set { session = value; }
		}

		public string EntityOrClassName
		{
			get { return entityOrClassName; }
		}

		public IDictionary<string, LockMode> LockModes
		{
			get { return lockModes; }
		}

		public ICriteria ProjectionCriteria
		{
			get { return projectionCriteria; }
		}

		public bool LookupByNaturalKey
		{
			get
			{
				if (projection != null)
				{
					return false;
				}
				if (subcriteriaList.Count > 0)
				{
					return false;
				}
				if (criteria.Count != 1)
				{
					return false;
				}

				CriterionEntry ce = criteria[0];
				return ce.Criterion is NaturalIdentifier;
			}
		}

		public string Alias
		{
			get { return rootAlias; }
		}

		public IProjection Projection
		{
			get { return projection; }
		}

		public FetchMode GetFetchMode(string path)
		{
			FetchMode result;
			if (!fetchModes.TryGetValue(path, out result))
			{
				result = FetchMode.Default;
			}
			return result;
		}

		public IResultTransformer ResultTransformer
		{
			get { return resultTransformer; }
		}

		public int MaxResults
		{
			get { return maxResults; }
		}

		public int FirstResult
		{
			get { return firstResult; }
		}

		public int FetchSize
		{
			get { return fetchSize; }
		}

		public int Timeout
		{
			get { return timeout; }
		}

		public bool Cacheable
		{
			get { return cacheable; }
		}

		public string CacheRegion
		{
			get { return cacheRegion; }
		}

		public string Comment
		{
			get { return comment; }
		}

		protected internal void Before()
		{
			if (flushMode.HasValue)
			{
				sessionFlushMode = Session.FlushMode;
				Session.FlushMode = flushMode.Value;
			}
			if (cacheMode.HasValue)
			{
				sessionCacheMode = Session.CacheMode;
				Session.CacheMode = cacheMode.Value;
			}
		}

		protected internal void After()
		{
			if (sessionFlushMode.HasValue)
			{
				Session.FlushMode = sessionFlushMode.Value;
				sessionFlushMode = null;
			}
			if (sessionCacheMode.HasValue)
			{
				Session.CacheMode = sessionCacheMode.Value;
				sessionCacheMode = null;
			}
		}

		public ICriteria SetMaxResults(int maxResults)
		{
			this.maxResults = maxResults;
			return this;
		}

		public ICriteria SetFirstResult(int firstResult)
		{
			this.firstResult = firstResult;
			return this;
		}

		public ICriteria SetTimeout(int timeout)
		{
			this.timeout = timeout;
			return this;
		}

		public ICriteria SetFetchSize(int fetchSize)
		{
			this.fetchSize = fetchSize;
			return this;
		}

		public ICriteria Add(ICriterion expression)
		{
			Add(this, expression);
			return this;
		}

		public IList List()
		{
			ArrayList results = new ArrayList();
			List(results);
			return results;
		}

		public void List(IList results)
		{
			Before();
			try
			{
				session.List(this, results);
			}
			finally
			{
				After();
			}
		}

		public IList<T> List<T>()
		{
			List<T> results = new List<T>();
			List(results);
			return results;
		}

		public T UniqueResult<T>()
		{
			object result = UniqueResult();
			if (result == null && typeof (T).IsValueType)
			{
				return default(T);
			}
			else
			{
				return (T) result;
			}
		}

		public void ClearOrders()
		{
			orderEntries.Clear();
		}

		public IEnumerable<CriterionEntry> IterateExpressionEntries()
		{
			return criteria;
		}

		public IEnumerable<OrderEntry> IterateOrderings()
		{
			return orderEntries;
		}

		public IEnumerable<Subcriteria> IterateSubcriteria()
		{
			return subcriteriaList;
		}

		public override string ToString()
		{
			bool first = true;
			StringBuilder builder = new StringBuilder();
			foreach (CriterionEntry criterionEntry in criteria)
			{
				if (!first)
				{
					builder.Append(" and ");
				}
				builder.Append(criterionEntry.ToString());
				first = false;
			}
			if (orderEntries.Count != 0)
			{
				builder.Append(Environment.NewLine);
			}
			first = true;
			foreach (OrderEntry orderEntry in orderEntries)
			{
				if (!first)
				{
					builder.Append(" and ");
				}
				builder.Append(orderEntry.ToString());
				first = false;
			}
			return builder.ToString();
		}

		public ICriteria AddOrder(Order ordering)
		{
			orderEntries.Add(new OrderEntry(ordering, this));
			return this;
		}

		public ICriteria SetFetchMode(string associationPath, FetchMode mode)
		{
			fetchModes[associationPath] = mode;
			return this;
		}

		public ICriteria CreateAlias(string associationPath, string alias)
		{
			CreateAlias(associationPath, alias, JoinType.InnerJoin);
			return this;
		}

		public ICriteria CreateAlias(string associationPath, string alias, JoinType joinType)
		{
			new Subcriteria(this, this, associationPath, alias, joinType);
			return this;
		}

		public ICriteria Add(ICriteria criteriaInst, ICriterion expression)
		{
			criteria.Add(new CriterionEntry(expression, criteriaInst));
			return this;
		}

		public ICriteria CreateCriteria(string associationPath)
		{
			return CreateCriteria(associationPath, JoinType.InnerJoin);
		}

		public ICriteria CreateCriteria(string associationPath, JoinType joinType)
		{
			return new Subcriteria(this, this, associationPath, joinType);
		}

		public ICriteria CreateCriteria(string associationPath, string alias)
		{
			return CreateCriteria(associationPath, alias, JoinType.InnerJoin);
		}

		public ICriteria CreateCriteria(string associationPath, string alias, JoinType joinType)
		{
			return new Subcriteria(this, this, associationPath, alias, joinType);
		}

		public object UniqueResult()
		{
			return AbstractQueryImpl.UniqueElement(List());
		}

		public ICriteria SetLockMode(LockMode lockMode)
		{
			return SetLockMode(CriteriaSpecification.RootAlias, lockMode);
		}

		public ICriteria SetLockMode(string alias, LockMode lockMode)
		{
			lockModes[alias] = lockMode;
			return this;
		}

		public ICriteria SetResultTransformer(IResultTransformer tupleMapper)
		{
			resultTransformer = tupleMapper;
			return this;
		}

		public ICriteria SetCacheable(bool cacheable)
		{
			this.cacheable = cacheable;
			return this;
		}

		public ICriteria SetCacheRegion(string cacheRegion)
		{
			this.cacheRegion = cacheRegion.Trim();
			return this;
		}

		public ICriteria SetComment(string comment)
		{
			this.comment = comment;
			return this;
		}

		public ICriteria SetFlushMode(FlushMode flushMode)
		{
			this.flushMode = flushMode;
			return this;
		}

		public ICriteria SetProjection(IProjection projection)
		{
			this.projection = projection;
			projectionCriteria = this;
			SetResultTransformer(CriteriaSpecification.Projection);
			return this;
		}

		/// <summary> Override the cache mode for this particular query. </summary>
		/// <param name="cacheMode">The cache mode to use. </param>
		/// <returns> this (for method chaining) </returns>
		public ICriteria SetCacheMode(CacheMode cacheMode)
		{
			this.cacheMode = cacheMode;
			return this;
		}

		public object Clone()
		{
			CriteriaImpl clone = new CriteriaImpl(entityOrClassName, Alias, Session);
			CloneSubcriteria(clone);
			foreach (KeyValuePair<string, FetchMode> de in fetchModes)
			{
				clone.fetchModes.Add(de.Key, de.Value);
			}
			foreach (KeyValuePair<string, LockMode> de in lockModes)
			{
				clone.lockModes.Add(de.Key, de.Value);
			}
			clone.maxResults = maxResults;
			clone.firstResult = firstResult;
			clone.timeout = timeout;
			clone.fetchSize = fetchSize;
			clone.cacheable = cacheable;
			clone.cacheRegion = cacheRegion;
			clone.SetProjection(projection);
			CloneProjectCrtieria(clone);
			clone.SetResultTransformer(resultTransformer);
			clone.comment = comment;
			if (flushMode.HasValue)
			{
				clone.SetFlushMode(flushMode.Value);
			}
			if (cacheMode.HasValue)
			{
				clone.SetCacheMode(cacheMode.Value);
			}
			return clone;
		}

		private void CloneProjectCrtieria(CriteriaImpl clone)
		{
			if (projectionCriteria != null)
			{
				if (projectionCriteria == this)
				{
					clone.projectionCriteria = clone;
				}
				else
				{
					ICriteria clonedProjectionCriteria = (ICriteria) projectionCriteria.Clone();
					clone.projectionCriteria = clonedProjectionCriteria;
				}
			}
		}

		private void CloneSubcriteria(CriteriaImpl clone)
		{
			//we need to preserve the parent criteria, we rely on the orderring when creating the 
			//subcriterias initially here, so we don't need to make more than a single pass
			Dictionary<ICriteria, ICriteria> newParents = new Dictionary<ICriteria, ICriteria>();
			newParents[this] = clone;

			foreach (Subcriteria subcriteria in IterateSubcriteria())
			{
				ICriteria currentParent;
				if (!newParents.TryGetValue(subcriteria.Parent, out currentParent))
				{
					throw new AssertionFailure(
						"Could not find parent for subcriteria in the previous subcriteria. If you see this error, it is a bug");
				}
				Subcriteria clonedSubCriteria =
					new Subcriteria(clone, currentParent, subcriteria.Path, subcriteria.Alias, subcriteria.JoinType);
				clonedSubCriteria.SetLockMode(subcriteria.LockMode);
				newParents[subcriteria] = clonedSubCriteria;
			}

			// remap the orders
			foreach (OrderEntry orderEntry in IterateOrderings())
			{
				ICriteria currentParent;
				if (!newParents.TryGetValue(orderEntry.Criteria, out currentParent))
				{
					throw new AssertionFailure(
						"Could not find parent for order in the previous criteria. If you see this error, it is a bug");
				}
				currentParent.AddOrder(orderEntry.Order);
			}

			// remap the restrictions to appropriate criterias
			foreach (CriterionEntry criterionEntry in criteria)
			{
				ICriteria currentParent;
				if (!newParents.TryGetValue(criterionEntry.Criteria, out currentParent))
				{
					throw new AssertionFailure(
						"Could not find parent for restriction in the previous criteria. If you see this error, it is a bug.");
				}

				currentParent.Add(criterionEntry.Criterion);
			}		
		}

		public ICriteria GetCriteriaByPath(string path)
		{
			ICriteria result;
			subcriteriaByPath.TryGetValue(path, out result);
			return result;
		}

		public ICriteria GetCriteriaByAlias(string alias)
{'fraction_non_alphanumeric': 0.08765300901326406, 'fraction_numerical': 0.0004723107804935648, 'mean_word_length': 3.6305813741570985, 'pattern_counts': {'":': 0, '<': 75, '<?xml version=': 0, '>': 77, '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': '4314718', 'n_tokens_mistral': 9282, 'n_tokens_neox': 7596, 'n_words': 1909}