public interface ITransform<TSource, TDestination>
{
IEnumerable<TDestination> Do(IEnumerable<TSource> lines);
void Cancel();
bool Cancelled { get; }
}
public abstract class TransformBase<TSource, TDestination> : ITransform<TSource, TDestination>
public abstract IEnumerable<TDestination> Do(IEnumerable<TSource> lines);
public void Cancel()
Cancelled = true;
public bool Cancelled { get; private set; }
public class NoTransform<T> : TransformBase<T, T>
public override IEnumerable<T> Do(IEnumerable<T> lines)
return lines;
public override string ToString()
return "";
public interface ITransformInstanciator<TSource, TDestination>
ITransform<TSource, TDestination> CreateInstance();
public abstract class PerStringTransformBase : TransformBase<string, string>
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()
[Export(typeof(ITransformInstanciator<string, string>))]
public class InvertCharsPerStringTransformInstanciator : ITransformInstanciator<string, string>
internal InvertCharsPerStringTransformInstanciator()
public ITransform<string, string> CreateInstance()
return new InvertCharsPerStringTransform();
return TransformResources.InvertCharsPerStringTransform;
public class NumberPerStringTransformInstanciator : ITransformInstanciator<string, string>
internal NumberPerStringTransformInstanciator()
return new NumberPerStringTransform();
return TransformResources.NumberPerStringTransform;
public class CompositeStringsTransform : TransformBase<string, string>
private List<ITransform<string, string>> _transforms = new List<ITransform<string, string>>();
public CompositeStringsTransform(IEnumerable<ITransformInstanciator<string, string>> transforms)
_transforms.AddRange(transforms.Select(s => s.CreateInstance()));
public CompositeStringsTransform(params ITransformInstanciator<string, string>[] transforms)
: this((IEnumerable<ITransformInstanciator<string, string>>)transforms)
public List<ITransform<string, string>> Transforms
get { return _transforms; }
foreach (var transform in Transforms)
break;
lines = transform.Do(lines);
var sb = new StringBuilder();
sb.Append(" & ");
sb.Append(transform);
return sb.ToString().Substring(3);
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<ITransformInstanciator<string, string>> transforms, Action<string> showError,
Func<string, bool> showErrorWithRetry, Action<bool, FileTransformInfo> transformDone)
ITransform<string, string> transform;
if (transforms.Skip(1).Any())
transform = new CompositeStringsTransform(transforms);
else
var transformInstanciator = transforms.FirstOrDefault();
transform = transformInstanciator == null ? new NoTransform<string>() :
transformInstanciator.CreateInstance();
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);
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));