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.Collections.Specialized;
37 using System.Configuration;
38 using System.Threading;
42 namespace System.Diagnostics
44 internal sealed class DiagnosticsConfiguration
47 private static object lock_ = new object();
49 private static object settings;
51 public static IDictionary Settings {
54 if (settings == null) {
55 object s = ConfigurationSettings.GetConfig ("system.diagnostics");
57 throw new Exception ("INTERNAL configuration error: failed to get configuration 'system.diagnostics'");
58 Thread.MemoryBarrier ();
59 while (Interlocked.CompareExchange (ref settings, s, null) == null) {
60 // do nothing; we're just setting settings.
62 Thread.MemoryBarrier ();
67 settings = ConfigurationSettings.GetConfig ("system.diagnostics");
70 return (IDictionary) settings;
75 public class DiagnosticsConfigurationHandler : IConfigurationSectionHandler
77 delegate void ElementHandler (IDictionary d, XmlNode node);
79 IDictionary elementHandlers = new Hashtable ();
81 public DiagnosticsConfigurationHandler ()
83 elementHandlers ["assert"] = new ElementHandler (AddAssertNode);
84 elementHandlers ["switches"] = new ElementHandler (AddSwitchesNode);
85 elementHandlers ["trace"] = new ElementHandler (AddTraceNode);
87 elementHandlers ["sources"] = new ElementHandler (AddSourcesNode);
88 elementHandlers ["sharedListeners"] = new ElementHandler (AddSharedListenersNode);
92 public virtual object Create (object parent, object configContext, XmlNode section)
96 d = new Hashtable (CaseInsensitiveHashCodeProvider.Default, CaseInsensitiveComparer.Default);
98 d = (IDictionary) ((ICloneable)parent).Clone();
100 foreach (XmlNode child in section.ChildNodes) {
101 XmlNodeType type = child.NodeType;
105 case XmlNodeType.Whitespace:
106 case XmlNodeType.Comment:
108 case XmlNodeType.Element:
109 ElementHandler eh = (ElementHandler) elementHandlers [child.Name];
113 ThrowUnrecognizedElement (child);
116 ThrowUnrecognizedElement (child);
124 // Remarks: Both attribute are optional
125 private void AddAssertNode (IDictionary d, XmlNode node)
127 XmlAttributeCollection c = node.Attributes;
128 string assertuienabled = GetAttribute (c, "assertuienabled", false, node);
129 string logfilename = GetAttribute (c, "logfilename", false, node);
130 ValidateInvalidAttributes (c, node);
131 if (assertuienabled != null) {
133 d ["assertuienabled"] = bool.Parse (assertuienabled);
135 catch (Exception e) {
136 throw new ConfigurationException ("The `assertuienabled' attribute must be `true' or `false'",
141 if (logfilename != null)
142 d ["logfilename"] = logfilename;
144 DefaultTraceListener dtl = (DefaultTraceListener) TraceImpl.Listeners["Default"];
146 if (assertuienabled != null)
147 dtl.AssertUiEnabled = (bool) d ["assertuienabled"];
148 if (logfilename != null)
149 dtl.LogFileName = logfilename;
152 if (node.ChildNodes.Count > 0)
153 ThrowUnrecognizedElement (node.ChildNodes[0]);
156 // name and value attributes are required
157 // Docs do not define "remove" or "clear" elements, but .NET recognizes
159 private void AddSwitchesNode (IDictionary d, XmlNode node)
162 // There are no attributes on <switch/>
163 ValidateInvalidAttributes (node.Attributes, node);
165 IDictionary newNodes = new Hashtable ();
167 foreach (XmlNode child in node.ChildNodes) {
168 XmlNodeType t = child.NodeType;
169 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
171 if (t == XmlNodeType.Element) {
172 XmlAttributeCollection attributes = child.Attributes;
175 switch (child.Name) {
177 name = GetAttribute (attributes, "name", true, child);
178 value = GetAttribute (attributes, "value", true, child);
179 value = AsString (value); ValidateIntegralValue (name, value);
180 newNodes [name] = value;
183 name = GetAttribute (attributes, "name", true, child);
184 newNodes.Remove (name);
190 ThrowUnrecognizedElement (child);
193 ValidateInvalidAttributes (attributes, child);
196 ThrowUnrecognizedNode (child);
199 d [node.Name] = newNodes;
203 private static int ValidateIntegralValue (string name, string value)
206 return int.Parse (value);
208 throw new ConfigurationException (string.Format (
210 "The value of a switch must be integral", name));
214 private void AddTraceNode (IDictionary d, XmlNode node)
216 AddTraceAttributes (d, node);
218 foreach (XmlNode child in node.ChildNodes) {
219 XmlNodeType t = child.NodeType;
220 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
222 if (t == XmlNodeType.Element) {
223 if (child.Name == "listeners")
224 AddTraceListeners (child);
226 ThrowUnrecognizedElement (child);
227 ValidateInvalidAttributes (child.Attributes, child);
230 ThrowUnrecognizedNode (child);
234 // all attributes are optional
235 private void AddTraceAttributes (IDictionary d, XmlNode node)
237 XmlAttributeCollection c = node.Attributes;
238 string autoflush = GetAttribute (c, "autoflush", false, node);
239 string indentsize = GetAttribute (c, "indentsize", false, node);
240 ValidateInvalidAttributes (c, node);
241 if (autoflush != null) {
243 bool b = bool.Parse (autoflush);
245 TraceImpl.AutoFlush = b;
247 catch (Exception e) {
248 throw new ConfigurationException ("The `autoflush' attribute must be `true' or `false'",
252 if (indentsize != null) {
254 int n = int.Parse (indentsize);
255 d ["indentsize"] = n;
256 TraceImpl.IndentSize = n;
258 catch (ConfigurationException e) {
261 catch (Exception e) {
262 throw new ConfigurationException ("The `indentsize' attribute must be an integral value.",
269 static readonly Hashtable static_sources = new Hashtable ();
272 private void AddSharedListenersNode (IDictionary d, XmlNode node)
276 private void AddSourcesNode (IDictionary d, XmlNode node)
278 // FIXME: are there valid attributes?
279 ValidateInvalidAttributes (node.Attributes, node);
280 Hashtable sources = d ["sources"] as Hashtable;
281 if (sources == null) {
282 sources = new Hashtable ();
283 d ["sources"] = sources;
285 // FIXME: here I replace the table with fake static variable.
286 sources = static_sources;
288 foreach (XmlNode child in node.ChildNodes) {
289 XmlNodeType t = child.NodeType;
290 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
292 if (t == XmlNodeType.Element) {
293 if (child.Name == "source")
294 AddTraceSource (sources, child);
296 ThrowUnrecognizedElement (child);
297 // ValidateInvalidAttributes (child.Attributes, child);
300 ThrowUnrecognizedNode (child);
304 private void AddTraceSource (Hashtable sources, XmlNode node)
307 SourceLevels levels = SourceLevels.Error;
308 StringDictionary atts = new StringDictionary ();
309 foreach (XmlAttribute a in node.Attributes) {
315 levels = (SourceLevels) Enum.Parse (typeof (SourceLevels), a.Value);
318 atts [a.Name] = a.Value;
323 throw new ConfigurationException ("Mandatory attribute 'name' is missing in 'source' element.");
325 // FIXME: it should raise an error for duplicate name sources.
326 if (sources.ContainsKey (name))
329 TraceSource source = new TraceSource (name, levels);
330 sources.Add (source.Name, source);
332 foreach (XmlNode child in node.ChildNodes) {
333 XmlNodeType t = child.NodeType;
334 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
336 if (t == XmlNodeType.Element) {
337 if (child.Name == "listeners")
338 AddTraceListeners (child);
340 ThrowUnrecognizedElement (child);
341 ValidateInvalidAttributes (child.Attributes, child);
344 ThrowUnrecognizedNode (child);
349 // only defines "add" and "remove", but "clear" also works
350 // for add, "name" and "type" are required; initializeData is optional
351 private void AddTraceListeners (XmlNode listenersNode)
354 // There are no attributes on <listeners/>
355 ValidateInvalidAttributes (listenersNode.Attributes, listenersNode);
357 foreach (XmlNode child in listenersNode.ChildNodes) {
358 XmlNodeType t = child.NodeType;
359 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
361 if (t == XmlNodeType.Element) {
362 XmlAttributeCollection attributes = child.Attributes;
366 switch (child.Name) {
368 name = GetAttribute (attributes, "name", true, child);
369 type = GetAttribute (attributes, "type", true, child);
370 id = GetAttribute (attributes, "initializeData", false, child);
371 AddTraceListener (name, type, id);
374 name = GetAttribute (attributes, "name", true, child);
375 RemoveTraceListener (name);
378 TraceImpl.Listeners.Clear ();
381 ThrowUnrecognizedElement (child);
384 ValidateInvalidAttributes (attributes, child);
387 ThrowUnrecognizedNode (child);
392 private void AddTraceListener (string name, string type, string initializeData)
394 Type t = Type.GetType (type);
396 throw new ConfigurationException (string.Format ("Invalid Type Specified: {0}", type));
401 if (initializeData != null) {
402 args = new object[] { initializeData };
403 types = new Type[] { typeof(string) };
410 System.Reflection.ConstructorInfo ctor = t.GetConstructor (types);
412 throw new ConfigurationException ("Couldn't find constructor for class " + type);
414 TraceListener l = (TraceListener) ctor.Invoke (args);
416 TraceImpl.Listeners.Add (l);
419 private void RemoveTraceListener (string name)
422 TraceImpl.Listeners.Remove (name);
424 catch (ArgumentException) {
425 // The specified listener wasn't in the collection
426 // Ignore this; .NET does.
428 catch (Exception e) {
429 throw new ConfigurationException (
430 string.Format ("Unknown error removing listener: {0}", name),
435 private string GetAttribute (XmlAttributeCollection attrs, string attr, bool required, XmlNode node)
437 XmlAttribute a = attrs[attr];
444 ValidateAttribute (attr, r, node);
448 ThrowMissingAttribute (attr, node);
453 private string AsString (string s)
455 return s == null ? string.Empty : s;
458 private void ValidateAttribute (string attribute, string value, XmlNode node)
460 if (value == null || value.Length == 0)
461 throw new ConfigurationException (string.Format ("Required attribute `{0}' cannot be empty.", attribute), node);
464 private void ValidateInvalidAttributes (XmlAttributeCollection c, XmlNode node)
467 ThrowUnrecognizedAttribute (c[0].Name, node);
470 private void ThrowMissingAttribute (string attribute, XmlNode node)
472 throw new ConfigurationException (string.Format ("Missing required attribute `{0}'.", attribute), node);
475 private void ThrowUnrecognizedNode (XmlNode node)
477 throw new ConfigurationException (
478 string.Format ("Unrecognized node `{0}'; nodeType={1}", node.Name, node.NodeType),
482 private void ThrowUnrecognizedElement (XmlNode node)
484 throw new ConfigurationException (
485 string.Format ("Unrecognized element <{0}/>", node.Name),
489 private void ThrowUnrecognizedAttribute (string attribute, XmlNode node)
491 throw new ConfigurationException (
492 string.Format ("Unrecognized attribute `{0}' on element <{1}/>.", attribute, node.Name),