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