2 // System.Diagnostics.DiagnosticsConfigurationHandler.cs
4 // Comments from John R. Hicks <angryjohn69@nc.rr.com> original implementation
5 // can be found at: /mcs/docs/apidocs/xml/en/System.Diagnostics
8 // John R. Hicks <angryjohn69@nc.rr.com>
9 // Jonathan Pryor <jonpryor@vt.edu>
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Collections;
36 using System.Configuration;
37 using System.Threading;
41 namespace System.Diagnostics
43 internal sealed class DiagnosticsConfiguration
46 private static object lock_ = new object();
48 private static object settings;
50 public static IDictionary Settings {
53 if (settings == null) {
54 object s = ConfigurationSettings.GetConfig ("system.diagnostics");
56 throw new Exception ("INTERNAL configuration error: failed to get configuration 'system.diagnostics'");
57 Thread.MemoryBarrier ();
58 while (Interlocked.CompareExchange (ref settings, s, null) == null) {
59 // do nothing; we're just setting settings.
61 Thread.MemoryBarrier ();
66 settings = ConfigurationSettings.GetConfig ("system.diagnostics");
69 return (IDictionary) settings;
74 public class DiagnosticsConfigurationHandler : IConfigurationSectionHandler
76 delegate void ElementHandler (IDictionary d, XmlNode node);
78 IDictionary elementHandlers = new Hashtable ();
80 public DiagnosticsConfigurationHandler ()
82 elementHandlers ["assert"] = new ElementHandler (AddAssertNode);
83 elementHandlers ["switches"] = new ElementHandler (AddSwitchesNode);
84 elementHandlers ["trace"] = new ElementHandler (AddTraceNode);
87 public virtual object Create (object parent, object configContext, XmlNode section)
91 d = new Hashtable (CaseInsensitiveHashCodeProvider.Default, CaseInsensitiveComparer.Default);
93 d = (IDictionary) ((ICloneable)parent).Clone();
95 foreach (XmlNode child in section.ChildNodes) {
96 XmlNodeType type = child.NodeType;
100 case XmlNodeType.Whitespace:
101 case XmlNodeType.Comment:
103 case XmlNodeType.Element:
104 ElementHandler eh = (ElementHandler) elementHandlers [child.Name];
108 ThrowUnrecognizedElement (child);
111 ThrowUnrecognizedElement (child);
119 // Remarks: Both attribute are optional
120 private void AddAssertNode (IDictionary d, XmlNode node)
122 XmlAttributeCollection c = node.Attributes;
123 string assertuienabled = GetAttribute (c, "assertuienabled", false, node);
124 string logfilename = GetAttribute (c, "logfilename", false, node);
125 ValidateInvalidAttributes (c, node);
126 if (assertuienabled != null) {
128 d ["assertuienabled"] = bool.Parse (assertuienabled);
130 catch (Exception e) {
131 throw new ConfigurationException ("The `assertuienabled' attribute must be `true' or `false'",
136 if (logfilename != null)
137 d ["logfilename"] = logfilename;
139 DefaultTraceListener dtl = (DefaultTraceListener) TraceImpl.Listeners["Default"];
141 if (assertuienabled != null)
142 dtl.AssertUiEnabled = (bool) d ["assertuienabled"];
143 if (logfilename != null)
144 dtl.LogFileName = logfilename;
147 if (node.ChildNodes.Count > 0)
148 ThrowUnrecognizedElement (node.ChildNodes[0]);
151 // name and value attributes are required
152 // Docs do not define "remove" or "clear" elements, but .NET recognizes
154 private void AddSwitchesNode (IDictionary d, XmlNode node)
156 // There are no attributes on <switch/>
157 ValidateInvalidAttributes (node.Attributes, node);
159 IDictionary newNodes = new Hashtable ();
161 foreach (XmlNode child in node.ChildNodes) {
162 XmlNodeType t = child.NodeType;
163 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
165 if (t == XmlNodeType.Element) {
166 XmlAttributeCollection attributes = child.Attributes;
169 switch (child.Name) {
171 name = GetAttribute (attributes, "name", true, child);
172 value = GetAttribute (attributes, "value", true, child);
173 value = AsString (value); ValidateIntegralValue (name, value);
174 newNodes [name] = value;
177 name = GetAttribute (attributes, "name", true, child);
178 newNodes.Remove (name);
184 ThrowUnrecognizedElement (child);
187 ValidateInvalidAttributes (attributes, child);
190 ThrowUnrecognizedNode (child);
193 d [node.Name] = newNodes;
196 private static int ValidateIntegralValue (string name, string value)
199 return int.Parse (value);
201 throw new ConfigurationException (string.Format (
203 "The value of a switch must be integral", name));
207 private void AddTraceNode (IDictionary d, XmlNode node)
209 AddTraceAttributes (d, node);
211 foreach (XmlNode child in node.ChildNodes) {
212 XmlNodeType t = child.NodeType;
213 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
215 if (t == XmlNodeType.Element) {
216 if (child.Name == "listeners")
217 AddTraceListeners (child);
219 ThrowUnrecognizedElement (child);
220 ValidateInvalidAttributes (child.Attributes, child);
223 ThrowUnrecognizedNode (child);
227 // all attributes are optional
228 private void AddTraceAttributes (IDictionary d, XmlNode node)
230 XmlAttributeCollection c = node.Attributes;
231 string autoflush = GetAttribute (c, "autoflush", false, node);
232 string indentsize = GetAttribute (c, "indentsize", false, node);
233 ValidateInvalidAttributes (c, node);
234 if (autoflush != null) {
236 bool b = bool.Parse (autoflush);
238 TraceImpl.AutoFlush = b;
240 catch (Exception e) {
241 throw new ConfigurationException ("The `autoflush' attribute must be `true' or `false'",
245 if (indentsize != null) {
247 int n = int.Parse (indentsize);
248 d ["indentsize"] = n;
249 TraceImpl.IndentSize = n;
251 catch (Exception e) {
252 throw new ConfigurationException ("The `indentsize' attribute must be an integral value.",
258 // only defines "add" and "remove", but "clear" also works
259 // for add, "name" and "type" are required; initializeData is optional
260 private void AddTraceListeners (XmlNode listenersNode)
262 // There are no attributes on <listeners/>
263 ValidateInvalidAttributes (listenersNode.Attributes, listenersNode);
265 foreach (XmlNode child in listenersNode.ChildNodes) {
266 XmlNodeType t = child.NodeType;
267 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
269 if (t == XmlNodeType.Element) {
270 XmlAttributeCollection attributes = child.Attributes;
274 switch (child.Name) {
276 name = GetAttribute (attributes, "name", true, child);
277 type = GetAttribute (attributes, "type", true, child);
278 id = GetAttribute (attributes, "initializeData", false, child);
279 AddTraceListener (name, type, id);
282 name = GetAttribute (attributes, "name", true, child);
283 RemoveTraceListener (name);
286 TraceImpl.Listeners.Clear ();
289 ThrowUnrecognizedElement (child);
292 ValidateInvalidAttributes (attributes, child);
295 ThrowUnrecognizedNode (child);
299 private void AddTraceListener (string name, string type, string initializeData)
301 Type t = Type.GetType (type);
303 throw new ConfigurationException (string.Format ("Invalid Type Specified: {0}", type));
308 if (initializeData != null) {
309 args = new object[] { initializeData };
310 types = new Type[] { typeof(string) };
317 System.Reflection.ConstructorInfo ctor = t.GetConstructor (types);
319 throw new ConfigurationException ("Couldn't find constructor for class " + type);
321 TraceListener l = (TraceListener) ctor.Invoke (args);
323 TraceImpl.Listeners.Add (l);
326 private void RemoveTraceListener (string name)
329 TraceImpl.Listeners.Remove (name);
331 catch (ArgumentException) {
332 // The specified listener wasn't in the collection
333 // Ignore this; .NET does.
335 catch (Exception e) {
336 throw new ConfigurationException (
337 string.Format ("Unknown error removing listener: {0}", name),
342 private string GetAttribute (XmlAttributeCollection attrs, string attr, bool required, XmlNode node)
344 XmlAttribute a = attrs[attr];
351 ValidateAttribute (attr, r, node);
355 ThrowMissingAttribute (attr, node);
360 private string AsString (string s)
362 return s == null ? string.Empty : s;
365 private void ValidateAttribute (string attribute, string value, XmlNode node)
367 if (value == null || value.Length == 0)
368 throw new ConfigurationException (string.Format ("Required attribute `{0}' cannot be empty.", attribute), node);
371 private void ValidateInvalidAttributes (XmlAttributeCollection c, XmlNode node)
374 ThrowUnrecognizedAttribute (c[0].Name, node);
377 private void ThrowMissingAttribute (string attribute, XmlNode node)
379 throw new ConfigurationException (string.Format ("Missing required attribute `{0}'.", attribute), node);
382 private void ThrowUnrecognizedNode (XmlNode node)
384 throw new ConfigurationException (
385 string.Format ("Unrecognized node `{0}'; nodeType={1}", node.Name, node.NodeType),
389 private void ThrowUnrecognizedElement (XmlNode node)
391 throw new ConfigurationException (
392 string.Format ("Unrecognized element <{0}/>", node.Name),
396 private void ThrowUnrecognizedAttribute (string attribute, XmlNode node)
398 throw new ConfigurationException (
399 string.Format ("Unrecognized attribute `{0}' on element <{1}/>.", attribute, node.Name),