BindingFlags.Public needed here as Exception.HResult is now public in .NET 4.5. This...
[mono.git] / mcs / class / System.Web / System.Web.Configuration_2.0 / nBrowser / Node.cs
1 #if NET_2_0
2 /*
3 Used to determine Browser Capabilities by the Browsers UserAgent String and related
4 Browser supplied Headers.
5 Copyright (C) 2002-Present  Owen Brady (Ocean at owenbrady dot net) 
6 and Dean Brettle (dean at brettle dot com)
7
8 Permission is hereby granted, free of charge, to any person obtaining a copy 
9 of this software and associated documentation files (the "Software"), to deal
10 in the Software without restriction, including without limitation the rights 
11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
12 copies of the Software, and to permit persons to whom the Software is furnished
13 to do so, subject to the following conditions:
14
15 The above copyright notice and this permission notice shall be included in all 
16 copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
19 INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
20 PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
21 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
22 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
23 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26 namespace System.Web.Configuration.nBrowser
27 {
28         using System;
29         using System.Collections.Generic;
30         using System.Text;
31         using System.Text.RegularExpressions;
32                 
33         internal class Node
34         {
35                 #region Public Properties
36                 /// <summary>
37                 /// 
38                 /// </summary>
39                 public NodeType NameType
40                 {
41                         get
42                         {
43                                 return pName;
44                         }
45                         set
46                         {
47                                 pName = value;
48                         }
49                 }
50                 /// <summary>
51                 /// 
52                 /// </summary>
53                 public string Id
54                 {
55                         get
56                         {
57                                 return pId;
58                         }
59                         set
60                         {
61                                 pId = value;
62                         }
63                 }
64                 /// <summary>
65                 /// 
66                 /// </summary>
67                 public string ParentId
68                 {
69                         get
70                         {
71                                 return pParentID;
72                         }
73                         set
74                         {
75                                 pParentID = value;
76                         }
77                 }
78                 /// <summary>
79                 /// 
80                 /// </summary>
81                 public string RefId
82                 {
83                         get
84                         {
85                                 return pRefID;
86                         }
87                         set
88                         {
89                                 pRefID = value;
90                         }
91                 }
92                 /// <summary>
93                 /// 
94                 /// </summary>
95                 public string MarkupTextWriterType
96                 {
97                         get
98                         {
99                                 return pMarkupTextWriterType;
100                         }
101                         set
102                         {
103                                 pMarkupTextWriterType = value;
104                         }
105                 }
106                 /// <summary>
107                 /// 
108                 /// </summary>
109                 public string FileName
110                 {
111                         get
112                         {
113                                 return pFileName;
114                         }
115                         set
116                         {
117                                 pFileName = value;
118                         }
119                 }
120                 #endregion
121
122                 private NodeType pName = NodeType.None;
123                 private string pId = string.Empty;
124                 private string pParentID = string.Empty;
125                 private string pRefID = string.Empty;
126                 private string pMarkupTextWriterType = string.Empty;
127                 private string pFileName = string.Empty;
128
129                 private System.Xml.XmlNode xmlNode;
130                 private Identification[] Identification;
131                 private Identification[] Capture;
132                 private System.Collections.Specialized.NameValueCollection Capabilities;
133                 private System.Collections.Specialized.NameValueCollection Adapter;
134                 private Type[] AdapterControlTypes, AdapterTypes;
135                 private System.Collections.Generic.List<string> ChildrenKeys;
136                 private System.Collections.Generic.List<string> DefaultChildrenKeys;
137                 private System.Collections.Generic.SortedList<string, nBrowser.Node> Children;
138                 private System.Collections.Generic.SortedList<string, nBrowser.Node> DefaultChildren;
139                 private System.Collections.Specialized.NameValueCollection sampleHeaders;
140
141
142                 /// <summary>
143                 /// 
144                 /// </summary>
145                 /// <param name="xmlNode"></param>
146                 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1059")]
147                 public Node(System.Xml.XmlNode xmlNode)
148                 {
149                         this.xmlNode = xmlNode;
150                         this.ResetChildern();
151                         this.Reset();
152                 }
153                 /// <summary>
154                 /// 
155                 /// </summary>
156                 internal Node()
157                 {
158                         this.ResetChildern();
159                         Identification = new System.Web.Configuration.nBrowser.Identification[1];
160                         Identification[0] = new System.Web.Configuration.nBrowser.Identification(true, "header", "User-Agent", ".");
161                         this.Id = "[Base Node]";
162                         this.NameType = NodeType.Browser;
163
164                 }
165                 /// <summary>
166                 /// 
167                 /// </summary>
168                 /// <param name="node"></param>
169                 private void ProcessIdentification(System.Xml.XmlNode node)
170                 {
171                         //I know not all of these will be used but enough will
172                         Identification = new System.Web.Configuration.nBrowser.Identification[node.ChildNodes.Count];
173
174                         int i = -1;
175                         for (int a = 0;a <= node.ChildNodes.Count - 1;a++)
176                         {
177                                 switch (node.ChildNodes[a].NodeType)
178                                 {
179                                         case System.Xml.XmlNodeType.Text:
180                                                 continue;
181                                         case System.Xml.XmlNodeType.Comment:
182                                                 continue;
183                                 }
184
185                                 string patterngroup = string.Empty;
186                                 string patternname = string.Empty;
187
188                                 if (string.Compare(node.ChildNodes[a].Name, "userAgent", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
189                                 {
190                                         patterngroup = "header";
191                                         patternname = "User-Agent";
192                                 }
193                                 else if (string.Compare(node.ChildNodes[a].Name, "header", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
194                                 {
195                                         patterngroup = node.ChildNodes[a].Name;
196                                         patternname = node.ChildNodes[a].Attributes["name"].Value;
197                                 }
198                                 else if (string.Compare(node.ChildNodes[a].Name, "capability", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
199                                 {
200                                         patterngroup = node.ChildNodes[a].Name;
201                                         patternname = node.ChildNodes[a].Attributes["name"].Value;
202                                 }
203                                 else
204                                 {
205                                         throw new nBrowser.Exception("Invalid Node found in Identification");
206                                 }
207
208                                 if (node.ChildNodes[a].Attributes["match"] != null)
209                                 {
210                                         i++;
211                                         Identification[i] = new System.Web.Configuration.nBrowser.Identification(true, patterngroup, patternname, node.ChildNodes[a].Attributes["match"].Value);
212                                 }
213                                 else if (node.ChildNodes[a].Attributes["nonMatch"] != null)
214                                 {
215                                         i++;
216                                         Identification[i] = new System.Web.Configuration.nBrowser.Identification(false, patterngroup, patternname, node.ChildNodes[a].Attributes["nonMatch"].Value);
217                                 }
218                         }
219                 }
220                 /// <summary>
221                 /// 
222                 /// </summary>
223                 /// <param name="node"></param>
224                 private void ProcessCapture(System.Xml.XmlNode node)
225                 {
226                         //I know not all of these will be used but enough will
227                         Capture = new System.Web.Configuration.nBrowser.Identification[node.ChildNodes.Count];
228
229                         int i = -1;
230                         for (int a = 0;a <= node.ChildNodes.Count - 1;a++)
231                         {
232                                 switch (node.ChildNodes[a].NodeType)
233                                 {
234                                         case System.Xml.XmlNodeType.Text:
235                                                 continue;
236                                         case System.Xml.XmlNodeType.Comment:
237                                                 continue;
238                                 }
239
240                                 string pattern = string.Empty;
241                                 string patterngroup = string.Empty;
242                                 string patternname = string.Empty;
243
244                                 if (node.ChildNodes[a].Name == "userAgent")
245                                 {
246                                         patterngroup = "header";
247                                         patternname = "User-Agent";
248                                 }
249                                 else
250                                 {
251                                         patterngroup = node.ChildNodes[a].Name;
252                                         patternname = node.ChildNodes[a].Attributes["name"].Value;
253                                 }
254                                 pattern = node.ChildNodes[a].Attributes["match"].Value;
255                                 i++;
256                                 Capture[i] = new System.Web.Configuration.nBrowser.Identification(true, patterngroup, patternname, pattern);
257                         }
258                 }
259                 /// <summary>
260                 /// 
261                 /// </summary>
262                 /// <param name="node"></param>
263                 private void ProcessCapabilities(System.Xml.XmlNode node)
264                 {
265                         Capabilities = new System.Collections.Specialized.NameValueCollection(node.ChildNodes.Count);
266
267                         for (int a = 0;a <= node.ChildNodes.Count - 1;a++)
268                         {
269                                 if (node.ChildNodes[a].NodeType == System.Xml.XmlNodeType.Comment)
270                                 {
271                                         continue;
272                                 }
273                                 string name = string.Empty;
274                                 string value = string.Empty;
275                                 for (int b = 0;b <= node.ChildNodes[a].Attributes.Count - 1;b++)
276                                 {
277                                         switch (node.ChildNodes[a].Attributes[b].Name)
278                                         {
279                                                 case "name":
280                                                         name = node.ChildNodes[a].Attributes[b].Value;
281                                                         break;
282                                                 case "value":
283                                                         value = node.ChildNodes[a].Attributes[b].Value;
284                                                         break;
285                                         }
286                                 }
287                                 if (name.Length > 0)
288                                 {
289                                         Capabilities[name] = value;
290                                 }
291                         }
292                 }
293                 /// <summary>
294                 /// 
295                 /// </summary>
296                 /// <param name="node"></param>
297                 private void ProcessControlAdapters(System.Xml.XmlNode node)
298                 {
299                         Adapter = new System.Collections.Specialized.NameValueCollection();
300                         for (int b = 0;b <= node.Attributes.Count - 1;b++)
301                         {
302                                 switch (node.Attributes[b].Name)
303                                 {
304                                         case "markupTextWriterType":
305                                                 MarkupTextWriterType = node.Attributes[b].Value;
306                                                 break;
307                                 }
308                         }
309                         for (int a = 0;a <= node.ChildNodes.Count - 1;a++)
310                         {
311                                 if (node.ChildNodes[a].NodeType == System.Xml.XmlNodeType.Comment)
312                                 {
313                                         continue;
314                                 }
315                                 else if (node.ChildNodes[a].NodeType == System.Xml.XmlNodeType.Text)
316                                 {
317                                         continue;
318                                 }
319                                 System.Xml.XmlNode x = node.ChildNodes[a];
320                                 string controlType = string.Empty;
321                                 string adapterType = string.Empty;
322                                 for (int i = 0;i <= x.Attributes.Count - 1;i++)
323                                 {
324                                         if (string.Compare(x.Attributes[i].Name, "controlType", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
325                                         {
326                                                 controlType = x.Attributes[i].Value;
327                                         }
328                                         else if (string.Compare(x.Attributes[i].Name, "adapterType", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
329                                         {
330                                                 adapterType = x.Attributes[i].Value;
331                                         }
332                                 }
333                                 if (controlType.Length > 0 && adapterType.Length > 0)
334                                 {
335                                         Adapter[controlType] = adapterType;
336                                 }
337                         }
338
339                         AdapterControlTypes = null;
340                         AdapterTypes = null;
341                 }
342
343                 /// <summary>
344                 /// 
345                 /// </summary>
346                 /// <param name="node"></param>
347                 private void ProcessSampleHeaders(System.Xml.XmlNode node)
348                 {
349                         sampleHeaders = new System.Collections.Specialized.NameValueCollection(node.ChildNodes.Count);
350
351                         for (int a = 0;a <= node.ChildNodes.Count - 1;a++)
352                         {
353                                 if (node.ChildNodes[a].NodeType == System.Xml.XmlNodeType.Comment)
354                                 {
355                                         continue;
356                                 }
357                                 string name = string.Empty;
358                                 string value = string.Empty;
359                                 for (int b = 0;b <= node.ChildNodes[a].Attributes.Count - 1;b++)
360                                 {
361                                         switch (node.ChildNodes[a].Attributes[b].Name)
362                                         {
363                                                 case "name":
364                                                         name = node.ChildNodes[a].Attributes[b].Value;
365                                                         break;
366                                                 case "value":
367                                                         value = node.ChildNodes[a].Attributes[b].Value;
368                                                         break;
369                                         }
370                                 }
371                                 if (name.Length > 0)
372                                 {
373                                         sampleHeaders[name] = value;
374                                 }
375                         }
376                 }
377                 internal void ResetChildern()
378                 {
379                         Children = new System.Collections.Generic.SortedList<string, nBrowser.Node>();
380                         DefaultChildren = new System.Collections.Generic.SortedList<string, nBrowser.Node>();
381                         ChildrenKeys = new System.Collections.Generic.List<string>();
382                         DefaultChildrenKeys = new System.Collections.Generic.List<string>();
383                 }
384                 /// <summary>
385                 /// 
386                 /// </summary>
387                 public bool HasChildren
388                 {
389                         get
390                         {
391                                 if (Children.Count > -1)
392                                 {
393                                         return true;
394                                 }
395                                 return false;
396                         }
397                 }
398                 /// <summary>
399                 /// 
400                 /// </summary>
401                 public void Reset()
402                 {
403                         //Making sure we start off on a good footing.
404                         Capture = null;
405                         Capabilities = null;
406                         Adapter = null;
407                         AdapterControlTypes = null;
408                         AdapterTypes = null;
409                         if (string.Compare(this.xmlNode.Name, "browser", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
410                         {
411                                 this.NameType = NodeType.Browser;
412                         }
413                         else if (string.Compare(this.xmlNode.Name, "defaultBrowser", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
414                         {
415                                 this.NameType = NodeType.DefaultBrowser;
416                         }
417                         else if (string.Compare(this.xmlNode.Name, "gateway", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
418                         {
419                                 this.NameType = NodeType.Gateway;
420                         }
421
422
423                         //-------------------------------------------------------------------------
424                         //Looping though the Attributes is easier since and more efficient
425                         //then doing finds for specific attribute names. This also handles the
426                         //cases where there are no attributes really well. Also it doesn't care
427                         //about the order in witch the attributes are found either
428                         //-------------------------------------------------------------------------
429                         for (int a = 0;a <= xmlNode.Attributes.Count - 1;a++)
430                         {
431                                 //Reason I am not using a switch here because I do not have the ability
432                                 //to make sure the items are in the same upper/lower case as I am expecting
433                                 //so I default to ignore case and compare.
434                                 if (string.Compare(xmlNode.Attributes[a].Name, "id", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
435                                 {
436                                         Id = xmlNode.Attributes[a].Value.ToLower(System.Globalization.CultureInfo.CurrentCulture);
437                                 }
438                                 else if (string.Compare(xmlNode.Attributes[a].Name, "parentID", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
439                                 {
440                                         ParentId = xmlNode.Attributes[a].Value.ToLower(System.Globalization.CultureInfo.CurrentCulture);
441                                 }
442                                 else if (string.Compare(xmlNode.Attributes[a].Name, "refID", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
443                                 {
444                                         RefId = xmlNode.Attributes[a].Value.ToLower(System.Globalization.CultureInfo.CurrentCulture);
445                                 }
446                         }
447
448                         for (int a = 0;a <= xmlNode.ChildNodes.Count - 1;a++)
449                         {
450                                 //Reason I am not using a switch here because I do not have the ability
451                                 //to make sure the items are in the same upper/lower case as I am expecting
452                                 //so I default to ignore case and compare.
453                                 if (string.Compare(xmlNode.ChildNodes[a].Name, "identification", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
454                                 {
455                                         ProcessIdentification(xmlNode.ChildNodes[a]);
456                                 }
457                                 else if (string.Compare(xmlNode.ChildNodes[a].Name, "capture", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
458                                 {
459                                         ProcessCapture(xmlNode.ChildNodes[a]);
460                                 }
461                                 else if (string.Compare(xmlNode.ChildNodes[a].Name, "capabilities", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
462                                 {
463                                         ProcessCapabilities(xmlNode.ChildNodes[a]);
464                                 }
465                                 else if (string.Compare(xmlNode.ChildNodes[a].Name, "controlAdapters", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
466                                 {
467                                         ProcessControlAdapters(xmlNode.ChildNodes[a]);
468                                 }
469                                 else if (string.Compare(xmlNode.ChildNodes[a].Name, "sampleHeaders", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
470                                 {
471                                         ProcessSampleHeaders(xmlNode.ChildNodes[a]);
472                                 }
473                                 
474                                 if (Id == "default" && (Identification == null || Identification.Length == 0))
475                                 {
476                                         Identification = new Identification[1];
477                                         Identification[0] = new System.Web.Configuration.nBrowser.Identification(true, "header", "User-Agent", ".");
478                                 }
479                         }
480                 }
481                 /// <summary>
482                 /// 
483                 /// </summary>
484                 /// <param name="child"></param>
485                 public void AddChild(Node child)
486                 {
487                         if (child == null)
488                         {
489                                 return;
490                         }
491                         if (child.NameType == nBrowser.NodeType.Browser || child.NameType == nBrowser.NodeType.Gateway)
492                         {
493                                 Children.Add(child.Id, child);
494                                 ChildrenKeys.Add(child.Id);
495                         }
496                         else if (child.NameType == NodeType.DefaultBrowser)
497                         {
498                                 DefaultChildren.Add(child.Id, child);
499                                 DefaultChildrenKeys.Add(child.Id);
500                         }
501                 }
502                 /// <summary>
503                 /// 
504                 /// </summary>
505                 /// <param name="child"></param>
506                 public void RemoveChild(Node child)
507                 {
508                         if (child == null)
509                         {
510                                 return;
511                         }
512                         if (child.NameType == nBrowser.NodeType.Browser || child.NameType == nBrowser.NodeType.Gateway)
513                         {
514                                 Children.Remove(child.Id);
515                                 ChildrenKeys.Remove(child.Id);
516                         }
517                         else if (child.NameType == NodeType.DefaultBrowser)
518                         {
519                                 DefaultChildren.Remove(child.Id);
520                                 DefaultChildrenKeys.Remove(child.Id);
521                         }
522                 }
523                                 
524                 private Type FindType(string typeName)
525                 {
526                         foreach (System.Reflection.Assembly a in System.AppDomain.CurrentDomain.GetAssemblies())
527                         {
528                                 string fullTypeName = typeName + "," + a.FullName;
529                                 Type t = System.Type.GetType(fullTypeName); // case-sensitive
530                                 if (t != null)
531                                         return t;
532                                 t = System.Type.GetType(fullTypeName, false, true); // case-insensitive
533                                 if (t != null)
534                                         return t;
535                         }
536                         throw new TypeLoadException(typeName);
537                 }
538                 
539                 /// <summary>
540                 /// Matches the header collection against this subtree and uses the matchList
541                 /// and any new matches to augment the result.  This method calls ProcessSubtree()
542                 /// but then removes the matches that it adds to the matchList.
543                 /// </summary>
544                 /// <param name="header">the header collection to evaluate (invariant)</param>
545                 /// <param name="result">the result of the match (might be changed if a match is found)</param>
546                 /// <param name="matchList">the matches to use to do substitutions (invariant)</param>
547                 /// <returns>true iff this node or one of it's descendants matches</returns>
548                 internal bool Process(System.Collections.Specialized.NameValueCollection header, nBrowser.Result result,
549                                       System.Collections.Generic.List<Match> matchList)
550                 {
551                         // The real work is done in ProcessSubtree.  This method just ensures that matchList is restored
552                         // to its original state before returning.
553                         int origMatchListCount = matchList.Count;
554                         bool matched = ProcessSubtree(header, result, matchList);
555                         if (matchList.Count > origMatchListCount)
556                                 matchList.RemoveRange(origMatchListCount, matchList.Count-origMatchListCount);
557                         return matched;
558                 }
559                 
560                 /// <summary>
561                 /// Matches the header collection against this subtree, adds any new matches for this node to
562                 /// matchList, and uses the matchList to augment the result.  
563                 /// </summary>
564                 /// <param name="header">the header collection to evaluate (invariant)</param>
565                 /// <param name="result">the result of the match (might be changed if a match is found)</param>
566                 /// <param name="matchList">the matches to use to do substitutions, 
567                 /// possibly including new matches for this node.</param>
568                 /// <returns>true iff this node or one of it's descendants matches</returns>
569                 private bool ProcessSubtree(System.Collections.Specialized.NameValueCollection header, nBrowser.Result result, System.Collections.Generic.List<Match> matchList)
570                 {
571                         //----------------------------------------------------------------------
572                         //This is just coded over from MS version since if you pass in an empty
573                         //string for the key it returns the UserAgent header as a response.
574                         //----------------------------------------------------------------------
575                         result.AddCapabilities("", header["User-Agent"]);
576
577                         if (RefId.Length == 0 && this.NameType != NodeType.DefaultBrowser)
578                         {
579                                 //----------------------------------------------------------------------
580                                 //BrowserIdentification adds all the Identifiction matches to the match
581                                 //list if this node matches.
582                                 //----------------------------------------------------------------------
583                                 if (!BrowserIdentification(header, result, matchList))
584                                         return false;
585                         }
586
587                         result.AddMatchingBrowserId (this.Id);
588                         #region Browser Identification Successfull
589                         //----------------------------------------------------------------------
590                         //By reaching this point, it either means there were no Identification 
591                         //items to be processed or that all the Identification items have been 
592                         //passed. So just for debuging I want to output this Groups unique ID.
593                         //----------------------------------------------------------------------
594                         result.AddTrack("[" + this.NameType + "]\t" + this.Id);
595
596                         //----------------------------------------------------------------------
597                         //Just adding all the Adapters to the current list.
598                         //----------------------------------------------------------------------
599                         if (Adapter != null)
600                         {
601                                 LookupAdapterTypes();
602                                 for (int i = 0;i <= Adapter.Count - 1;i++)
603                                 {
604                                         result.AddAdapter(AdapterControlTypes [i], AdapterTypes [i]);
605                                 }
606                         }
607
608                         //----------------------------------------------------------------------
609                         //Set the MarkupTextWriter in the result if set in this node.
610                         //----------------------------------------------------------------------
611                         if (MarkupTextWriterType != null && MarkupTextWriterType.Length > 0)
612                         {
613                                 // Look for the type using a case-sensitive search
614                                 result.MarkupTextWriter = Type.GetType(MarkupTextWriterType);
615                                 // If we don't find it, try again using a case-insensitive search and throw
616                                 // and exception if we can't find it.
617                                 if (result.MarkupTextWriter == null)
618                                         result.MarkupTextWriter = Type.GetType(MarkupTextWriterType, true, true);
619                         }
620
621                         #endregion
622                         #region Capture
623                         if (Capture != null)
624                         {
625                                 //----------------------------------------------------------------------
626                                 //Adds all the sucessfull Capture matches to the matchList
627                                 //----------------------------------------------------------------------
628                                 for (int i = 0;i <= Capture.Length - 1;i++)
629                                 {
630                                         //shouldn't happen often, the null should
631                                         //signal the end of the list, I keep procssing
632                                         //the rest just in case
633                                         if (Capture[i] == null)
634                                         {
635                                                 continue;
636                                         }
637                                         Match m = null;
638                                         if (Capture[i].Group == "header")
639                                         {
640                                                 m = Capture[i].GetMatch(header[Capture[i].Name]);
641                                         }
642                                         else if (Capture[i].Group == "capability")
643                                         {
644                                                 m = Capture[i].GetMatch(result[Capture[i].Name]);
645                                         }
646                                         if (Capture[i].IsMatchSuccessful(m) && m.Groups.Count > 0)
647                                         {
648                                                 matchList.Add(m);
649                                         }
650                                 }
651                         }
652                         #endregion
653                         #region Capabilities
654                         if (Capabilities != null)
655                         {
656                                 //----------------------------------------------------------------------
657                                 //This section is what the whole exercise is about. Determining
658                                 //the Browser Capabilities. We know already that the current
659                                 //browser matches the criteria, now its a mater of updating
660                                 //the results with the new Capabilties listed.
661                                 //----------------------------------------------------------------------
662                                 for (int i = 0;i <= Capabilities.Count - 1;i++)
663                                 {
664                                         //----------------------------------------------------------------------
665                                         //We need to further process these Capabilities to 
666                                         //insert the proper information.
667                                         //----------------------------------------------------------------------
668                                         string v = Capabilities[i];
669
670                                         //----------------------------------------------------------------------
671                                         //Loop though the list of Identifiction/Capture Matches
672                                         //in reverse order. Meaning the newest Items in the list
673                                         //get checked first, then working to the oldest. Often times
674                                         //Minor /Major revisition numbers will be listed multple times
675                                         //and only the newest one (most recent matches) are the ones
676                                         //we want to insert.
677                                         //----------------------------------------------------------------------
678                                         for (int a = matchList.Count - 1; a >= 0 && v != null && v.Length > 0 &&  v.IndexOf('$') > -1; a--)
679                                         {
680                                                 // Don't do substitution if the match has no groups or was a nonMatch
681                                                 if (matchList[a].Groups.Count == 0 || !matchList[a].Success)
682                                                         continue;
683                                                 v = matchList[a].Result(v);
684                                         }
685
686                                         //----------------------------------------------------------------------
687                                         //Checks to make sure we extract the result we where looking for.
688                                         //----------------------------------------------------------------------
689                                         if (v.IndexOf('$') > -1 || v.IndexOf('%') > -1)
690                                         {
691                                                 //----------------------------------------------------------------------
692                                                 //Microsoft has a nasty habbit of using capability items in regular expressions
693                                                 //so I have to figure a nice way to working around it
694                                                 // <capability name="msdomversion"              value="${majorversion}${minorversion}" />
695                                                 //----------------------------------------------------------------------
696
697                                                 //double checks the values against the current Capabilities. to 
698                                                 //find any last minute matches. that are not defined by regluar
699                                                 //expressions
700                                                 v = result.Replace(v);
701                                         }
702
703                                         result.AddCapabilities(Capabilities.Keys[i], v);
704                                 }
705                         }
706                         #endregion
707
708                         //----------------------------------------------------------------------
709                         //Run the Default Children after the Parent Node is finished with 
710                         //what it is doing
711                         //----------------------------------------------------------------------
712                         for (int i = 0;i <= DefaultChildren.Count - 1;i++)
713                         {
714                                 string key = DefaultChildrenKeys[i];
715                                 Node node = DefaultChildren[key];
716                                 if (node.NameType == NodeType.DefaultBrowser)
717                                 {
718                                         node.Process(header, result, matchList);
719                                 }
720                         }
721                         //----------------------------------------------------------------------
722                         //processing all the children Browsers of this Parent if there are any.
723                         //----------------------------------------------------------------------
724                         //In nBrowser files, the Gateways should of been sorted so they are all 
725                         //at the top so that they can be ran first.
726                         //----------------------------------------------------------------------
727                         //According to the msdn2 documentation Gateways are suppost to be
728                         //all processed first. before the browser objects.
729                         for (int i = 0;i <= Children.Count - 1;i++)
730                         {
731                                 string key = ChildrenKeys[i];
732                                 Node node = Children[key];
733                                 if (node.NameType == NodeType.Gateway)
734                                 {
735                                         node.Process(header, result, matchList);
736                                 }
737                         }
738                         for (int i = 0;i <= Children.Count - 1;i++)
739                         {
740                                 string key = ChildrenKeys[i];
741                                 Node node = Children[key];
742                                 if (node.NameType == NodeType.Browser 
743                                     && node.Process(header, result, matchList))
744                                         break;
745                         }
746
747                         return true;
748                 }
749                 
750                 /// <summary>
751                 /// 
752                 /// </summary>
753                 /// <param name="header"></param>
754                 /// <param name="result"></param>
755                 /// <param name="matchList"></param>
756                 /// <returns>true iff this node is a match</returns>
757                 private bool BrowserIdentification(System.Collections.Specialized.NameValueCollection header, System.Web.Configuration.CapabilitiesResult result, System.Collections.Generic.List<Match> matchList)
758                 {
759                         if (Id.Length > 0 && RefId.Length > 0)
760                         {
761                                 throw new nBrowser.Exception("Id and refID Attributes givin when there should only be one set not both");
762                         }
763                         if (Identification == null || Identification.Length == 0)
764                         {
765                                 throw new nBrowser.Exception(String.Format("Missing Identification Section where one is required (Id={0}, RefID={1})", Id, RefId));
766                         }
767                         if (header == null)
768                         {
769                                 throw new nBrowser.Exception("Null Value where NameValueCollection expected ");
770                         }
771                         if (result == null)
772                         {
773                                 throw new nBrowser.Exception("Null Value where Result expected ");
774                         }
775
776 #if trace                          
777                         System.Diagnostics.Trace.WriteLine(string.Format("{0}[{1}]", ("[" + this.Id + "]").PadRight(45), this.ParentId));
778 #endif
779                         
780                         for (int i = 0;i <= Identification.Length - 1;i++)
781                         {
782
783                                 //shouldn't happen often, the null should
784                                 //signal the end of the list, I keep procssing
785                                 //the rest just in case
786                                 if (Identification[i] == null)
787                                 {
788                                         continue;
789                                 }
790                                 string v = string.Empty;
791                                 if (string.Compare(Identification[i].Group, "header", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
792                                 {
793                                         v = header[Identification[i].Name];
794                                 }
795                                 else if (string.Compare(Identification[i].Group, "capability", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
796                                 {
797                                         v = result[Identification[i].Name];
798                                 }
799                                 //Not all headers will be sent by all browsers.
800                                 //so often a header search will return Null.
801                                 if (v == null)
802                                 {
803                                         v = string.Empty;
804                                 }
805                                 Match m = Identification[i].GetMatch(v);
806                                 //----------------------------------------------------------------------
807                                 //we exit this method return the orginal Result back to  the calling method.
808                                 //----------------------------------------------------------------------
809                                 if (Identification[i].IsMatchSuccessful(m) == false)
810                                 {
811 #if trace 
812                                         System.Diagnostics.Trace.WriteLine(string.Format("{0}{1}", "Failed:".PadRight(45), Identification[i].Pattern));
813 #endif
814                                         return false;
815                                 }
816                                 else
817                                 {
818 #if trace 
819                                         System.Diagnostics.Trace.WriteLine(string.Format("{0}{1}", "Passed:".PadRight(45), Identification[i].Pattern));
820 #endif
821                                         if (m.Groups.Count > 0)
822                                         {
823                                                 matchList.Add(m);
824                                         }
825                                 }
826                         }
827 #if trace
828                         System.Diagnostics.Trace.WriteLine("");
829 #endif
830                         return true;
831                 }
832                 
833                 private bool HaveAdapterTypes = false;
834                 private object LookupAdapterTypesLock = new object();
835                 private void LookupAdapterTypes()
836                 {
837                         if (Adapter == null || HaveAdapterTypes) return;
838                         lock (LookupAdapterTypesLock)
839                         {
840                                 if (HaveAdapterTypes) return;
841                                 /* Lookup the types and store them for future use */
842                                 if (AdapterControlTypes == null)
843                                         AdapterControlTypes = new Type [Adapter.Count];
844                                 if (AdapterTypes == null)
845                                         AdapterTypes = new Type [Adapter.Count];
846                                 for (int i = 0;i <= Adapter.Count - 1;i++) {
847                                         if (AdapterControlTypes [i] == null)
848                                                 AdapterControlTypes [i] = FindType (Adapter.GetKey (i));
849                                         if (AdapterTypes [i] == null)
850                                                 AdapterTypes [i] = FindType (Adapter [i]);
851                                 }
852                                 HaveAdapterTypes = true;
853                         }
854                 }
855                 
856                 /// <summary>
857                 /// 
858                 /// </summary>
859                 public System.Collections.Specialized.NameValueCollection SampleHeader
860                 {
861                         get
862                         {
863                                 return sampleHeaders;
864                         }
865                 }
866                 /// <summary>
867                 /// Used to Display a Tree Like View of how the Nodes are organized.
868                 /// </summary>
869                 /// <param name="writer"></param>
870                 /// <param name="Position"></param>
871                 public void Tree(System.Xml.XmlTextWriter xmlwriter, int position)
872                 {
873                         if (position == 0)
874                         {
875                                 xmlwriter.WriteStartDocument();
876                                 xmlwriter.WriteStartElement(this.NameType.ToString());
877                                 xmlwriter.WriteRaw(System.Environment.NewLine);
878                         }
879
880                         string f = this.FileName;
881                         xmlwriter.WriteStartElement(this.NameType.ToString());
882                         xmlwriter.WriteAttributeString("FileName", f);
883                         xmlwriter.WriteAttributeString("ID", this.Id);
884                         xmlwriter.WriteRaw(System.Environment.NewLine);
885
886                         if (position != int.MaxValue)
887                         {
888                                 position++;
889                         }
890                         for (int i = 0;i <= DefaultChildren.Count - 1;i++)
891                         {
892                                 string key = (string)DefaultChildrenKeys[i];
893                                 Node node = DefaultChildren[key];
894                                 if (node.NameType == nBrowser.NodeType.DefaultBrowser)
895                                 {
896                                         node.Tree(xmlwriter, position);
897                                 }
898                         }
899
900                         for (int i = 0;i <= Children.Count - 1;i++)
901                         {
902                                 string key = (string)ChildrenKeys[i];
903                                 Node node = Children[key];
904                                 if (node.NameType == nBrowser.NodeType.Gateway)
905                                 {
906                                         node.Tree(xmlwriter, position);
907                                 }
908                         }
909
910                         for (int i = 0;i <= Children.Count - 1;i++)
911                         {
912                                 string key = (string)ChildrenKeys[i];
913                                 Node node = Children[key];
914                                 if (node.NameType == nBrowser.NodeType.Browser)
915                                 {
916                                         node.Tree(xmlwriter, position);
917                                 }
918                         }
919                         if (position != int.MinValue)
920                         {
921                                 position--;
922                         }
923                         xmlwriter.WriteEndElement();
924                         xmlwriter.WriteRaw(System.Environment.NewLine);
925                         if (position == 0)
926                         {
927                                 xmlwriter.WriteEndDocument();
928                                 xmlwriter.Flush();
929                         }
930                 }
931                 public System.Collections.ObjectModel.Collection<string> HeaderNames(System.Collections.ObjectModel.Collection<string> list)
932                 {
933                         if (Identification != null)
934                         {
935                                 for (int i = 0;i <= Identification.Length - 1;i++)
936                                 {
937                                         if (Identification[i] == null)
938                                         {
939                                                 continue;
940                                         }
941                                         if (Identification[i].Group == "header")
942                                         {
943                                                 if (list.Contains(Identification[i].Name) == false)
944                                                 {
945                                                         list.Add(Identification[i].Name);
946                                                 }
947                                         }
948                                 }
949                         }
950                         if (Capture != null)
951                         {
952                                 for (int i = 0;i <= Capture.Length - 1;i++)
953                                 {
954                                         if (Capture[i] == null)
955                                         {
956                                                 continue;
957                                         }
958                                         if (Capture[i].Group == "header")
959                                         {
960                                                 if (list.Contains(Capture[i].Name) == false)
961                                                 {
962                                                         list.Add(Capture[i].Name);
963                                                 }
964                                         }
965                                 }
966                         }
967                         for (int i = 0;i <= DefaultChildren.Count - 1;i++)
968                         {
969                                 string key = (string)DefaultChildrenKeys[i];
970                                 Node node = DefaultChildren[key];
971                                 if (node.NameType == nBrowser.NodeType.DefaultBrowser)
972                                 {
973                                         list = node.HeaderNames(list);
974                                 }
975                         }
976
977                         for (int i = 0;i <= Children.Count - 1;i++)
978                         {
979                                 string key = (string)ChildrenKeys[i];
980                                 Node node = Children[key];
981                                 if (node.NameType == nBrowser.NodeType.Gateway)
982                                 {
983                                         list = node.HeaderNames(list);
984                                 }
985                         }
986
987                         for (int i = 0;i <= Children.Count - 1;i++)
988                         {
989                                 string key = (string)ChildrenKeys[i];
990                                 Node node = Children[key];
991                                 if (node.NameType == nBrowser.NodeType.Browser)
992                                 {
993                                         list = node.HeaderNames(list);
994                                 }
995                         }
996                         return list;
997                 }
998                 
999                 /// <summary>
1000                 /// Merge capabilities, captures, markupTextWriters, and adapters from another Node into this Node.
1001                 /// </summary>
1002                 /// <param name="n">node to merge with this node</param>
1003                 public void MergeFrom(Node n)
1004                 {
1005                         if (n.Capabilities != null)
1006                         {
1007                                 if (Capabilities == null)
1008                                         Capabilities =  new System.Collections.Specialized.NameValueCollection(n.Capabilities.Count);
1009                                 foreach (string capName in n.Capabilities)
1010                                         Capabilities[capName] = n.Capabilities[capName];
1011                         }
1012                         
1013                         int newLength = 0;
1014                         if (Capture != null)
1015                                 newLength += Capture.Length;
1016                         if (n.Capture != null)
1017                                 newLength += n.Capture.Length;
1018                         Identification[] newCapture = new Identification[newLength];
1019                         if (Capture != null)
1020                                 Array.Copy(Capture, 0, newCapture, 0, Capture.Length);
1021                         if (n.Capture != null)
1022                                 Array.Copy(n.Capture, 0, newCapture, (Capture != null ? Capture.Length : 0), n.Capture.Length);
1023                         Capture = newCapture;
1024                         
1025                         if (n.MarkupTextWriterType != null && n.MarkupTextWriterType.Length > 0)
1026                                 MarkupTextWriterType = n.MarkupTextWriterType;
1027                         
1028                         if (n.Adapter != null)
1029                         {
1030                                 if (Adapter == null)
1031                                         Adapter = new System.Collections.Specialized.NameValueCollection();
1032                                 foreach (string controlType in n.Adapter)
1033                                         Adapter[controlType] = n.Adapter[controlType];
1034                         }
1035                 }
1036         }
1037 }
1038 #endif