/// <summary>
        ///     Builds a <see cref="DetachedCriteria" /> from the given <see cref="IMorphableFlowQuery" /> query.
        /// </summary>
        /// <param name="query">
        ///     The query from which to build a <see cref="ICriteria" />.
        /// </param>
        /// <typeparam name="TSource">
        ///     The <see cref="System.Type" /> of the underlying entity for the given query.
        /// </typeparam>
        /// <returns>
        ///     The built <see cref="DetachedCriteria" />.
        /// </returns>
        public virtual DetachedCriteria Build <TSource>(IMorphableFlowQuery query)
        {
            if (query == null)
            {
                throw new ArgumentNullException("query");
            }

            DetachedCriteria criteria = DetachedCriteria.For <TSource>(query.Alias);

            foreach (Join join in query.Joins)
            {
                criteria.CreateAlias(join.Property, join.Alias, join.JoinType, join.WithClause);
            }

            foreach (ICriterion criterion in query.Criterions)
            {
                criteria.Add(criterion);
            }

            foreach (Lock queryLock in query.Locks)
            {
                if (queryLock.Alias == null)
                {
                    criteria.SetLockMode(queryLock.LockMode);
                }
                else
                {
                    criteria.SetLockMode(queryLock.Alias, queryLock.LockMode);
                }
            }

            bool skips = query.ResultsToSkip.HasValue && query.ResultsToSkip.Value > 0;

            if (skips)
            {
                criteria.SetFirstResult(query.ResultsToSkip.Value);
            }

            bool takes = query.ResultsToTake.HasValue && query.ResultsToTake.Value > 0;

            if (takes)
            {
                criteria.SetMaxResults(query.ResultsToTake.Value);
            }

            if (query.IsCacheable)
            {
                criteria.SetCacheable(true);

                if (query.CacheMode.HasValue)
                {
                    criteria.SetCacheMode(query.CacheMode.Value);
                }

                if (!string.IsNullOrEmpty(query.CacheRegion))
                {
                    criteria.SetCacheRegion(query.CacheRegion);
                }
            }

            criteria
            .SetProjection
            (
                GetProjection(query)
            );

            if (query.ResultTransformer != null)
            {
                criteria.SetResultTransformer(query.ResultTransformer);
            }

            if (query.Orders.Count > 0 && (skips || takes))
            {
                foreach (OrderByStatement statement in query.Orders)
                {
                    if (statement.IsBasedOnSource)
                    {
                        criteria.AddOrder(statement.Order);
                    }
                }
            }

            return(criteria);
        }