1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
|
//---------------------------------------------------------------------
// <copyright file="ItemCollection.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
// @owner Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
namespace System.Data.Metadata.Edm
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Common.Utils;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
/// <summary>
/// Class for representing a collection of items.
/// Most of the implemetation for actual maintainance of the collection is
/// done by MetadataCollection
/// </summary>
[CLSCompliant(false)]
public abstract class ItemCollection : ReadOnlyMetadataCollection<GlobalItem>
{
#region Constructors
/// <summary>
/// The default constructor for ItemCollection
/// </summary>
internal ItemCollection(DataSpace dataspace)
: base(new MetadataCollection<GlobalItem>())
{
_space = dataspace;
}
#endregion
#region Fields
private readonly DataSpace _space;
private Dictionary<string, System.Collections.ObjectModel.ReadOnlyCollection<EdmFunction>> _functionLookUpTable;
private Memoizer<Type, ICollection> _itemsCache;
private int _itemCount;
#endregion
#region Properties
/// <summary>
/// Dataspace associated with ItemCollection
/// </summary>
public DataSpace DataSpace
{
get
{
return this._space;
}
}
/// <summary>
/// Return the function lookUpTable
/// </summary>
internal Dictionary<string, System.Collections.ObjectModel.ReadOnlyCollection<EdmFunction>> FunctionLookUpTable
{
get
{
if (_functionLookUpTable == null)
{
Dictionary<string, System.Collections.ObjectModel.ReadOnlyCollection<EdmFunction>> functionLookUpTable = PopulateFunctionLookUpTable(this);
Interlocked.CompareExchange(ref _functionLookUpTable, functionLookUpTable, null);
}
return _functionLookUpTable;
}
}
#endregion
#region Methods
/// <summary>
/// Adds an item to the collection
/// </summary>
/// <param name="item">The item to add to the list</param>
/// <exception cref="System.ArgumentNullException">Thrown if item argument is null</exception>
/// <exception cref="System.InvalidOperationException">Thrown if the item passed in or the collection itself instance is in ReadOnly state</exception>
/// <exception cref="System.ArgumentException">Thrown if the item that is being added already belongs to another ItemCollection</exception>
/// <exception cref="System.ArgumentException">Thrown if the ItemCollection already contains an item with the same identity</exception>
internal void AddInternal(GlobalItem item)
{
Debug.Assert(item.IsReadOnly, "The item is not readonly, it should be by the time it is added to the item collection");
Debug.Assert(item.DataSpace == this.DataSpace);
base.Source.Add(item);
}
/// <summary>
/// Adds a collection of items to the collection
/// </summary>
/// <param name="items">The items to add to the list</param>
/// <exception cref="System.ArgumentNullException">Thrown if item argument is null</exception>
/// <exception cref="System.InvalidOperationException">Thrown if the item passed in or the collection itself instance is in ReadOnly state</exception>
/// <exception cref="System.ArgumentException">Thrown if the item that is being added already belongs to another ItemCollection</exception>
/// <exception cref="System.ArgumentException">Thrown if the ItemCollection already contains an item with the same identity</exception>
internal bool AtomicAddRange(List<GlobalItem> items)
{
#if DEBUG
// We failed to add, so undo the setting of the ItemCollection reference
foreach (GlobalItem item in items)
{
Debug.Assert(item.IsReadOnly, "The item is not readonly, it should be by the time it is added to the item collection");
Debug.Assert(item.DataSpace == this.DataSpace);
}
#endif
if (base.Source.AtomicAddRange(items))
{
return true;
}
return false;
}
/// <summary>
/// Returns strongly typed MetadataItem from the collection that has
/// the passed in identity.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="identity">Identity of the item to look up for</param>
/// <returns>returns the item if a match is found, otherwise throwns an exception</returns>
/// <exception cref="System.ArgumentNullException">Thrown if identity argument passed in is null</exception>
/// <exception cref="System.ArgumentException">Thrown if the Collection does not have an item with the given identity</exception>
public T GetItem<T>(string identity) where T : GlobalItem
{
return this.GetItem<T>(identity, false /*ignoreCase*/);
}
/// <summary>
/// Returns strongly typed MetadataItem from the collection that has
/// the passed in identity.
/// Returns null if the item is not found.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="identity"></param>
/// <param name="item"></param>
/// <returns></returns>
/// <exception cref="System.ArgumentNullException">if identity argument is null</exception>
public bool TryGetItem<T>(string identity, out T item) where T : GlobalItem
{
return this.TryGetItem<T>(identity, false /*ignorecase*/, out item);
}
/// <summary>
/// Returns strongly typed MetadataItem from the collection that has
/// the passed in identity.
/// Returns null if the item is not found.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="identity">identity of the type to look up for</param>
/// <param name="ignoreCase">true for case-insensitive lookup</param>
/// <param name="item">item with the given identity if a match is found, otherwise returns null</param>
/// <returns>returns true if a match is found, otherwise returns false</returns>
/// <exception cref="System.ArgumentNullException">if identity argument is null</exception>
public bool TryGetItem<T>(string identity, bool ignoreCase, out T item) where T : GlobalItem
{
GlobalItem outItem = null;
TryGetValue(identity, ignoreCase, out outItem);
item = outItem as T;
return item != null;
}
/// <summary>
/// Returns strongly typed MetadataItem from the collection that has
/// the passed in identity with either case sensitive or case insensitive search
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="identity">identity of the type to look up for</param>
/// <param name="ignoreCase">true for case-insensitive lookup</param>
/// <returns>returns item if a match is found, otherwise returns throws an argument exception</returns>
/// <exception cref="System.ArgumentNullException">Thrown if identity argument passed in is null</exception>
/// <exception cref="System.ArgumentException">Thrown if no item is found with the given identity</exception>
public T GetItem<T>(string identity, bool ignoreCase) where T : GlobalItem
{
T item;
if (TryGetItem<T>(identity, ignoreCase, out item))
{
return item;
}
throw EntityUtil.ItemInvalidIdentity(identity, "identity");
}
/// <summary>
/// Returns ReadOnlyCollection of the Items of the given type
/// in the item collection.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public virtual System.Collections.ObjectModel.ReadOnlyCollection<T> GetItems<T>() where T : GlobalItem
{
Memoizer<Type, ICollection> currentValueForItemCache = _itemsCache;
// initialize the memoizer, update the _itemCache and _itemCount
if (_itemsCache == null || this._itemCount != this.Count)
{
Memoizer<Type, ICollection> itemsCache =
new Memoizer<Type, ICollection>(InternalGetItems, null);
Interlocked.CompareExchange(ref _itemsCache, itemsCache, currentValueForItemCache);
this._itemCount = this.Count;
}
Debug.Assert(_itemsCache != null, "check the initialization of the Memoizer");
// use memoizer so that it won't create a new list every time this method get called
ICollection items = this._itemsCache.Evaluate(typeof(T));
System.Collections.ObjectModel.ReadOnlyCollection<T> returnItems = items as System.Collections.ObjectModel.ReadOnlyCollection<T>;
return returnItems;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
internal ICollection InternalGetItems(Type type)
{
MethodInfo mi = typeof(ItemCollection).GetMethod("GenericGetItems", BindingFlags.NonPublic | BindingFlags.Static);
MethodInfo genericMi = mi.MakeGenericMethod(type);
return genericMi.Invoke(null, new object[] { this }) as ICollection;
}
private static System.Collections.ObjectModel.ReadOnlyCollection<TItem> GenericGetItems<TItem>(ItemCollection collection) where TItem : GlobalItem
{
List<TItem> list = new List<TItem>();
foreach (GlobalItem item in collection)
{
TItem stronglyTypedItem = item as TItem;
if (stronglyTypedItem != null)
{
list.Add(stronglyTypedItem);
}
}
return list.AsReadOnly();
}
/// <summary>
/// Search for a type metadata with the specified name and namespace name in the given space.
/// </summary>
/// <param name="name">name of the type</param>
/// <param name="namespaceName">namespace of the type</param>
/// <returns>Returns null if no match found.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if name or namespaceName arguments passed in are null</exception>
/// <exception cref="System.ArgumentException">Thrown if the ItemCollection for this space does not have a type with the given name and namespaceName</exception>
public EdmType GetType(string name, string namespaceName)
{
return this.GetType(name, namespaceName, false /*ignoreCase*/);
}
/// <summary>
/// Search for a type metadata with the specified name and namespace name in the given space.
/// </summary>
/// <param name="name">name of the type</param>
/// <param name="namespaceName">namespace of the type</param>
/// <param name="type">The type that needs to be filled with the return value</param>
/// <returns>Returns null if no match found.</returns>
/// <exception cref="System.ArgumentNullException">if name or namespaceName argument is null</exception>
public bool TryGetType(string name, string namespaceName, out EdmType type)
{
return this.TryGetType(name, namespaceName, false /*ignoreCase*/, out type);
}
/// <summary>
/// Search for a type metadata with the specified key.
/// </summary>
/// <param name="name">name of the type</param>
/// <param name="namespaceName">namespace of the type</param>
/// <param name="ignoreCase">true for case-insensitive lookup</param>
/// <returns>Returns null if no match found.</returns>
/// <exception cref="System.ArgumentNullException">Thrown if name or namespaceName arguments passed in are null</exception>
public EdmType GetType(string name, string namespaceName, bool ignoreCase)
{
EntityUtil.GenericCheckArgumentNull(name, "name");
EntityUtil.GenericCheckArgumentNull(namespaceName, "namespaceName");
return GetItem<EdmType>(EdmType.CreateEdmTypeIdentity(namespaceName, name), ignoreCase);
}
/// <summary>
/// Search for a type metadata with the specified name and namespace name in the given space.
/// </summary>
/// <param name="name">name of the type</param>
/// <param name="namespaceName">namespace of the type</param>
/// <param name="ignoreCase">true for case-insensitive lookup</param>
/// <param name="type">The type that needs to be filled with the return value</param>
/// <returns>Returns null if no match found.</returns>
/// <exception cref="System.ArgumentNullException">if name or namespaceName argument is null</exception>
public bool TryGetType(string name, string namespaceName, bool ignoreCase, out EdmType type)
{
EntityUtil.GenericCheckArgumentNull(name, "name");
EntityUtil.GenericCheckArgumentNull(namespaceName, "namespaceName");
GlobalItem item = null;
TryGetValue(EdmType.CreateEdmTypeIdentity(namespaceName, name), ignoreCase, out item);
type = item as EdmType;
return type != null;
}
/// <summary>
/// Get all the overloads of the function with the given name
/// </summary>
/// <param name="functionName">The full name of the function</param>
/// <returns>A collection of all the functions with the given name in the given data space</returns>
/// <exception cref="System.ArgumentNullException">Thrown if functionaName argument passed in is null</exception>
public System.Collections.ObjectModel.ReadOnlyCollection<EdmFunction> GetFunctions(string functionName)
{
return this.GetFunctions(functionName, false /*ignoreCase*/);
}
/// <summary>
/// Get all the overloads of the function with the given name
/// </summary>
/// <param name="functionName">The full name of the function</param>
/// <param name="ignoreCase">true for case-insensitive lookup</param>
/// <returns>A collection of all the functions with the given name in the given data space</returns>
/// <exception cref="System.ArgumentNullException">Thrown if functionaName argument passed in is null</exception>
public System.Collections.ObjectModel.ReadOnlyCollection<EdmFunction> GetFunctions(string functionName, bool ignoreCase)
{
return GetFunctions(this.FunctionLookUpTable, functionName, ignoreCase);
}
/// <summary>
/// Look for the functions in the given collection and
/// returns all the functions with the given name
/// </summary>
/// <param name="functionCollection"></param>
/// <param name="functionName"></param>
/// <param name="ignoreCase"></param>
/// <returns></returns>
protected static System.Collections.ObjectModel.ReadOnlyCollection<EdmFunction> GetFunctions(
Dictionary<string, System.Collections.ObjectModel.ReadOnlyCollection<EdmFunction>> functionCollection,
string functionName, bool ignoreCase)
{
System.Collections.ObjectModel.ReadOnlyCollection<EdmFunction> functionOverloads;
if (functionCollection.TryGetValue(functionName, out functionOverloads))
{
if (ignoreCase)
{
return functionOverloads;
}
return GetCaseSensitiveFunctions(functionOverloads, functionName);
}
return Helper.EmptyEdmFunctionReadOnlyCollection;
}
internal static System.Collections.ObjectModel.ReadOnlyCollection<EdmFunction> GetCaseSensitiveFunctions(
System.Collections.ObjectModel.ReadOnlyCollection<EdmFunction> functionOverloads,
string functionName)
{
// For case-sensitive match, first check if there are anything with a different case
// its very rare to have functions with different case. So optimizing the case where all
// functions are of same case
// Else create a new list with the functions with the exact name
List<EdmFunction> caseSensitiveFunctionOverloads = new List<EdmFunction>(functionOverloads.Count);
for (int i = 0; i < functionOverloads.Count; i++)
{
if (functionOverloads[i].FullName == functionName)
{
caseSensitiveFunctionOverloads.Add(functionOverloads[i]);
}
}
// If there are no functions with different case, just return the collection
if (caseSensitiveFunctionOverloads.Count != functionOverloads.Count)
{
functionOverloads = caseSensitiveFunctionOverloads.AsReadOnly();
}
return functionOverloads;
}
/// <summary>
/// Gets the function as specified by the function key.
/// All parameters are assumed to be <see cref="ParameterMode.In"/>.
/// </summary>
/// <param name="functionName">Name of the function</param>
/// <param name="parameterTypes">types of the parameters</param>
/// <param name="ignoreCase">true for case-insensitive lookup</param>
/// <param name="function">The function that needs to be returned</param>
/// <returns> The function as specified in the function key or null</returns>
/// <exception cref="System.ArgumentNullException">if functionName or parameterTypes argument is null</exception>
/// <exception cref="System.ArgumentException">if no function is found with the given name or with given input parameters</exception>
internal bool TryGetFunction(string functionName, TypeUsage[] parameterTypes, bool ignoreCase, out EdmFunction function)
{
EntityUtil.GenericCheckArgumentNull(functionName, "functionName");
EntityUtil.GenericCheckArgumentNull(parameterTypes, "parameterTypes");
string functionIdentity = EdmFunction.BuildIdentity(functionName, parameterTypes);
GlobalItem item = null;
function = null;
if (TryGetValue(functionIdentity, ignoreCase, out item) && Helper.IsEdmFunction(item))
{
function = (EdmFunction)item;
return true;
}
return false;
}
/// <summary>
/// Get an entity container based upon the strong name of the container
/// If no entity container is found, returns null, else returns the first one/// </summary>
/// <param name="name">name of the entity container</param>
/// <returns>The EntityContainer</returns>
/// <exception cref="System.ArgumentNullException">Thrown if name argument passed in is null</exception>
public EntityContainer GetEntityContainer(string name)
{
EntityUtil.GenericCheckArgumentNull(name, "name");
return this.GetEntityContainer(name, false /*ignoreCase*/);
}
/// <summary>
/// Get an entity container based upon the strong name of the container
/// If no entity container is found, returns null, else returns the first one/// </summary>
/// <param name="name">name of the entity container</param>
/// <param name="entityContainer"></param>
/// <exception cref="System.ArgumentNullException">if name argument is null</exception>
public bool TryGetEntityContainer(string name, out EntityContainer entityContainer)
{
EntityUtil.GenericCheckArgumentNull(name, "name");
return this.TryGetEntityContainer(name, false /*ignoreCase*/, out entityContainer);
}
/// <summary>
/// Get an entity container based upon the strong name of the container
/// If no entity container is found, returns null, else returns the first one/// </summary>
/// <param name="name">name of the entity container</param>
/// <param name="ignoreCase">true for case-insensitive lookup</param>
/// <returns>The EntityContainer</returns>
/// <exception cref="System.ArgumentNullException">Thrown if name argument passed in is null</exception>
/// <exception cref="System.ArgumentException">Thrown if no entity container with the given name is found</exception>
public EntityContainer GetEntityContainer(string name, bool ignoreCase)
{
EntityContainer container = GetValue(name, ignoreCase) as EntityContainer;
if (null != container)
{
return container;
}
throw EntityUtil.ItemInvalidIdentity(name, "name");
}
/// <summary>
/// Get an entity container based upon the strong name of the container
/// If no entity container is found, returns null, else returns the first one/// </summary>
/// <param name="name">name of the entity container</param>
/// <param name="ignoreCase">true for case-insensitive lookup</param>
/// <param name="entityContainer"></param>
/// <exception cref="System.ArgumentNullException">if name argument is null</exception>
public bool TryGetEntityContainer(string name, bool ignoreCase, out EntityContainer entityContainer)
{
EntityUtil.GenericCheckArgumentNull(name, "name");
GlobalItem item = null;
if (TryGetValue(name, ignoreCase, out item) && Helper.IsEntityContainer(item))
{
entityContainer = (EntityContainer)item;
return true;
}
entityContainer = null;
return false;
}
/// <summary>
/// Given the canonical primitive type, get the mapping primitive type in the given dataspace
/// </summary>
/// <param name="primitiveTypeKind">canonical primitive type</param>
/// <returns>The mapped scalar type</returns>
internal virtual PrimitiveType GetMappedPrimitiveType(PrimitiveTypeKind primitiveTypeKind)
{
//The method needs to be overloaded on methods that support this
throw System.Data.Entity.Error.NotSupported();
}
/// <summary>
/// Determines whether this item collection is equivalent to another. At present, we look only
/// at object reference equivalence. This is a somewhat reasonable approximation when caching
/// is enabled, because collections are identical when their source resources (including
/// provider) are known to be identical.
/// </summary>
/// <param name="other">Collection to compare.</param>
/// <returns>true if the collections are equivalent; false otherwise</returns>
internal virtual bool MetadataEquals(ItemCollection other)
{
return Object.ReferenceEquals(this, other);
}
static private Dictionary<string, System.Collections.ObjectModel.ReadOnlyCollection<EdmFunction>> PopulateFunctionLookUpTable(ItemCollection itemCollection)
{
var tempFunctionLookUpTable = new Dictionary<string, List<EdmFunction>>(StringComparer.OrdinalIgnoreCase);
foreach (EdmFunction function in itemCollection.GetItems<EdmFunction>())
{
List<EdmFunction> functionList;
if (!tempFunctionLookUpTable.TryGetValue(function.FullName, out functionList))
{
functionList = new List<EdmFunction>();
tempFunctionLookUpTable[function.FullName] = functionList;
}
functionList.Add(function);
}
var functionLookUpTable = new Dictionary<string, System.Collections.ObjectModel.ReadOnlyCollection<EdmFunction>>(StringComparer.OrdinalIgnoreCase);
foreach (List<EdmFunction> functionList in tempFunctionLookUpTable.Values)
{
functionLookUpTable.Add(functionList[0].FullName, new System.Collections.ObjectModel.ReadOnlyCollection<EdmFunction>(functionList.ToArray()));
}
return functionLookUpTable;
}
#endregion
}//---- ItemCollection
}//----
|