UidManager.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / BuildTasks / Microsoft / Build / Tasks / Windows / UidManager.cs / 1305600 / UidManager.cs

                             
//----------------------------------------------------------------------------
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved. 
// 
// 
// Description: 
//
//     Adds, updates or checks validity of Uids (Unique identifiers) 
//     on all XAML elements in XAML files.
//
// History:
//  07/24/2003 : dbrown - Created 
//  04/02/2004 : garyyang - preserved format using XmlTextWriter + StreamWriter
//  11/17/2004 : garyyang - rework UidManager. Preserve source file format 
//                          using only stream writer. 
//---------------------------------------------------------------------------
 

using System;
using System.Xml;
using System.IO; 
using System.Text;
using System.Text.RegularExpressions; 
using System.Reflection; 
using System.Collections;
using System.Collections.Generic; 
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
 
using MS.Internal.Markup;
 
using Microsoft.Build.Framework; 
using Microsoft.Build.Utilities;
 
using MS.Utility;                   // For SR
using MS.Internal.Tasks;

// Since we disable PreSharp warnings in this file, we first need to disable warnings about unknown message numbers and unknown pragmas. 
#pragma warning disable 1634, 1691
 
namespace Microsoft.Build.Tasks.Windows 
{
    ///  
    /// An MSBuild task that checks or corrects unique identifiers in
    /// XAML markup.
    /// 
    public sealed class UidManager : Task 
    {
        //----------------------------------------------------- 
        // 
        //  Constructors
        // 
        //-----------------------------------------------------

        #region Constructors
 
        /// 
        /// Create a UidManager object. 
        ///  
        public UidManager() : base(SR.ResourceManager)
        { 
            _backupPath = Directory.GetCurrentDirectory();
        }

        #endregion 

        //------------------------------------------------------ 
        // 
        //  Public Methods
        // 
        //-----------------------------------------------------

        #region Public Methods
 
        /// 
        /// The method invoked by MSBuild to check or correct Uids. 
        ///  
        public override bool Execute()
        { 
            TaskHelper.DisplayLogo(Log, SR.Get(SRID.UidManagerTask));

            if (MarkupFiles == null || MarkupFiles.Length == 0)
            { 
                Log.LogErrorWithCodeFromResources(SRID.SourceFileNameNeeded);
                return false; 
            } 

            try 
            {
                _task = (UidTask)Enum.Parse(typeof(UidTask), _taskAsString);
            }
            catch (ArgumentException) 
            {
                Log.LogErrorWithCodeFromResources(SRID.BadUidTask, _taskAsString); 
                return false; 
            }
 

            bool allFilesOk;
            try
            { 
                allFilesOk = ManageUids();
            } 
            catch (Exception e) 
            {
                // PreSharp Complaint 6500 - do not handle null-ref or SEH exceptions. 
                if (e is NullReferenceException || e is SEHException)
                {
                    throw;
                } 
                else
                { 
                    string message; 
                    string errorId;
 
                    errorId = Log.ExtractMessageCode(e.Message, out message);

                    if (String.IsNullOrEmpty(errorId))
                    { 
                        errorId = UnknownErrorID;
                        message = SR.Get(SRID.UnknownBuildError, message); 
                    } 

                    Log.LogError(null, errorId, null, null, 0, 0, 0, 0, message, null); 

                    allFilesOk = false;
                }
            } 
#pragma warning disable 6500
            catch // Non-CLS compliant errors 
            { 
                Log.LogErrorWithCodeFromResources(SRID.NonClsError);
                allFilesOk = false; 
            }
#pragma warning restore 6500

            return allFilesOk; 
        }
 
        #endregion 

        //------------------------------------------------------ 
        //
        //  Public Properties
        //
        //------------------------------------------------------ 

        #region Public Properties 
        /// 
        /// The markup file(s) to be checked or updated.
        /// 
        [Required]
        public ITaskItem[] MarkupFiles
        {
            get { return _markupFiles; } 
            set { _markupFiles = value; }
        } 
 
        /// 
        /// The directory for intermedia files 
        /// 
        /// 
        /// 
        public string IntermediateDirectory 
        {
            get { return _backupPath; } 
            set 
            {
                string sourceDir = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar; 
                _backupPath = TaskHelper.CreateFullFilePath(value, sourceDir);
            }
        }
 

        /// 
        /// Enum to determine which Uid management task to undertake 
        ///
        private enum UidTask 
        {

            ///
            /// Uid managment task to check validity of Uids 
            ///
            Check = 0, 
 
            ///
            /// Uid managment task to Update Uids to a valid state 
            ///
            Update = 1,

            /// 
            /// Uid managment task to remove all Uids
            /// 
            Remove = 2, 
        }
 
        ///
        /// Uid management task required
        ///
        [Required] 
        public string Task
        { 
            get { return _taskAsString; } 
            set { _taskAsString = value; }
        } 

        #endregion

        //----------------------------------------------------- 
        //
        //  Private Methods 
        // 
        //------------------------------------------------------
 
        private bool ManageUids()
        {
            int countGoodFiles = 0;
            // enumerate through each file 
            foreach (ITaskItem inputFile in _markupFiles)
            { 
                Log.LogMessageFromResources(SRID.CheckingUids, inputFile.ItemSpec); 
                switch (_task)
                { 
                    case UidTask.Check:
                    {
                        UidCollector collector = ParseFile(inputFile.ItemSpec);
 
                        bool success = VerifyUid(
                            collector,          // uid collector 
                            true                // log error 
                            );
 
                        if (success) countGoodFiles++;
                        break;
                    }
                    case UidTask.Update: 
                    {
                        UidCollector collector = ParseFile(inputFile.ItemSpec); 
 
                        bool success = VerifyUid(
                            collector,          // uid collector 
                            false               // log error
                            );

                        if (!success) 
                        {
                            if (SetupBackupDirectory()) 
                            { 
                                // resolve errors
                                collector.ResolveUidErrors(); 

                                // temp file to write to
                                string tempFile   = GetTempFileName(inputFile.ItemSpec);
 
                                // backup file of the source file before it is overwritten.
                                string backupFile = GetBackupFileName(inputFile.ItemSpec); 
 
                                using (Stream uidStream = new FileStream(tempFile, FileMode.Create))
                                { 
                                    using (Stream source = File.OpenRead(inputFile.ItemSpec))
                                    {
                                        UidWriter writer = new UidWriter(collector, source, uidStream);
                                        writer.UpdateUidWrite(); 
                                    }
                                } 
 
                                // backup source file by renaming it. Expect to be (close to) atomic op.
                                RenameFile(inputFile.ItemSpec, backupFile); 

                                // rename the uid output onto the source file. Expect to be (close to) atomic op.
                                RenameFile(tempFile, inputFile.ItemSpec);
 
                                // remove the temp files
                                RemoveFile(tempFile); 
                                RemoveFile(backupFile); 

                                countGoodFiles++; 
                            }
                        }
                        else
                        { 
                            // all uids are good. No-op
                            countGoodFiles++; 
                        } 

                        break; 
                    }
                    case UidTask.Remove:
                    {
                        UidCollector collector = ParseFile(inputFile.ItemSpec); 

                        bool hasUid = false; 
                        for (int i = 0; i < collector.Count; i++) 
                        {
                            if (collector[i].Status != UidStatus.Absent) 
                            {
                                hasUid = true;
                                break;
                            } 
                        }
 
                        if (hasUid) 
                        {
                            if (SetupBackupDirectory()) 
                            {
                                // temp file to write to
                                string tempFile   = GetTempFileName(inputFile.ItemSpec);
 
                                // backup file of the source file before it is overwritten.
                                string backupFile = GetBackupFileName(inputFile.ItemSpec); 
 
                                using (Stream uidStream = new FileStream(tempFile, FileMode.Create))
                                { 
                                    using (Stream source = File.OpenRead(inputFile.ItemSpec))
                                    {
                                        UidWriter writer = new UidWriter(collector, source, uidStream);
                                        writer.RemoveUidWrite(); 
                                    }
                                } 
 
                                // rename the source file to the backup file name. Expect to be (close to) atomic op.
                                RenameFile(inputFile.ItemSpec, backupFile); 

                                // rename the output file over to the source file. Expect to be (close to) atomic op.
                                RenameFile(tempFile, inputFile.ItemSpec);
 
                                // remove the temp files
                                RemoveFile(tempFile); 
                                RemoveFile(backupFile); 

                                countGoodFiles++; 
                            }
                        }
                        else
                        { 
                            // There is no Uid in the file. No need to do remove.
                            countGoodFiles++; 
                        } 

                        break; 
                    }
                }
            }
 
            // spew out the overral log info for the task
            switch (_task) 
            { 
                case UidTask.Remove:
                    Log.LogMessageFromResources(SRID.FilesRemovedUid, countGoodFiles); 
                    break;

                case UidTask.Update:
                    Log.LogMessageFromResources(SRID.FilesUpdatedUid, countGoodFiles); 
                    break;
 
                case UidTask.Check: 
                    Log.LogMessageFromResources(SRID.FilesPassedUidCheck, countGoodFiles);
 
                    if (_markupFiles.Length > countGoodFiles)
                    {
                        Log.LogErrorWithCodeFromResources(SRID.FilesFailedUidCheck, _markupFiles.Length - countGoodFiles);
                    } 
                    break;
            } 
 
            return _markupFiles.Length == countGoodFiles;
        } 


       private string GetTempFileName(string fileName)
        { 
            return Path.Combine(_backupPath, Path.ChangeExtension(Path.GetFileName(fileName), "uidtemp"));
        } 
 
        private string GetBackupFileName (string fileName)
        { 
            return Path.Combine(_backupPath, Path.ChangeExtension(Path.GetFileName(fileName), "uidbackup"));
        }

        private void RenameFile(string src, string dest) 
        {
            RemoveFile(dest); 
            File.Move(src, dest); 
        }
 
        private void RemoveFile(string fileName)
        {
            if (File.Exists(fileName))
            { 
                File.Delete(fileName);
            } 
        } 

        private bool SetupBackupDirectory() 
        {
            try
            {
                if (!Directory.Exists(_backupPath)) 
                {
                    Directory.CreateDirectory(_backupPath); 
                } 

                return true; 
            }
            catch (Exception e)
            {
                // PreSharp Complaint 6500 - do not handle null-ref or SEH exceptions. 
                if (e is NullReferenceException || e is SEHException)
                { 
                    throw; 
                }
                else 
                {
                    Log.LogErrorWithCodeFromResources(SRID.IntermediateDirectoryError, _backupPath);
                    return false;
                } 
            }
#pragma warning disable 6500 
            catch   // Non-cls compliant errors 
            {
                Log.LogErrorWithCodeFromResources(SRID.IntermediateDirectoryError, _backupPath); 
                return false;
            }
#pragma warning restore 6500
        } 

 
 

        ///  
        /// Verify the Uids in the file
        /// 
        /// UidCollector containing all Uid instances
        /// true to log errors while verifying 
        /// true indicates no errors
        private bool VerifyUid( 
            UidCollector collector, 
            bool         logError
            ) 
        {
            bool errorFound = false;

            for (int i = 0; i < collector.Count; i++) 
            {
                Uid currentUid = collector[i]; 
                if (currentUid.Status == UidStatus.Absent) 
                {
                    // Uid missing 
                    if (logError)
                    {
                        Log.LogErrorWithCodeFromResources(
                             null, 
                             collector.FileName,
                             currentUid.LineNumber, 
                             currentUid.LinePosition, 
                             0, 0,
                             SRID.UidMissing, currentUid.ElementName 
                         );
                    }

                    errorFound = true; 
                }
                else if (currentUid.Status == UidStatus.Duplicate) 
                { 
                    // Uid duplicates
                    if (logError) 
                    {
                        Log.LogErrorWithCodeFromResources(
                              null,
                              collector.FileName, 
                              currentUid.LineNumber,
                              currentUid.LinePosition, 
                              0, 0, 
                              SRID.MultipleUidUse, currentUid.Value, currentUid.ElementName
                         ); 

                    }

                    errorFound = true; 
                }
 
            } 

            return !errorFound; 
        }

        /// 
        /// Parse the input file and get all the information of Uids 
        /// 
        /// input file 
        /// UidCollector containing all the information for the Uids in the file 
        private UidCollector ParseFile(string fileName)
        { 
            UidCollector collector = new UidCollector(fileName  );

            using (Stream xamlStream = File.OpenRead(fileName))
            { 
                XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable());
                XmlParserContext context  = new XmlParserContext( 
                    null,                 // nametable 
                    nsmgr,                // namespace manager
                    null,                 // xml:Lang scope 
                    XmlSpace.Default      // XmlSpace
                    );

                XmlTextReader reader = new XmlTextReader( 
                    xamlStream,           // xml stream
                    XmlNodeType.Document, // parsing document 
                    context               // parser context 
                    );
 
                while (reader.Read())
                {
                    switch (reader.NodeType)
                    { 
                        case XmlNodeType.Element :
                        { 
                            if (collector.RootElementLineNumber < 0) 
                            {
                                collector.RootElementLineNumber   = reader.LineNumber; 
                                collector.RootElementLinePosition = reader.LinePosition;
                            }

                            if (reader.Name.IndexOf('.') >= 0) 
                            {
                                // the name has a dot, which suggests it is a property tag. 
                                // we will ignore adding uid 
                                continue;
                            } 

                            Uid currentUid = new Uid(
                                    reader.LineNumber,
                                    reader.LinePosition + reader.Name.Length, 
                                    reader.Name,
                                    SpaceInsertion.BeforeUid  // insert space before the Uid 
                                    );                                   ; 

                            if (reader.HasAttributes) 
                            {
                                reader.MoveToNextAttribute();

                                // As a heuristic to better preserve the source file, add uid to the place of the 
                                // first attribute
                                currentUid.LineNumber   = reader.LineNumber; 
                                currentUid.LinePosition = reader.LinePosition; 
                                currentUid.Space        = SpaceInsertion.AfterUid;
 
                                do
                                {
                                    string namespaceUri = nsmgr.LookupNamespace(reader.Prefix);
 
                                    if (reader.LocalName == XamlReaderHelper.DefinitionUid
                                     && namespaceUri == XamlReaderHelper.DefinitionNamespaceURI) 
                                    { 
                                        // found x:Uid attribute, store the actual value and position
                                        currentUid.Value        = reader.Value; 
                                        currentUid.LineNumber   = reader.LineNumber;
                                        currentUid.LinePosition = reader.LinePosition;
                                    }
                                    else if (reader.LocalName == "Name" 
                                          && namespaceUri == XamlReaderHelper.DefaultNamespaceURI)
                                    { 
                                        // found Name attribute, store the Name value 
                                        currentUid.FrameworkElementName = reader.Value;
                                    } 
                                    else if (reader.LocalName == "Name"
                                          && namespaceUri == XamlReaderHelper.DefinitionNamespaceURI)
                                    {
                                        // found x:Name attribute, store the Name value 
                                        currentUid.FrameworkElementName = reader.Value;
                                    } 
                                    else if (reader.Prefix == "xmlns") 
                                    {
                                        // found a namespace declaration, store the namespace prefix 
                                        // so that when we need to add a new namespace declaration later
                                        // we won't reuse the namespace prefix.
                                        collector.AddNamespacePrefix(reader.LocalName);
                                    } 
                                }
                                while (reader.MoveToNextAttribute()); 
 
                            }
 
                            if (currentUid.Value == null)
                            {
                                // there is no x:Uid found on this element, we need to resolve the
                                // namespace prefix in order to add the Uid 
                                string prefix = nsmgr.LookupPrefix(XamlReaderHelper.DefinitionNamespaceURI);
                                if (prefix != string.Empty) 
                                    currentUid.NamespacePrefix = prefix; 
                            }
 
                            collector.AddUid(currentUid);
                            break;
                        }
                    } 
                }
            } 
 
            return collector;
        } 

        //-----------------------------------
        // Private members
        //----------------------------------- 
        private UidTask       _task;            // task
        private ITaskItem[]   _markupFiles;     // input Xaml files 
        private string        _taskAsString;    // task string 
        private string        _backupPath;      // path to store to backup source Xaml files
        private const string UnknownErrorID = "UM1000"; 
    }

    // represent all the information about a Uid
    // The uid may be valid, absent, or duplicate 
    internal sealed class Uid
    { 
        internal Uid( 
            int     lineNumber,
            int     linePosition, 
            string  elementName,
            SpaceInsertion    spaceInsertion
            )
        { 
            LineNumber          = lineNumber;
            LinePosition        = linePosition; 
            ElementName         = elementName; 
            Value               = null;
            NamespacePrefix     = null; 
            FrameworkElementName  = null;
            Status              = UidStatus.Valid;
            Space               = spaceInsertion;
 
        }
 
        internal int        LineNumber;         // Referenced line number of the original document 
        internal int        LinePosition;       // Reference line position of the original document
        internal string     ElementName;        // name of the element that needs this uid 
        internal SpaceInsertion    Space;       // Insert a space before/after the Uid

        internal string     Value;              // value of the uid
        internal string     NamespacePrefix;    // namespace prefix for the uid 
        internal string     FrameworkElementName; // the FrameworkElement.Name of element
        internal UidStatus  Status;             // the status of the this uid 
 
    }
 
    internal enum UidStatus : byte
    {
        Valid       = 0,    // uid is valid
        Absent      = 1,    // uid is absent 
        Duplicate   = 2,    // uid is duplicated
    } 
 
    internal enum SpaceInsertion : byte
    { 
        BeforeUid,          // Insert a space before the Uid
        AfterUid            // Insert a space after the Uid
    }
 
    // a class collects all the information about Uids per file
    internal sealed class UidCollector 
    { 
        public UidCollector(string fileName)
        { 
            _uids               = new List(32);
            _namespacePrefixes  = new List(2);
            _uidTable           = new Hashtable();
            _fileName           = fileName; 
            _sequenceMaxIds     = new Hashtable();
        } 
 
        // remembering all the namespace prefixes in the file
        // in case we need to add a new definition namespace declaration 
        public void AddNamespacePrefix(string prefix)
        {
            _namespacePrefixes.Add(prefix);
        } 

        // add the uid to the collector 
        public void AddUid(Uid uid) 
        {
            _uids.Add(uid); 

            // set the uid status according to the raw data
            if (uid.Value == null)
            { 
                uid.Status = UidStatus.Absent;
            } 
            else  if (_uidTable.Contains(uid.Value)) 
            {
                uid.Status = UidStatus.Duplicate; 
            }
            else
            {
                // valid uid, store it 
                StoreUid(uid.Value);
            } 
        } 

        public void ResolveUidErrors() 
        {
            for (int i = 0; i < _uids.Count; i++)
            {
                Uid currentUid = _uids[i]; 

                if ( currentUid.Status == UidStatus.Absent 
                  && currentUid.NamespacePrefix == null 
                  && _namespacePrefixForMissingUid == null)
                { 
                    // there is Uid not in scope of any definition namespace
                    // we will need to generate a new namespace prefix for them
                    _namespacePrefixForMissingUid = GeneratePrefix();
                } 

                if (currentUid.Status != UidStatus.Valid) 
                { 
                    // resolve invalid uids
                    currentUid.Value = GetAvailableUid(currentUid); 
                }
            }
        }
 
        public int RootElementLineNumber
        { 
            get { return _rootElementLineNumber; } 
            set { _rootElementLineNumber = value; }
        } 

        public int RootElementLinePosition
        {
            get { return _rootElementLinePosition; } 
            set { _rootElementLinePosition = value; }
        } 
 
        public string FileName
        { 
            get { return _fileName; }
        }

        public Uid this[int index] 
        {
            get 
            { 
                return _uids[index];
            } 
        }

        public int Count
        { 
            get { return _uids.Count; }
        } 
 
        public string NamespaceAddedForMissingUid
        { 
            get { return _namespacePrefixForMissingUid; }
        }

 
        //-------------------------------------
        // Private methods 
        //------------------------------------- 
        private void StoreUid(string value)
        { 
            // we just want to check for existence, so storing a null
            _uidTable[value] = null;

            string uidSequence; 
            Int64 index;
 
            ParseUid(value, out uidSequence, out index); 
            if (uidSequence != null)
            { 
                if (_sequenceMaxIds.Contains(uidSequence))
                {
                    Int64 maxIndex = (Int64)_sequenceMaxIds[uidSequence];
                    if (maxIndex < index) 
                    {
                        _sequenceMaxIds[uidSequence] = index; 
                    } 
                }
                else 
                {
                    _sequenceMaxIds[uidSequence] = index;
                }
            } 
        }
 
        private string GetAvailableUid(Uid uid) 
        {
            string availableUid; 

            // copy the ID if available
            if (uid.FrameworkElementName != null
             && (!_uidTable.Contains(uid.FrameworkElementName)) 
             )
            { 
                availableUid = uid.FrameworkElementName; 
            }
            else 
            {
                // generate a new id
                string sequence = GetElementLocalName(uid.ElementName);
                Int64 index; 

                if (_sequenceMaxIds.Contains(sequence)) 
                { 
                    index = (Int64) _sequenceMaxIds[sequence];
 
                    if (index == Int64.MaxValue)
                    {
                        // this sequence reaches the max
                        // we fallback to create a new sequence 
                        index = -1;
                        while (index < 0) 
                        { 
                            sequence = (_uidSequenceFallbackCount == 0) ?
                                UidFallbackSequence 
                              : UidFallbackSequence + _uidSequenceFallbackCount;

                            if (_sequenceMaxIds.Contains(sequence))
                            { 
                                index = (Int64) _sequenceMaxIds[sequence];
                                if (index < Int64.MaxValue) 
                                { 
                                    // found the fallback sequence with valid index
                                    index ++; 
                                    break;
                                }
                            }
                            else 
                            {
                                // create a new sequence from 1 
                                index = 1; 
                                break;
                            } 

                            _uidSequenceFallbackCount ++;
                        }
                    } 
                    else
                    { 
                        index ++; 
                    }
                } 
                else
                {
                    // a new sequence
                    index = 1; 
                }
 
                availableUid = sequence + UidSeparator + index; 
            }
 
            // store the uid so that it won't be used again
            StoreUid(availableUid);
            return availableUid;
        } 

        private void ParseUid(string uid, out string prefix, out Int64 index) 
        { 
            // set prefix and index to invalid values
            prefix = null; 
            index  = -1;

            if (uid == null) return;
 
            int separatorIndex = uid.LastIndexOf(UidSeparator);
            if (separatorIndex > 0) 
            { 
                string suffix = uid.Substring(separatorIndex + 1);
 
                // Disable Presharp warning 6502 : catch block shouldn't have empty body
                #pragma warning disable 6502
                try {
                    index  = Int64.Parse(suffix, TypeConverterHelper.InvariantEnglishUS); 
                    prefix = uid.Substring(0, separatorIndex);
                } 
                catch (FormatException) 
                {
                    // wrong format 
                }
                catch (OverflowException)
                {
                    // not acceptable uid 
                }
                #pragma warning restore 6502 
            } 
        }
 
        private string GetElementLocalName(string typeFullName)
        {
            int index = typeFullName.LastIndexOf('.');
            if (index > 0) 
            {
                return typeFullName.Substring(index + 1); 
            } 
            else
            { 
                return typeFullName;
            }
        }
 
        private string GeneratePrefix()
        { 
            Int64 ext = 1; 
            string prefix = UidNamespaceAbbreviation.ToString(TypeConverterHelper.InvariantEnglishUS);
 
            // Disable Presharp warning 6502 : catch block shouldn't have empty body
            #pragma warning disable 6502
            try
            { 
                // find a prefix that is not used in the Xaml
                // from x1, x2, ... x[n] 
                while (_namespacePrefixes.Contains(prefix)) 
                {
                    prefix = UidNamespaceAbbreviation + ext.ToString(TypeConverterHelper.InvariantEnglishUS); 
                    ext++;
                }
                return prefix;
            } 
            catch (OverflowException)
            { 
            } 
            #pragma warning restore 6502
 
            // if overflows, (extreamly imposible), we will return a guid as the prefix
            return Guid.NewGuid().ToString();
        }
 
        private List             _uids;
        private Hashtable             _uidTable; 
        private string                _fileName; 
        private Hashtable             _sequenceMaxIds;
        private List          _namespacePrefixes; 
        private int                   _rootElementLineNumber        = -1;
        private int                   _rootElementLinePosition      = -1;
        private string                _namespacePrefixForMissingUid = null;
        private int                   _uidSequenceFallbackCount     = 0; 

        private const char UidNamespaceAbbreviation   = 'x'; 
        private const char UidSeparator               = '_'; 
        private const string UidFallbackSequence      = "_Uid";
    } 


    // writing to a file, removing or updating uid
    internal sealed class UidWriter 
    {
        internal UidWriter(UidCollector collector, Stream source, Stream target) 
        { 
            _collector     = collector;
            _sourceReader  = new StreamReader(source); 

             UTF8Encoding encoding = new UTF8Encoding(true);
            _targetWriter  = new StreamWriter(target, encoding);
            _lineBuffer    = new LineBuffer(_sourceReader.ReadLine()); 
        }
 
        // write to target stream and update uids 
        internal bool UpdateUidWrite()
        { 
            try {
                // we need to add a new namespace
                if (_collector.NamespaceAddedForMissingUid != null)
                { 
                    // write to the beginning of the root element
                    WriteTillSourcePosition( 
                        _collector.RootElementLineNumber, 
                        _collector.RootElementLinePosition
                        ); 

                    WriteElementTag();
                    WriteSpace();
                    WriteNewNamespace(); 
                }
 
                for (int i = 0; i < _collector.Count; i++) 
                {
                    Uid currentUid = _collector[i]; 
                    WriteTillSourcePosition(currentUid.LineNumber, currentUid.LinePosition);

                    if (currentUid.Status == UidStatus.Absent)
                    { 

                        if (currentUid.Space == SpaceInsertion.BeforeUid) 
                        { 
                            WriteSpace();
                        } 

                        WriteNewUid(currentUid);

                        if (currentUid.Space == SpaceInsertion.AfterUid) 
                        {
                            WriteSpace(); 
                        } 
                    }
                    else if (currentUid.Status == UidStatus.Duplicate) 
                    {
                        ProcessAttributeStart(WriterAction.Write);
                        SkipSourceAttributeValue();
                        WriteNewAttributeValue(currentUid.Value); 
                    }
                } 
                WriteTillEof(); 
                return true;
            } 
            catch (Exception e)
            {
                // PreSharp Complaint 6500 - do not handle null-ref or SEH exceptions.
                if (e is NullReferenceException || e is SEHException) 
                {
                    throw; 
                } 

                return false; 
            }
#pragma warning disable 6500
            catch
            { 
                return false;
            } 
#pragma warning restore 6500 
        }
 
        // writing to the target stream removing uids
        internal bool RemoveUidWrite()
        {
            try { 
                for (int i = 0; i < _collector.Count; i++)
                { 
                    Uid currentUid = _collector[i]; 

                    // skipping valid and duplicate uids. 
                    if ( currentUid.Status == UidStatus.Duplicate
                      || currentUid.Status == UidStatus.Valid)
                    {
                        // write till the space in front of the Uid 
                        WriteTillSourcePosition(currentUid.LineNumber, currentUid.LinePosition - 1);
 
                        // skip the uid 
                        ProcessAttributeStart(WriterAction.Skip);
                        SkipSourceAttributeValue(); 
                    }
                }

                WriteTillEof(); 
                return true;
            } 
            catch (Exception e) 
            {
                // PreSharp Complaint 6500 - do not handle null-ref or SEH exceptions. 
                if (e is NullReferenceException || e is SEHException)
                {
                    throw;
                } 

                return false; 
            } 
#pragma warning disable 6500
            catch 
            {
                return false;
            }
#pragma warning restore 6500 
        }
 
        private void WriteTillSourcePosition(int lineNumber, int linePosition) 
        {
            // write to the correct line 
            while (_currentLineNumber < lineNumber)
            {
                // write out the line buffer
                _targetWriter.WriteLine(_lineBuffer.ReadToEnd()); 
                _currentLineNumber++;
                _currentLinePosition = 1; 
 
                // read one more line
                _lineBuffer.SetLine(_sourceReader.ReadLine()); 
            }

            // write to the correct line position
            while (_currentLinePosition < linePosition) 
            {
                _targetWriter.Write(_lineBuffer.Read()); 
                _currentLinePosition++; 
            }
 
        }

        private void WriteElementTag()
        { 
            if (_lineBuffer.EOL)
            { 
                // advance to the non-empty line 
                AdvanceTillNextNonEmptyLine(WriterAction.Write);
            } 

            char ch = _lineBuffer.Peek();

            // stop when we see space, "/" or ">". That is the end of the 
            // element name
            while (!Char.IsWhiteSpace(ch) 
                   && ch != '/' 
                   && ch != '>'
                  ) 
            {
                _targetWriter.Write(ch);

                _currentLinePosition++; 
                _lineBuffer.Read();
                if (_lineBuffer.EOL) 
                { 
                    AdvanceTillNextNonEmptyLine(WriterAction.Write);
                } 

                ch = _lineBuffer.Peek();
            }
        } 

        private void WriteNewUid(Uid uid) 
        { 
            // construct the attribute name, e.g. x:Uid
            // "x" will be the resolved namespace prefix for the definition namespace 
            string attributeName =
                (uid.NamespacePrefix == null) ?
                 _collector.NamespaceAddedForMissingUid + ":" + XamlReaderHelper.DefinitionUid
               : uid.NamespacePrefix + ":" + XamlReaderHelper.DefinitionUid; 

            // escape all the Xml entities in the value 
            string attributeValue = EscapedXmlEntities.Replace( 
                uid.Value,
                EscapeMatchEvaluator 
                );

            string clause = string.Format(
                TypeConverterHelper.InvariantEnglishUS, 
                "{0}=\"{1}\"",
                attributeName, 
                attributeValue 
                );
 
            _targetWriter.Write(clause);
        }

        private void WriteNewNamespace() 
        {
            string clause = string.Format( 
                TypeConverterHelper.InvariantEnglishUS, 
                "xmlns:{0}=\"{1}\"",
                _collector.NamespaceAddedForMissingUid, 
                XamlReaderHelper.DefinitionNamespaceURI
                );

            _targetWriter.Write(clause); 
        }
 
        private void WriteNewAttributeValue(string value) 
        {
            string attributeValue = EscapedXmlEntities.Replace( 
                value,
                EscapeMatchEvaluator
                );
 
            _targetWriter.Write(
                string.Format( 
                    TypeConverterHelper.InvariantEnglishUS, 
                    "\"{0}\"",
                    value 
                    )
                );
        }
 
        private void WriteSpace()
        { 
             // insert a space 
            _targetWriter.Write(" ");
        } 

        private void WriteTillEof()
        {
            _targetWriter.WriteLine(_lineBuffer.ReadToEnd()); 
            _targetWriter.Write(_sourceReader.ReadToEnd());
            _targetWriter.Flush(); 
        } 

        private void SkipSourceAttributeValue() 
        {
            char ch = (char) 0;

            // read to the start quote of the attribute value 
            while (ch != '\"' && ch != '\'')
            { 
                if (_lineBuffer.EOL) 
                {
                    AdvanceTillNextNonEmptyLine(WriterAction.Skip); 
                }

                ch = _lineBuffer.Read();
                _currentLinePosition ++; 
            }
 
            char attributeValueStart = ch; 
            // read to the end quote of the attribute value
            ch = (char) 0; 
            while (ch != attributeValueStart)
            {
                if (_lineBuffer.EOL)
                { 
                    AdvanceTillNextNonEmptyLine(WriterAction.Skip);
                } 
 
                ch = _lineBuffer.Read();
                _currentLinePosition ++; 
            }
        }

        private void AdvanceTillNextNonEmptyLine(WriterAction action) 
        {
            do 
            { 
                if (action == WriterAction.Write)
                { 
                    _targetWriter.WriteLine();
                }

                _lineBuffer.SetLine(_sourceReader.ReadLine()); 
                _currentLineNumber++;
                _currentLinePosition = 1; 
 
            } while (_lineBuffer.EOL);
        } 


        private void ProcessAttributeStart(WriterAction action)
        { 
            if (_lineBuffer.EOL)
            { 
                AdvanceTillNextNonEmptyLine(action); 
            }
 
            char ch;
            do
            {
                ch = _lineBuffer.Read(); 

                if (action == WriterAction.Write) 
                { 
                    _targetWriter.Write(ch);
                } 

                _currentLinePosition++;

                if (_lineBuffer.EOL) 
                {
                    AdvanceTillNextNonEmptyLine(action); 
                } 

            } while (ch != '='); 
        }

        //
        // source position in a file starts from (1,1) 
        //
 
        private int             _currentLineNumber   = 1;   // current line number in the source stream 
        private int             _currentLinePosition = 1;   // current line position in the source stream
        private LineBuffer      _lineBuffer;                // buffer for one line's content 

        private UidCollector    _collector;
        private StreamReader    _sourceReader;
        private StreamWriter    _targetWriter; 

        // 
        // buffer for the content of a line 
        // The UidWriter always reads one line at a time from the source
        // and store the line in this buffer. 
        //
        private sealed class LineBuffer
        {
            private int    Index; 
            private string Content;
 
            public LineBuffer(string line) 
            {
                SetLine(line); 
            }

            public void SetLine(string line)
            { 
                Content = (line == null) ? string.Empty : line;
                Index   = 0; 
            } 

            public bool EOL 
            {
                get { return (Index == Content.Length); }
            }
 
            public char Read()
            { 
                if (!EOL) 
                {
                    return Content[Index++]; 
                }

                throw new InvalidOperationException();
            } 

            public char Peek() 
            { 
                if (!EOL)
                { 
                    return Content[Index];
                }

                throw new InvalidOperationException(); 
            }
 
            public string ReadToEnd() 
            {
                if (!EOL) 
                {
                    int temp = Index;
                    Index    = Content.Length;
 
                    return Content.Substring(temp);
                } 
 
                return string.Empty;
            } 
        }

        private enum WriterAction
        { 
            Write = 0,  // write the content
            Skip  = 1,  // skip the content 
        } 

        private static Regex          EscapedXmlEntities   = new Regex("(<|>|\"|'|&)", RegexOptions.CultureInvariant | RegexOptions.Compiled); 
        private static MatchEvaluator EscapeMatchEvaluator = new MatchEvaluator(EscapeMatch);

        /// 
        /// the delegate to escape the matched pattern 
        /// 
        private static string EscapeMatch(Match match) 
        { 
            switch (match.Value)
            { 
                case "<":
                    return "<";
                case ">":
                    return ">"; 
                case "&":
                    return "&"; 
                case "\"": 
                    return """;
                case "'": 
                    return "'";
                default:
                   return match.Value;
            } 
        }
    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
 
//----------------------------------------------------------------------------
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved. 
// 
// 
// Description: 
//
//     Adds, updates or checks validity of Uids (Unique identifiers) 
//     on all XAML elements in XAML files.
//
// History:
//  07/24/2003 : dbrown - Created 
//  04/02/2004 : garyyang - preserved format using XmlTextWriter + StreamWriter
//  11/17/2004 : garyyang - rework UidManager. Preserve source file format 
//                          using only stream writer. 
//---------------------------------------------------------------------------
 

using System;
using System.Xml;
using System.IO; 
using System.Text;
using System.Text.RegularExpressions; 
using System.Reflection; 
using System.Collections;
using System.Collections.Generic; 
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
 
using MS.Internal.Markup;
 
using Microsoft.Build.Framework; 
using Microsoft.Build.Utilities;
 
using MS.Utility;                   // For SR
using MS.Internal.Tasks;

// Since we disable PreSharp warnings in this file, we first need to disable warnings about unknown message numbers and unknown pragmas. 
#pragma warning disable 1634, 1691
 
namespace Microsoft.Build.Tasks.Windows 
{
    ///  
    /// An MSBuild task that checks or corrects unique identifiers in
    /// XAML markup.
    /// 
    public sealed class UidManager : Task 
    {
        //----------------------------------------------------- 
        // 
        //  Constructors
        // 
        //-----------------------------------------------------

        #region Constructors
 
        /// 
        /// Create a UidManager object. 
        ///  
        public UidManager() : base(SR.ResourceManager)
        { 
            _backupPath = Directory.GetCurrentDirectory();
        }

        #endregion 

        //------------------------------------------------------ 
        // 
        //  Public Methods
        // 
        //-----------------------------------------------------

        #region Public Methods
 
        /// 
        /// The method invoked by MSBuild to check or correct Uids. 
        ///  
        public override bool Execute()
        { 
            TaskHelper.DisplayLogo(Log, SR.Get(SRID.UidManagerTask));

            if (MarkupFiles == null || MarkupFiles.Length == 0)
            { 
                Log.LogErrorWithCodeFromResources(SRID.SourceFileNameNeeded);
                return false; 
            } 

            try 
            {
                _task = (UidTask)Enum.Parse(typeof(UidTask), _taskAsString);
            }
            catch (ArgumentException) 
            {
                Log.LogErrorWithCodeFromResources(SRID.BadUidTask, _taskAsString); 
                return false; 
            }
 

            bool allFilesOk;
            try
            { 
                allFilesOk = ManageUids();
            } 
            catch (Exception e) 
            {
                // PreSharp Complaint 6500 - do not handle null-ref or SEH exceptions. 
                if (e is NullReferenceException || e is SEHException)
                {
                    throw;
                } 
                else
                { 
                    string message; 
                    string errorId;
 
                    errorId = Log.ExtractMessageCode(e.Message, out message);

                    if (String.IsNullOrEmpty(errorId))
                    { 
                        errorId = UnknownErrorID;
                        message = SR.Get(SRID.UnknownBuildError, message); 
                    } 

                    Log.LogError(null, errorId, null, null, 0, 0, 0, 0, message, null); 

                    allFilesOk = false;
                }
            } 
#pragma warning disable 6500
            catch // Non-CLS compliant errors 
            { 
                Log.LogErrorWithCodeFromResources(SRID.NonClsError);
                allFilesOk = false; 
            }
#pragma warning restore 6500

            return allFilesOk; 
        }
 
        #endregion 

        //------------------------------------------------------ 
        //
        //  Public Properties
        //
        //------------------------------------------------------ 

        #region Public Properties 
        /// 
        /// The markup file(s) to be checked or updated.
        /// 
        [Required]
        public ITaskItem[] MarkupFiles
        {
            get { return _markupFiles; } 
            set { _markupFiles = value; }
        } 
 
        /// 
        /// The directory for intermedia files 
        /// 
        /// 
        /// 
        public string IntermediateDirectory 
        {
            get { return _backupPath; } 
            set 
            {
                string sourceDir = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar; 
                _backupPath = TaskHelper.CreateFullFilePath(value, sourceDir);
            }
        }
 

        /// 
        /// Enum to determine which Uid management task to undertake 
        ///
        private enum UidTask 
        {

            ///
            /// Uid managment task to check validity of Uids 
            ///
            Check = 0, 
 
            ///
            /// Uid managment task to Update Uids to a valid state 
            ///
            Update = 1,

            /// 
            /// Uid managment task to remove all Uids
            /// 
            Remove = 2, 
        }
 
        ///
        /// Uid management task required
        ///
        [Required] 
        public string Task
        { 
            get { return _taskAsString; } 
            set { _taskAsString = value; }
        } 

        #endregion

        //----------------------------------------------------- 
        //
        //  Private Methods 
        // 
        //------------------------------------------------------
 
        private bool ManageUids()
        {
            int countGoodFiles = 0;
            // enumerate through each file 
            foreach (ITaskItem inputFile in _markupFiles)
            { 
                Log.LogMessageFromResources(SRID.CheckingUids, inputFile.ItemSpec); 
                switch (_task)
                { 
                    case UidTask.Check:
                    {
                        UidCollector collector = ParseFile(inputFile.ItemSpec);
 
                        bool success = VerifyUid(
                            collector,          // uid collector 
                            true                // log error 
                            );
 
                        if (success) countGoodFiles++;
                        break;
                    }
                    case UidTask.Update: 
                    {
                        UidCollector collector = ParseFile(inputFile.ItemSpec); 
 
                        bool success = VerifyUid(
                            collector,          // uid collector 
                            false               // log error
                            );

                        if (!success) 
                        {
                            if (SetupBackupDirectory()) 
                            { 
                                // resolve errors
                                collector.ResolveUidErrors(); 

                                // temp file to write to
                                string tempFile   = GetTempFileName(inputFile.ItemSpec);
 
                                // backup file of the source file before it is overwritten.
                                string backupFile = GetBackupFileName(inputFile.ItemSpec); 
 
                                using (Stream uidStream = new FileStream(tempFile, FileMode.Create))
                                { 
                                    using (Stream source = File.OpenRead(inputFile.ItemSpec))
                                    {
                                        UidWriter writer = new UidWriter(collector, source, uidStream);
                                        writer.UpdateUidWrite(); 
                                    }
                                } 
 
                                // backup source file by renaming it. Expect to be (close to) atomic op.
                                RenameFile(inputFile.ItemSpec, backupFile); 

                                // rename the uid output onto the source file. Expect to be (close to) atomic op.
                                RenameFile(tempFile, inputFile.ItemSpec);
 
                                // remove the temp files
                                RemoveFile(tempFile); 
                                RemoveFile(backupFile); 

                                countGoodFiles++; 
                            }
                        }
                        else
                        { 
                            // all uids are good. No-op
                            countGoodFiles++; 
                        } 

                        break; 
                    }
                    case UidTask.Remove:
                    {
                        UidCollector collector = ParseFile(inputFile.ItemSpec); 

                        bool hasUid = false; 
                        for (int i = 0; i < collector.Count; i++) 
                        {
                            if (collector[i].Status != UidStatus.Absent) 
                            {
                                hasUid = true;
                                break;
                            } 
                        }
 
                        if (hasUid) 
                        {
                            if (SetupBackupDirectory()) 
                            {
                                // temp file to write to
                                string tempFile   = GetTempFileName(inputFile.ItemSpec);
 
                                // backup file of the source file before it is overwritten.
                                string backupFile = GetBackupFileName(inputFile.ItemSpec); 
 
                                using (Stream uidStream = new FileStream(tempFile, FileMode.Create))
                                { 
                                    using (Stream source = File.OpenRead(inputFile.ItemSpec))
                                    {
                                        UidWriter writer = new UidWriter(collector, source, uidStream);
                                        writer.RemoveUidWrite(); 
                                    }
                                } 
 
                                // rename the source file to the backup file name. Expect to be (close to) atomic op.
                                RenameFile(inputFile.ItemSpec, backupFile); 

                                // rename the output file over to the source file. Expect to be (close to) atomic op.
                                RenameFile(tempFile, inputFile.ItemSpec);
 
                                // remove the temp files
                                RemoveFile(tempFile); 
                                RemoveFile(backupFile); 

                                countGoodFiles++; 
                            }
                        }
                        else
                        { 
                            // There is no Uid in the file. No need to do remove.
                            countGoodFiles++; 
                        } 

                        break; 
                    }
                }
            }
 
            // spew out the overral log info for the task
            switch (_task) 
            { 
                case UidTask.Remove:
                    Log.LogMessageFromResources(SRID.FilesRemovedUid, countGoodFiles); 
                    break;

                case UidTask.Update:
                    Log.LogMessageFromResources(SRID.FilesUpdatedUid, countGoodFiles); 
                    break;
 
                case UidTask.Check: 
                    Log.LogMessageFromResources(SRID.FilesPassedUidCheck, countGoodFiles);
 
                    if (_markupFiles.Length > countGoodFiles)
                    {
                        Log.LogErrorWithCodeFromResources(SRID.FilesFailedUidCheck, _markupFiles.Length - countGoodFiles);
                    } 
                    break;
            } 
 
            return _markupFiles.Length == countGoodFiles;
        } 


       private string GetTempFileName(string fileName)
        { 
            return Path.Combine(_backupPath, Path.ChangeExtension(Path.GetFileName(fileName), "uidtemp"));
        } 
 
        private string GetBackupFileName (string fileName)
        { 
            return Path.Combine(_backupPath, Path.ChangeExtension(Path.GetFileName(fileName), "uidbackup"));
        }

        private void RenameFile(string src, string dest) 
        {
            RemoveFile(dest); 
            File.Move(src, dest); 
        }
 
        private void RemoveFile(string fileName)
        {
            if (File.Exists(fileName))
            { 
                File.Delete(fileName);
            } 
        } 

        private bool SetupBackupDirectory() 
        {
            try
            {
                if (!Directory.Exists(_backupPath)) 
                {
                    Directory.CreateDirectory(_backupPath); 
                } 

                return true; 
            }
            catch (Exception e)
            {
                // PreSharp Complaint 6500 - do not handle null-ref or SEH exceptions. 
                if (e is NullReferenceException || e is SEHException)
                { 
                    throw; 
                }
                else 
                {
                    Log.LogErrorWithCodeFromResources(SRID.IntermediateDirectoryError, _backupPath);
                    return false;
                } 
            }
#pragma warning disable 6500 
            catch   // Non-cls compliant errors 
            {
                Log.LogErrorWithCodeFromResources(SRID.IntermediateDirectoryError, _backupPath); 
                return false;
            }
#pragma warning restore 6500
        } 

 
 

        ///  
        /// Verify the Uids in the file
        /// 
        /// UidCollector containing all Uid instances
        /// true to log errors while verifying 
        /// true indicates no errors
        private bool VerifyUid( 
            UidCollector collector, 
            bool         logError
            ) 
        {
            bool errorFound = false;

            for (int i = 0; i < collector.Count; i++) 
            {
                Uid currentUid = collector[i]; 
                if (currentUid.Status == UidStatus.Absent) 
                {
                    // Uid missing 
                    if (logError)
                    {
                        Log.LogErrorWithCodeFromResources(
                             null, 
                             collector.FileName,
                             currentUid.LineNumber, 
                             currentUid.LinePosition, 
                             0, 0,
                             SRID.UidMissing, currentUid.ElementName 
                         );
                    }

                    errorFound = true; 
                }
                else if (currentUid.Status == UidStatus.Duplicate) 
                { 
                    // Uid duplicates
                    if (logError) 
                    {
                        Log.LogErrorWithCodeFromResources(
                              null,
                              collector.FileName, 
                              currentUid.LineNumber,
                              currentUid.LinePosition, 
                              0, 0, 
                              SRID.MultipleUidUse, currentUid.Value, currentUid.ElementName
                         ); 

                    }

                    errorFound = true; 
                }
 
            } 

            return !errorFound; 
        }

        /// 
        /// Parse the input file and get all the information of Uids 
        /// 
        /// input file 
        /// UidCollector containing all the information for the Uids in the file 
        private UidCollector ParseFile(string fileName)
        { 
            UidCollector collector = new UidCollector(fileName  );

            using (Stream xamlStream = File.OpenRead(fileName))
            { 
                XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable());
                XmlParserContext context  = new XmlParserContext( 
                    null,                 // nametable 
                    nsmgr,                // namespace manager
                    null,                 // xml:Lang scope 
                    XmlSpace.Default      // XmlSpace
                    );

                XmlTextReader reader = new XmlTextReader( 
                    xamlStream,           // xml stream
                    XmlNodeType.Document, // parsing document 
                    context               // parser context 
                    );
 
                while (reader.Read())
                {
                    switch (reader.NodeType)
                    { 
                        case XmlNodeType.Element :
                        { 
                            if (collector.RootElementLineNumber < 0) 
                            {
                                collector.RootElementLineNumber   = reader.LineNumber; 
                                collector.RootElementLinePosition = reader.LinePosition;
                            }

                            if (reader.Name.IndexOf('.') >= 0) 
                            {
                                // the name has a dot, which suggests it is a property tag. 
                                // we will ignore adding uid 
                                continue;
                            } 

                            Uid currentUid = new Uid(
                                    reader.LineNumber,
                                    reader.LinePosition + reader.Name.Length, 
                                    reader.Name,
                                    SpaceInsertion.BeforeUid  // insert space before the Uid 
                                    );                                   ; 

                            if (reader.HasAttributes) 
                            {
                                reader.MoveToNextAttribute();

                                // As a heuristic to better preserve the source file, add uid to the place of the 
                                // first attribute
                                currentUid.LineNumber   = reader.LineNumber; 
                                currentUid.LinePosition = reader.LinePosition; 
                                currentUid.Space        = SpaceInsertion.AfterUid;
 
                                do
                                {
                                    string namespaceUri = nsmgr.LookupNamespace(reader.Prefix);
 
                                    if (reader.LocalName == XamlReaderHelper.DefinitionUid
                                     && namespaceUri == XamlReaderHelper.DefinitionNamespaceURI) 
                                    { 
                                        // found x:Uid attribute, store the actual value and position
                                        currentUid.Value        = reader.Value; 
                                        currentUid.LineNumber   = reader.LineNumber;
                                        currentUid.LinePosition = reader.LinePosition;
                                    }
                                    else if (reader.LocalName == "Name" 
                                          && namespaceUri == XamlReaderHelper.DefaultNamespaceURI)
                                    { 
                                        // found Name attribute, store the Name value 
                                        currentUid.FrameworkElementName = reader.Value;
                                    } 
                                    else if (reader.LocalName == "Name"
                                          && namespaceUri == XamlReaderHelper.DefinitionNamespaceURI)
                                    {
                                        // found x:Name attribute, store the Name value 
                                        currentUid.FrameworkElementName = reader.Value;
                                    } 
                                    else if (reader.Prefix == "xmlns") 
                                    {
                                        // found a namespace declaration, store the namespace prefix 
                                        // so that when we need to add a new namespace declaration later
                                        // we won't reuse the namespace prefix.
                                        collector.AddNamespacePrefix(reader.LocalName);
                                    } 
                                }
                                while (reader.MoveToNextAttribute()); 
 
                            }
 
                            if (currentUid.Value == null)
                            {
                                // there is no x:Uid found on this element, we need to resolve the
                                // namespace prefix in order to add the Uid 
                                string prefix = nsmgr.LookupPrefix(XamlReaderHelper.DefinitionNamespaceURI);
                                if (prefix != string.Empty) 
                                    currentUid.NamespacePrefix = prefix; 
                            }
 
                            collector.AddUid(currentUid);
                            break;
                        }
                    } 
                }
            } 
 
            return collector;
        } 

        //-----------------------------------
        // Private members
        //----------------------------------- 
        private UidTask       _task;            // task
        private ITaskItem[]   _markupFiles;     // input Xaml files 
        private string        _taskAsString;    // task string 
        private string        _backupPath;      // path to store to backup source Xaml files
        private const string UnknownErrorID = "UM1000"; 
    }

    // represent all the information about a Uid
    // The uid may be valid, absent, or duplicate 
    internal sealed class Uid
    { 
        internal Uid( 
            int     lineNumber,
            int     linePosition, 
            string  elementName,
            SpaceInsertion    spaceInsertion
            )
        { 
            LineNumber          = lineNumber;
            LinePosition        = linePosition; 
            ElementName         = elementName; 
            Value               = null;
            NamespacePrefix     = null; 
            FrameworkElementName  = null;
            Status              = UidStatus.Valid;
            Space               = spaceInsertion;
 
        }
 
        internal int        LineNumber;         // Referenced line number of the original document 
        internal int        LinePosition;       // Reference line position of the original document
        internal string     ElementName;        // name of the element that needs this uid 
        internal SpaceInsertion    Space;       // Insert a space before/after the Uid

        internal string     Value;              // value of the uid
        internal string     NamespacePrefix;    // namespace prefix for the uid 
        internal string     FrameworkElementName; // the FrameworkElement.Name of element
        internal UidStatus  Status;             // the status of the this uid 
 
    }
 
    internal enum UidStatus : byte
    {
        Valid       = 0,    // uid is valid
        Absent      = 1,    // uid is absent 
        Duplicate   = 2,    // uid is duplicated
    } 
 
    internal enum SpaceInsertion : byte
    { 
        BeforeUid,          // Insert a space before the Uid
        AfterUid            // Insert a space after the Uid
    }
 
    // a class collects all the information about Uids per file
    internal sealed class UidCollector 
    { 
        public UidCollector(string fileName)
        { 
            _uids               = new List(32);
            _namespacePrefixes  = new List(2);
            _uidTable           = new Hashtable();
            _fileName           = fileName; 
            _sequenceMaxIds     = new Hashtable();
        } 
 
        // remembering all the namespace prefixes in the file
        // in case we need to add a new definition namespace declaration 
        public void AddNamespacePrefix(string prefix)
        {
            _namespacePrefixes.Add(prefix);
        } 

        // add the uid to the collector 
        public void AddUid(Uid uid) 
        {
            _uids.Add(uid); 

            // set the uid status according to the raw data
            if (uid.Value == null)
            { 
                uid.Status = UidStatus.Absent;
            } 
            else  if (_uidTable.Contains(uid.Value)) 
            {
                uid.Status = UidStatus.Duplicate; 
            }
            else
            {
                // valid uid, store it 
                StoreUid(uid.Value);
            } 
        } 

        public void ResolveUidErrors() 
        {
            for (int i = 0; i < _uids.Count; i++)
            {
                Uid currentUid = _uids[i]; 

                if ( currentUid.Status == UidStatus.Absent 
                  && currentUid.NamespacePrefix == null 
                  && _namespacePrefixForMissingUid == null)
                { 
                    // there is Uid not in scope of any definition namespace
                    // we will need to generate a new namespace prefix for them
                    _namespacePrefixForMissingUid = GeneratePrefix();
                } 

                if (currentUid.Status != UidStatus.Valid) 
                { 
                    // resolve invalid uids
                    currentUid.Value = GetAvailableUid(currentUid); 
                }
            }
        }
 
        public int RootElementLineNumber
        { 
            get { return _rootElementLineNumber; } 
            set { _rootElementLineNumber = value; }
        } 

        public int RootElementLinePosition
        {
            get { return _rootElementLinePosition; } 
            set { _rootElementLinePosition = value; }
        } 
 
        public string FileName
        { 
            get { return _fileName; }
        }

        public Uid this[int index] 
        {
            get 
            { 
                return _uids[index];
            } 
        }

        public int Count
        { 
            get { return _uids.Count; }
        } 
 
        public string NamespaceAddedForMissingUid
        { 
            get { return _namespacePrefixForMissingUid; }
        }

 
        //-------------------------------------
        // Private methods 
        //------------------------------------- 
        private void StoreUid(string value)
        { 
            // we just want to check for existence, so storing a null
            _uidTable[value] = null;

            string uidSequence; 
            Int64 index;
 
            ParseUid(value, out uidSequence, out index); 
            if (uidSequence != null)
            { 
                if (_sequenceMaxIds.Contains(uidSequence))
                {
                    Int64 maxIndex = (Int64)_sequenceMaxIds[uidSequence];
                    if (maxIndex < index) 
                    {
                        _sequenceMaxIds[uidSequence] = index; 
                    } 
                }
                else 
                {
                    _sequenceMaxIds[uidSequence] = index;
                }
            } 
        }
 
        private string GetAvailableUid(Uid uid) 
        {
            string availableUid; 

            // copy the ID if available
            if (uid.FrameworkElementName != null
             && (!_uidTable.Contains(uid.FrameworkElementName)) 
             )
            { 
                availableUid = uid.FrameworkElementName; 
            }
            else 
            {
                // generate a new id
                string sequence = GetElementLocalName(uid.ElementName);
                Int64 index; 

                if (_sequenceMaxIds.Contains(sequence)) 
                { 
                    index = (Int64) _sequenceMaxIds[sequence];
 
                    if (index == Int64.MaxValue)
                    {
                        // this sequence reaches the max
                        // we fallback to create a new sequence 
                        index = -1;
                        while (index < 0) 
                        { 
                            sequence = (_uidSequenceFallbackCount == 0) ?
                                UidFallbackSequence 
                              : UidFallbackSequence + _uidSequenceFallbackCount;

                            if (_sequenceMaxIds.Contains(sequence))
                            { 
                                index = (Int64) _sequenceMaxIds[sequence];
                                if (index < Int64.MaxValue) 
                                { 
                                    // found the fallback sequence with valid index
                                    index ++; 
                                    break;
                                }
                            }
                            else 
                            {
                                // create a new sequence from 1 
                                index = 1; 
                                break;
                            } 

                            _uidSequenceFallbackCount ++;
                        }
                    } 
                    else
                    { 
                        index ++; 
                    }
                } 
                else
                {
                    // a new sequence
                    index = 1; 
                }
 
                availableUid = sequence + UidSeparator + index; 
            }
 
            // store the uid so that it won't be used again
            StoreUid(availableUid);
            return availableUid;
        } 

        private void ParseUid(string uid, out string prefix, out Int64 index) 
        { 
            // set prefix and index to invalid values
            prefix = null; 
            index  = -1;

            if (uid == null) return;
 
            int separatorIndex = uid.LastIndexOf(UidSeparator);
            if (separatorIndex > 0) 
            { 
                string suffix = uid.Substring(separatorIndex + 1);
 
                // Disable Presharp warning 6502 : catch block shouldn't have empty body
                #pragma warning disable 6502
                try {
                    index  = Int64.Parse(suffix, TypeConverterHelper.InvariantEnglishUS); 
                    prefix = uid.Substring(0, separatorIndex);
                } 
                catch (FormatException) 
                {
                    // wrong format 
                }
                catch (OverflowException)
                {
                    // not acceptable uid 
                }
                #pragma warning restore 6502 
            } 
        }
 
        private string GetElementLocalName(string typeFullName)
        {
            int index = typeFullName.LastIndexOf('.');
            if (index > 0) 
            {
                return typeFullName.Substring(index + 1); 
            } 
            else
            { 
                return typeFullName;
            }
        }
 
        private string GeneratePrefix()
        { 
            Int64 ext = 1; 
            string prefix = UidNamespaceAbbreviation.ToString(TypeConverterHelper.InvariantEnglishUS);
 
            // Disable Presharp warning 6502 : catch block shouldn't have empty body
            #pragma warning disable 6502
            try
            { 
                // find a prefix that is not used in the Xaml
                // from x1, x2, ... x[n] 
                while (_namespacePrefixes.Contains(prefix)) 
                {
                    prefix = UidNamespaceAbbreviation + ext.ToString(TypeConverterHelper.InvariantEnglishUS); 
                    ext++;
                }
                return prefix;
            } 
            catch (OverflowException)
            { 
            } 
            #pragma warning restore 6502
 
            // if overflows, (extreamly imposible), we will return a guid as the prefix
            return Guid.NewGuid().ToString();
        }
 
        private List             _uids;
        private Hashtable             _uidTable; 
        private string                _fileName; 
        private Hashtable             _sequenceMaxIds;
        private List          _namespacePrefixes; 
        private int                   _rootElementLineNumber        = -1;
        private int                   _rootElementLinePosition      = -1;
        private string                _namespacePrefixForMissingUid = null;
        private int                   _uidSequenceFallbackCount     = 0; 

        private const char UidNamespaceAbbreviation   = 'x'; 
        private const char UidSeparator               = '_'; 
        private const string UidFallbackSequence      = "_Uid";
    } 


    // writing to a file, removing or updating uid
    internal sealed class UidWriter 
    {
        internal UidWriter(UidCollector collector, Stream source, Stream target) 
        { 
            _collector     = collector;
            _sourceReader  = new StreamReader(source); 

             UTF8Encoding encoding = new UTF8Encoding(true);
            _targetWriter  = new StreamWriter(target, encoding);
            _lineBuffer    = new LineBuffer(_sourceReader.ReadLine()); 
        }
 
        // write to target stream and update uids 
        internal bool UpdateUidWrite()
        { 
            try {
                // we need to add a new namespace
                if (_collector.NamespaceAddedForMissingUid != null)
                { 
                    // write to the beginning of the root element
                    WriteTillSourcePosition( 
                        _collector.RootElementLineNumber, 
                        _collector.RootElementLinePosition
                        ); 

                    WriteElementTag();
                    WriteSpace();
                    WriteNewNamespace(); 
                }
 
                for (int i = 0; i < _collector.Count; i++) 
                {
                    Uid currentUid = _collector[i]; 
                    WriteTillSourcePosition(currentUid.LineNumber, currentUid.LinePosition);

                    if (currentUid.Status == UidStatus.Absent)
                    { 

                        if (currentUid.Space == SpaceInsertion.BeforeUid) 
                        { 
                            WriteSpace();
                        } 

                        WriteNewUid(currentUid);

                        if (currentUid.Space == SpaceInsertion.AfterUid) 
                        {
                            WriteSpace(); 
                        } 
                    }
                    else if (currentUid.Status == UidStatus.Duplicate) 
                    {
                        ProcessAttributeStart(WriterAction.Write);
                        SkipSourceAttributeValue();
                        WriteNewAttributeValue(currentUid.Value); 
                    }
                } 
                WriteTillEof(); 
                return true;
            } 
            catch (Exception e)
            {
                // PreSharp Complaint 6500 - do not handle null-ref or SEH exceptions.
                if (e is NullReferenceException || e is SEHException) 
                {
                    throw; 
                } 

                return false; 
            }
#pragma warning disable 6500
            catch
            { 
                return false;
            } 
#pragma warning restore 6500 
        }
 
        // writing to the target stream removing uids
        internal bool RemoveUidWrite()
        {
            try { 
                for (int i = 0; i < _collector.Count; i++)
                { 
                    Uid currentUid = _collector[i]; 

                    // skipping valid and duplicate uids. 
                    if ( currentUid.Status == UidStatus.Duplicate
                      || currentUid.Status == UidStatus.Valid)
                    {
                        // write till the space in front of the Uid 
                        WriteTillSourcePosition(currentUid.LineNumber, currentUid.LinePosition - 1);
 
                        // skip the uid 
                        ProcessAttributeStart(WriterAction.Skip);
                        SkipSourceAttributeValue(); 
                    }
                }

                WriteTillEof(); 
                return true;
            } 
            catch (Exception e) 
            {
                // PreSharp Complaint 6500 - do not handle null-ref or SEH exceptions. 
                if (e is NullReferenceException || e is SEHException)
                {
                    throw;
                } 

                return false; 
            } 
#pragma warning disable 6500
            catch 
            {
                return false;
            }
#pragma warning restore 6500 
        }
 
        private void WriteTillSourcePosition(int lineNumber, int linePosition) 
        {
            // write to the correct line 
            while (_currentLineNumber < lineNumber)
            {
                // write out the line buffer
                _targetWriter.WriteLine(_lineBuffer.ReadToEnd()); 
                _currentLineNumber++;
                _currentLinePosition = 1; 
 
                // read one more line
                _lineBuffer.SetLine(_sourceReader.ReadLine()); 
            }

            // write to the correct line position
            while (_currentLinePosition < linePosition) 
            {
                _targetWriter.Write(_lineBuffer.Read()); 
                _currentLinePosition++; 
            }
 
        }

        private void WriteElementTag()
        { 
            if (_lineBuffer.EOL)
            { 
                // advance to the non-empty line 
                AdvanceTillNextNonEmptyLine(WriterAction.Write);
            } 

            char ch = _lineBuffer.Peek();

            // stop when we see space, "/" or ">". That is the end of the 
            // element name
            while (!Char.IsWhiteSpace(ch) 
                   && ch != '/' 
                   && ch != '>'
                  ) 
            {
                _targetWriter.Write(ch);

                _currentLinePosition++; 
                _lineBuffer.Read();
                if (_lineBuffer.EOL) 
                { 
                    AdvanceTillNextNonEmptyLine(WriterAction.Write);
                } 

                ch = _lineBuffer.Peek();
            }
        } 

        private void WriteNewUid(Uid uid) 
        { 
            // construct the attribute name, e.g. x:Uid
            // "x" will be the resolved namespace prefix for the definition namespace 
            string attributeName =
                (uid.NamespacePrefix == null) ?
                 _collector.NamespaceAddedForMissingUid + ":" + XamlReaderHelper.DefinitionUid
               : uid.NamespacePrefix + ":" + XamlReaderHelper.DefinitionUid; 

            // escape all the Xml entities in the value 
            string attributeValue = EscapedXmlEntities.Replace( 
                uid.Value,
                EscapeMatchEvaluator 
                );

            string clause = string.Format(
                TypeConverterHelper.InvariantEnglishUS, 
                "{0}=\"{1}\"",
                attributeName, 
                attributeValue 
                );
 
            _targetWriter.Write(clause);
        }

        private void WriteNewNamespace() 
        {
            string clause = string.Format( 
                TypeConverterHelper.InvariantEnglishUS, 
                "xmlns:{0}=\"{1}\"",
                _collector.NamespaceAddedForMissingUid, 
                XamlReaderHelper.DefinitionNamespaceURI
                );

            _targetWriter.Write(clause); 
        }
 
        private void WriteNewAttributeValue(string value) 
        {
            string attributeValue = EscapedXmlEntities.Replace( 
                value,
                EscapeMatchEvaluator
                );
 
            _targetWriter.Write(
                string.Format( 
                    TypeConverterHelper.InvariantEnglishUS, 
                    "\"{0}\"",
                    value 
                    )
                );
        }
 
        private void WriteSpace()
        { 
             // insert a space 
            _targetWriter.Write(" ");
        } 

        private void WriteTillEof()
        {
            _targetWriter.WriteLine(_lineBuffer.ReadToEnd()); 
            _targetWriter.Write(_sourceReader.ReadToEnd());
            _targetWriter.Flush(); 
        } 

        private void SkipSourceAttributeValue() 
        {
            char ch = (char) 0;

            // read to the start quote of the attribute value 
            while (ch != '\"' && ch != '\'')
            { 
                if (_lineBuffer.EOL) 
                {
                    AdvanceTillNextNonEmptyLine(WriterAction.Skip); 
                }

                ch = _lineBuffer.Read();
                _currentLinePosition ++; 
            }
 
            char attributeValueStart = ch; 
            // read to the end quote of the attribute value
            ch = (char) 0; 
            while (ch != attributeValueStart)
            {
                if (_lineBuffer.EOL)
                { 
                    AdvanceTillNextNonEmptyLine(WriterAction.Skip);
                } 
 
                ch = _lineBuffer.Read();
                _currentLinePosition ++; 
            }
        }

        private void AdvanceTillNextNonEmptyLine(WriterAction action) 
        {
            do 
            { 
                if (action == WriterAction.Write)
                { 
                    _targetWriter.WriteLine();
                }

                _lineBuffer.SetLine(_sourceReader.ReadLine()); 
                _currentLineNumber++;
                _currentLinePosition = 1; 
 
            } while (_lineBuffer.EOL);
        } 


        private void ProcessAttributeStart(WriterAction action)
        { 
            if (_lineBuffer.EOL)
            { 
                AdvanceTillNextNonEmptyLine(action); 
            }
 
            char ch;
            do
            {
                ch = _lineBuffer.Read(); 

                if (action == WriterAction.Write) 
                { 
                    _targetWriter.Write(ch);
                } 

                _currentLinePosition++;

                if (_lineBuffer.EOL) 
                {
                    AdvanceTillNextNonEmptyLine(action); 
                } 

            } while (ch != '='); 
        }

        //
        // source position in a file starts from (1,1) 
        //
 
        private int             _currentLineNumber   = 1;   // current line number in the source stream 
        private int             _currentLinePosition = 1;   // current line position in the source stream
        private LineBuffer      _lineBuffer;                // buffer for one line's content 

        private UidCollector    _collector;
        private StreamReader    _sourceReader;
        private StreamWriter    _targetWriter; 

        // 
        // buffer for the content of a line 
        // The UidWriter always reads one line at a time from the source
        // and store the line in this buffer. 
        //
        private sealed class LineBuffer
        {
            private int    Index; 
            private string Content;
 
            public LineBuffer(string line) 
            {
                SetLine(line); 
            }

            public void SetLine(string line)
            { 
                Content = (line == null) ? string.Empty : line;
                Index   = 0; 
            } 

            public bool EOL 
            {
                get { return (Index == Content.Length); }
            }
 
            public char Read()
            { 
                if (!EOL) 
                {
                    return Content[Index++]; 
                }

                throw new InvalidOperationException();
            } 

            public char Peek() 
            { 
                if (!EOL)
                { 
                    return Content[Index];
                }

                throw new InvalidOperationException(); 
            }
 
            public string ReadToEnd() 
            {
                if (!EOL) 
                {
                    int temp = Index;
                    Index    = Content.Length;
 
                    return Content.Substring(temp);
                } 
 
                return string.Empty;
            } 
        }

        private enum WriterAction
        { 
            Write = 0,  // write the content
            Skip  = 1,  // skip the content 
        } 

        private static Regex          EscapedXmlEntities   = new Regex("(<|>|\"|'|&)", RegexOptions.CultureInvariant | RegexOptions.Compiled); 
        private static MatchEvaluator EscapeMatchEvaluator = new MatchEvaluator(EscapeMatch);

        /// 
        /// the delegate to escape the matched pattern 
        /// 
        private static string EscapeMatch(Match match) 
        { 
            switch (match.Value)
            { 
                case "<":
                    return "<";
                case ">":
                    return ">"; 
                case "&":
                    return "&"; 
                case "\"": 
                    return """;
                case "'": 
                    return "'";
                default:
                   return match.Value;
            } 
        }
    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK