| namespace Microsoft.Web.Mvc.Build { |
| using System; |
| using System.Collections.Generic; |
| using System.IO; |
| using System.Linq; |
| using System.Xml.Serialization; |
| using Microsoft.Build.Framework; |
| using Microsoft.Build.Utilities; |
| // consumes "{areaname}.area-manifest.xml" files and performs copy of <content> |
| public class CopyAreaManifests : Task { |
| private IFileSystem _fileSystem; |
| [Required] |
| public bool CrossCopy { get; set; } |
| internal IFileSystem FileSystem { |
| get { |
| if (_fileSystem == null) { |
| _fileSystem = new FileSystem(); |
| } |
| return _fileSystem; |
| } |
| set { |
| _fileSystem = value; |
| } |
| } |
| [Required] |
| public string ManifestPath { get; set; } |
| [Required] |
| public bool RenameViews { get; set; } |
| public override bool Execute() { |
| string[] files = FileSystem.GetFiles(ManifestPath, "*.area-manifest.xml", SearchOption.TopDirectoryOnly); |
| AreaInfo[] areaInfos = Array.ConvertAll<string, AreaInfo>(files, DeserializeFile); |
| // first, cross-copy between parent and child areas |
| var infosByType = areaInfos.ToLookup(info => info.Type); |
| foreach (AreaInfo parentInfo in infosByType[AreaType.Parent]) { |
| foreach (AreaInfo childInfo in infosByType[AreaType.Child]) { |
| Copy(childInfo, parentInfo, CrossCopy); |
| } |
| } |
| // next, copy content areas everywhere (if cross-copy enabled) |
| var infosByIsContent = areaInfos.ToLookup(info => info.Type == AreaType.Content); |
| foreach (AreaInfo contentInfo in infosByIsContent[true]) { |
| foreach (AreaInfo targetInfo in infosByIsContent[false]) { |
| // don't copy content -> child if cross-copy disabled |
| if (CrossCopy || targetInfo.Type == AreaType.Parent) { |
| Copy(contentInfo, targetInfo, false /* doCrossCopy */); |
| } |
| } |
| } |
| // if we got this far, success! |
| return true; |
| } |
| private AreaInfo DeserializeFile(string file) { |
| XmlSerializer serializer = new XmlSerializer(typeof(AreaInfo)); |
| using (Stream stream = FileSystem.FileOpen(file, FileMode.Open)) { |
| return (AreaInfo)serializer.Deserialize(stream); |
| } |
| } |
| private void Copy(AreaInfo originInfo, AreaInfo destinationInfo, bool doCrossCopy) { |
| // need to be careful not to delete existing <content> files, but can overwrite existing non-<content> files |
| { // step 1 - copy from origin to destination |
| HashSet<string> existingDestinationContentFiles = new HashSet<string>(destinationInfo.ContentFiles, StringComparer.OrdinalIgnoreCase); |
| var filesToCopy = from originFileName in originInfo.ContentFiles |
| let destinationFileName = RewriteViewPath(originInfo.Name, originFileName) |
| where !existingDestinationContentFiles.Contains(destinationFileName) |
| select new { |
| OriginFile = originFileName, |
| DestinationFile = destinationFileName |
| }; |
| foreach (var fileToCopy in filesToCopy) { |
| CopyFile(originInfo.Path, fileToCopy.OriginFile, destinationInfo.Path, fileToCopy.DestinationFile); |
| } |
| } |
| if (doCrossCopy) { |
| // step 2 - copy from destination to origin |
| var filesToCopy = destinationInfo.ContentFiles.Except(originInfo.ContentFiles); |
| foreach (string fileToCopy in filesToCopy) { |
| CopyFile(destinationInfo.Path, fileToCopy, originInfo.Path, fileToCopy); |
| } |
| } |
| } |
| private void CopyFile(string originBasePath, string originFileName, string destinationBasePath, string destinationFileName) { |
| string originPath = Path.Combine(originBasePath, originFileName); |
| if (!FileSystem.FileExists(originPath)) { |
| // if file does not exist, assume the manifest is old and just move on |
| return; |
| } |
| // ensure destination directory exists before copy |
| string destinationPath = Path.Combine(destinationBasePath, destinationFileName); |
| string destinationDir = Path.GetDirectoryName(destinationPath); |
| FileSystem.CreateDirectory(destinationDir); |
| FileSystem.FileCopy(originPath, destinationPath, true /* overwrite */); |
| } |
| private string RewriteViewPath(string areaName, string relativePath) { |
| if (RenameViews && relativePath.StartsWith(@"Views\", StringComparison.OrdinalIgnoreCase)) { |
| return String.Format(@"Views\Areas\{0}\{1}", areaName, relativePath.Substring(@"Views\".Length)); |
| } |
| else { |
| return relativePath; |
| } |
| } |
| } |
| } |
Комментарии