Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / mscorlib / system / AggregateException.cs
1 // ==++==
2 //
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
7 //
8 // AggregateException.cs
9 //
10 // <OWNER>Microsoft</OWNER>
11 //
12 // Public type to communicate multiple failures to an end-user.
13 //
14 // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
15
16 using System;
17 using System.Collections.Generic;
18 using System.Collections.ObjectModel;
19 using System.Diagnostics;
20 using System.Globalization;
21 using System.Runtime.ExceptionServices;
22 using System.Runtime.Serialization;
23 using System.Security;
24 using System.Threading;
25
26 namespace System
27 {
28
29     /// <summary>Represents one or more errors that occur during application execution.</summary>
30     /// <remarks>
31     /// <see cref="AggregateException"/> is used to consolidate multiple failures into a single, throwable
32     /// exception object.
33     /// </remarks>
34     [Serializable]
35     [DebuggerDisplay("Count = {InnerExceptionCount}")]
36     public class AggregateException : Exception
37     {
38
39         private ReadOnlyCollection<Exception> m_innerExceptions; // Complete set of exceptions.
40
41         /// <summary>
42         /// Initializes a new instance of the <see cref="AggregateException"/> class.
43         /// </summary>
44         public AggregateException()
45             : base(Environment.GetResourceString("AggregateException_ctor_DefaultMessage"))
46         {
47             m_innerExceptions = new ReadOnlyCollection<Exception>(new Exception[0]);
48         }
49
50         /// <summary>
51         /// Initializes a new instance of the <see cref="AggregateException"/> class with
52         /// a specified error message.
53         /// </summary>
54         /// <param name="message">The error message that explains the reason for the exception.</param>
55         public AggregateException(string message)
56             : base(message)
57         {
58             m_innerExceptions = new ReadOnlyCollection<Exception>(new Exception[0]);
59         }
60
61         /// <summary>
62         /// Initializes a new instance of the <see cref="AggregateException"/> class with a specified error
63         /// message and a reference to the inner exception that is the cause of this exception.
64         /// </summary>
65         /// <param name="message">The error message that explains the reason for the exception.</param>
66         /// <param name="innerException">The exception that is the cause of the current exception.</param>
67         /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerException"/> argument
68         /// is null.</exception>
69         public AggregateException(string message, Exception innerException)
70             : base(message, innerException)
71         {
72             if (innerException == null)
73             {
74                 throw new ArgumentNullException("innerException");
75             }
76
77             m_innerExceptions = new ReadOnlyCollection<Exception>(new Exception[] { innerException });
78         }
79
80         /// <summary>
81         /// Initializes a new instance of the <see cref="AggregateException"/> class with
82         /// references to the inner exceptions that are the cause of this exception.
83         /// </summary>
84         /// <param name="innerExceptions">The exceptions that are the cause of the current exception.</param>
85         /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerExceptions"/> argument
86         /// is null.</exception>
87         /// <exception cref="T:System.ArgumentException">An element of <paramref name="innerExceptions"/> is
88         /// null.</exception>
89         public AggregateException(IEnumerable<Exception> innerExceptions) :
90             this(Environment.GetResourceString("AggregateException_ctor_DefaultMessage"), innerExceptions)
91         {
92         }
93
94         /// <summary>
95         /// Initializes a new instance of the <see cref="AggregateException"/> class with
96         /// references to the inner exceptions that are the cause of this exception.
97         /// </summary>
98         /// <param name="innerExceptions">The exceptions that are the cause of the current exception.</param>
99         /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerExceptions"/> argument
100         /// is null.</exception>
101         /// <exception cref="T:System.ArgumentException">An element of <paramref name="innerExceptions"/> is
102         /// null.</exception>
103         public AggregateException(params Exception[] innerExceptions) :
104             this(Environment.GetResourceString("AggregateException_ctor_DefaultMessage"), innerExceptions)
105         {
106         }
107
108         /// <summary>
109         /// Initializes a new instance of the <see cref="AggregateException"/> class with a specified error
110         /// message and references to the inner exceptions that are the cause of this exception.
111         /// </summary>
112         /// <param name="message">The error message that explains the reason for the exception.</param>
113         /// <param name="innerExceptions">The exceptions that are the cause of the current exception.</param>
114         /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerExceptions"/> argument
115         /// is null.</exception>
116         /// <exception cref="T:System.ArgumentException">An element of <paramref name="innerExceptions"/> is
117         /// null.</exception>
118         public AggregateException(string message, IEnumerable<Exception> innerExceptions)
119             // If it's already an IList, pass that along (a defensive copy will be made in the delegated ctor).  If it's null, just pass along
120             // null typed correctly.  Otherwise, create an IList from the enumerable and pass that along. 
121             : this(message, innerExceptions as IList<Exception> ?? (innerExceptions == null ? (List<Exception>)null : new List<Exception>(innerExceptions)))
122         {
123         }
124
125         /// <summary>
126         /// Initializes a new instance of the <see cref="AggregateException"/> class with a specified error
127         /// message and references to the inner exceptions that are the cause of this exception.
128         /// </summary>
129         /// <param name="message">The error message that explains the reason for the exception.</param>
130         /// <param name="innerExceptions">The exceptions that are the cause of the current exception.</param>
131         /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerExceptions"/> argument
132         /// is null.</exception>
133         /// <exception cref="T:System.ArgumentException">An element of <paramref name="innerExceptions"/> is
134         /// null.</exception>
135         public AggregateException(string message, params Exception[] innerExceptions) :
136             this(message, (IList<Exception>)innerExceptions)
137         {
138         }
139
140         /// <summary>
141         /// Allocates a new aggregate exception with the specified message and list of inner exceptions.
142         /// </summary>
143         /// <param name="message">The error message that explains the reason for the exception.</param>
144         /// <param name="innerExceptions">The exceptions that are the cause of the current exception.</param>
145         /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerExceptions"/> argument
146         /// is null.</exception>
147         /// <exception cref="T:System.ArgumentException">An element of <paramref name="innerExceptions"/> is
148         /// null.</exception>
149         private AggregateException(string message, IList<Exception> innerExceptions)
150             : base(message, innerExceptions != null && innerExceptions.Count > 0 ? innerExceptions[0] : null)
151         {
152             if (innerExceptions == null)
153             {
154                 throw new ArgumentNullException("innerExceptions");
155             }
156
157             // Copy exceptions to our internal array and validate them. We must copy them,
158             // because we're going to put them into a ReadOnlyCollection which simply reuses
159             // the list passed in to it. We don't want callers subsequently mutating.
160             Exception[] exceptionsCopy = new Exception[innerExceptions.Count];
161
162             for (int i = 0; i < exceptionsCopy.Length; i++)
163             {
164                 exceptionsCopy[i] = innerExceptions[i];
165
166                 if (exceptionsCopy[i] == null)
167                 {
168                     throw new ArgumentException(Environment.GetResourceString("AggregateException_ctor_InnerExceptionNull"));
169                 }
170             }
171
172             m_innerExceptions = new ReadOnlyCollection<Exception>(exceptionsCopy);
173         }
174
175         /// <summary>
176         /// Initializes a new instance of the <see cref="AggregateException"/> class with
177         /// references to the inner exception dispatch info objects that represent the cause of this exception.
178         /// </summary>
179         /// <param name="innerExceptionInfos">
180         /// Information about the exceptions that are the cause of the current exception.
181         /// </param>
182         /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerExceptionInfos"/> argument
183         /// is null.</exception>
184         /// <exception cref="T:System.ArgumentException">An element of <paramref name="innerExceptionInfos"/> is
185         /// null.</exception>
186         internal AggregateException(IEnumerable<ExceptionDispatchInfo> innerExceptionInfos) :
187             this(Environment.GetResourceString("AggregateException_ctor_DefaultMessage"), innerExceptionInfos)
188         {
189         }
190
191         /// <summary>
192         /// Initializes a new instance of the <see cref="AggregateException"/> class with a specified error
193         /// message and references to the inner exception dispatch info objects that represent the cause of 
194         /// this exception.
195         /// </summary>
196         /// <param name="message">The error message that explains the reason for the exception.</param>
197         /// <param name="innerExceptionInfos">
198         /// Information about the exceptions that are the cause of the current exception.
199         /// </param>
200         /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerExceptionInfos"/> argument
201         /// is null.</exception>
202         /// <exception cref="T:System.ArgumentException">An element of <paramref name="innerExceptionInfos"/> is
203         /// null.</exception>
204         internal AggregateException(string message, IEnumerable<ExceptionDispatchInfo> innerExceptionInfos)
205             // If it's already an IList, pass that along (a defensive copy will be made in the delegated ctor).  If it's null, just pass along
206             // null typed correctly.  Otherwise, create an IList from the enumerable and pass that along. 
207             : this(message, innerExceptionInfos as IList<ExceptionDispatchInfo> ?? 
208                                 (innerExceptionInfos == null ? 
209                                     (List<ExceptionDispatchInfo>)null : 
210                                     new List<ExceptionDispatchInfo>(innerExceptionInfos)))
211         {
212         }
213
214         /// <summary>
215         /// Allocates a new aggregate exception with the specified message and list of inner 
216         /// exception dispatch info objects.
217         /// </summary>
218         /// <param name="message">The error message that explains the reason for the exception.</param>
219         /// <param name="innerExceptionInfos">
220         /// Information about the exceptions that are the cause of the current exception.
221         /// </param>
222         /// <exception cref="T:System.ArgumentNullException">The <paramref name="innerExceptionInfos"/> argument
223         /// is null.</exception>
224         /// <exception cref="T:System.ArgumentException">An element of <paramref name="innerExceptionInfos"/> is
225         /// null.</exception>
226         private AggregateException(string message, IList<ExceptionDispatchInfo> innerExceptionInfos)
227             : base(message, innerExceptionInfos != null && innerExceptionInfos.Count > 0 && innerExceptionInfos[0] != null ?
228                                 innerExceptionInfos[0].SourceException : null)
229         {
230             if (innerExceptionInfos == null)
231             {
232                 throw new ArgumentNullException("innerExceptionInfos");
233             }
234
235             // Copy exceptions to our internal array and validate them. We must copy them,
236             // because we're going to put them into a ReadOnlyCollection which simply reuses
237             // the list passed in to it. We don't want callers subsequently mutating.
238             Exception[] exceptionsCopy = new Exception[innerExceptionInfos.Count];
239
240             for (int i = 0; i < exceptionsCopy.Length; i++)
241             {
242                 var edi = innerExceptionInfos[i];
243                 if (edi != null) exceptionsCopy[i] = edi.SourceException;
244
245                 if (exceptionsCopy[i] == null)
246                 {
247                     throw new ArgumentException(Environment.GetResourceString("AggregateException_ctor_InnerExceptionNull"));
248                 }
249             }
250
251             m_innerExceptions = new ReadOnlyCollection<Exception>(exceptionsCopy);
252         }
253
254         /// <summary>
255         /// Initializes a new instance of the <see cref="AggregateException"/> class with serialized data.
256         /// </summary>
257         /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds
258         /// the serialized object data about the exception being thrown.</param>
259         /// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that
260         /// contains contextual information about the source or destination. </param>
261         /// <exception cref="T:System.ArgumentNullException">The <paramref name="info"/> argument is null.</exception>
262         /// <exception cref="T:System.Runtime.Serialization.SerializationException">The exception could not be deserialized correctly.</exception>
263         [SecurityCritical]
264         protected AggregateException(SerializationInfo info, StreamingContext context) :
265             base(info, context)
266         {
267             if (info == null)
268             {
269                 throw new ArgumentNullException("info");
270             }
271
272             Exception[] innerExceptions = info.GetValue("InnerExceptions", typeof(Exception[])) as Exception[];
273             if (innerExceptions == null)
274             {
275                 throw new SerializationException(Environment.GetResourceString("AggregateException_DeserializationFailure"));
276             }
277
278             m_innerExceptions = new ReadOnlyCollection<Exception>(innerExceptions);
279         }
280
281         /// <summary>
282         /// Sets the <see cref="T:System.Runtime.Serialization.SerializationInfo"/> with information about
283         /// the exception.
284         /// </summary>
285         /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds
286         /// the serialized object data about the exception being thrown.</param>
287         /// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that
288         /// contains contextual information about the source or destination. </param>
289         /// <exception cref="T:System.ArgumentNullException">The <paramref name="info"/> argument is null.</exception>
290         [SecurityCritical]
291         public override void GetObjectData(SerializationInfo info, StreamingContext context)
292         {
293             if (info == null)
294             {
295                 throw new ArgumentNullException("info");
296             }
297
298             base.GetObjectData(info, context);
299
300             Exception[] innerExceptions = new Exception[m_innerExceptions.Count];
301             m_innerExceptions.CopyTo(innerExceptions, 0);
302             info.AddValue("InnerExceptions", innerExceptions, typeof(Exception[]));
303         }
304
305         /// <summary>
306         /// Returns the <see cref="System.AggregateException"/> that is the root cause of this exception.
307         /// </summary>
308         public override Exception GetBaseException()
309         {
310             // Returns the first inner AggregateException that contains more or less than one inner exception
311
312             // Recursively traverse the inner exceptions as long as the inner exception of type AggregateException and has only one inner exception
313             Exception back = this;
314             AggregateException backAsAggregate = this;
315             while (backAsAggregate != null && backAsAggregate.InnerExceptions.Count == 1)
316             {
317                 back = back.InnerException;
318                 backAsAggregate = back as AggregateException;
319             }
320             return back;
321         }
322
323         /// <summary>
324         /// Gets a read-only collection of the <see cref="T:System.Exception"/> instances that caused the
325         /// current exception.
326         /// </summary>
327         public ReadOnlyCollection<Exception> InnerExceptions
328         {
329             get { return m_innerExceptions; }
330         }
331
332
333         /// <summary>
334         /// Invokes a handler on each <see cref="T:System.Exception"/> contained by this <see
335         /// cref="AggregateException"/>.
336         /// </summary>
337         /// <param name="predicate">The predicate to execute for each exception. The predicate accepts as an
338         /// argument the <see cref="T:System.Exception"/> to be processed and returns a Boolean to indicate
339         /// whether the exception was handled.</param>
340         /// <remarks>
341         /// Each invocation of the <paramref name="predicate"/> returns true or false to indicate whether the
342         /// <see cref="T:System.Exception"/> was handled. After all invocations, if any exceptions went
343         /// unhandled, all unhandled exceptions will be put into a new <see cref="AggregateException"/>
344         /// which will be thrown. Otherwise, the <see cref="Handle"/> method simply returns. If any
345         /// invocations of the <paramref name="predicate"/> throws an exception, it will halt the processing
346         /// of any more exceptions and immediately propagate the thrown exception as-is.
347         /// </remarks>
348         /// <exception cref="AggregateException">An exception contained by this <see
349         /// cref="AggregateException"/> was not handled.</exception>
350         /// <exception cref="T:System.ArgumentNullException">The <paramref name="predicate"/> argument is
351         /// null.</exception>
352         public void Handle(Func<Exception, bool> predicate)
353         {
354             if (predicate == null)
355             {
356                 throw new ArgumentNullException("predicate");
357             }
358
359             List<Exception> unhandledExceptions = null;
360             for (int i = 0; i < m_innerExceptions.Count; i++)
361             {
362                 // If the exception was not handled, lazily allocate a list of unhandled
363                 // exceptions (to be rethrown later) and add it.
364                 if (!predicate(m_innerExceptions[i]))
365                 {
366                     if (unhandledExceptions == null)
367                     {
368                         unhandledExceptions = new List<Exception>();
369                     }
370
371                     unhandledExceptions.Add(m_innerExceptions[i]);
372                 }
373             }
374
375             // If there are unhandled exceptions remaining, throw them.
376             if (unhandledExceptions != null)
377             {
378                 throw new AggregateException(Message, unhandledExceptions);
379             }
380         }
381
382
383         /// <summary>
384         /// Flattens an <see cref="AggregateException"/> instances into a single, new instance.
385         /// </summary>
386         /// <returns>A new, flattened <see cref="AggregateException"/>.</returns>
387         /// <remarks>
388         /// If any inner exceptions are themselves instances of
389         /// <see cref="AggregateException"/>, this method will recursively flatten all of them. The
390         /// inner exceptions returned in the new <see cref="AggregateException"/>
391         /// will be the union of all of the the inner exceptions from exception tree rooted at the provided
392         /// <see cref="AggregateException"/> instance.
393         /// </remarks>
394         public AggregateException Flatten()
395         {
396             // Initialize a collection to contain the flattened exceptions.
397             List<Exception> flattenedExceptions = new List<Exception>();
398
399             // Create a list to remember all aggregates to be flattened, this will be accessed like a FIFO queue
400             List<AggregateException> exceptionsToFlatten = new List<AggregateException>();
401             exceptionsToFlatten.Add(this);
402             int nDequeueIndex = 0;
403
404             // Continue removing and recursively flattening exceptions, until there are no more.
405             while (exceptionsToFlatten.Count > nDequeueIndex)
406             {
407                 // dequeue one from exceptionsToFlatten
408                 IList<Exception> currentInnerExceptions = exceptionsToFlatten[nDequeueIndex++].InnerExceptions;
409
410                 for (int i = 0; i < currentInnerExceptions.Count; i++)
411                 {
412                     Exception currentInnerException = currentInnerExceptions[i];
413
414                     if (currentInnerException == null)
415                     {
416                         continue;
417                     }
418
419                     AggregateException currentInnerAsAggregate = currentInnerException as AggregateException;
420
421                     // If this exception is an aggregate, keep it around for later.  Otherwise,
422                     // simply add it to the list of flattened exceptions to be returned.
423                     if (currentInnerAsAggregate != null)
424                     {
425                         exceptionsToFlatten.Add(currentInnerAsAggregate);
426                     }
427                     else
428                     {
429                         flattenedExceptions.Add(currentInnerException);
430                     }
431                 }
432             }
433
434
435             return new AggregateException(Message, flattenedExceptions);
436         }
437
438         /// <summary>
439         /// Creates and returns a string representation of the current <see cref="AggregateException"/>.
440         /// </summary>
441         /// <returns>A string representation of the current exception.</returns>
442         public override string ToString()
443         {
444             string text = base.ToString();
445
446             for (int i = 0; i < m_innerExceptions.Count; i++)
447             {
448                 text = String.Format(
449                     CultureInfo.InvariantCulture,
450                     Environment.GetResourceString("AggregateException_ToString"),
451                     text, Environment.NewLine, i, m_innerExceptions[i].ToString(), "<---", Environment.NewLine);
452             }
453
454             return text;
455         }
456
457         /// <summary>
458         /// This helper property is used by the DebuggerDisplay.
459         /// 
460         /// Note that we don't want to remove this property and change the debugger display to {InnerExceptions.Count} 
461         /// because DebuggerDisplay should be a single property access or parameterless method call, so that the debugger 
462         /// can use a fast path without using the expression evaluator.
463         /// 
464         /// See http://msdn.microsoft.com/en-us/library/x810d419.aspx
465         /// </summary>
466         private int InnerExceptionCount
467         {
468             get
469             {
470                 return InnerExceptions.Count;
471             }
472         }
473     }
474
475 }