49c2b04f6e11387b466e95641b5f3093afe9f1a4
[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.Collections.Specialized;
37 using System.Configuration;
38 using System.Reflection;
39 using System.Threading;
40 #if (XML_DEP)
41 using System.Xml;
42 #endif
43 namespace System.Diagnostics
44 {
45         // It handles following elements in <system.diagnostics> :
46         //      - <sharedListeners> [2.0]
47         //      - <sources>
48         //              - <source>
49         //                      - <listeners> (collection)
50         //      - <switches>
51         //              - <add name=string value=string />
52         //      - <trace autoflush=bool indentsize=int useGlobalLock=bool>
53         //              - <listeners>
54         internal sealed class DiagnosticsConfiguration
55         {
56 #if NO_LOCK_FREE
57                 private static object lock_ = new object();
58 #endif
59                 private static object settings;
60
61                 public static IDictionary Settings {
62                         get {
63 #if !NO_LOCK_FREE
64                                 if (settings == null) {
65                                         object s = ConfigurationSettings.GetConfig ("system.diagnostics");
66                                         if (s == null)
67                                                 throw new Exception ("INTERNAL configuration error: failed to get configuration 'system.diagnostics'");
68                                         Thread.MemoryBarrier ();
69                                         while (Interlocked.CompareExchange (ref settings, s, null) == null) {
70                                                 // do nothing; we're just setting settings.
71                                         }
72                                         Thread.MemoryBarrier ();
73                                 }
74 #else
75                                 lock (lock_) {
76                                         if (settings == null)
77                                                 settings = ConfigurationSettings.GetConfig ("system.diagnostics");
78                                 }
79 #endif
80                                 return (IDictionary) settings;
81                         }
82                 }
83         }
84 #if (XML_DEP)
85         [Obsolete ("This class is obsoleted")]
86         public class DiagnosticsConfigurationHandler : IConfigurationSectionHandler
87         {
88                 TraceImplSettings configValues;
89
90                 delegate void ElementHandler (IDictionary d, XmlNode node);
91
92                 IDictionary elementHandlers = new Hashtable ();
93
94                 public DiagnosticsConfigurationHandler ()
95                 {
96                         elementHandlers ["assert"] = new ElementHandler (AddAssertNode);
97                         elementHandlers ["switches"] = new ElementHandler (AddSwitchesNode);
98                         elementHandlers ["trace"] = new ElementHandler (AddTraceNode);
99                         elementHandlers ["sources"] = new ElementHandler (AddSourcesNode);
100                 }
101
102                 public virtual object Create (object parent, object configContext, XmlNode section)
103                 {
104                         IDictionary d;
105                         if (parent == null)
106                                 d = new Hashtable (CaseInsensitiveHashCodeProvider.Default, CaseInsensitiveComparer.Default);
107                         else
108                                 d = (IDictionary) ((ICloneable)parent).Clone();
109
110                         if (d.Contains (TraceImplSettings.Key))
111                                 configValues = (TraceImplSettings) d [TraceImplSettings.Key];
112                         else
113                                 d.Add (TraceImplSettings.Key, configValues = new TraceImplSettings ());
114
115                         // process <sharedListeners> first
116                         foreach (XmlNode child in section.ChildNodes) {
117                                 switch (child.NodeType) {
118                                 case XmlNodeType.Element:
119                                         if (child.LocalName != "sharedListeners")
120                                                 continue;
121                                         AddTraceListeners (d, child, GetSharedListeners (d));
122                                         break;
123                                 }
124                         }
125
126                         foreach (XmlNode child in section.ChildNodes) {
127                                 XmlNodeType type = child.NodeType;
128
129                                 switch (type) {
130                                 /* ignore */
131                                 case XmlNodeType.Whitespace:
132                                 case XmlNodeType.Comment:
133                                         continue;
134                                 case XmlNodeType.Element:
135                                         if (child.LocalName == "sharedListeners")
136                                                 continue;
137                                         ElementHandler eh = (ElementHandler) elementHandlers [child.Name];
138                                         if (eh != null)
139                                                 eh (d, child);
140                                         else
141                                                 ThrowUnrecognizedElement (child);
142                                         break;
143                                 default:
144                                         ThrowUnrecognizedElement (child);
145                                         break;
146                                 }
147                         }
148
149                         return d;
150                 }
151
152                 // Remarks: Both attribute are optional
153                 private void AddAssertNode (IDictionary d, XmlNode node)
154                 {
155                         XmlAttributeCollection c = node.Attributes;
156                         string assertuienabled = GetAttribute (c, "assertuienabled", false, node);
157                         string logfilename = GetAttribute (c, "logfilename", false, node);
158                         ValidateInvalidAttributes (c, node);
159                         if (assertuienabled != null) {
160                                 try {
161                                         d ["assertuienabled"] = bool.Parse (assertuienabled);
162                                 }
163                                 catch (Exception e) {
164                                         throw new ConfigurationException ("The `assertuienabled' attribute must be `true' or `false'",
165                                                         e, node);
166                                 }
167                         }
168
169                         if (logfilename != null)
170                                 d ["logfilename"] = logfilename;
171
172                         DefaultTraceListener dtl = (DefaultTraceListener) configValues.Listeners["Default"];
173                         if (dtl != null) {
174                                 if (assertuienabled != null)
175                                         dtl.AssertUiEnabled = (bool) d ["assertuienabled"];
176                                 if (logfilename != null)
177                                         dtl.LogFileName = logfilename;
178                         }
179
180                         if (node.ChildNodes.Count > 0)
181                                 ThrowUnrecognizedElement (node.ChildNodes[0]);
182                 }
183
184                 // name and value attributes are required
185                 // Docs do not define "remove" or "clear" elements, but .NET recognizes
186                 // them
187                 private void AddSwitchesNode (IDictionary d, XmlNode node)
188                 {
189 #if !TARGET_JVM
190                         // There are no attributes on <switch/>
191                         ValidateInvalidAttributes (node.Attributes, node);
192
193                         IDictionary newNodes = new Hashtable ();
194
195                         foreach (XmlNode child in node.ChildNodes) {
196                                 XmlNodeType t = child.NodeType;
197                                 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
198                                         continue;
199                                 if (t == XmlNodeType.Element) {
200                                         XmlAttributeCollection attributes = child.Attributes;
201                                         string name = null;
202                                         string value = null;
203                                         switch (child.Name) {
204                                                 case "add":
205                                                         name = GetAttribute (attributes, "name", true, child);
206                                                         value = GetAttribute (attributes, "value", true, child);
207                                                         newNodes [name] = GetSwitchValue (name, value);
208                                                         break;
209                                                 case "remove":
210                                                         name = GetAttribute (attributes, "name", true, child);
211                                                         newNodes.Remove (name);
212                                                         break;
213                                                 case "clear":
214                                                         newNodes.Clear ();
215                                                         break;
216                                                 default:
217                                                         ThrowUnrecognizedElement (child);
218                                                         break;
219                                         }
220                                         ValidateInvalidAttributes (attributes, child);
221                                 }
222                                 else
223                                         ThrowUnrecognizedNode (child);
224                         }
225
226                         d [node.Name] = newNodes;
227 #endif
228                 }
229
230                 private static object GetSwitchValue (string name, string value)
231                 {
232                         return value;
233                 }
234
235                 private void AddTraceNode (IDictionary d, XmlNode node)
236                 {
237                         AddTraceAttributes (d, node);
238
239                         foreach (XmlNode child in node.ChildNodes) {
240                                 XmlNodeType t = child.NodeType;
241                                 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
242                                         continue;
243                                 if (t == XmlNodeType.Element) {
244                                         if (child.Name == "listeners")
245                                                 AddTraceListeners (d, child, configValues.Listeners);
246                                         else
247                                                 ThrowUnrecognizedElement (child);
248                                         ValidateInvalidAttributes (child.Attributes, child);
249                                 }
250                                 else
251                                         ThrowUnrecognizedNode (child);
252                         }
253                 }
254
255                 // all attributes are optional
256                 private void AddTraceAttributes (IDictionary d, XmlNode node)
257                 {
258                         XmlAttributeCollection c = node.Attributes;
259                         string autoflushConf = GetAttribute (c, "autoflush", false, node);
260                         string indentsizeConf = GetAttribute (c, "indentsize", false, node);
261                         ValidateInvalidAttributes (c, node);
262                         if (autoflushConf != null) {
263                                 bool autoflush = false;
264                                 try {
265                                         autoflush = bool.Parse (autoflushConf);
266                                         d ["autoflush"] = autoflush;
267                                 } catch (Exception e) {
268                                         throw new ConfigurationException ("The `autoflush' attribute must be `true' or `false'",
269                                                         e, node);
270                                 }
271                                 configValues.AutoFlush = autoflush;
272                         }
273                         if (indentsizeConf != null) {
274                                 int indentsize = 0;
275                                 try {
276                                         indentsize = int.Parse (indentsizeConf);
277                                         d ["indentsize"] = indentsize;
278                                 } catch (Exception e) {
279                                         throw new ConfigurationException ("The `indentsize' attribute must be an integral value.",
280                                                         e, node);
281                                 }
282                                 configValues.IndentSize = indentsize;
283                         }
284                 }
285
286                 private TraceListenerCollection GetSharedListeners (IDictionary d)
287                 {
288                         TraceListenerCollection shared_listeners = d ["sharedListeners"] as TraceListenerCollection;
289                         if (shared_listeners == null) {
290                                 shared_listeners = new TraceListenerCollection (false);
291                                 d ["sharedListeners"] = shared_listeners;
292                         }
293                         return shared_listeners;
294                 }
295
296                 private void AddSourcesNode (IDictionary d, XmlNode node)
297                 {
298                         // FIXME: are there valid attributes?
299                         ValidateInvalidAttributes (node.Attributes, node);
300                         Hashtable sources = d ["sources"] as Hashtable;
301                         if (sources == null) {
302                                 sources = new Hashtable ();
303                                 d ["sources"] = sources;
304                         }
305
306                         foreach (XmlNode child in node.ChildNodes) {
307                                 XmlNodeType t = child.NodeType;
308                                 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
309                                         continue;
310                                 if (t == XmlNodeType.Element) {
311                                         if (child.Name == "source")
312                                                 AddTraceSource (d, sources, child);
313                                         else
314                                                 ThrowUnrecognizedElement (child);
315 //                                      ValidateInvalidAttributes (child.Attributes, child);
316                                 }
317                                 else
318                                         ThrowUnrecognizedNode (child);
319                         }
320                 }
321
322                 private void AddTraceSource (IDictionary d, Hashtable sources, XmlNode node)
323                 {
324                         string name = null;
325                         SourceLevels levels = SourceLevels.Error;
326                         StringDictionary atts = new StringDictionary ();
327                         foreach (XmlAttribute a in node.Attributes) {
328                                 switch (a.Name) {
329                                 case "name":
330                                         name = a.Value;
331                                         break;
332                                 case "switchValue":
333                                         levels = (SourceLevels) Enum.Parse (typeof (SourceLevels), a.Value);
334                                         break;
335                                 default:
336                                         atts [a.Name] = a.Value;
337                                         break;
338                                 }
339                         }
340                         if (name == null)
341                                 throw new ConfigurationException ("Mandatory attribute 'name' is missing in 'source' element.");
342
343                         // ignore duplicate ones (no error occurs)
344                         if (sources.ContainsKey (name))
345                                 return;
346
347                         TraceSourceInfo sinfo = new TraceSourceInfo (name, levels, configValues);
348                         sources.Add (sinfo.Name, sinfo);
349                         
350                         foreach (XmlNode child in node.ChildNodes) {
351                                 XmlNodeType t = child.NodeType;
352                                 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
353                                         continue;
354                                 if (t == XmlNodeType.Element) {
355                                         if (child.Name == "listeners")
356                                                 AddTraceListeners (d, child, sinfo.Listeners);
357                                         else
358                                                 ThrowUnrecognizedElement (child);
359                                         ValidateInvalidAttributes (child.Attributes, child);
360                                 }
361                                 else
362                                         ThrowUnrecognizedNode (child);
363                         }
364                 }
365
366                 // only defines "add" and "remove", but "clear" also works
367                 // for add, "name" is required; initializeData is optional; "type" is required in 1.x, optional in 2.0.
368                 private void AddTraceListeners (IDictionary d, XmlNode listenersNode, TraceListenerCollection listeners)
369                 {
370 #if !TARGET_JVM
371                         // There are no attributes on <listeners/>
372                         ValidateInvalidAttributes (listenersNode.Attributes, listenersNode);
373
374                         foreach (XmlNode child in listenersNode.ChildNodes) {
375                                 XmlNodeType t = child.NodeType;
376                                 if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
377                                         continue;
378                                 if (t == XmlNodeType.Element) {
379                                         XmlAttributeCollection attributes = child.Attributes;
380                                         string name = null;
381                                         switch (child.Name) {
382                                                 case "add":
383                                                         AddTraceListener (d, child, attributes, listeners);
384                                                         break;
385                                                 case "remove":
386                                                         name = GetAttribute (attributes, "name", true, child);
387                                                         RemoveTraceListener (name);
388                                                         break;
389                                                 case "clear":
390                                                         configValues.Listeners.Clear ();
391                                                         break;
392                                                 default:
393                                                         ThrowUnrecognizedElement (child);
394                                                         break;
395                                         }
396                                         ValidateInvalidAttributes (attributes, child);
397                                 }
398                                 else
399                                         ThrowUnrecognizedNode (child);
400                         }
401 #endif
402                 }
403
404                 private void AddTraceListener (IDictionary d, XmlNode child, XmlAttributeCollection attributes, TraceListenerCollection listeners)
405                 {
406                         string name = GetAttribute (attributes, "name", true, child);
407                         string type = null;
408
409 #if CONFIGURATION_DEP
410                         type = GetAttribute (attributes, "type", false, child);
411                         if (type == null) {
412                                 // indicated by name.
413                                 TraceListener shared = GetSharedListeners (d) [name];
414                                 if (shared == null)
415                                         throw new ConfigurationException (String.Format ("Shared trace listener {0} does not exist.", name));
416                                 if (attributes.Count != 0)
417                                         throw new ConfigurationErrorsException (string.Format (
418                                                 "Listener '{0}' references a shared " +
419                                                 "listener and can only have a 'Name' " +
420                                                 "attribute.", name));
421                                 listeners.Add (shared, configValues);
422                                 return;
423                         }
424 #else
425                         type = GetAttribute (attributes, "type", true, child);
426 #endif
427
428                         Type t = Type.GetType (type);
429                         if (t == null)
430                                 throw new ConfigurationException (string.Format ("Invalid Type Specified: {0}", type));
431
432                         object[] args;
433                         Type[] types;
434
435                         string initializeData = GetAttribute (attributes, "initializeData", false, child);
436                         if (initializeData != null) {
437                                 args = new object[] { initializeData };
438                                 types = new Type[] { typeof(string) };
439                         } else {
440                                 args = null;
441                                 types = Type.EmptyTypes;
442                         }
443
444                         BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
445                         if (t.Assembly == GetType ().Assembly)
446                                 flags |= BindingFlags.NonPublic;
447
448                         ConstructorInfo ctor = t.GetConstructor (flags, null, types, null);
449                         if (ctor == null) 
450                                 throw new ConfigurationException ("Couldn't find constructor for class " + type);
451                         
452                         TraceListener l = (TraceListener) ctor.Invoke (args);
453                         l.Name = name;
454
455 #if CONFIGURATION_DEP
456                         string trace = GetAttribute (attributes, "traceOutputOptions", false, child);
457                         if (trace != null) {
458                                 if (trace != trace.Trim ())
459                                         throw new ConfigurationErrorsException (string.Format (
460                                                 "Invalid value '{0}' for 'traceOutputOptions'.",
461                                                 trace), child);
462
463                                 TraceOptions trace_options;
464         
465                                 try {
466                                         trace_options = (TraceOptions) Enum.Parse (
467                                                 typeof (TraceOptions), trace);
468                                 } catch (ArgumentException) {
469                                         throw new ConfigurationErrorsException (string.Format (
470                                                 "Invalid value '{0}' for 'traceOutputOptions'.",
471                                                 trace), child);
472                                 }
473
474                                 l.TraceOutputOptions = trace_options;
475                         }
476
477                         string [] supported_attributes = l.GetSupportedAttributes ();
478                         if (supported_attributes != null) {
479                                 for (int i = 0; i < supported_attributes.Length; i++) {
480                                         string key = supported_attributes [i];
481                                         string value = GetAttribute (attributes, key, false, child);
482                                         if (value != null)
483                                                 l.Attributes.Add (key, value);
484                                 }
485                         }
486 #endif
487
488                         listeners.Add (l, configValues);
489                 }
490
491                 private void RemoveTraceListener (string name)
492                 {
493                         try {
494                                 configValues.Listeners.Remove (name);
495                         }
496                         catch (ArgumentException) {
497                                 // The specified listener wasn't in the collection
498                                 // Ignore this; .NET does.
499                         }
500                         catch (Exception e) {
501                                 throw new ConfigurationException (
502                                                 string.Format ("Unknown error removing listener: {0}", name),
503                                                 e);
504                         }
505                 }
506
507                 private string GetAttribute (XmlAttributeCollection attrs, string attr, bool required, XmlNode node)
508                 {
509                         XmlAttribute a = attrs[attr];
510
511                         string r = null;
512
513                         if (a != null) {
514                                 r = a.Value;
515                                 if (required)
516                                         ValidateAttribute (attr, r, node);
517                                 attrs.Remove (a);
518                         }
519                         else if (required)
520                                 ThrowMissingAttribute (attr, node);
521
522                         return r;
523                 }
524
525                 private void ValidateAttribute (string attribute, string value, XmlNode node)
526                 {
527                         if (value == null || value.Length == 0)
528                                 throw new ConfigurationException (string.Format ("Required attribute '{0}' cannot be empty.", attribute), node);
529                 }
530
531                 private void ValidateInvalidAttributes (XmlAttributeCollection c, XmlNode node)
532                 {
533                         if (c.Count != 0)
534                                 ThrowUnrecognizedAttribute (c[0].Name, node);
535                 }
536
537                 private void ThrowMissingAttribute (string attribute, XmlNode node)
538                 {
539                         throw new ConfigurationException (string.Format ("Required attribute '{0}' not found.", attribute), node);
540                 }
541
542                 private void ThrowUnrecognizedNode (XmlNode node)
543                 {
544                         throw new ConfigurationException (
545                                         string.Format ("Unrecognized node `{0}'; nodeType={1}", node.Name, node.NodeType),
546                                         node);
547                 }
548
549                 private void ThrowUnrecognizedElement (XmlNode node)
550                 {
551                         throw new ConfigurationException (
552                                         string.Format ("Unrecognized element '{0}'.", node.Name),
553                                         node);
554                 }
555
556                 private void ThrowUnrecognizedAttribute (string attribute, XmlNode node)
557                 {
558                         throw new ConfigurationException (
559                                         string.Format ("Unrecognized attribute '{0}' on element <{1}/>.", attribute, node.Name),
560                                         node);
561                 }
562         }
563 #endif
564 }
565