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