Merge pull request #2955 from ludovic-henry/mono_msec_ticks-overflow
[mono.git] / mcs / class / referencesource / System.Web / SiteMapNode.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="SiteMapNode.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 /*
8  * SiteMapNode class definition
9  *
10  * Copyright (c) 2002 Microsoft Corporation
11  */
12
13 namespace System.Web {
14
15     using System;
16     using System.Configuration.Provider;
17     using System.Collections;
18     using System.Collections.Specialized;
19     using System.ComponentModel;
20     using System.Resources;
21     using System.Security.Permissions;
22     using System.Web.Configuration;
23     using System.Web.Compilation;
24     using System.Web.UI;
25     using System.Web.UI.WebControls;
26     using System.Web.Util;
27
28     /// <devdoc>
29     /// <para></para>
30     /// </devdoc>
31     public class SiteMapNode : ICloneable, IHierarchyData, INavigateUIData {
32
33         private static readonly string _siteMapNodeType = typeof(SiteMapNode).Name;
34
35         private SiteMapProvider _provider;
36
37         private bool _readonly;
38         private bool _parentNodeSet;
39         private bool _childNodesSet;
40
41         private VirtualPath _virtualPath;
42         private string _title;
43         private string _description;
44         private string _url;
45         private string _key;
46         private string _resourceKey;
47
48         private IList _roles;
49         private NameValueCollection _attributes;
50         private NameValueCollection _resourceKeys;
51
52         private SiteMapNode _parentNode;
53         private SiteMapNodeCollection _childNodes;
54
55         public SiteMapNode(SiteMapProvider provider, string key) :
56             this(provider, key, null, null, null, null, null, null, null) {
57         }
58
59         public SiteMapNode(SiteMapProvider provider, string key, string url) :
60             this(provider, key, url, null, null, null, null, null, null) {
61         }
62
63         public SiteMapNode(SiteMapProvider provider, string key, string url, string title) :
64             this(provider, key, url, title, null, null, null, null, null) {
65         }
66
67         public SiteMapNode(SiteMapProvider provider, string key, string url, string title, string description) :
68             this(provider, key, url, title, description, null, null, null, null) {
69         }
70
71         public SiteMapNode(SiteMapProvider provider, string key, string url, string title, string description,
72             IList roles, NameValueCollection attributes, NameValueCollection explicitResourceKeys, string implicitResourceKey) {
73
74             _provider = provider;
75             _title = title;
76             _description = description;
77             _roles = roles;
78             _attributes = attributes;
79             _key = key;
80             _resourceKeys = explicitResourceKeys;
81             _resourceKey = implicitResourceKey;
82
83             if (url != null) {
84                 _url = url.Trim();
85             }
86
87             _virtualPath = CreateVirtualPathFromUrl(_url);
88
89             if (_key == null) {
90                 throw new ArgumentNullException("key");
91             }
92
93             if (_provider == null) {
94                 throw new ArgumentNullException("provider");
95             }
96         }
97
98         protected NameValueCollection Attributes {
99             get {
100                 return _attributes;
101             }
102             set {
103                 if (_readonly) {
104                     throw new InvalidOperationException(SR.GetString(SR.SiteMapNode_readonly, "Attributes"));
105                 }
106
107                 _attributes = value;
108             }
109         }
110
111         // Access custom attributes.
112         public virtual string this[string key] {
113             get {
114                 string text = null;
115                 if (_attributes != null) {
116                     text = _attributes[key];
117                 }
118
119                 if (_provider.EnableLocalization) {
120                     // Try the implicit resource first
121                     string localizedText = GetImplicitResourceString(key);
122                     if (localizedText != null) {
123                         return localizedText;
124                     }
125
126                     // If not found, try the explicit resource.
127                     localizedText = GetExplicitResourceString(key, text, true);
128                     if (localizedText != null) {
129                         return localizedText;
130                     }
131                 }
132
133                 return text;
134             }
135
136             set {
137                 if (_readonly) {
138                     throw new InvalidOperationException(SR.GetString(SR.SiteMapNode_readonly, "Item"));
139                 }
140
141                 if (_attributes == null) {
142                     _attributes = new NameValueCollection();
143                 }
144
145                 _attributes[key] = value;
146             }
147         }
148
149         public virtual SiteMapNodeCollection ChildNodes {
150             get {
151                 if (_childNodesSet)
152                     return _childNodes;
153
154                 return _provider.GetChildNodes(this);
155             }
156             set {
157                 if (_readonly) {
158                     throw new InvalidOperationException(SR.GetString(SR.SiteMapNode_readonly, "ChildNodes"));
159                 }
160
161                 _childNodes = value;
162                 _childNodesSet = true;
163             }
164         }
165
166         [
167         Localizable(true)
168         ]
169         public virtual string Description {
170             get {
171                 if (_provider.EnableLocalization) {
172                     string localizedText = GetImplicitResourceString("description");
173                     if (localizedText != null) {
174                         return localizedText;
175                     }
176
177                     localizedText = GetExplicitResourceString("description", _description, true);
178                     if (localizedText != null) {
179                         return localizedText;
180                     }
181                 }
182
183                 return _description == null? String.Empty : _description;
184             }
185             set {
186                 if (_readonly) {
187                     throw new InvalidOperationException(SR.GetString(SR.SiteMapNode_readonly, "Description"));
188                 }
189
190                 _description = value;
191             }
192         }
193
194         public string Key {
195             get {
196                 return _key;
197             }
198         }
199
200         public virtual bool HasChildNodes {
201             get {
202                 IList children = ChildNodes;
203                 return children != null && children.Count > 0;
204             }
205         }
206
207         public virtual SiteMapNode NextSibling {
208             get {
209                 IList siblings = SiblingNodes;
210                 if (siblings == null) {
211                     return null;
212                 }
213
214                 int index = siblings.IndexOf(this);
215                 if (index >= 0 && index < siblings.Count - 1) {
216                     return (SiteMapNode)siblings[index + 1];
217                 }
218
219                 return null;
220             }
221         }
222
223         // Get parent node. If not found in current provider, search recursively in parent providers.
224         public virtual SiteMapNode ParentNode {
225             get {
226                 if (_parentNodeSet)
227                     return _parentNode;
228
229                 return _provider.GetParentNode(this);
230             }
231             set {
232                 if (_readonly) {
233                     throw new InvalidOperationException(SR.GetString(SR.SiteMapNode_readonly, "ParentNode"));
234                 }
235
236                 _parentNode = value;
237                 _parentNodeSet = true;
238             }
239         }
240
241         public virtual SiteMapNode PreviousSibling {
242             get {
243                 IList siblings = SiblingNodes;
244                 if (siblings == null) {
245                     return null;
246                 }
247
248                 int index = siblings.IndexOf(this);
249                 if (index > 0 && index <= siblings.Count - 1) {
250                     return (SiteMapNode)siblings[index - 1];
251                 }
252
253                 return null;
254             }
255         }
256
257         public SiteMapProvider Provider {
258             get {
259                 return _provider;
260             }
261         }
262
263         public bool ReadOnly {
264             get {
265                 return _readonly;
266             }
267             set {
268                 _readonly = value;
269             }
270         }
271
272         public String ResourceKey {
273             get {
274                 return _resourceKey;
275             }
276             set {
277                 if (_readonly) {
278                     throw new InvalidOperationException(SR.GetString(SR.SiteMapNode_readonly, "ResourceKey"));
279                 }
280
281                 _resourceKey = value;
282             }
283         }
284
285         public IList Roles {
286             get {
287                 return _roles;
288             }
289             set {
290                 if (_readonly) {
291                     throw new InvalidOperationException(SR.GetString(SR.SiteMapNode_readonly, "Roles"));
292                 }
293
294                 _roles = value;
295             }
296         }
297
298         public virtual SiteMapNode RootNode {
299             get {
300                 SiteMapNode root = _provider.RootProvider.RootNode;
301                 if (root == null) {
302                     String name = ((ProviderBase)_provider.RootProvider).Name;
303                     throw new InvalidOperationException(SR.GetString(SR.SiteMapProvider_Invalid_RootNode, name));
304                 }
305
306                 return root;
307             }
308         }
309
310         private SiteMapNodeCollection SiblingNodes {
311             get {
312                 SiteMapNode parent = ParentNode;
313                 return parent == null? null : parent.ChildNodes;
314             }
315         }
316
317         [
318         Localizable(true)
319         ]
320         public virtual string Title {
321             get {
322                 if (_provider.EnableLocalization) {
323                     string localizedText = GetImplicitResourceString("title");
324                     if (localizedText != null) {
325                         return localizedText;
326                     }
327
328                     localizedText = GetExplicitResourceString("title", _title, true);
329                     if (localizedText != null) {
330                         return localizedText;
331                     }
332                 }
333
334                 return _title == null? String.Empty : _title;
335             }
336             set {
337                 if (_readonly) {
338                     throw new InvalidOperationException(SR.GetString(SR.SiteMapNode_readonly, "Title"));
339                 }
340
341                 _title = value;
342             }
343         }
344
345         public virtual string Url {
346             get {
347                 return _url == null? String.Empty : _url;
348             }
349             set {
350                 if (_readonly) {
351                     throw new InvalidOperationException(SR.GetString(SR.SiteMapNode_readonly, "Url"));
352                 }
353
354                 if (value != null) {
355                     _url = value.Trim();
356                 }
357
358                 _virtualPath = CreateVirtualPathFromUrl(_url);
359             }
360         }
361
362         internal VirtualPath VirtualPath {
363             get {
364                 return _virtualPath;
365             }
366         }
367
368         private VirtualPath CreateVirtualPathFromUrl(string url) {
369             if (String.IsNullOrEmpty(url)) {
370                 return null;
371             }
372
373             if (!UrlPath.IsValidVirtualPathWithoutProtocol(url)) {
374                 return null;
375             }
376
377             if (UrlPath.IsAbsolutePhysicalPath(url)) {
378                 return null;
379             }
380
381             // Do not generate the virtualPath class at designtime.
382             if (HttpRuntime.AppDomainAppVirtualPath == null) {
383                 return null;
384             }
385
386             if (UrlPath.IsRelativeUrl(url) && !UrlPath.IsAppRelativePath(url)) {
387                 url = UrlPath.Combine(HttpRuntime.AppDomainAppVirtualPathString, url);
388             }
389
390             // Remove the query string from url so the path can be validated by Authorization module.
391             int queryStringIndex = url.IndexOf('?');
392             if (queryStringIndex != -1) {
393                 url = url.Substring(0, queryStringIndex);
394             }
395
396             return VirtualPath.Create(url, 
397                 VirtualPathOptions.AllowAbsolutePath | VirtualPathOptions.AllowAppRelativePath);
398         }
399
400         public virtual SiteMapNode Clone() {
401             ArrayList newRoles = null;
402             NameValueCollection newAttributes = null;
403             NameValueCollection newResourceKeys = null;
404
405             if (_roles != null) {
406                 newRoles = new ArrayList(_roles);
407             }
408             if (_attributes != null) {
409                 newAttributes = new NameValueCollection(_attributes);
410             }
411             if (_resourceKeys != null) {
412                 newResourceKeys = new NameValueCollection(_resourceKeys);
413             }
414
415             SiteMapNode newNode = new SiteMapNode(_provider, Key, Url, Title, Description, newRoles, newAttributes, newResourceKeys, _resourceKey);
416             return newNode;
417         }
418
419         public virtual SiteMapNode Clone(bool cloneParentNodes) {
420             SiteMapNode current = Clone();
421
422             if (cloneParentNodes) {
423                 SiteMapNode node = current;
424                 SiteMapNode parent = ParentNode;
425                 while (parent != null) {
426                     SiteMapNode cloneParent = parent.Clone();
427                     node.ParentNode = cloneParent;
428                     cloneParent.ChildNodes = new SiteMapNodeCollection(node);
429
430                     parent = parent.ParentNode;
431                     node = cloneParent;
432                 }
433             }
434
435             return current;
436         }
437
438         public override bool Equals(object obj) {
439             SiteMapNode node = obj as SiteMapNode;
440             return node != null && (_key == node.Key) && 
441                 (String.Equals(_url, node._url, StringComparison.OrdinalIgnoreCase));
442         }
443
444         public SiteMapNodeCollection GetAllNodes() {
445             SiteMapNodeCollection collection = new SiteMapNodeCollection();
446             GetAllNodesRecursive(collection);
447             return SiteMapNodeCollection.ReadOnly(collection);
448         }
449
450         private void GetAllNodesRecursive(SiteMapNodeCollection collection) {
451             SiteMapNodeCollection childNodes = this.ChildNodes;
452
453             if (childNodes != null && childNodes.Count > 0) {
454                 collection.AddRange(childNodes);
455                 foreach(SiteMapNode node in childNodes)
456                     node.GetAllNodesRecursive(collection);
457             }
458         }
459
460         public SiteMapDataSourceView GetDataSourceView(SiteMapDataSource owner, string viewName) {
461             return new SiteMapDataSourceView(owner, viewName, this);
462         }
463
464
465         public SiteMapHierarchicalDataSourceView GetHierarchicalDataSourceView() {
466             return new SiteMapHierarchicalDataSourceView(this);
467         }
468
469         // Helpe method to retrieve localized string based on attribute name
470         protected string GetExplicitResourceString(string attributeName, string defaultValue, bool throwIfNotFound) {
471             if (attributeName == null) {
472                 throw new ArgumentNullException("attributeName");
473             }
474
475             string text = null;
476             if (_resourceKeys != null) {
477                 string[] keys = _resourceKeys.GetValues(attributeName);
478                 if (keys != null && keys.Length > 1) {
479                     try {
480                         text = ResourceExpressionBuilder.GetGlobalResourceObject(keys[0], keys[1]) as string;
481                     }
482                     catch (MissingManifestResourceException) {
483                         if (defaultValue != null) {
484                             return defaultValue;
485                         }
486                     }
487
488                     if (text == null && throwIfNotFound) {
489                         // throw if default value is not specified.
490                         throw new InvalidOperationException(
491                             SR.GetString(SR.Res_not_found_with_class_and_key, keys[0], keys[1])); ;
492                     }
493                 }
494             }
495
496             return text;
497         }
498
499         // Only use the key to get the hashcode since url can be changed and makes the objects mutable.
500         public override int GetHashCode() {
501             return _key.GetHashCode();
502         }
503
504         // Helper method to retrieve localized string based on attribute name
505         protected string GetImplicitResourceString(string attributeName) {
506             if (attributeName == null) {
507                 throw new ArgumentNullException("attributeName");
508             }
509
510             string text = null;
511             if (!String.IsNullOrEmpty(_resourceKey)) {
512                 try {
513                     text = ResourceExpressionBuilder.GetGlobalResourceObject(Provider.ResourceKey, ResourceKey + "." + attributeName) as String;
514                 }
515                 catch { }
516             }
517
518             return text;
519         }
520
521         public virtual bool IsAccessibleToUser(HttpContext context) {
522             return _provider.IsAccessibleToUser(context, this);
523         }
524
525         public virtual bool IsDescendantOf(SiteMapNode node) {
526             SiteMapNode parent = ParentNode;
527             while (parent != null) {
528                 if (parent.Equals(node)) {
529                     return true;
530                 }
531
532                 parent = parent.ParentNode;
533             }
534
535             return false;
536         }
537
538         public override string ToString() {
539             return Title;
540         }
541
542         #region ICloneable implementation
543
544         /// <internalonly/>
545         object ICloneable.Clone() {
546             return Clone();
547         }
548         #endregion
549
550         #region IHierarchyData implementation
551
552         /// <internalonly/>
553         bool IHierarchyData.HasChildren {
554             get {
555                 return HasChildNodes;
556             }
557         }
558
559
560         /// <internalonly/>
561         object IHierarchyData.Item {
562             get {
563                 return this;
564             }
565         }
566
567
568         /// <internalonly/>
569         string IHierarchyData.Path {
570             get {
571                 return Key;
572             }
573         }
574
575
576         /// <internalonly/>
577         string IHierarchyData.Type {
578             get {
579                 return _siteMapNodeType;
580             }
581         }
582
583
584         /// <internalonly/>
585         IHierarchicalEnumerable IHierarchyData.GetChildren() {
586             return ChildNodes;
587         }
588
589
590         /// <internalonly/>
591         IHierarchyData IHierarchyData.GetParent() {
592             SiteMapNode parentNode = ParentNode;
593             if (parentNode == null)
594                 return null;
595
596             return parentNode;
597         }
598         #endregion
599
600         #region INavigateUIData implementations
601         string INavigateUIData.Description {
602             get {
603                 return Description;
604             }
605         }
606         
607
608         /// <internalonly/>
609         string INavigateUIData.Name {
610              get {
611                 return Title;
612              }
613         }
614
615
616         /// <internalonly/>
617         string INavigateUIData.NavigateUrl {
618             get {
619                 return Url;
620             }
621         }
622
623
624         /// <internalonly/>
625         string INavigateUIData.Value {
626             get {
627                 return Title;
628             }
629         }
630         #endregion
631     }
632 }