File: System\Data\Services\ExpandSegment.cs
Project: ndp\fx\src\DataWeb\Server\System.Data.Services.csproj (System.Data.Services)
//---------------------------------------------------------------------
// <copyright file="ExpandSegment.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <summary>
//      Provides a description of a segment in an $expand query option
//      for a WCF Data Service.
// </summary>
//
// @owner  Microsoft
//---------------------------------------------------------------------
 
namespace System.Data.Services
{
    #region Namespaces.
 
    using System;
    using System.Collections.Generic;
    using System.Data.Services.Providers;
    using System.Diagnostics;
    using System.Linq.Expressions;
 
    #endregion Namespaces.
 
    /// <summary>
    /// Provides a description of a segment in an $expand query option for a WCF Data Service.
    /// </summary>
    /// <remarks>
    /// INTERNAL
    /// Expand providers may replace segments to indicate a different expansion shape. However, they are
    /// unable to set the MaxResultsExpected. The value for the instances created by external providers
    /// will always be Int32.MaxValue, but the value enforced by the serializers will be the one declared
    /// by the data service in the configuration.
    /// 
    /// When the configuration supports a more granular value, we should overload the constructor to make
    /// the MaxResultsExpected property settable as well. 
    /// </remarks>
    [DebuggerDisplay("ExpandSegment ({name},Filter={filter})]")]
    public class ExpandSegment
    {
        #region Private fields.
 
        /// <summary>Container to which the segment belongs; possibly null.</summary>
        private readonly ResourceSetWrapper container;
 
        /// <summary>Filter expression for this segment on an $expand path.</summary>
        private readonly Expression filter;
 
        /// <summary>Name for this segment on an $expand path.</summary>
        private readonly string name;
 
        /// <summary>Property being expanded.</summary>
        private readonly ResourceProperty expandedProperty;
 
        /// <summary>
        /// The maximum number of results expected for this property; Int32.MaxValue if no limit is expected.
        /// </summary>
        private readonly int maxResultsExpected;
 
        /// <summary>Collection of ordering information for this segment, used for paging</summary>
        private readonly OrderingInfo orderingInfo;
 
        #endregion Private fields.
 
        #region Constructors.
 
        /// <summary>Initializes a new <see cref="ExpandSegment"/> instance.</summary>
        /// <param name="name">Segment name.</param>
        /// <param name="filter">Filter expression for segment, possibly null.</param>
        public ExpandSegment(string name, Expression filter) 
            : this(name, filter, Int32.MaxValue, null, null, null)
        {
        }
 
        /// <summary>Initializes a new <see cref="ExpandSegment"/> instance.</summary>
        /// <param name="name">Segment name.</param>
        /// <param name="filter">Filter expression for segment, possibly null.</param>
        /// <param name="maxResultsExpected">
        /// Expand providers may choose to return at most MaxResultsExpected + 1 elements to allow the
        /// data service to detect a failure to meet this constraint.
        /// </param>
        /// <param name="container">Container to which the segment belongs; possibly null.</param>
        /// <param name="expandedProperty">Property expanded by this expand segment</param>
        /// <param name="orderingInfo">Collection of ordering information for this segment, used for paging</param>
        internal ExpandSegment(
            string name, 
            Expression filter, 
            int maxResultsExpected, 
            ResourceSetWrapper container, 
            ResourceProperty expandedProperty, 
            OrderingInfo orderingInfo)
        {
            WebUtil.CheckArgumentNull(name, "name");
            CheckFilterType(filter);
            this.name = name;
            this.filter = filter;
            this.container = container;
            this.maxResultsExpected = maxResultsExpected;
            this.expandedProperty = expandedProperty;
            this.orderingInfo = orderingInfo;
        }
 
        #endregion Constructors.
 
        #region Public properties.
 
        /// <summary>Filter expression for this segment on an $expand path.</summary>
        public Expression Filter
        {
            get { return this.filter; }
        }
 
        /// <summary>Whether this segment has a filter.</summary>
        public bool HasFilter
        {
            get { return this.Filter != null; }
        }
 
        /// <summary>
        /// The maximum number of results expected for this property; Int32.MaxValue if no limit is expected.
        /// </summary>
        /// <remarks>
        /// Expand providers may choose to return at most MaxResultsExpected + 1 elements to allow the
        /// data service to detect a failure to meet this constraint.
        /// </remarks>
        public int MaxResultsExpected
        {
            get { return this.maxResultsExpected; }
        }
 
        /// <summary>Name for this segment on an $expand path.</summary>
        public string Name
        {
            get { return this.name; }
        }
 
        /// <summary>Property being expanded.</summary>
        public ResourceProperty ExpandedProperty
        {
            get { return this.expandedProperty; }
        }
 
        /// <summary>Collection of ordering information for this segment, used for paging</summary>
        internal OrderingInfo OrderingInfo
        {
            get
            {
                return this.orderingInfo;
            }
        }
 
        #endregion Public properties.
 
        #region Internal properties.
 
        /// <summary>Gets the container to which this segment belongs; possibly null.</summary>
        internal ResourceSetWrapper Container
        {
            get { return this.container; }
        }
 
        #endregion Internal properties.
 
        #region Public methods.
 
        /// <summary>Checks whether any segments in the specified <paramref name="path"/> have a filter.</summary>
        /// <param name="path">Path with segments to check.</param>
        /// <returns>true if any of the segments in <paramref name="path"/> has a filter; false otherwise.</returns>
        public static bool PathHasFilter(IEnumerable<ExpandSegment> path)
        {
            WebUtil.CheckArgumentNull(path, "path");
            foreach (ExpandSegment segment in path)
            {
                if (segment.HasFilter)
                {
                    return true;
                }
            }
 
            return false;
        }
 
        #endregion Public methods.
 
        #region Private methods.
 
        /// <summary>Checks that the specified filter is of the right type.</summary>
        /// <param name="filter">Filter to check.</param>
        private static void CheckFilterType(Expression filter)
        {
            if (filter == null)
            {
                return;
            }
 
            if (filter.NodeType != ExpressionType.Lambda)
            {
                throw new ArgumentException(Strings.ExpandSegment_FilterShouldBeLambda(filter.NodeType), "filter");
            }
 
            LambdaExpression lambda = (LambdaExpression)filter;
            if (lambda.Body.Type != typeof(bool) && lambda.Body.Type != typeof(bool?))
            {
                throw new ArgumentException(
                    Strings.ExpandSegment_FilterBodyShouldReturnBool(lambda.Body.Type), "filter");
            }
 
            if (lambda.Parameters.Count != 1)
            {
                throw new ArgumentException(
                    Strings.ExpandSegment_FilterBodyShouldTakeOneParameter(lambda.Parameters.Count), "filter");
            }
        }
 
        #endregion Private methods.
    }
}