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)
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:
15 The above copyright notice and this permission notice shall be included in all
16 copies or substantial portions of the Software.
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.
26 namespace System.Web.Configuration.nBrowser
29 using System.Collections.Generic;
31 using System.Text.RegularExpressions;
35 #region Public Properties
39 public NodeType NameType
67 public string ParentId
95 public string MarkupTextWriterType
99 return pMarkupTextWriterType;
103 pMarkupTextWriterType = value;
109 public string FileName
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;
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;
145 /// <param name="xmlNode"></param>
146 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1059")]
147 public Node(System.Xml.XmlNode xmlNode)
149 this.xmlNode = xmlNode;
150 this.ResetChildern();
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;
168 /// <param name="node"></param>
169 private void ProcessIdentification(System.Xml.XmlNode node)
171 //I know not all of these will be used but enough will
172 Identification = new System.Web.Configuration.nBrowser.Identification[node.ChildNodes.Count];
175 for (int a = 0;a <= node.ChildNodes.Count - 1;a++)
177 switch (node.ChildNodes[a].NodeType)
179 case System.Xml.XmlNodeType.Text:
181 case System.Xml.XmlNodeType.Comment:
185 string patterngroup = string.Empty;
186 string patternname = string.Empty;
188 if (string.Compare(node.ChildNodes[a].Name, "userAgent", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
190 patterngroup = "header";
191 patternname = "User-Agent";
193 else if (string.Compare(node.ChildNodes[a].Name, "header", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
195 patterngroup = node.ChildNodes[a].Name;
196 patternname = node.ChildNodes[a].Attributes["name"].Value;
198 else if (string.Compare(node.ChildNodes[a].Name, "capability", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
200 patterngroup = node.ChildNodes[a].Name;
201 patternname = node.ChildNodes[a].Attributes["name"].Value;
205 throw new nBrowser.Exception("Invalid Node found in Identification");
208 if (node.ChildNodes[a].Attributes["match"] != null)
211 Identification[i] = new System.Web.Configuration.nBrowser.Identification(true, patterngroup, patternname, node.ChildNodes[a].Attributes["match"].Value);
213 else if (node.ChildNodes[a].Attributes["nonMatch"] != null)
216 Identification[i] = new System.Web.Configuration.nBrowser.Identification(false, patterngroup, patternname, node.ChildNodes[a].Attributes["nonMatch"].Value);
223 /// <param name="node"></param>
224 private void ProcessCapture(System.Xml.XmlNode node)
226 //I know not all of these will be used but enough will
227 Capture = new System.Web.Configuration.nBrowser.Identification[node.ChildNodes.Count];
230 for (int a = 0;a <= node.ChildNodes.Count - 1;a++)
232 switch (node.ChildNodes[a].NodeType)
234 case System.Xml.XmlNodeType.Text:
236 case System.Xml.XmlNodeType.Comment:
240 string pattern = string.Empty;
241 string patterngroup = string.Empty;
242 string patternname = string.Empty;
244 if (node.ChildNodes[a].Name == "userAgent")
246 patterngroup = "header";
247 patternname = "User-Agent";
251 patterngroup = node.ChildNodes[a].Name;
252 patternname = node.ChildNodes[a].Attributes["name"].Value;
254 pattern = node.ChildNodes[a].Attributes["match"].Value;
256 Capture[i] = new System.Web.Configuration.nBrowser.Identification(true, patterngroup, patternname, pattern);
262 /// <param name="node"></param>
263 private void ProcessCapabilities(System.Xml.XmlNode node)
265 Capabilities = new System.Collections.Specialized.NameValueCollection(node.ChildNodes.Count);
267 for (int a = 0;a <= node.ChildNodes.Count - 1;a++)
269 if (node.ChildNodes[a].NodeType == System.Xml.XmlNodeType.Comment)
273 string name = string.Empty;
274 string value = string.Empty;
275 for (int b = 0;b <= node.ChildNodes[a].Attributes.Count - 1;b++)
277 switch (node.ChildNodes[a].Attributes[b].Name)
280 name = node.ChildNodes[a].Attributes[b].Value;
283 value = node.ChildNodes[a].Attributes[b].Value;
289 Capabilities[name] = value;
296 /// <param name="node"></param>
297 private void ProcessControlAdapters(System.Xml.XmlNode node)
299 Adapter = new System.Collections.Specialized.NameValueCollection();
300 for (int b = 0;b <= node.Attributes.Count - 1;b++)
302 switch (node.Attributes[b].Name)
304 case "markupTextWriterType":
305 MarkupTextWriterType = node.Attributes[b].Value;
309 for (int a = 0;a <= node.ChildNodes.Count - 1;a++)
311 if (node.ChildNodes[a].NodeType == System.Xml.XmlNodeType.Comment)
315 else if (node.ChildNodes[a].NodeType == System.Xml.XmlNodeType.Text)
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++)
324 if (string.Compare(x.Attributes[i].Name, "controlType", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
326 controlType = x.Attributes[i].Value;
328 else if (string.Compare(x.Attributes[i].Name, "adapterType", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
330 adapterType = x.Attributes[i].Value;
333 if (controlType.Length > 0 && adapterType.Length > 0)
335 Adapter[controlType] = adapterType;
339 AdapterControlTypes = null;
346 /// <param name="node"></param>
347 private void ProcessSampleHeaders(System.Xml.XmlNode node)
349 sampleHeaders = new System.Collections.Specialized.NameValueCollection(node.ChildNodes.Count);
351 for (int a = 0;a <= node.ChildNodes.Count - 1;a++)
353 if (node.ChildNodes[a].NodeType == System.Xml.XmlNodeType.Comment)
357 string name = string.Empty;
358 string value = string.Empty;
359 for (int b = 0;b <= node.ChildNodes[a].Attributes.Count - 1;b++)
361 switch (node.ChildNodes[a].Attributes[b].Name)
364 name = node.ChildNodes[a].Attributes[b].Value;
367 value = node.ChildNodes[a].Attributes[b].Value;
373 sampleHeaders[name] = value;
377 internal void ResetChildern()
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>();
387 public bool HasChildren
391 if (Children.Count > -1)
403 //Making sure we start off on a good footing.
407 AdapterControlTypes = null;
409 if (string.Compare(this.xmlNode.Name, "browser", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
411 this.NameType = NodeType.Browser;
413 else if (string.Compare(this.xmlNode.Name, "defaultBrowser", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
415 this.NameType = NodeType.DefaultBrowser;
417 else if (string.Compare(this.xmlNode.Name, "gateway", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
419 this.NameType = NodeType.Gateway;
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++)
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)
436 Id = xmlNode.Attributes[a].Value.ToLower(System.Globalization.CultureInfo.CurrentCulture);
438 else if (string.Compare(xmlNode.Attributes[a].Name, "parentID", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
440 ParentId = xmlNode.Attributes[a].Value.ToLower(System.Globalization.CultureInfo.CurrentCulture);
442 else if (string.Compare(xmlNode.Attributes[a].Name, "refID", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
444 RefId = xmlNode.Attributes[a].Value.ToLower(System.Globalization.CultureInfo.CurrentCulture);
448 for (int a = 0;a <= xmlNode.ChildNodes.Count - 1;a++)
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)
455 ProcessIdentification(xmlNode.ChildNodes[a]);
457 else if (string.Compare(xmlNode.ChildNodes[a].Name, "capture", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
459 ProcessCapture(xmlNode.ChildNodes[a]);
461 else if (string.Compare(xmlNode.ChildNodes[a].Name, "capabilities", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
463 ProcessCapabilities(xmlNode.ChildNodes[a]);
465 else if (string.Compare(xmlNode.ChildNodes[a].Name, "controlAdapters", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
467 ProcessControlAdapters(xmlNode.ChildNodes[a]);
469 else if (string.Compare(xmlNode.ChildNodes[a].Name, "sampleHeaders", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
471 ProcessSampleHeaders(xmlNode.ChildNodes[a]);
474 if (Id == "default" && (Identification == null || Identification.Length == 0))
476 Identification = new Identification[1];
477 Identification[0] = new System.Web.Configuration.nBrowser.Identification(true, "header", "User-Agent", ".");
484 /// <param name="child"></param>
485 public void AddChild(Node child)
491 if (child.NameType == nBrowser.NodeType.Browser || child.NameType == nBrowser.NodeType.Gateway)
493 Children.Add(child.Id, child);
494 ChildrenKeys.Add(child.Id);
496 else if (child.NameType == NodeType.DefaultBrowser)
498 DefaultChildren.Add(child.Id, child);
499 DefaultChildrenKeys.Add(child.Id);
505 /// <param name="child"></param>
506 public void RemoveChild(Node child)
512 if (child.NameType == nBrowser.NodeType.Browser || child.NameType == nBrowser.NodeType.Gateway)
514 Children.Remove(child.Id);
515 ChildrenKeys.Remove(child.Id);
517 else if (child.NameType == NodeType.DefaultBrowser)
519 DefaultChildren.Remove(child.Id);
520 DefaultChildrenKeys.Remove(child.Id);
524 private Type FindType(string typeName)
526 foreach (System.Reflection.Assembly a in System.AppDomain.CurrentDomain.GetAssemblies())
528 string fullTypeName = typeName + "," + a.FullName;
529 Type t = System.Type.GetType(fullTypeName); // case-sensitive
532 t = System.Type.GetType(fullTypeName, false, true); // case-insensitive
536 throw new TypeLoadException(typeName);
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.
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)
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);
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.
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)
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"]);
577 if (RefId.Length == 0 && this.NameType != NodeType.DefaultBrowser)
579 //----------------------------------------------------------------------
580 //BrowserIdentification adds all the Identifiction matches to the match
581 //list if this node matches.
582 //----------------------------------------------------------------------
583 if (!BrowserIdentification(header, result, matchList))
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);
596 //----------------------------------------------------------------------
597 //Just adding all the Adapters to the current list.
598 //----------------------------------------------------------------------
601 LookupAdapterTypes();
602 for (int i = 0;i <= Adapter.Count - 1;i++)
604 result.AddAdapter(AdapterControlTypes [i], AdapterTypes [i]);
608 //----------------------------------------------------------------------
609 //Set the MarkupTextWriter in the result if set in this node.
610 //----------------------------------------------------------------------
611 if (MarkupTextWriterType != null && MarkupTextWriterType.Length > 0)
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);
625 //----------------------------------------------------------------------
626 //Adds all the sucessfull Capture matches to the matchList
627 //----------------------------------------------------------------------
628 for (int i = 0;i <= Capture.Length - 1;i++)
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)
638 if (Capture[i].Group == "header")
640 m = Capture[i].GetMatch(header[Capture[i].Name]);
642 else if (Capture[i].Group == "capability")
644 m = Capture[i].GetMatch(result[Capture[i].Name]);
646 if (Capture[i].IsMatchSuccessful(m) && m.Groups.Count > 0)
654 if (Capabilities != null)
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++)
664 //----------------------------------------------------------------------
665 //We need to further process these Capabilities to
666 //insert the proper information.
667 //----------------------------------------------------------------------
668 string v = Capabilities[i];
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
677 //----------------------------------------------------------------------
678 for (int a = matchList.Count - 1; a >= 0 && v != null && v.Length > 0 && v.IndexOf('$') > -1; a--)
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)
683 v = matchList[a].Result(v);
686 //----------------------------------------------------------------------
687 //Checks to make sure we extract the result we where looking for.
688 //----------------------------------------------------------------------
689 if (v.IndexOf('$') > -1 || v.IndexOf('%') > -1)
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 //----------------------------------------------------------------------
697 //double checks the values against the current Capabilities. to
698 //find any last minute matches. that are not defined by regluar
700 v = result.Replace(v);
703 result.AddCapabilities(Capabilities.Keys[i], v);
708 //----------------------------------------------------------------------
709 //Run the Default Children after the Parent Node is finished with
711 //----------------------------------------------------------------------
712 for (int i = 0;i <= DefaultChildren.Count - 1;i++)
714 string key = DefaultChildrenKeys[i];
715 Node node = DefaultChildren[key];
716 if (node.NameType == NodeType.DefaultBrowser)
718 node.Process(header, result, matchList);
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++)
731 string key = ChildrenKeys[i];
732 Node node = Children[key];
733 if (node.NameType == NodeType.Gateway)
735 node.Process(header, result, matchList);
738 for (int i = 0;i <= Children.Count - 1;i++)
740 string key = ChildrenKeys[i];
741 Node node = Children[key];
742 if (node.NameType == NodeType.Browser
743 && node.Process(header, result, matchList))
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)
759 if (Id.Length > 0 && RefId.Length > 0)
761 throw new nBrowser.Exception("Id and refID Attributes givin when there should only be one set not both");
763 if (Identification == null || Identification.Length == 0)
765 throw new nBrowser.Exception(String.Format("Missing Identification Section where one is required (Id={0}, RefID={1})", Id, RefId));
769 throw new nBrowser.Exception("Null Value where NameValueCollection expected ");
773 throw new nBrowser.Exception("Null Value where Result expected ");
777 System.Diagnostics.Trace.WriteLine(string.Format("{0}[{1}]", ("[" + this.Id + "]").PadRight(45), this.ParentId));
780 for (int i = 0;i <= Identification.Length - 1;i++)
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)
790 string v = string.Empty;
791 if (string.Compare(Identification[i].Group, "header", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
793 v = header[Identification[i].Name];
795 else if (string.Compare(Identification[i].Group, "capability", true, System.Globalization.CultureInfo.CurrentCulture) == 0)
797 v = result[Identification[i].Name];
799 //Not all headers will be sent by all browsers.
800 //so often a header search will return Null.
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)
812 System.Diagnostics.Trace.WriteLine(string.Format("{0}{1}", "Failed:".PadRight(45), Identification[i].Pattern));
819 System.Diagnostics.Trace.WriteLine(string.Format("{0}{1}", "Passed:".PadRight(45), Identification[i].Pattern));
821 if (m.Groups.Count > 0)
828 System.Diagnostics.Trace.WriteLine("");
833 private bool HaveAdapterTypes = false;
834 private object LookupAdapterTypesLock = new object();
835 private void LookupAdapterTypes()
837 if (Adapter == null || HaveAdapterTypes) return;
838 lock (LookupAdapterTypesLock)
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]);
852 HaveAdapterTypes = true;
859 public System.Collections.Specialized.NameValueCollection SampleHeader
863 return sampleHeaders;
867 /// Used to Display a Tree Like View of how the Nodes are organized.
869 /// <param name="writer"></param>
870 /// <param name="Position"></param>
871 public void Tree(System.Xml.XmlTextWriter xmlwriter, int position)
875 xmlwriter.WriteStartDocument();
876 xmlwriter.WriteStartElement(this.NameType.ToString());
877 xmlwriter.WriteRaw(System.Environment.NewLine);
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);
886 if (position != int.MaxValue)
890 for (int i = 0;i <= DefaultChildren.Count - 1;i++)
892 string key = (string)DefaultChildrenKeys[i];
893 Node node = DefaultChildren[key];
894 if (node.NameType == nBrowser.NodeType.DefaultBrowser)
896 node.Tree(xmlwriter, position);
900 for (int i = 0;i <= Children.Count - 1;i++)
902 string key = (string)ChildrenKeys[i];
903 Node node = Children[key];
904 if (node.NameType == nBrowser.NodeType.Gateway)
906 node.Tree(xmlwriter, position);
910 for (int i = 0;i <= Children.Count - 1;i++)
912 string key = (string)ChildrenKeys[i];
913 Node node = Children[key];
914 if (node.NameType == nBrowser.NodeType.Browser)
916 node.Tree(xmlwriter, position);
919 if (position != int.MinValue)
923 xmlwriter.WriteEndElement();
924 xmlwriter.WriteRaw(System.Environment.NewLine);
927 xmlwriter.WriteEndDocument();
931 public System.Collections.ObjectModel.Collection<string> HeaderNames(System.Collections.ObjectModel.Collection<string> list)
933 if (Identification != null)
935 for (int i = 0;i <= Identification.Length - 1;i++)
937 if (Identification[i] == null)
941 if (Identification[i].Group == "header")
943 if (list.Contains(Identification[i].Name) == false)
945 list.Add(Identification[i].Name);
952 for (int i = 0;i <= Capture.Length - 1;i++)
954 if (Capture[i] == null)
958 if (Capture[i].Group == "header")
960 if (list.Contains(Capture[i].Name) == false)
962 list.Add(Capture[i].Name);
967 for (int i = 0;i <= DefaultChildren.Count - 1;i++)
969 string key = (string)DefaultChildrenKeys[i];
970 Node node = DefaultChildren[key];
971 if (node.NameType == nBrowser.NodeType.DefaultBrowser)
973 list = node.HeaderNames(list);
977 for (int i = 0;i <= Children.Count - 1;i++)
979 string key = (string)ChildrenKeys[i];
980 Node node = Children[key];
981 if (node.NameType == nBrowser.NodeType.Gateway)
983 list = node.HeaderNames(list);
987 for (int i = 0;i <= Children.Count - 1;i++)
989 string key = (string)ChildrenKeys[i];
990 Node node = Children[key];
991 if (node.NameType == nBrowser.NodeType.Browser)
993 list = node.HeaderNames(list);
1000 /// Merge capabilities, captures, markupTextWriters, and adapters from another Node into this Node.
1002 /// <param name="n">node to merge with this node</param>
1003 public void MergeFrom(Node n)
1005 if (n.Capabilities != null)
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];
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;
1025 if (n.MarkupTextWriterType != null && n.MarkupTextWriterType.Length > 0)
1026 MarkupTextWriterType = n.MarkupTextWriterType;
1028 if (n.Adapter != null)
1030 if (Adapter == null)
1031 Adapter = new System.Collections.Specialized.NameValueCollection();
1032 foreach (string controlType in n.Adapter)
1033 Adapter[controlType] = n.Adapter[controlType];