// Copyright (C) Stichting Deltares 2016. All rights reserved.
//
// This file is part of Ringtoets.
//
// Ringtoets is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
// All names, logos, and references to "Deltares" are registered trademarks of
// Stichting Deltares and remain full property of Stichting Deltares at all times.
// All rights reserved.
using System;
using System.IO;
using Application.Ringtoets.Storage.Exceptions;
using Application.Ringtoets.Storage.Properties;
using Core.Common.Utils;
namespace Application.Ringtoets.Storage
{
///
/// Class for providing a safe way of writing files by creating a temporary backup file of targeted files.
///
public class BackedUpFileWriter
{
private const string temporarySuffix = "~";
private readonly string temporaryFilePath;
private readonly string targetFilePath;
private bool isTemporaryFileCreated;
///
/// Creates an instance of .
///
/// The path of the file which will be overwritten.
/// is not a valid path.
public BackedUpFileWriter(string targetFilePath)
{
FileUtils.ValidateFilePath(targetFilePath);
this.targetFilePath = targetFilePath;
temporaryFilePath = targetFilePath + temporarySuffix;
}
///
/// Performs the in a safe way by backing up the targeted file provided when
/// constructing the . It is expected that the
/// will throw an exception when the operation fails, so that the backed up target file can be restored.
///
/// The action to perform after backing up the targeted file.
/// Thrown when:
///
/// - The temporary file already exists and cannot be deleted.
/// - The temporary file cannot be created from the target file.
/// - When reverting, the original file cannot be restored.
///
///
/// When cleaning up, the temporary file cannot be removed.
/// Any thrown by will be rethrown.
public void Perform(Action writeAction)
{
CreateTemporaryFile();
try
{
writeAction();
}
catch
{
Revert();
throw;
}
Finish();
}
///
/// Removes the temporary file if it was created.
///
/// The temporary file cannot be removed.
private void Finish()
{
if (isTemporaryFileCreated)
{
DeleteTemporaryFile();
}
}
///
/// Reverts the target file to the temporary file if it was created. If the operation fails,
/// the temporary file will remain in the directory of the target file.
///
/// The original file cannot be restored.
private void Revert()
{
if (isTemporaryFileCreated)
{
RestoreOriginalFile();
}
}
///
/// Creates a temporary file from the target file, if there is any. Creates a new file at the target
/// file path.
///
/// Thrown when either:
///
/// - The temporary file already exists and cannot be deleted.
/// - The temporary file cannot be created from the target file.
///
///
private void CreateTemporaryFile()
{
isTemporaryFileCreated = false;
if (File.Exists(targetFilePath))
{
RemoveAlreadyExistingTemporaryFile();
CreateNewTemporaryFile();
isTemporaryFileCreated = true;
}
}
///
/// Removes the temporary file for the target file if it already exists.
///
/// The temporary file already exists and cannot be deleted.
private void RemoveAlreadyExistingTemporaryFile()
{
if (File.Exists(temporaryFilePath))
{
try
{
File.Delete(temporaryFilePath);
}
catch (Exception e)
{
if (e is ArgumentException || e is IOException || e is SystemException)
{
var message = string.Format(
Resources.SafeOverwriteFileHelper_RemoveAlreadyExistingTemporaryFile_Already_existing_temporary_file_at_FilePath_0_could_not_be_removed,
temporaryFilePath);
throw new IOException(message, e);
}
throw;
}
}
}
///
/// Creates a temporary file from the target file.
///
/// The temporary file cannot be created from the target file.
private void CreateNewTemporaryFile()
{
try
{
File.Move(targetFilePath, temporaryFilePath);
}
catch (Exception e)
{
if (e is ArgumentException || e is IOException || e is SystemException)
{
var message = string.Format(
Resources.SafeOverwriteFileHelper_CreateNewTemporaryFile_Cannot_create_temporary_FilePath_0_Try_change_save_location,
targetFilePath);
throw new IOException(message, e);
}
throw;
}
}
///
/// Moves the temporary file back to the original path. If the operation fails, the temporary file
/// will remain.
///
/// Thrown when either:
///
/// - The new file could not be deleted.
/// - The temporary file could not be moved to its original place.
///
private void RestoreOriginalFile()
{
try
{
File.Delete(targetFilePath);
File.Move(temporaryFilePath, targetFilePath);
}
catch (Exception e)
{
if (e is ArgumentException || e is IOException || e is SystemException)
{
var message = string.Format(
Resources.SafeOverwriteFileHelper_RestoreOriginalFile_Cannot_revert_to_original_FilePath_0_Try_reverting_manually,
targetFilePath);
throw new IOException(message, e);
}
throw;
}
}
///
/// Deletes the created temporary file.
///
/// The temporary file cannot be removed.
private void DeleteTemporaryFile()
{
try
{
File.Delete(temporaryFilePath);
}
catch (Exception e)
{
if (e is ArgumentException || e is IOException || e is SystemException)
{
var message = string.Format(
Resources.SafeOverwriteFileHelper_DeleteTemporaryFile_Cannot_remove_temporary_FilePath_0_Try_removing_manually,
temporaryFilePath);
throw new CannotDeleteBackupFileException(message, e);
}
throw;
}
}
}
}