-
-
Notifications
You must be signed in to change notification settings - Fork 43
Expand file tree
/
Copy pathTaskReport.cs
More file actions
351 lines (308 loc) · 9.08 KB
/
Copy pathTaskReport.cs
File metadata and controls
351 lines (308 loc) · 9.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
// Copyright (c) 2002-2017 SIL International
// This software is licensed under the LGPL, version 2.1 or later
// (http://www.gnu.org/licenses/lgpl-2.1.html)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Xml.Linq;
namespace SIL.FieldWorks.WordWorks.Parser
{
/// <summary>
/// Summary description for TaskReport.
/// </summary>
public sealed class TaskReport : IDisposable
{
public enum TaskPhase {Started, Finished, ErrorEncountered};
private readonly Action<TaskReport> m_taskUpdate;
private List<TaskReport> m_subTasks;
private string m_description;
private readonly long m_start;
private long m_finish;
private TaskPhase m_phase;
private TaskReport m_owningTask;
private bool m_isInDispose; // ture if we're in the dispose method
/// <summary>
/// this was added to hold the results of a trace request
/// </summary>
private XDocument m_details;
/// -----------------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the <see cref="TaskReport"/> class.
/// </summary>
/// -----------------------------------------------------------------------------------
public TaskReport(string description, Action<TaskReport> eventReceiver)
: this(description, (TaskReport) null)
{
m_taskUpdate = eventReceiver;
InformListeners(TaskPhase.Started); // don't move this down to the other constructor
}
internal TaskReport(string description, TaskReport owningTask)
{
m_owningTask = owningTask;
m_phase = TaskPhase.Started;
m_description = description;
m_start = DateTime.Now.Ticks;
}
#region IDisposable & Co. implementation
// Region last reviewed: never
/// <summary>
/// Check to see if the object has been disposed.
/// All public Properties and Methods should call this
/// before doing anything else.
/// </summary>
public void CheckDisposed()
{
if (IsDisposed)
throw new ObjectDisposedException(String.Format("'{0}' in use after being disposed.", GetType().Name));
}
/// <summary>
/// True, if the object has been disposed.
/// </summary>
private bool m_isDisposed;
/// <summary>
/// See if the object has been disposed.
/// </summary>
public bool IsDisposed
{
get { return m_isDisposed; }
}
/// <summary>
/// Finalizer, in case client doesn't dispose it.
/// Force Dispose(false) if not already called (i.e. m_isDisposed is true)
/// </summary>
/// <remarks>
/// In case some clients forget to dispose it directly.
/// </remarks>
~TaskReport()
{
Dispose(false);
// The base class finalizer is called automatically.
}
/// <summary>
///
/// </summary>
/// <remarks>Must not be virtual.</remarks>
public void Dispose()
{
Dispose(true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
/// <summary>
/// Executes in two distinct scenarios.
///
/// 1. If disposing is true, the method has been called directly
/// or indirectly by a user's code via the Dispose method.
/// Both managed and unmanaged resources can be disposed.
///
/// 2. If disposing is false, the method has been called by the
/// runtime from inside the finalizer and you should not reference (access)
/// other managed objects, as they already have been garbage collected.
/// Only unmanaged resources can be disposed.
/// </summary>
/// <param name="disposing"></param>
/// <remarks>
/// If any exceptions are thrown, that is fine.
/// If the method is being done in a finalizer, it will be ignored.
/// If it is thrown by client code calling Dispose,
/// it needs to be handled by fixing the bug.
///
/// If subclasses override this method, they should call the base implementation.
/// </remarks>
private void Dispose(bool disposing)
{
Debug.WriteLineIf(!disposing, "****************** Missing Dispose() call for " + GetType().Name + ". ******************");
// Must not be run more than once.
if (m_isDisposed)
return;
if (disposing)
{
m_isInDispose = true;
// handle the case where the child is disposed of before the parent and not by the parent directly
if (m_owningTask != null)
m_owningTask.RemoveChildTaskReport(this); // break assocations before disposing
// Dispose managed resources here.
if (m_subTasks != null)
{
foreach (TaskReport task in m_subTasks)
task.Dispose();
}
Finish();
if (m_subTasks != null)
m_subTasks.Clear();
m_isInDispose = false;
}
// Dispose unmanaged resources here, whether disposing is true or false.
m_owningTask = null;
m_details = null;
m_description = null;
m_subTasks = null;
m_isDisposed = true;
}
#endregion IDisposable & Co. implementation
internal TaskReport AddSubTask(string description)
{
CheckDisposed();
Debug.Assert(m_phase != TaskPhase.Finished);
var t = new TaskReport(description, this);
if (m_subTasks == null)
m_subTasks = new List<TaskReport>();
else
{ //a new set task should not be added until the previous one is finished.
TaskPhase phase = m_subTasks[m_subTasks.Count - 1].Phase;
Debug.Assert(phase == TaskPhase.Finished);// || phase == TaskPhase.ErrorEncountered);
}
m_subTasks.Add(t);
//this cannot be in the constructor because if the listener asks
//for the most recent task, it will not get this one until after
//this has been added to the subtasks list.
t.InformListeners(TaskPhase.Started);
return t;
}
internal void Finish()
{
CheckDisposed();
//this is called when we are disposed; it is possible that we were explicitly finished already.
if (m_phase == TaskPhase.Finished || m_phase == TaskPhase.ErrorEncountered)
return;
m_finish = DateTime.Now.Ticks;
InformListeners(TaskPhase.Finished);
}
public string Description
{
get
{
CheckDisposed();
return m_phase == TaskPhase.ErrorEncountered
? string.Format(ParserCoreStrings.ksX_error, m_description)
: m_description;
}
}
/// <summary>
/// this is used to hold the results of a trace request
/// </summary>
public XDocument Details
{
set
{
CheckDisposed();
m_details = value;
}
get
{
CheckDisposed();
return m_details;
}
}
public long DurationTicks
{
get
{
CheckDisposed();
return m_finish - m_start;
}
}
public float DurationSeconds
{
get
{
CheckDisposed();
return (float) (DurationTicks / 10000000.0);
}
}
public List<TaskReport> SubTasks
{
get
{
CheckDisposed();
return m_subTasks;
}
}
internal void InformListeners(TaskPhase phase)
{
CheckDisposed();
m_phase = phase;
InformListeners(this);
}
private void InformListeners(TaskReport task)
{
//the root task executes this
if (m_taskUpdate != null)
m_taskUpdate(task);
//all children tasks execute this
else if (m_owningTask != null)
m_owningTask.InformListeners(this);
}
public TaskPhase Phase
{
get
{
CheckDisposed();
return m_phase;
}
}
public string PhaseDescription
{
get
{
CheckDisposed();
switch (m_phase)
{
case TaskPhase.Started:
return ParserCoreStrings.ksStarted;
case TaskPhase.Finished:
return ParserCoreStrings.ksFinished;
case TaskPhase.ErrorEncountered :
return ParserCoreStrings.ksErrorEncountered;
default:
return ParserCoreStrings.ksQuestions;
}
}
}
public TaskReport MostRecentTask
{
get
{
CheckDisposed();
if (m_phase == TaskPhase.ErrorEncountered || m_phase==TaskPhase.Finished || m_subTasks== null)
return this;
return m_subTasks[m_subTasks.Count -1].MostRecentTask;
}
}
public int Depth
{
get
{
CheckDisposed();
return m_owningTask == null ? 0 : m_owningTask.Depth + 1;
}
}
/// <summary>
/// This method was added because child TaskReport objects were
/// being disposed of while they were still contained in the parents
/// subtask list. Then when the parent was disposed of it would dispose
/// of the child TaskReports that it had and one or more would already
/// have been disposed. the child
/// would be attempted to dispose
/// </summary>
/// <param name="report"></param>
private void RemoveChildTaskReport(TaskReport report)
{
// If we're in the Dispose, part of it will call dispose on the child
// objects which will call back here to let the parent break the ties,
// but when we're in the dispose we don't want that to happen as we're
// most likely walking the subtask list and this isn't needed.
if (m_isInDispose == false && m_subTasks != null)
{
// Debug.WriteLine("** Disposing subtask <"+report.GetHashCode()+"> owned by " + GetHashCode().ToString());
m_subTasks.Remove(report);
if (m_subTasks.Count == 0) // other places in the code expect a non null value to mean data, so set it to null when empty.
m_subTasks = null;
}
}
}
}