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