//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Globalization;
using System.Collections;
using System.Threading;
using System.Text;
namespace Microsoft.SqlTools.ServiceLayer.Management
{
///
/// Allows for the mapping of objects that implement IProgressItem to individual items in the
/// progress dialog.
///
public class ProgressItemCollection : ICollection
{
#region internal helper classes
///
/// Allows us to map an action to its index in the progress dialog.
///
public class ActionIndexMap
{
///
/// action
///
public IProgressItem Action;
///
/// index
///
public int Index;
public ActionIndexMap(IProgressItem action)
{
this.Action = action;
// index isn't known yet
this.Index = -1;
}
}
#endregion
#region private data members
///
/// list of actions we will perform.
///
private ArrayList actions = new ArrayList();
#endregion
#region construction
public ProgressItemCollection()
{
}
#endregion
#region properties
private bool closeOnUserCancel = false;
///
/// Indicates whether to close the dialog immediately if the user cancels an operation
///
public bool CloseOnUserCancel
{
get
{
return closeOnUserCancel;
}
set
{
closeOnUserCancel = value;
}
}
private bool automaticClose = false;
///
/// Indicates whether to automatically close the dialog when all actions are complete
/// successfully.
///
public bool CloseOnSuccessfulCompletion
{
get
{
return automaticClose;
}
set
{
automaticClose = value;
}
}
private bool quitOnError = false;
///
/// Indicates whether the operation should be terminated if any individual step fails.
///
public bool QuitOnError
{
get
{
return this.quitOnError;
}
set
{
this.quitOnError = value;
}
}
private OperationStatus operationStatus = OperationStatus.Invalid;
///
/// Indicates the status of the operation.
///
public OperationStatus OperationStatus
{
get
{
return this.operationStatus;
}
}
///
/// Progress object this action collection will work with
///
private IProgress progress = null;
public IProgress Progress
{
get
{
return this.progress;
}
set
{
if (this.progress != value)
{
this.progress = value;
if (this.progress != null)
{
// add the actions to the progress dialog, and
// fixup our event handler
FixUpActionsToProgress();
}
}
}
}
#endregion
#region public overrides
///
/// Generate a string representaion of this object. It will convert all of it's IProgressItem members
/// to strings in a new line.
///
/// string description of the actions this object contains
public override string ToString()
{
// if there are no actions then just return the default ToString
if (this.actions == null || this.actions.Count == 0)
{
return base.ToString();
}
else
{
// convert all of the actions to strings on their own line
StringBuilder sb = new StringBuilder(((ActionIndexMap)actions[0]).Action.ToString());
for (int i = 1; i < this.actions.Count; i++)
{
sb.AppendFormat(CultureInfo.InvariantCulture, "\r\n{0}", ((ActionIndexMap)actions[i]).Action.ToString());
}
return sb.ToString();
}
}
#endregion
#region ICollection implementation
///
/// Gets the number of actions in this collection
///
public int Count
{
get
{
return this.actions.Count;
}
}
///
/// not supported
///
public bool IsSynchronized
{
get
{
throw new NotSupportedException();
}
}
///
/// not supported
///
public object SyncRoot
{
get
{
throw new NotSupportedException();
}
}
public void CopyTo(IProgressItem[] array, int start)
{
this.actions.CopyTo(array, start);
}
public void CopyTo(Array array, int start)
{
this.actions.CopyTo(array, start);
}
public IEnumerator GetEnumerator()
{
return this.actions.GetEnumerator();
}
#endregion
#region public methods
///
/// Add an action to the collection
///
/// action to be added
public void AddAction(IProgressItem action)
{
ActionIndexMap map = new ActionIndexMap(action);
this.actions.Add(map);
}
#endregion
#region internal implementation
///
/// delegate called when the progress dialog wants us to perform work on a new thread.
///
private void DoWorkOnThread()
{
if (this.Progress == null)
{
return;
}
try
{
System.Threading.Thread.CurrentThread.Name = "Worker thread for " + progress.GetType();
}
catch (InvalidOperationException)
{ }
// default to succeeded.
operationStatus = OperationStatus.Success;
// carry out each action.
foreach (ActionIndexMap map in this.actions)
{
// abort if the user has decided to cancel.
if (this.Progress.IsAborted)
{
this.Progress.UpdateActionStatus(map.Index, ProgressStatus.Aborted);
operationStatus = OperationStatus.Aborted;
break;
}
ProgressStatus stepStatus = ProgressStatus.Invalid;
try
{
// perform the action.
stepStatus = map.Action.DoAction(this, map.Index);
this.Progress.UpdateActionStatus(map.Index, stepStatus);
}
catch (Exception e)
{
// fail the step with errors, add the error messages to the control.
this.Progress.AddActionException(map.Index, e);
this.Progress.UpdateActionStatus(map.Index, ProgressStatus.Error);
stepStatus = ProgressStatus.Error;
}
if (stepStatus == ProgressStatus.Error)
{
// see if we're supposed to fail if any step fails
if (this.QuitOnError == true)
{
// fail and quit
this.operationStatus = OperationStatus.Error;
break;
}
else
{
this.operationStatus = OperationStatus.CompletedWithErrors;
}
}
else if (stepStatus != ProgressStatus.Success)
{
this.operationStatus = OperationStatus.CompletedWithErrors;
}
}
// tell the dialog we're finishing.
this.Progress.WorkerThreadExiting(operationStatus);
// close the dialog if asked to. We have to put this after
// the WorkerThreadExiting call because the progress dialog
// won't allow itself to be closed until worker thread says
// it's finished.
if ((this.CloseOnSuccessfulCompletion && (this.operationStatus == OperationStatus.Success)) ||
(this.CloseOnUserCancel && this.Progress.IsAborted))
{
//((Form)this.Progress).BeginInvoke(new CloseProgressWindowCallback(CloseProgressWindowHandler), new object[] { this.Progress });
}
}
private delegate void CloseProgressWindowCallback(IProgress progress);
private void CloseProgressWindowHandler(IProgress progress)
{
}
///
/// Adds the actions to an IProgress interface.
///
private void FixUpActionsToProgress()
{
if (this.Progress == null)
{
return;
}
// add actions
foreach (ActionIndexMap map in this.actions)
{
map.Index = this.Progress.AddAction(map.Action.ToString());
}
// add our delegate
this.Progress.WorkerThreadStart = new ThreadStart(this.DoWorkOnThread);
}
#endregion
}
}