File: System\Data\Services\Epm\EpmSourceTree.cs
Project: ndp\fx\src\DataWeb\Server\System.Data.Services.csproj (System.Data.Services)
//---------------------------------------------------------------------
// <copyright file="EpmSourceTree.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <summary>
// Tree for managing SourceNames on EntityPropertyMappingAttributes
// for a ResourceType.
// </summary>
//
// @owner  Microsoft
//---------------------------------------------------------------------
 
namespace System.Data.Services.Common
{
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
#if ASTORIA_CLIENT
    using System.Data.Services.Client;
#else
    using System.Data.Services;
#endif
 
    /// <summary>
    /// Tree representing the sourceName properties in all the EntityPropertyMappingAttributes for a resource type
    /// </summary>
    internal sealed class EpmSourceTree
    {
        #region Fields
 
        /// <summary>Root of the tree</summary>
        private readonly EpmSourcePathSegment root;
        
        /// <summary><see cref="EpmTargetTree"/> corresponding to this tree</summary>
        private readonly EpmTargetTree epmTargetTree;
 
        #endregion
 
        /// <summary>Default constructor creates a null root</summary>
        /// <param name="epmTargetTree">Target xml tree</param>
        internal EpmSourceTree(EpmTargetTree epmTargetTree)
        {
            this.root = new EpmSourcePathSegment("");
            this.epmTargetTree = epmTargetTree;
        }
 
        #region Properties
 
        /// <summary>
        /// Root of the tree
        /// </summary>
        internal EpmSourcePathSegment Root
        {
            get
            {
                return this.root;
            }
        }
 
        #endregion
 
        /// <summary>
        /// Adds a path to the source and target tree which is obtained by looking at the EntityPropertyMappingAttribute in the <paramref name="epmInfo"/>
        /// </summary>
        /// <param name="epmInfo">EnitityPropertyMappingInfo holding the source path</param>
        internal void Add(EntityPropertyMappingInfo epmInfo)
        {
            String sourceName = epmInfo.Attribute.SourcePath;
            EpmSourcePathSegment currentProperty = this.Root;
            IList<EpmSourcePathSegment> activeSubProperties = currentProperty.SubProperties;
            EpmSourcePathSegment foundProperty = null;
 
            Debug.Assert(!String.IsNullOrEmpty(sourceName), "Must have been validated during EntityPropertyMappingAttribute construction");
            foreach (String propertyName in sourceName.Split('/'))
            {
                if (propertyName.Length == 0)
                {
                    throw new InvalidOperationException(Strings.EpmSourceTree_InvalidSourcePath(epmInfo.DefiningType.Name, sourceName));
                }
 
                foundProperty = activeSubProperties.SingleOrDefault(e => e.PropertyName == propertyName);
                if (foundProperty != null)
                {
                    currentProperty = foundProperty;
                }
                else
                {
                    currentProperty = new EpmSourcePathSegment(propertyName);
                    activeSubProperties.Add(currentProperty);
                }
 
                activeSubProperties = currentProperty.SubProperties;
            }
 
            // Two EpmAttributes with same PropertyName in the same ResourceType, this could be a result of inheritance
            if (foundProperty != null)
            {
                Debug.Assert(Object.ReferenceEquals(foundProperty, currentProperty), "currentProperty variable should have been updated already to foundProperty");
 
                // Check for duplicates on the same entity type
#if !ASTORIA_CLIENT
                Debug.Assert(foundProperty.SubProperties.Count == 0, "If non-leaf, it means we allowed complex type to be a leaf node");
                if (foundProperty.EpmInfo.DefiningType == epmInfo.DefiningType)
                {
                    throw new InvalidOperationException(Strings.EpmSourceTree_DuplicateEpmAttrsWithSameSourceName(epmInfo.Attribute.SourcePath, epmInfo.DefiningType.Name));
                }
#else                
                if (foundProperty.EpmInfo.DefiningType.Name == epmInfo.DefiningType.Name)
                {
                    throw new InvalidOperationException(Strings.EpmSourceTree_DuplicateEpmAttrsWithSameSourceName(epmInfo.Attribute.SourcePath, epmInfo.DefiningType.Name));
                }
#endif
 
                // In case of inheritance, we need to remove the node from target tree which was mapped to base type property
                this.epmTargetTree.Remove(foundProperty.EpmInfo);
            }
 
            currentProperty.EpmInfo = epmInfo;
            this.epmTargetTree.Add(epmInfo);
        }
    }
}