Implement DiagnosticsConfigurationHandler, so that .config files can contain
[mono.git] / mcs / class / System / System.Diagnostics / DiagnosticsConfigurationHandler.cs
index f99cd1094c4e3287279c3e7bbe5a3b6143bdb99f..5f102e481de6fd5389ddbb58fc0a3aea5a615428 100644 (file)
 // Comments from John R. Hicks <angryjohn69@nc.rr.com> original implementation \r
 // can be found at: /mcs/docs/apidocs/xml/en/System.Diagnostics\r
 //\r
-// Author: \r
+// Authors\r
 //     John R. Hicks <angryjohn69@nc.rr.com>\r
+//     Jonathan Pryor <jonpryor@vt.edu>\r
 //\r
 // (C) 2002\r
 //\r
 using System;\r
+using System.Collections;\r
 using System.Configuration;\r
 using System.Xml;\r
 \r
 namespace System.Diagnostics\r
 {\r
-       public class DiagnosticsConfigurationHandler :\r
-               IConfigurationSectionHandler\r
+       internal sealed class DiagnosticsConfiguration\r
        {\r
-               public DiagnosticsConfigurationHandler()\r
+               private static IDictionary settings = null;\r
+\r
+               public static IDictionary Settings {\r
+                       get {\r
+                               // TODO: Does anybody know if this is actually thread-safe under .NET?\r
+                               // I've heard that this construct isn't safe under Java, but it's used\r
+                               // reasonably often under C++, so I'm not sure about .NET.\r
+                               if (settings == null) {\r
+                                       lock (typeof(DiagnosticsConfiguration)) {\r
+                                               if (settings == null)\r
+                                                       settings = (IDictionary) ConfigurationSettings.GetConfig ("system.diagnostics");\r
+                                       }\r
+                               }\r
+                               return settings;\r
+                       }\r
+               }\r
+       }\r
+\r
+       public class DiagnosticsConfigurationHandler : IConfigurationSectionHandler\r
+       {\r
+               delegate void ElementHandler (IDictionary d, XmlNode node);\r
+\r
+               IDictionary elementHandlers = new Hashtable ();\r
+\r
+               public DiagnosticsConfigurationHandler ()\r
+               {\r
+                       elementHandlers ["assert"] = new ElementHandler (AddAssertNode);\r
+                       elementHandlers ["switches"] = new ElementHandler (AddSwitchesNode);\r
+                       elementHandlers ["trace"] = new ElementHandler (AddTraceNode);\r
+               }\r
+\r
+               public virtual object Create (object parent, object configContext, XmlNode section)\r
+               {\r
+                       IDictionary d;\r
+                       if (parent == null)\r
+                               d = new Hashtable (CaseInsensitiveHashCodeProvider.Default, CaseInsensitiveComparer.Default);\r
+                       else\r
+                               d = (IDictionary) ((ICloneable)parent).Clone();\r
+\r
+                       foreach (XmlNode child in section.ChildNodes) {\r
+                               XmlNodeType type = child.NodeType;\r
+\r
+                               switch (type) {\r
+                               /* ignore */\r
+                               case XmlNodeType.Whitespace:\r
+                               case XmlNodeType.Comment:\r
+                                       continue;\r
+                               case XmlNodeType.Element:\r
+                                       ElementHandler eh = (ElementHandler) elementHandlers [child.Name];\r
+                                       if (eh != null)\r
+                                               eh (d, child);\r
+                                       else\r
+                                               ThrowUnrecognizedElement (child);\r
+                                       break;\r
+                               default:\r
+                                       ThrowUnrecognizedElement (child);\r
+                                       break;\r
+                               }\r
+                       }\r
+\r
+                       return d;\r
+               }\r
+\r
+               // Remarks: Both attribute are optional\r
+               private void AddAssertNode (IDictionary d, XmlNode node)\r
                {\r
-                       \r
+                       XmlAttributeCollection c = node.Attributes;\r
+                       string assertuienabled = GetAttribute (c, "assertuienabled", false, node);\r
+                       string logfilename = GetAttribute (c, "logfilename", false, node);\r
+                       ValidateInvalidAttributes (c, node);\r
+                       try {\r
+                               d ["assertuienabled"] = bool.Parse (assertuienabled);\r
+                       }\r
+                       catch (Exception e) {\r
+                               throw new ConfigurationException ("The `assertuienabled' attribute must be `true' or `false'",\r
+                                               e, node);\r
+                       }\r
+                       d ["logfilename"] = logfilename;\r
                }\r
-               \r
-               public virtual object Create(\r
-                                            object parent,\r
-                                            object configContext,\r
-                                            XmlNode section)\r
+\r
+               // name attribute is required, value is optional\r
+               // Docs do not define "remove" or "clear" elements, but .NET recognizes\r
+               // them\r
+               private void AddSwitchesNode (IDictionary d, XmlNode node)\r
                {\r
-                   throw new NotImplementedException();                                \r
+                       // There are no attributes on <switch/>\r
+                       ValidateInvalidAttributes (node.Attributes, node);\r
+\r
+                       IDictionary newNodes = new Hashtable ();\r
+\r
+                       foreach (XmlNode child in node.ChildNodes) {\r
+                               XmlNodeType t = child.NodeType;\r
+                               if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)\r
+                                       continue;\r
+                               if (t == XmlNodeType.Element) {\r
+                                       XmlAttributeCollection attributes = child.Attributes;\r
+                                       string name = null;\r
+                                       string value = null;\r
+                                       switch (child.Name) {\r
+                                               case "add":\r
+                                                       name = GetAttribute (attributes, "name", true, child);\r
+                                                       value = GetAttribute (attributes, "value", false, child);\r
+                                                       newNodes[name] = value;\r
+                                                       break;\r
+                                               case "remove":\r
+                                                       name = GetAttribute (attributes, "name", true, child);\r
+                                                       newNodes.Remove (name);\r
+                                                       break;\r
+                                               case "clear":\r
+                                                       newNodes.Clear ();\r
+                                                       break;\r
+                                               default:\r
+                                                       ThrowUnrecognizedElement (child);\r
+                                                       break;\r
+                                       }\r
+                                       ValidateInvalidAttributes (attributes, child);\r
+                               }\r
+                               else\r
+                                       ThrowUnrecognizedNode (child);\r
+                       }\r
+\r
+                       d [node.Name] = newNodes;\r
                }\r
-               \r
-               \r
-               ~DiagnosticsConfigurationHandler() \r
+\r
+               private void AddTraceNode (IDictionary d, XmlNode node)\r
+               {\r
+                       AddTraceAttributes (d, node);\r
+\r
+                       foreach (XmlNode child in node.ChildNodes) {\r
+                               XmlNodeType t = child.NodeType;\r
+                               if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)\r
+                                       continue;\r
+                               if (t == XmlNodeType.Element) {\r
+                                       if (child.Name == "listeners")\r
+                                               AddTraceListeners (child);\r
+                                       else\r
+                                               ThrowUnrecognizedElement (child);\r
+                                       ValidateInvalidAttributes (child.Attributes, child);\r
+                               }\r
+                               else\r
+                                       ThrowUnrecognizedNode (child);\r
+                       }\r
+               }\r
+\r
+               // all attributes are optional\r
+               private void AddTraceAttributes (IDictionary d, XmlNode node)\r
+               {\r
+                       XmlAttributeCollection c = node.Attributes;\r
+                       string autoflush = GetAttribute (c, "autoflush", false, node);\r
+                       string indentsize = GetAttribute (c, "indentsize", false, node);\r
+                       ValidateInvalidAttributes (c, node);\r
+                       try {\r
+                               d ["autoflush"] = bool.Parse (autoflush);\r
+                       }\r
+                       catch (Exception e) {\r
+                               throw new ConfigurationException ("The `autoflush' attribute must be `true' or `false'",\r
+                                               e, node);\r
+                       }\r
+                       try {\r
+                               d ["indentsize"] = int.Parse (indentsize);\r
+                       }\r
+                       catch (Exception e) {\r
+                               throw new ConfigurationException ("The `indentsize' attribute must be an integral value.",\r
+                                               e, node);\r
+                       }\r
+               }\r
+\r
+               // only defines "add" and "remove", but "clear" also works\r
+               // for add, "name" and "type" are required; initializeData is optional\r
+               private void AddTraceListeners (XmlNode listenersNode)\r
+               {\r
+                       // There are no attributes on <listeners/>\r
+                       ValidateInvalidAttributes (listenersNode.Attributes, listenersNode);\r
+\r
+                       foreach (XmlNode child in listenersNode.ChildNodes) {\r
+                               XmlNodeType t = child.NodeType;\r
+                               if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)\r
+                                       continue;\r
+                               if (t == XmlNodeType.Element) {\r
+                                       XmlAttributeCollection attributes = child.Attributes;\r
+                                       string name = null;\r
+                                       string type = null;\r
+                                       string id = null;\r
+                                       switch (child.Name) {\r
+                                               case "add":\r
+                                                       name = GetAttribute (attributes, "name", true, child);\r
+                                                       type = GetAttribute (attributes, "type", true, child);\r
+                                                       id = GetAttribute (attributes, "initializeData", false, child);\r
+                                                       AddTraceListener (name, type, id);\r
+                                                       break;\r
+                                               case "remove":\r
+                                                       name = GetAttribute (attributes, "name", true, child);\r
+                                                       RemoveTraceListener (name);\r
+                                                       break;\r
+                                               case "clear":\r
+                                                       TraceImpl.Listeners.Clear ();\r
+                                                       break;\r
+                                               default:\r
+                                                       ThrowUnrecognizedElement (child);\r
+                                                       break;\r
+                                       }\r
+                                       ValidateInvalidAttributes (attributes, child);\r
+                               }\r
+                               else\r
+                                       ThrowUnrecognizedNode (child);\r
+                       }\r
+               }\r
+\r
+               private void AddTraceListener (string name, string type, string initializeData)\r
+               {\r
+                       Type t = Type.GetType (type);\r
+                       object[] args = null;\r
+                       if (initializeData == string.Empty)\r
+                               args = new object[]{name};\r
+                       else\r
+                               args = new object[]{initializeData, name};\r
+                       TraceListener l = (TraceListener) Activator.CreateInstance (t, args);\r
+                       TraceImpl.Listeners.Add (l);\r
+               }\r
+\r
+               private void RemoveTraceListener (string name)\r
+               {\r
+                       try {\r
+                               TraceImpl.Listeners.Remove (name);\r
+                       }\r
+                       catch (ArgumentException e) {\r
+                               // The specified listener wasn't in the collection\r
+                               // Ignore this; .NET does.\r
+                       }\r
+               }\r
+\r
+               private string GetAttribute (XmlAttributeCollection attrs, string attr, bool required, XmlNode node)\r
+               {\r
+                       XmlAttribute a = attrs[attr];\r
+\r
+                       string r = string.Empty;\r
+\r
+                       if (a != null) {\r
+                               r = a.Value;\r
+                               if (required)\r
+                                       ValidateAttribute (attr, r, node);\r
+                               attrs.Remove (a);\r
+                       }\r
+                       else if (required)\r
+                               ThrowMissingAttribute (attr, node);\r
+\r
+                       return r;\r
+               }\r
+\r
+               private void ValidateAttribute (string attribute, string value, XmlNode node)\r
+               {\r
+                       // Don't need to check for null; handled in GetAttribute\r
+                       if (value.Length == 0)\r
+                               throw new ConfigurationException (string.Format ("Required attribute `{0}' cannot be empty.", attribute), node);\r
+               }\r
+\r
+               private void ValidateInvalidAttributes (XmlAttributeCollection c, XmlNode node)\r
+               {\r
+                       if (c.Count != 0)\r
+                               ThrowUnrecognizedAttribute (c[0].Name, node);\r
+               }\r
+\r
+               private void ThrowMissingAttribute (string attribute, XmlNode node)\r
+               {\r
+                       throw new ConfigurationException (string.Format ("Missing required attribute `{0}'.", attribute), node);\r
+               }\r
+\r
+               private void ThrowUnrecognizedNode (XmlNode node)\r
+               {\r
+                       throw new ConfigurationException (\r
+                                       string.Format ("Unrecognized node `{0}'; nodeType={1}", node.Name, node.NodeType),\r
+                                       node);\r
+               }\r
+\r
+               private void ThrowUnrecognizedElement (XmlNode node)\r
+               {\r
+                       throw new ConfigurationException (\r
+                                       string.Format ("Unrecognized element <{0}/>", node.Name),\r
+                                       node);\r
+               }\r
+\r
+               private void ThrowUnrecognizedAttribute (string attribute, XmlNode node)\r
                {\r
-                       \r
+                       throw new ConfigurationException (\r
+                                       string.Format ("Unrecognized attribute `{0}' on element <{1}/>.", attribute, node.Name),\r
+                                       node);\r
                }\r
        }\r
 }\r