File: SiteMapNode.cs
Project: ndp\fx\src\xsp\system\Web\System.Web.csproj (System.Web)
//------------------------------------------------------------------------------
// <copyright file="SiteMapNode.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
/*
 * SiteMapNode class definition
 *
 * Copyright (c) 2002 Microsoft Corporation
 */
 
namespace System.Web {
 
    using System;
    using System.Configuration.Provider;
    using System.Collections;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.Resources;
    using System.Security.Permissions;
    using System.Web.Configuration;
    using System.Web.Compilation;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.Util;
 
    /// <devdoc>
    /// <para></para>
    /// </devdoc>
    public class SiteMapNode : ICloneable, IHierarchyData, INavigateUIData {
 
        private static readonly string _siteMapNodeType = typeof(SiteMapNode).Name;
 
        private SiteMapProvider _provider;
 
        private bool _readonly;
        private bool _parentNodeSet;
        private bool _childNodesSet;
 
        private VirtualPath _virtualPath;
        private string _title;
        private string _description;
        private string _url;
        private string _key;
        private string _resourceKey;
 
        private IList _roles;
        private NameValueCollection _attributes;
        private NameValueCollection _resourceKeys;
 
        private SiteMapNode _parentNode;
        private SiteMapNodeCollection _childNodes;
 
        public SiteMapNode(SiteMapProvider provider, string key) :
            this(provider, key, null, null, null, null, null, null, null) {
        }
 
        public SiteMapNode(SiteMapProvider provider, string key, string url) :
            this(provider, key, url, null, null, null, null, null, null) {
        }
 
        public SiteMapNode(SiteMapProvider provider, string key, string url, string title) :
            this(provider, key, url, title, null, null, null, null, null) {
        }
 
        public SiteMapNode(SiteMapProvider provider, string key, string url, string title, string description) :
            this(provider, key, url, title, description, null, null, null, null) {
        }
 
        public SiteMapNode(SiteMapProvider provider, string key, string url, string title, string description,
            IList roles, NameValueCollection attributes, NameValueCollection explicitResourceKeys, string implicitResourceKey) {
 
            _provider = provider;
            _title = title;
            _description = description;
            _roles = roles;
            _attributes = attributes;
            _key = key;
            _resourceKeys = explicitResourceKeys;
            _resourceKey = implicitResourceKey;
 
            if (url != null) {
                _url = url.Trim();
            }
 
            _virtualPath = CreateVirtualPathFromUrl(_url);
 
            if (_key == null) {
                throw new ArgumentNullException("key");
            }
 
            if (_provider == null) {
                throw new ArgumentNullException("provider");
            }
        }
 
        protected NameValueCollection Attributes {
            get {
                return _attributes;
            }
            set {
                if (_readonly) {
                    throw new InvalidOperationException(SR.GetString(SR.SiteMapNode_readonly, "Attributes"));
                }
 
                _attributes = value;
            }
        }
 
        // Access custom attributes.
        public virtual string this[string key] {
            get {
                string text = null;
                if (_attributes != null) {
                    text = _attributes[key];
                }
 
                if (_provider.EnableLocalization) {
                    // Try the implicit resource first
                    string localizedText = GetImplicitResourceString(key);
                    if (localizedText != null) {
                        return localizedText;
                    }
 
                    // If not found, try the explicit resource.
                    localizedText = GetExplicitResourceString(key, text, true);
                    if (localizedText != null) {
                        return localizedText;
                    }
                }
 
                return text;
            }
 
            set {
                if (_readonly) {
                    throw new InvalidOperationException(SR.GetString(SR.SiteMapNode_readonly, "Item"));
                }
 
                if (_attributes == null) {
                    _attributes = new NameValueCollection();
                }
 
                _attributes[key] = value;
            }
        }
 
        public virtual SiteMapNodeCollection ChildNodes {
            get {
                if (_childNodesSet)
                    return _childNodes;
 
                return _provider.GetChildNodes(this);
            }
            set {
                if (_readonly) {
                    throw new InvalidOperationException(SR.GetString(SR.SiteMapNode_readonly, "ChildNodes"));
                }
 
                _childNodes = value;
                _childNodesSet = true;
            }
        }
 
        [
        Localizable(true)
        ]
        public virtual string Description {
            get {
                if (_provider.EnableLocalization) {
                    string localizedText = GetImplicitResourceString("description");
                    if (localizedText != null) {
                        return localizedText;
                    }
 
                    localizedText = GetExplicitResourceString("description", _description, true);
                    if (localizedText != null) {
                        return localizedText;
                    }
                }
 
                return _description == null? String.Empty : _description;
            }
            set {
                if (_readonly) {
                    throw new InvalidOperationException(SR.GetString(SR.SiteMapNode_readonly, "Description"));
                }
 
                _description = value;
            }
        }
 
        public string Key {
            get {
                return _key;
            }
        }
 
        public virtual bool HasChildNodes {
            get {
                IList children = ChildNodes;
                return children != null && children.Count > 0;
            }
        }
 
        public virtual SiteMapNode NextSibling {
            get {
                IList siblings = SiblingNodes;
                if (siblings == null) {
                    return null;
                }
 
                int index = siblings.IndexOf(this);
                if (index >= 0 && index < siblings.Count - 1) {
                    return (SiteMapNode)siblings[index + 1];
                }
 
                return null;
            }
        }
 
        // Get parent node. If not found in current provider, search recursively in parent providers.
        public virtual SiteMapNode ParentNode {
            get {
                if (_parentNodeSet)
                    return _parentNode;
 
                return _provider.GetParentNode(this);
            }
            set {
                if (_readonly) {
                    throw new InvalidOperationException(SR.GetString(SR.SiteMapNode_readonly, "ParentNode"));
                }
 
                _parentNode = value;
                _parentNodeSet = true;
            }
        }
 
        public virtual SiteMapNode PreviousSibling {
            get {
                IList siblings = SiblingNodes;
                if (siblings == null) {
                    return null;
                }
 
                int index = siblings.IndexOf(this);
                if (index > 0 && index <= siblings.Count - 1) {
                    return (SiteMapNode)siblings[index - 1];
                }
 
                return null;
            }
        }
 
        public SiteMapProvider Provider {
            get {
                return _provider;
            }
        }
 
        public bool ReadOnly {
            get {
                return _readonly;
            }
            set {
                _readonly = value;
            }
        }
 
        public String ResourceKey {
            get {
                return _resourceKey;
            }
            set {
                if (_readonly) {
                    throw new InvalidOperationException(SR.GetString(SR.SiteMapNode_readonly, "ResourceKey"));
                }
 
                _resourceKey = value;
            }
        }
 
        public IList Roles {
            get {
                return _roles;
            }
            set {
                if (_readonly) {
                    throw new InvalidOperationException(SR.GetString(SR.SiteMapNode_readonly, "Roles"));
                }
 
                _roles = value;
            }
        }
 
        public virtual SiteMapNode RootNode {
            get {
                SiteMapNode root = _provider.RootProvider.RootNode;
                if (root == null) {
                    String name = ((ProviderBase)_provider.RootProvider).Name;
                    throw new InvalidOperationException(SR.GetString(SR.SiteMapProvider_Invalid_RootNode, name));
                }
 
                return root;
            }
        }
 
        private SiteMapNodeCollection SiblingNodes {
            get {
                SiteMapNode parent = ParentNode;
                return parent == null? null : parent.ChildNodes;
            }
        }
 
        [
        Localizable(true)
        ]
        public virtual string Title {
            get {
                if (_provider.EnableLocalization) {
                    string localizedText = GetImplicitResourceString("title");
                    if (localizedText != null) {
                        return localizedText;
                    }
 
                    localizedText = GetExplicitResourceString("title", _title, true);
                    if (localizedText != null) {
                        return localizedText;
                    }
                }
 
                return _title == null? String.Empty : _title;
            }
            set {
                if (_readonly) {
                    throw new InvalidOperationException(SR.GetString(SR.SiteMapNode_readonly, "Title"));
                }
 
                _title = value;
            }
        }
 
        public virtual string Url {
            get {
                return _url == null? String.Empty : _url;
            }
            set {
                if (_readonly) {
                    throw new InvalidOperationException(SR.GetString(SR.SiteMapNode_readonly, "Url"));
                }
 
                if (value != null) {
                    _url = value.Trim();
                }
 
                _virtualPath = CreateVirtualPathFromUrl(_url);
            }
        }
 
        internal VirtualPath VirtualPath {
            get {
                return _virtualPath;
            }
        }
 
        private VirtualPath CreateVirtualPathFromUrl(string url) {
            if (String.IsNullOrEmpty(url)) {
                return null;
            }
 
            if (!UrlPath.IsValidVirtualPathWithoutProtocol(url)) {
                return null;
            }
 
            if (UrlPath.IsAbsolutePhysicalPath(url)) {
                return null;
            }
 
            // Do not generate the virtualPath class at designtime.
            if (HttpRuntime.AppDomainAppVirtualPath == null) {
                return null;
            }
 
            if (UrlPath.IsRelativeUrl(url) && !UrlPath.IsAppRelativePath(url)) {
                url = UrlPath.Combine(HttpRuntime.AppDomainAppVirtualPathString, url);
            }
 
            // Remove the query string from url so the path can be validated by Authorization module.
            int queryStringIndex = url.IndexOf('?');
            if (queryStringIndex != -1) {
                url = url.Substring(0, queryStringIndex);
            }
 
            return VirtualPath.Create(url, 
                VirtualPathOptions.AllowAbsolutePath | VirtualPathOptions.AllowAppRelativePath);
        }
 
        public virtual SiteMapNode Clone() {
            ArrayList newRoles = null;
            NameValueCollection newAttributes = null;
            NameValueCollection newResourceKeys = null;
 
            if (_roles != null) {
                newRoles = new ArrayList(_roles);
            }
            if (_attributes != null) {
                newAttributes = new NameValueCollection(_attributes);
            }
            if (_resourceKeys != null) {
                newResourceKeys = new NameValueCollection(_resourceKeys);
            }
 
            SiteMapNode newNode = new SiteMapNode(_provider, Key, Url, Title, Description, newRoles, newAttributes, newResourceKeys, _resourceKey);
            return newNode;
        }
 
        public virtual SiteMapNode Clone(bool cloneParentNodes) {
            SiteMapNode current = Clone();
 
            if (cloneParentNodes) {
                SiteMapNode node = current;
                SiteMapNode parent = ParentNode;
                while (parent != null) {
                    SiteMapNode cloneParent = parent.Clone();
                    node.ParentNode = cloneParent;
                    cloneParent.ChildNodes = new SiteMapNodeCollection(node);
 
                    parent = parent.ParentNode;
                    node = cloneParent;
                }
            }
 
            return current;
        }
 
        public override bool Equals(object obj) {
            SiteMapNode node = obj as SiteMapNode;
            return node != null && (_key == node.Key) && 
                (String.Equals(_url, node._url, StringComparison.OrdinalIgnoreCase));
        }
 
        public SiteMapNodeCollection GetAllNodes() {
            SiteMapNodeCollection collection = new SiteMapNodeCollection();
            GetAllNodesRecursive(collection);
            return SiteMapNodeCollection.ReadOnly(collection);
        }
 
        private void GetAllNodesRecursive(SiteMapNodeCollection collection) {
            SiteMapNodeCollection childNodes = this.ChildNodes;
 
            if (childNodes != null && childNodes.Count > 0) {
                collection.AddRange(childNodes);
                foreach(SiteMapNode node in childNodes)
                    node.GetAllNodesRecursive(collection);
            }
        }
 
        public SiteMapDataSourceView GetDataSourceView(SiteMapDataSource owner, string viewName) {
            return new SiteMapDataSourceView(owner, viewName, this);
        }
 
 
        public SiteMapHierarchicalDataSourceView GetHierarchicalDataSourceView() {
            return new SiteMapHierarchicalDataSourceView(this);
        }
 
        // Helpe method to retrieve localized string based on attribute name
        protected string GetExplicitResourceString(string attributeName, string defaultValue, bool throwIfNotFound) {
            if (attributeName == null) {
                throw new ArgumentNullException("attributeName");
            }
 
            string text = null;
            if (_resourceKeys != null) {
                string[] keys = _resourceKeys.GetValues(attributeName);
                if (keys != null && keys.Length > 1) {
                    try {
                        text = ResourceExpressionBuilder.GetGlobalResourceObject(keys[0], keys[1]) as string;
                    }
                    catch (MissingManifestResourceException) {
                        if (defaultValue != null) {
                            return defaultValue;
                        }
                    }
 
                    if (text == null && throwIfNotFound) {
                        // throw if default value is not specified.
                        throw new InvalidOperationException(
                            SR.GetString(SR.Res_not_found_with_class_and_key, keys[0], keys[1])); ;
                    }
                }
            }
 
            return text;
        }
 
        // Only use the key to get the hashcode since url can be changed and makes the objects mutable.
        public override int GetHashCode() {
            return _key.GetHashCode();
        }
 
        // Helper method to retrieve localized string based on attribute name
        protected string GetImplicitResourceString(string attributeName) {
            if (attributeName == null) {
                throw new ArgumentNullException("attributeName");
            }
 
            string text = null;
            if (!String.IsNullOrEmpty(_resourceKey)) {
                try {
                    text = ResourceExpressionBuilder.GetGlobalResourceObject(Provider.ResourceKey, ResourceKey + "." + attributeName) as String;
                }
                catch { }
            }
 
            return text;
        }
 
        public virtual bool IsAccessibleToUser(HttpContext context) {
            return _provider.IsAccessibleToUser(context, this);
        }
 
        public virtual bool IsDescendantOf(SiteMapNode node) {
            SiteMapNode parent = ParentNode;
            while (parent != null) {
                if (parent.Equals(node)) {
                    return true;
                }
 
                parent = parent.ParentNode;
            }
 
            return false;
        }
 
        public override string ToString() {
            return Title;
        }
 
        #region ICloneable implementation
 
        /// <internalonly/>
        object ICloneable.Clone() {
            return Clone();
        }
        #endregion
 
        #region IHierarchyData implementation
 
        /// <internalonly/>
        bool IHierarchyData.HasChildren {
            get {
                return HasChildNodes;
            }
        }
 
 
        /// <internalonly/>
        object IHierarchyData.Item {
            get {
                return this;
            }
        }
 
 
        /// <internalonly/>
        string IHierarchyData.Path {
            get {
                return Key;
            }
        }
 
 
        /// <internalonly/>
        string IHierarchyData.Type {
            get {
                return _siteMapNodeType;
            }
        }
 
 
        /// <internalonly/>
        IHierarchicalEnumerable IHierarchyData.GetChildren() {
            return ChildNodes;
        }
 
 
        /// <internalonly/>
        IHierarchyData IHierarchyData.GetParent() {
            SiteMapNode parentNode = ParentNode;
            if (parentNode == null)
                return null;
 
            return parentNode;
        }
        #endregion
 
        #region INavigateUIData implementations
        string INavigateUIData.Description {
            get {
                return Description;
            }
        }
        
 
        /// <internalonly/>
        string INavigateUIData.Name {
             get {
                return Title;
             }
        }
 
 
        /// <internalonly/>
        string INavigateUIData.NavigateUrl {
            get {
                return Url;
            }
        }
 
 
        /// <internalonly/>
        string INavigateUIData.Value {
            get {
                return Title;
            }
        }
        #endregion
    }
}