public interface IStringsTransform
{
IEnumerable<string> Do(IEnumerable<string> lines);
void Cancel();
bool Cancelled { get; }
}
public abstract class StringsTransformBase : IStringsTransform
public abstract IEnumerable<string> Do(IEnumerable<string> lines);
public void Cancel()
Cancelled = true;
public bool Cancelled { get; private set; }
public abstract class PerStringTransformBase : StringsTransformBase
public override IEnumerable<string> Do(IEnumerable<string> lines)
Init();
foreach (string line in lines)
if (Cancelled)
yield break;
yield return DoAction(line);
protected abstract string DoAction(string line);
protected virtual void Init()
public class CompositeStringsTransform : StringsTransformBase
private List<IStringsTransform> _transforms = new List<IStringsTransform>();
public CompositeStringsTransform(IEnumerable<IStringsTransformInstanciator> transforms)
_transforms.AddRange(transforms.Select(s => s.CreateInstance()));
public CompositeStringsTransform(params IStringsTransformInstanciator[] transforms)
: this((IEnumerable<IStringsTransformInstanciator>)transforms)
public List<IStringsTransform> Transforms
get { return _transforms; }
foreach (var transform in Transforms)
break;
lines = transform.Do(lines);
return lines;
public override string ToString()
var sb = new StringBuilder();
sb.Append(" & ");
sb.Append(transform);
return sb.ToString().Substring(3);
public class FileTransformInfo
internal FileTransformInfo()
public IStringsTransform Transform { get; internal set; }
public string InFilePath { get; internal set; }
public string OutFilePath { get; internal set; }
return string.Concat(Transform, " (", InFilePath, " --> ", OutFilePath, ")");
public static class FileTransform
private static Dictionary<string, BackgroundWorker> _transformsRunning = new Dictionary<string, BackgroundWorker>();
private static object _lockObject = new object();
public static FileTransformInfo Transform(string inFilePath, string outFilePath,
IEnumerable<IStringsTransformInstanciator> transforms, Action<string> showError,
Func<string, bool> showErrorWithRetry, Action<bool, FileTransformInfo> transformDone)
var transform = new CompositeStringsTransform(transforms);
var result = new FileTransformInfo
Transform = transform,
InFilePath = inFilePath,
OutFilePath = outFilePath
};
if (!(File.Exists(inFilePath) || _transformsRunning.ContainsKey(inFilePath)))
FileNotFoundShowError(inFilePath, showError);
else if (!Directory.Exists(Path.GetDirectoryName(outFilePath)))
DirectoryNotFoundShowError(outFilePath, showError);
else
var bg = new BackgroundWorker();
bg.DoWork += (s, e) =>
e.Result = false;
while (true)
try
FileUtil.WriteLines(outFilePath, transform.Do(FileUtil.GetLines(inFilePath)));
e.Result = !transform.Cancelled;
catch (FileNotFoundException)
catch (DirectoryNotFoundException)
catch (IOException)
if (showErrorWithRetry(string.Format(ErrorResources.IOException, outFilePath)))
continue;
catch (Exception ex)
showError(string.Format(ErrorResources.FileNotFoundException,
string.Concat(ex.GetType(), " : ", ex.Message)));
lock (_lockObject)
_transformsRunning.Remove(outFilePath);
bg.RunWorkerCompleted += (s, e) =>
TransformDone(transformDone, result, (bool)e.Result);
StartBGAndCache(result, bg, transformDone);
return result;
private static void TransformDone(Action<bool, FileTransformInfo> transformDone,
FileTransformInfo fileTransformInfo, bool transformResult)
if (transformDone != null)
transformDone(transformResult, fileTransformInfo);
private static void StartBGAndCache(FileTransformInfo fileTransformInfo, BackgroundWorker bg,
Action<bool, FileTransformInfo> transformDone)
if (fileTransformInfo.Transform.Cancelled)
TransformDone(transformDone, fileTransformInfo, false);
return;
var inFilePath = fileTransformInfo.InFilePath;
var outFilePath = fileTransformInfo.OutFilePath;
RunWorkerCompletedEventHandler runWorkerCompletedTryAgain = null;
Action<BackgroundWorker> tryAgain = (bgTryAgain) =>
StartBGAndCache(fileTransformInfo, bg, transformDone);
bgTryAgain.RunWorkerCompleted -= runWorkerCompletedTryAgain;
if (_transformsRunning.ContainsKey(inFilePath))
var bgTryAgain = _transformsRunning[inFilePath];
runWorkerCompletedTryAgain = (s, e) => tryAgain(bgTryAgain);
bgTryAgain.RunWorkerCompleted += runWorkerCompletedTryAgain;
else if (_transformsRunning.ContainsKey(outFilePath))
var bgTryAgain = _transformsRunning[outFilePath];
_transformsRunning.Add(outFilePath, bg);
bg.RunWorkerAsync();
private static void DirectoryNotFoundShowError(string directoryPath, Action<string> showError)
showError(string.Format(ErrorResources.DirectoryNotFoundException, Path.GetDirectoryName(directoryPath)));
private static void FileNotFoundShowError(string filePath, Action<string> showError)
showError(string.Format(ErrorResources.FileNotFoundException, filePath));
internal class StringsNoTransform : StringsTransformBase
return "";
IStringsTransform transform;
if (transforms.Skip(1).Any())
transform = new CompositeStringsTransform(transforms);
var transformInstanciator = transforms.FirstOrDefault();
transform = transformInstanciator == null ? new StringsNoTransform() :
transformInstanciator.CreateInstance();
private void runBtn_Click(object sender, EventArgs e)
processingTransformsLB.Items.Add(FileTransform.Transform(inFilePathTB.Text, outFilePathTB.Text,
transformsLB.Items.Cast<IStringsTransformInstanciator>(),
message => MessageBox.Show(message, Resources.ErrorTitle, MessageBoxButtons.OK, MessageBoxIcon.Error),
message =>
MessageBox.Show(message, Resources.ErrorTitle, MessageBoxButtons.RetryCancel, MessageBoxIcon.Error)
== DialogResult.Retry,
(result, fileTransform) =>
if (result)
transformsDoneLB.Items.Add(fileTransform);
processingTransformsLB.Items.Remove(fileTransform);
}));