File: AtomicFile.cs
Project: ndp\cdf\src\WCF\Tools\comsvcutil\ComSvcConfig.csproj (ComSvcConfig)
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
namespace Microsoft.Tools.ServiceModel.ComSvcConfig
{
    using System;
    using System.Diagnostics;
    using System.Configuration;
    using System.Collections;
    using System.Collections.Specialized;
    using System.Collections.Generic;
    using System.IO;
    using System.Reflection;
    using System.Text;
    using System.Threading;
    using System.Runtime.InteropServices;
    using System.Security;
    using System.ServiceModel;
    using System.ServiceModel.Configuration;
    using Microsoft.Tools.ServiceModel;
    using Microsoft.Tools.ServiceModel.SvcUtil;
 
    // Usage
    //
    // new AtomicFile
    // GetCurrent(readOnly)+
    // Delete (once), OR  GetCurrent(forUpdate)+
    // Prepare
    // Commit or Abort
    // 
    // Abort may also be called before Prepare
 
    class AtomicFile
    {
        bool prepared;
        string originalFileName;
        bool originalFileExists;
        string tempFileName;
        string backupOfOriginalFileName;
        bool deleteOriginalFile;
 
        public string OriginalFileName
        {
            get { return this.originalFileName; }
        }
 
        public bool OriginalFileExists
        {
            get { return this.originalFileExists; }
        }
 
        public AtomicFile(string fileName)
        {
            this.originalFileName = fileName;
            this.originalFileExists = File.Exists(this.originalFileName);
            this.tempFileName = null;
            this.backupOfOriginalFileName = null;
            this.deleteOriginalFile = false;
            this.prepared = false;
        }
 
        public bool CurrentExists()
        {
            if ((this.tempFileName != null))
            {
                return true;
            }
            else
            {
                return this.originalFileExists;
            }
        }
 
        public void Delete()
        {
            if (this.tempFileName != null)
            {
                // once you make changes, you cant ask for delete (we have no scenario to relax this restriction..)
                throw new InvalidOperationException();
            }
 
            if (!this.originalFileExists)
            {
                // cant delete if the original file didnt exist
                throw new InvalidOperationException();
            }
 
            this.deleteOriginalFile = true;
        }
 
        public string GetCurrentFileName(bool readOnly)
        {
            if (this.deleteOriginalFile)
            {
                // once you delete, you cant ask for this.
                throw new InvalidOperationException();
            }
 
            if (this.tempFileName != null)
            {
                return this.tempFileName;
            }
            else if (this.originalFileExists)
            {
                if (readOnly)
                {
                    return this.originalFileName;
                }
                else
                {
                    this.tempFileName = GetTempFileName();
                    File.Copy(this.originalFileName, this.tempFileName);
                    return this.tempFileName;
                }
            }
            else // there is no original file
            {
                if (readOnly)
                {
                    return null;    // nothing to read, caller should tolerate this
                }
                else
                {
                    // want to update, have no file yet, create one..
                    this.tempFileName = GetTempFileName();
                    return this.tempFileName;
                }
            }
        }
 
        // must be called before Preparing
        public bool HasBeenModified()
        {
            return (this.tempFileName != null || (this.originalFileExists && this.deleteOriginalFile));
        }
 
        public void Prepare()
        {
            if (this.tempFileName != null)
            {
                if (this.originalFileExists)
                {
                    this.backupOfOriginalFileName = GetTempFileName();
                    File.Copy(this.originalFileName, this.backupOfOriginalFileName);
                    SafeDeleteFile(this.originalFileName);
                }
 
                File.Copy(this.tempFileName, this.originalFileName);
                SafeDeleteFile(this.tempFileName);
            }
            else if (this.deleteOriginalFile)
            {
                this.backupOfOriginalFileName = GetTempFileName();
                File.Copy(this.originalFileName, this.backupOfOriginalFileName);
                SafeDeleteFile(this.originalFileName);
            }
 
            this.prepared = true;
        }
 
        public void Commit()
        {
            if (!this.prepared)
            {
                throw new InvalidOperationException();
            }
 
            SafeDeleteFile(this.backupOfOriginalFileName);
        }
 
        // keep track of whether we had a "clean" prepare / abort, if not clean we shouldnt delete the backups/tempFiles and point the user to them
        public void Abort()
        {
            if (this.originalFileExists)
            {
                if ((this.backupOfOriginalFileName != null) && File.Exists(this.backupOfOriginalFileName))
                {
                    SafeDeleteFile(this.originalFileName);
                    File.Copy(this.backupOfOriginalFileName, this.originalFileName);
                }
            }
            else
            {
                SafeDeleteFile(this.originalFileName);
            }
 
            SafeDeleteFile(this.backupOfOriginalFileName);
            SafeDeleteFile(this.tempFileName);
        }
 
        // safe as in handles null, and checks for existence first
        internal static void SafeDeleteFile(string fileName)
        {
            if ((!string.IsNullOrEmpty(fileName)) && File.Exists(fileName))
            {
                File.Delete(fileName);
            }
        }
 
        // this function guarantees to return the name of a temp file that doesnt exist
        string GetTempFileName()
        {
            return Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
        }
    }
}