첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
728x90
반응형
728x170

▶ Data.cs

using System.Xml.Linq;

namespace ResourceMerger
{
    /// <summary>
    /// 데이터
    /// </summary>
    public class Data
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 문서 - Document

        /// <summary>
        /// 문서
        /// </summary>
        public XDocument Document { get; set; }

        #endregion
        #region 종속 카운트 - DependencyCount

        /// <summary>
        /// 종속 카운트
        /// </summary>
        public int DependencyCount { get; set; }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - Data(document, dependencyCount)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="document">문서</param>
        /// <param name="dependencyCount">종속 카운트</param>
        public Data(XDocument document, int dependencyCount)
        {
            Document        = document;
            DependencyCount = dependencyCount;
        }

        #endregion
    }
}

 

728x90

 

▶ ResourceHelper.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;

namespace ResourceMerger
{
    /// <summary>
    /// 리소스 헬퍼
    /// </summary>
    public static class ResourceHelper
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 리소스 딕셔너리 병합하기 - MergeResourceDictionary(projectDirectoryPath, projectName, sourceRelativeFilePath, targetRelativeFilePath, errorMessage)

        /// <summary>
        /// 리소스 딕셔너리 병합하기
        /// </summary>
        /// <param name="projectDirectoryPath">프로젝트 디렉토리 경로</param>
        /// <param name="projectName">프로젝트명</param>
        /// <param name="sourceRelativeFilePath">소스 파일 상대 경로</param>
        /// <param name="targetRelativeFilePath">타겟 파일 상대 경로</param>
        /// <param name="errorMessage">에러 메시지</param>
        /// <returns>처리 결과</returns>
        public static bool MergeResourceDictionary
        (
            string     projectDirectoryPath,
            string     projectName,
            string     sourceRelativeFilePath,
            string     targetRelativeFilePath,
            out string errorMessage
        )
        {
            if(!Directory.Exists(projectDirectoryPath))
            {
                errorMessage = $"PROJECT DIRECTORY PATH DOES NOT EXISTS : {projectDirectoryPath}";

                return false;
            }

            projectName = string.IsNullOrWhiteSpace(projectName) ? Path.GetFileName(Path.GetDirectoryName(projectDirectoryPath)) : projectName;

            if(!sourceRelativeFilePath.EndsWith(".xaml", StringComparison.InvariantCultureIgnoreCase))
            {
                errorMessage = $"RELATIVE SOURCE FILE EXTENSON IS NOT .XAML : {sourceRelativeFilePath}";

                return false;
            }

            if(!targetRelativeFilePath.EndsWith(".xaml", StringComparison.InvariantCultureIgnoreCase))
            {
                errorMessage = $"RELATIVE TARGET FILE EXTENSON IS NOT .XAML : {targetRelativeFilePath}";

                return false;
            }

            string sourceFilePath = Path.Combine(projectDirectoryPath, sourceRelativeFilePath);

            if(!File.Exists(sourceFilePath))
            {
                errorMessage = $"SOURCE FILE DOES NOT EXISTS : {sourceFilePath}";

                return false;
            }

            XDocument sourceDocument = XDocument.Load(sourceFilePath);

            XNamespace defaultNamespace = sourceDocument.Root.GetDefaultNamespace();

            string resourceDictionaryName = sourceDocument.Root.Name.LocalName;

            XDocument targetDocument = XDocument.Parse("<" + resourceDictionaryName + " xmlns=\"" + defaultNamespace + "\"/>");

            Dictionary<string, Data> dataDictionary = new Dictionary<string, Data>();
          
            try
            {
                PrepareDataDictionary(ref dataDictionary, projectDirectoryPath, projectName, sourceRelativeFilePath);
            }
            catch(Exception exception)
            {
                errorMessage = $"ERROR PREPARE DATA DICTIONARY\n{exception.ToString()}";

                return false;
            }
       
            foreach(KeyValuePair<string, Data> keyValuePair in dataDictionary.OrderByDescending(item => item.Value.DependencyCount))
            {
                foreach(XAttribute attribute in keyValuePair.Value.Document.Root.Attributes())
                {
                    targetDocument.Root.SetAttributeValue(attribute.Name, attribute.Value);
                }

                targetDocument.Root.Add
                (
                    keyValuePair.Value.Document.Root.Elements().Where(e => !e.Name.LocalName.StartsWith(resourceDictionaryName))
                );
            }

            using(MemoryStream stream = new MemoryStream())
            {
                targetDocument.Save(stream);

                if(CompareByteArray(Path.Combine(projectDirectoryPath, targetRelativeFilePath), stream.ToArray()))
                {
                    errorMessage = null;

                    return true;
                }
            }

            string targetFilePath = Path.Combine(projectDirectoryPath, targetRelativeFilePath);

            targetDocument.Save(targetFilePath);

            errorMessage = null;

            return true;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Private

        #region 데이터 딕셔너리 준비하기 - PrepareDataDictionary(dataDictionary, projectDirectoryPath, projectName, sourceRelativeFilePath, first, parentDependencyCount)

        /// <summary>
        /// 데이터 딕셔너리 준비하기
        /// </summary>
        /// <param name="dataDictionary">데이터 딕셔너리</param>
        /// <param name="projectDirectoryPath">프로젝트 디렉토리 경로</param>
        /// <param name="projectName">프로젝트명</param>
        /// <param name="sourceRelativeFilePath">소스 상대 파일 경로</param>
        /// <param name="first">첫번째 여부</param>
        /// <param name="parentDependencyCount">부모 종속 카운트</param>
        private static void PrepareDataDictionary
        (
            ref Dictionary<string, Data> dataDictionary,
            string                       projectDirectoryPath,
            string                       projectName,
            string                       sourceRelativeFilePath,
            bool                         first = true,
            int                          parentDependencyCount = 0
        )
        {
            string sourceFilePath = Path.Combine(projectDirectoryPath, sourceRelativeFilePath.TrimStart('/'));

            if(!File.Exists(sourceFilePath))
            {
                throw new Exception($"SOURCE FILE PATH DOES NOT EXISTS : {sourceFilePath}");
            }

            XDocument document = XDocument.Load(sourceFilePath);

            string resourceDictionaryName = document.Root.Name.LocalName;

            XNamespace defaultNamespace = document.Root.GetDefaultNamespace();

            if(dataDictionary.ContainsKey(sourceFilePath))
            {
                dataDictionary[sourceFilePath].DependencyCount = Math.Max
                (
                    dataDictionary[sourceFilePath].DependencyCount + 1,
                    parentDependencyCount + 1
                );
            }
            else
            {
                dataDictionary.Add
                (
                    sourceFilePath,
                    new Data(document, first ? -1 : parentDependencyCount + 1)
                );
            }

            foreach(XElement dictionaryElement in document.Root.Descendants(defaultNamespace + resourceDictionaryName))
            {
                PrepareDataDictionary
                (
                    ref dataDictionary,
                    projectDirectoryPath,
                    projectName,
                    dictionaryElement.Attribute("Source").Value.Replace("/" + projectName + ";component/", string.Empty),
                    false,
                    dataDictionary[sourceFilePath].DependencyCount
                );
            }
        }

        #endregion
        #region 바이트 배열 비교하기 - CompareByteArray(targetFilePath, newByteArray)

        /// <summary>
        /// 바이트 배열 비교하기
        /// </summary>
        /// <param name="targetFilePath">타겟 파일 경로</param>
        /// <param name="newByteArray">신규 바이트 배열</param>
        /// <returns>바이트 배열 비교 결과</returns>
        private static bool CompareByteArray(string targetFilePath, byte[] newByteArray)
        {
            string finalTargetFilePath = targetFilePath.Replace("/", "\\");

            if(!File.Exists(finalTargetFilePath))
            {
                return false;
            }

            byte[] targetFileByteArray = File.ReadAllBytes(finalTargetFilePath);

            return newByteArray.SequenceEqual(targetFileByteArray); ;
        }

        #endregion
    }
}

 

300x250

 

▶ Program.cs

using System;

namespace ResourceMerger
{
    /// <summary>
    /// 프로그램
    /// </summary>
    public class Program
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Private

        #region 프로그램 시작하기 - Main(argumentArray)

        /// <summary>
        /// 프로그램 시작하기
        /// </summary>
        /// <param name="argumentArray">인자 배열</param>
        private static void Main(string[] argumentArray)
        {
            int argumentArrayLength = argumentArray.Length;

            if(argumentArrayLength != 4)
            {
                Console.WriteLine($"INVALID ARGUMENT ARRAY LENGTH : {argumentArray.Length}");

                return;
            }

            string projectDirectoryPath   = argumentArray[0];
            string projectName            = argumentArray[1];
            string relativeSourceFilePath = argumentArray[2];
            string relativeTargetFilePath = argumentArray[3];

            string errorMessage;

            bool result = ResourceHelper.MergeResourceDictionary
            (
                projectDirectoryPath,
                projectName,
                relativeSourceFilePath,
                relativeTargetFilePath,
                out errorMessage
            );

            if(!result)
            {
                Console.WriteLine(errorMessage);
            }
        }

        #endregion
    }
}

 

728x90
반응형
그리드형(광고전용)
Posted by icodebroker

댓글을 달아 주세요