Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Metadata / Edm / RowType.cs
1 //---------------------------------------------------------------------
2 // <copyright file="RowType.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9 using System;
10 using System.Collections.Generic;
11 using System.Globalization;
12 using System.Data.Common;
13 using System.Text;
14 using System.Data.Objects.ELinq;
15 using System.Threading;
16 using System.Diagnostics;
17
18 namespace System.Data.Metadata.Edm
19 {
20     /// <summary>
21     /// Represents the Edm Row Type
22     /// </summary>
23     public sealed class RowType : StructuralType
24     {
25         private ReadOnlyMetadataCollection<EdmProperty> _properties;
26         private readonly InitializerMetadata _initializerMetadata;
27     
28         #region Constructors
29         /// <summary>
30         /// Initializes a new instance of RowType class with the given list of members
31         /// </summary>
32         /// <param name="properties">properties for this row type</param>
33         /// <exception cref="System.ArgumentException">Thrown if any individual property in the passed in properties argument is null</exception>
34         internal RowType(IEnumerable<EdmProperty> properties)
35             : this(properties, null)
36         {
37         }
38
39         /// <summary>
40         /// Initializes a RowType with the given members and initializer metadata 
41         /// </summary>
42         internal RowType(IEnumerable<EdmProperty> properties, InitializerMetadata initializerMetadata)
43             : base(GetRowTypeIdentityFromProperties(CheckProperties(properties), initializerMetadata), EdmConstants.TransientNamespace, (DataSpace)(-1))
44         {
45             // Initialize the properties. 
46             if (null != properties)
47             {
48                 foreach (EdmProperty property in properties)
49                 {
50                     this.AddProperty(property);
51                 }
52             }
53
54             _initializerMetadata = initializerMetadata;
55
56             // Row types are immutable, so now that we're done initializing, set it
57             // to be read-only.
58             SetReadOnly();
59         }
60
61
62         #endregion
63
64         #region Properties
65         /// <summary>
66         /// Gets or sets LINQ initializer Metadata for this row type. If there is no associated
67         /// initializer type, value is null.
68         /// </summary>
69         internal InitializerMetadata InitializerMetadata
70         {
71             get { return _initializerMetadata; }
72         }
73
74         /// <summary>
75         /// Returns the kind of the type
76         /// </summary>
77         public override BuiltInTypeKind BuiltInTypeKind { get { return BuiltInTypeKind.RowType; } }
78
79         /// <summary>
80         /// Returns the list of properties for this row type
81         /// </summary>
82         /// <summary>
83         /// Returns just the properties from the collection
84         /// of members on this type
85         /// </summary>
86         public ReadOnlyMetadataCollection<EdmProperty> Properties
87         {
88             get
89             {
90                 Debug.Assert(IsReadOnly, "this is a wrapper around this.Members, don't call it during metadata loading, only call it after the metadata is set to readonly");
91                 if (null == _properties)
92                 {
93                     Interlocked.CompareExchange(ref _properties,
94                         new FilteredReadOnlyMetadataCollection<EdmProperty, EdmMember>(
95                             this.Members, Helper.IsEdmProperty), null);
96                 }
97                 return _properties;
98             }
99         }
100
101
102         /// <summary>
103         /// Adds a property
104         /// </summary>
105         /// <param name="property">The property to add</param>
106         private void AddProperty(EdmProperty property)
107         {
108             EntityUtil.GenericCheckArgumentNull(property, "property");
109             AddMember(property);
110         }
111
112         /// <summary>
113         /// Validates a EdmMember object to determine if it can be added to this type's 
114         /// Members collection. If this method returns without throwing, it is assumed
115         /// the member is valid. 
116         /// </summary>
117         /// <param name="member">The member to validate</param>
118         /// <exception cref="System.ArgumentException">Thrown if the member is not a EdmProperty</exception>
119         internal override void ValidateMemberForAdd(EdmMember member)
120         {
121             Debug.Assert(Helper.IsEdmProperty(member), "Only members of type Property may be added to Row types.");
122         }
123
124         /// <summary>
125         /// Calculates the row type identity that would result from 
126         /// a given set of properties.
127         /// </summary>
128         /// <param name="properties">The properties that determine the row type's structure</param>
129         /// <param name="initializerMetadata">Metadata describing materialization of this row type</param>
130         /// <returns>A string that identifies the row type</returns>
131         private static string GetRowTypeIdentityFromProperties(IEnumerable<EdmProperty> properties, InitializerMetadata initializerMetadata)
132         {
133             // The row type identity is formed as follows:
134             // "rowtype[" + a comma-separated list of property identities + "]"
135             StringBuilder identity = new StringBuilder("rowtype[");
136
137             if (null != properties)
138             {
139                 int i = 0;
140                 // For each property, append the type name and facets.
141                 foreach (EdmProperty property in properties)
142                 {
143                     if (i > 0)
144                     {
145                         identity.Append(",");
146                     }
147                     identity.Append("(");
148                     identity.Append(property.Name);
149                     identity.Append(",");
150                     property.TypeUsage.BuildIdentity(identity);
151                     identity.Append(")");
152                     i++;
153                 }
154             }
155             identity.Append("]");
156
157             if (null != initializerMetadata)
158             {
159                 identity.Append(",").Append(initializerMetadata.Identity);
160             }
161
162             return identity.ToString();
163         }
164
165
166         private static IEnumerable<EdmProperty> CheckProperties(IEnumerable<EdmProperty> properties)
167         {
168             if (null != properties)
169             {
170                 int i = 0;
171                 foreach (EdmProperty prop in properties)
172                 {
173                     if (prop == null)
174                     {
175                         throw EntityUtil.CollectionParameterElementIsNull("properties");
176                     }
177                     i++;
178                 }
179
180                 /*
181                 if (i < 1)
182                 {
183                     throw EntityUtil.ArgumentOutOfRange("properties");
184                 }
185                  */
186
187             }
188             return properties;
189         }
190         #endregion
191
192         #region Methods
193         /// <summary>
194         /// EdmEquals override verifying the equivalence of all members and their type usages.
195         /// </summary>
196         /// <param name="item"></param>
197         /// <returns></returns>
198         internal override bool EdmEquals(MetadataItem item)
199         {
200             // short-circuit if this and other are reference equivalent
201             if (Object.ReferenceEquals(this, item)) { return true; }
202
203             // check type of item
204             if (null == item || BuiltInTypeKind.RowType != item.BuiltInTypeKind) { return false; }
205             RowType other = (RowType)item;
206
207             // check each row type has the same number of members
208             if (this.Members.Count != other.Members.Count) { return false; }
209
210             // verify all members are equivalent
211             for (int ordinal = 0; ordinal < this.Members.Count; ordinal++)
212             {
213                 EdmMember thisMember = this.Members[ordinal];
214                 EdmMember otherMember = other.Members[ordinal];
215
216                 // if members are different, return false
217                 if (!thisMember.EdmEquals(otherMember) ||
218                     !thisMember.TypeUsage.EdmEquals(otherMember.TypeUsage))
219                 {
220                     return false;
221                 }
222             }
223
224             return true;
225         }
226         #endregion
227     }
228 }