New test.
[mono.git] / mcs / class / System / System.Diagnostics / DiagnosticsConfigurationHandler.cs
1 //
2 // System.Diagnostics.DiagnosticsConfigurationHandler.cs
3 //
4 // Comments from John R. Hicks <angryjohn69@nc.rr.com> original implementation 
5 // can be found at: /mcs/docs/apidocs/xml/en/System.Diagnostics
6 //
7 // Authors: 
8 //      John R. Hicks <angryjohn69@nc.rr.com>
9 //      Jonathan Pryor <jonpryor@vt.edu>
10 //
11 // (C) 2002, 2005
12 //
13
14 //
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:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
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.
33 //
34 using System;
35 using System.Collections;
36 using System.Configuration;
37 using System.Threading;
38 #if (XML_DEP)
39 using System.Xml;
40 #endif
41 namespace System.Diagnostics
42 {
43         internal sealed class DiagnosticsConfiguration
44         {
45 #if NO_LOCK_FREE
46                 private static object lock_ = new object();
47 #endif
48                 private static object settings;
49
50                 public static IDictionary Settings {
51                         get {
52 #if !NO_LOCK_FREE
53                                 if (settings == null) {
54                                         object s = ConfigurationSettings.GetConfig ("system.diagnostics");
55                                         if (s == null)
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.
60                                         }
61                                         Thread.MemoryBarrier ();
62                                 }
63 #else
64                                 lock (lock_) {
65                                         if (settings == null)
66                                                 settings = ConfigurationSettings.GetConfig ("system.diagnostics");
67                                 }
68 #endif
69                                 return (IDictionary) settings;
70                         }
71                 }
72         }
73 #if (XML_DEP)
74         public class DiagnosticsConfigurationHandler : IConfigurationSectionHandler
75         {
76                 delegate void ElementHandler (IDictionary d, XmlNode node);
77
78                 IDictionary elementHandlers = new Hashtable ();
79
80                 public DiagnosticsConfigurationHandler ()
81                 {
82                         elementHandlers ["assert"] = new ElementHandler (AddAssertNode);
83                         elementHandlers ["switches"] = new ElementHandler (AddSwitchesNode);
84                         elementHandlers ["trace"] = new ElementHandler (AddTraceNode);
85                 }
86
87                 public virtual object Create (object parent, object configContext, XmlNode section)
88                 {
89                         IDictionary d;
90                         if (parent == null)
91                                 d = new Hashtable (CaseInsensitiveHashCodeProvider.Default, CaseInsensitiveComparer.Default);
92                         else
93                                 d = (IDictionary) ((ICloneable)parent).Clone();
94
95                         foreach (XmlNode child in section.ChildNodes) {
96                                 XmlNodeType type = child.NodeType;
97
98                                 switch (type) {
99                                 /* ignore */
100                                 case XmlNodeType.Whitespace:
101                                 case XmlNodeType.Comment:
102                                         continue;
103                                 case XmlNodeType.Element:
104                                         ElementHandler eh = (ElementHandler) elementHandlers [child.Name];
105                                         if (eh != null)
106                                                 eh (d, child);
107                                         else
108                                                 ThrowUnrecognizedElement (child);
109                                         break;
110                                 default:
111                                         ThrowUnrecognizedElement (child);
112                                         break;
113                                 }
114                         }
115
116                         return d;
117                 }
118
119                 // Remarks: Both attribute are optional
120                 private void AddAssertNode (IDictionary d, XmlNode node)
121                 {
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) {
127                                 try {
128                                         d ["assertuienabled"] = bool.Parse (assertuienabled);
129                                 }
130                                 catch (Exception e) {
131                                         throw new ConfigurationException ("The `assertuienabled' attribute must be `true' or `false'",
132                                                         e, node);
133                                 }
134                         }
135
136                         if (logfilename != null)
137                                 d ["logfilename"] = logfilename;
138
139                         DefaultTraceListener dtl = (DefaultTraceListener) TraceImpl.Listeners["Default"];
140                         if (dtl != null) {
141                                 if (assertuienabled != null)
142                                         dtl.AssertUiEnabled = (bool) d ["assertuienabled"];
143                                 if (logfilename != null)
144                                         dtl.LogFileName = logfilename;
145                         }
146
147                         if (node.ChildNodes.Count > 0)
148                                 ThrowUnrecognizedElement (node.ChildNodes[0]);
149                 }
150
151                 // name and value attributes are required
152                 // Docs do not define "remove" or "clear" elements, but .NET recognizes
153                 // them
154                 private void AddSwitchesNode (IDictionary d, XmlNode node)
155                 {
156                         // There are no attributes on <switch/>
157                         ValidateInvalidAttributes (node.Attributes, node);
158
159                         IDictionary newNodes = new Hashtable ();
160
161                         foreach (XmlNode child in node.ChildNodes) {
162                                 XmlNodeType t = child.NodeType;
163                                 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
164                                         continue;
165                                 if (t == XmlNodeType.Element) {
166                                         XmlAttributeCollection attributes = child.Attributes;
167                                         string name = null;
168                                         string value = null;
169                                         switch (child.Name) {
170                                                 case "add":
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;
175                                                         break;
176                                                 case "remove":
177                                                         name = GetAttribute (attributes, "name", true, child);
178                                                         newNodes.Remove (name);
179                                                         break;
180                                                 case "clear":
181                                                         newNodes.Clear ();
182                                                         break;
183                                                 default:
184                                                         ThrowUnrecognizedElement (child);
185                                                         break;
186                                         }
187                                         ValidateInvalidAttributes (attributes, child);
188                                 }
189                                 else
190                                         ThrowUnrecognizedNode (child);
191                         }
192
193                         d [node.Name] = newNodes;
194                 }
195
196                 private static int ValidateIntegralValue (string name, string value)
197                 {
198                         try {
199                                 return int.Parse (value);
200                         } catch {
201                                 throw new ConfigurationException (string.Format (
202                                                         "Error in '{0}': " + 
203                                                         "The value of a switch must be integral", name));
204                         }
205                 }
206
207                 private void AddTraceNode (IDictionary d, XmlNode node)
208                 {
209                         AddTraceAttributes (d, node);
210
211                         foreach (XmlNode child in node.ChildNodes) {
212                                 XmlNodeType t = child.NodeType;
213                                 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
214                                         continue;
215                                 if (t == XmlNodeType.Element) {
216                                         if (child.Name == "listeners")
217                                                 AddTraceListeners (child);
218                                         else
219                                                 ThrowUnrecognizedElement (child);
220                                         ValidateInvalidAttributes (child.Attributes, child);
221                                 }
222                                 else
223                                         ThrowUnrecognizedNode (child);
224                         }
225                 }
226
227                 // all attributes are optional
228                 private void AddTraceAttributes (IDictionary d, XmlNode node)
229                 {
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) {
235                                 try {
236                                         bool b = bool.Parse (autoflush);
237                                         d ["autoflush"] = b;
238                                         TraceImpl.AutoFlush = b;
239                                 }
240                                 catch (Exception e) {
241                                         throw new ConfigurationException ("The `autoflush' attribute must be `true' or `false'",
242                                                         e, node);
243                                 }
244                         }
245                         if (indentsize != null) {
246                                 try {
247                                         int n = int.Parse (indentsize);
248                                         d ["indentsize"] = n;
249                                         TraceImpl.IndentSize = n;
250                                 }
251                                 catch (Exception e) {
252                                         throw new ConfigurationException ("The `indentsize' attribute must be an integral value.",
253                                                         e, node);
254                                 }
255                         }
256                 }
257
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)
261                 {
262                         // There are no attributes on <listeners/>
263                         ValidateInvalidAttributes (listenersNode.Attributes, listenersNode);
264
265                         foreach (XmlNode child in listenersNode.ChildNodes) {
266                                 XmlNodeType t = child.NodeType;
267                                 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
268                                         continue;
269                                 if (t == XmlNodeType.Element) {
270                                         XmlAttributeCollection attributes = child.Attributes;
271                                         string name = null;
272                                         string type = null;
273                                         string id = null;
274                                         switch (child.Name) {
275                                                 case "add":
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);
280                                                         break;
281                                                 case "remove":
282                                                         name = GetAttribute (attributes, "name", true, child);
283                                                         RemoveTraceListener (name);
284                                                         break;
285                                                 case "clear":
286                                                         TraceImpl.Listeners.Clear ();
287                                                         break;
288                                                 default:
289                                                         ThrowUnrecognizedElement (child);
290                                                         break;
291                                         }
292                                         ValidateInvalidAttributes (attributes, child);
293                                 }
294                                 else
295                                         ThrowUnrecognizedNode (child);
296                         }
297                 }
298
299                 private void AddTraceListener (string name, string type, string initializeData)
300                 {
301                         Type t = Type.GetType (type);
302                         if (t == null)
303                                 throw new ConfigurationException (string.Format ("Invalid Type Specified: {0}", type));
304
305                         object[] args;
306                         Type[] types;
307                         
308                         if (initializeData != null) {
309                                 args = new object[] { initializeData };
310                                 types = new Type[] { typeof(string) };
311                         }
312                         else {
313                                 args = null;
314                                 types = new Type[0];
315                         }
316                                 
317                         System.Reflection.ConstructorInfo ctor = t.GetConstructor (types);
318                         if (ctor == null) 
319                                 throw new ConfigurationException ("Couldn't find constructor for class " + type);
320                         
321                         TraceListener l = (TraceListener) ctor.Invoke (args);
322                         l.Name = name;
323                         TraceImpl.Listeners.Add (l);
324                 }
325
326                 private void RemoveTraceListener (string name)
327                 {
328                         try {
329                                 TraceImpl.Listeners.Remove (name);
330                         }
331                         catch (ArgumentException) {
332                                 // The specified listener wasn't in the collection
333                                 // Ignore this; .NET does.
334                         }
335                         catch (Exception e) {
336                                 throw new ConfigurationException (
337                                                 string.Format ("Unknown error removing listener: {0}", name),
338                                                 e);
339                         }
340                 }
341
342                 private string GetAttribute (XmlAttributeCollection attrs, string attr, bool required, XmlNode node)
343                 {
344                         XmlAttribute a = attrs[attr];
345
346                         string r = null;
347
348                         if (a != null) {
349                                 r = a.Value;
350                                 if (required)
351                                         ValidateAttribute (attr, r, node);
352                                 attrs.Remove (a);
353                         }
354                         else if (required)
355                                 ThrowMissingAttribute (attr, node);
356
357                         return r;
358                 }
359                 
360                 private string AsString (string s)
361                 {
362                         return s == null ? string.Empty : s;
363                 }
364
365                 private void ValidateAttribute (string attribute, string value, XmlNode node)
366                 {
367                         if (value == null || value.Length == 0)
368                                 throw new ConfigurationException (string.Format ("Required attribute `{0}' cannot be empty.", attribute), node);
369                 }
370
371                 private void ValidateInvalidAttributes (XmlAttributeCollection c, XmlNode node)
372                 {
373                         if (c.Count != 0)
374                                 ThrowUnrecognizedAttribute (c[0].Name, node);
375                 }
376
377                 private void ThrowMissingAttribute (string attribute, XmlNode node)
378                 {
379                         throw new ConfigurationException (string.Format ("Missing required attribute `{0}'.", attribute), node);
380                 }
381
382                 private void ThrowUnrecognizedNode (XmlNode node)
383                 {
384                         throw new ConfigurationException (
385                                         string.Format ("Unrecognized node `{0}'; nodeType={1}", node.Name, node.NodeType),
386                                         node);
387                 }
388
389                 private void ThrowUnrecognizedElement (XmlNode node)
390                 {
391                         throw new ConfigurationException (
392                                         string.Format ("Unrecognized element <{0}/>", node.Name),
393                                         node);
394                 }
395
396                 private void ThrowUnrecognizedAttribute (string attribute, XmlNode node)
397                 {
398                         throw new ConfigurationException (
399                                         string.Format ("Unrecognized attribute `{0}' on element <{1}/>.", attribute, node.Name),
400                                         node);
401                 }
402         }
403 #endif
404 }
405