// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Properties; using System.Web.Http.Routing; namespace System.Web.Http { public class HttpRouteCollection : ICollection, IDisposable { // Arbitrary base address for evaluating the root virtual path private static readonly Uri _referenceBaseAddress = new Uri("http://localhost"); private readonly string _virtualPathRoot; private readonly Collection _collection = new Collection(); private readonly IDictionary _dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); private bool _disposed; /// /// Initializes a new instance of the class with a /// value of "/". /// public HttpRouteCollection() : this("/") { } [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Relative URIs are not URIs")] public HttpRouteCollection(string virtualPathRoot) { if (virtualPathRoot == null) { throw Error.ArgumentNull("virtualPathRoot"); } // Validate virtual path Uri address = new Uri(_referenceBaseAddress, virtualPathRoot); _virtualPathRoot = address.AbsolutePath; } public virtual string VirtualPathRoot { get { return _virtualPathRoot; } } public virtual int Count { get { return _collection.Count; } } public virtual bool IsReadOnly { get { return false; } } public virtual IHttpRoute this[int index] { get { return _collection[index]; } } public virtual IHttpRoute this[string name] { get { return _dictionary[name]; } } public virtual IHttpRouteData GetRouteData(HttpRequestMessage request) { if (request == null) { throw Error.ArgumentNull("request"); } foreach (IHttpRoute route in _collection) { IHttpRouteData routeData = route.GetRouteData(_virtualPathRoot, request); if (routeData != null) { return routeData; } } return null; } public virtual IHttpVirtualPathData GetVirtualPath(HttpControllerContext controllerContext, string name, IDictionary values) { if (controllerContext == null) { throw Error.ArgumentNull("controllerContext"); } if (name == null) { throw Error.ArgumentNull("name"); } IHttpRoute route; if (!_dictionary.TryGetValue(name, out route)) { throw Error.Argument("name", SRResources.RouteCollection_NameNotFound, name); } IHttpVirtualPathData virtualPath = route.GetVirtualPath(controllerContext, values); if (virtualPath == null) { return null; } // Construct a new VirtualPathData with the resolved app path string virtualPathRoot = _virtualPathRoot; if (!virtualPathRoot.EndsWith("/", StringComparison.Ordinal)) { virtualPathRoot += "/"; } // Note: The virtual path root here always ends with a "/" and the // virtual path never starts with a "/" (that's how routes work). return new HttpVirtualPathData(virtualPath.Route, virtualPathRoot + virtualPath.VirtualPath); } public IHttpRoute CreateRoute(string routeTemplate, object defaults, object constraints, IDictionary parameters) { IDictionary dataTokens = new Dictionary(); return CreateRoute(routeTemplate, GetTypeProperties(defaults), GetTypeProperties(constraints), dataTokens, parameters); } public virtual IHttpRoute CreateRoute(string routeTemplate, IDictionary defaults, IDictionary constraints, IDictionary dataTokens, IDictionary parameters) { HttpRouteValueDictionary routeDefaults = defaults != null ? new HttpRouteValueDictionary(defaults) : null; HttpRouteValueDictionary routeConstraints = constraints != null ? new HttpRouteValueDictionary(constraints) : null; HttpRouteValueDictionary routeDataTokens = dataTokens != null ? new HttpRouteValueDictionary(dataTokens) : null; return new HttpRoute(routeTemplate, routeDefaults, routeConstraints, routeDataTokens); } void ICollection.Add(IHttpRoute route) { throw Error.NotSupported(SRResources.Route_AddRemoveWithNoKeyNotSupported, typeof(HttpRouteCollection).Name); } public virtual void Add(string name, IHttpRoute route) { if (name == null) { throw Error.ArgumentNull("name"); } if (route == null) { throw Error.ArgumentNull("route"); } _dictionary.Add(name, route); _collection.Add(route); } public virtual void Clear() { _dictionary.Clear(); _collection.Clear(); } public virtual bool Contains(IHttpRoute item) { if (item == null) { throw Error.ArgumentNull("item"); } return _collection.Contains(item); } public virtual bool ContainsKey(string name) { if (name == null) { throw Error.ArgumentNull("name"); } return _dictionary.ContainsKey(name); } public virtual void CopyTo(IHttpRoute[] array, int arrayIndex) { _collection.CopyTo(array, arrayIndex); } public virtual void CopyTo(KeyValuePair[] array, int arrayIndex) { _dictionary.CopyTo(array, arrayIndex); } public virtual void Insert(int index, string name, IHttpRoute value) { if (name == null) { throw Error.ArgumentNull("name"); } if (value == null) { throw Error.ArgumentNull("value"); } // Check that index is valid if (_collection[index] != null) { _dictionary.Add(name, value); _collection.Insert(index, value); } } bool ICollection.Remove(IHttpRoute route) { throw Error.NotSupported(SRResources.Route_AddRemoveWithNoKeyNotSupported, typeof(HttpRouteCollection).Name); } public virtual bool Remove(string name) { if (name == null) { throw Error.ArgumentNull("name"); } IHttpRoute value; if (_dictionary.TryGetValue(name, out value)) { bool dictionaryRemove = _dictionary.Remove(name); bool collectionRemove = _collection.Remove(value); Contract.Assert(dictionaryRemove == collectionRemove); return dictionaryRemove; } return false; } public virtual IEnumerator GetEnumerator() { return _collection.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return OnGetEnumerator(); } protected virtual IEnumerator OnGetEnumerator() { return _collection.GetEnumerator(); } public virtual bool TryGetValue(string name, out IHttpRoute route) { return _dictionary.TryGetValue(name, out route); } internal static IDictionary GetTypeProperties(object instance) { Dictionary result = new Dictionary(); if (instance != null) { PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(instance); foreach (PropertyDescriptor prop in properties) { object val = prop.GetValue(instance); result.Add(prop.Name, val); } } return result; } #region IDisposable public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!_disposed) { _disposed = true; } } #endregion } }