// https://raw.github.com/damieng/DamienGKit
// http://damieng.com/blog/2009/11/06/multiple-outputs-from-t4-made-easy-revisited
//Manager class records the various blocks so it can split them up
classManager {private classBlock {publicString Name;public intStart, Length;public boolIncludeInDefault;
}privateBlock currentBlock;private readonly List files = new List();private readonly Block footer = newBlock();private readonly Block header = newBlock();private readonlyITextTemplatingEngineHost host;private readonlyStringBuilder template;protected readonly List generatedFileNames = new List();public staticManager Create(ITextTemplatingEngineHost host, StringBuilder template) {return (host is IServiceProvider) ? new VSManager(host, template) : newManager(host, template);
}public voidStartNewFile(String name) {if (name == null)throw new ArgumentNullException("name");
CurrentBlock= new Block { Name =name };
}public void StartFooter(bool includeInDefault = true) {
CurrentBlock=footer;
footer.IncludeInDefault=includeInDefault;
}public void StartHeader(bool includeInDefault = true) {
CurrentBlock=header;
header.IncludeInDefault=includeInDefault;
}public voidEndBlock() {if (CurrentBlock == null)return;
CurrentBlock.Length= template.Length -CurrentBlock.Start;if (CurrentBlock != header && CurrentBlock !=footer)
files.Add(CurrentBlock);
currentBlock= null;
}public virtual void Process(bool split, bool sync = true) {if(split) {
EndBlock();
String headerText=template.ToString(header.Start, header.Length);
String footerText=template.ToString(footer.Start, footer.Length);
String outputPath=Path.GetDirectoryName(host.TemplateFile);
files.Reverse();if (!footer.IncludeInDefault)
template.Remove(footer.Start, footer.Length);foreach(Block block infiles) {
String fileName=Path.Combine(outputPath, block.Name);
String content= headerText + template.ToString(block.Start, block.Length) +footerText;
generatedFileNames.Add(fileName);
CreateFile(fileName, content);
template.Remove(block.Start, block.Length);
}if (!header.IncludeInDefault)
template.Remove(header.Start, header.Length);
}
}protected virtual voidCreateFile(String fileName, String content) {if(IsFileContentDifferent(fileName, content))
File.WriteAllText(fileName, content);
}public virtualString GetCustomToolNamespace(String fileName) {return null;
}public virtualString DefaultProjectNamespace {get { return null; }
}protected boolIsFileContentDifferent(String fileName, String newContent) {return !(File.Exists(fileName) && File.ReadAllText(fileName) ==newContent);
}privateManager(ITextTemplatingEngineHost host, StringBuilder template) {this.host =host;this.template =template;
}privateBlock CurrentBlock {get { returncurrentBlock; }set{if (CurrentBlock != null)
EndBlock();if (value != null)
value.Start=template.Length;
currentBlock=value;
}
}private classVSManager: Manager {private readonlyEnvDTE.ProjectItem templateProjectItem;private readonlyEnvDTE.DTE dte;private readonly ActioncheckOutAction;private readonly Action>projectSyncAction;public overrideString DefaultProjectNamespace {get{return templateProjectItem.ContainingProject.Properties.Item("DefaultNamespace").Value.ToString();
}
}public override String GetCustomToolNamespace(stringfileName) {return dte.Solution.FindProjectItem(fileName).Properties.Item("CustomToolNamespace").Value.ToString();
}public override void Process(bool split, boolsync) {if (templateProjectItem.ProjectItems == null)return;base.Process(split, sync);if(sync)
projectSyncAction.EndInvoke(projectSyncAction.BeginInvoke(generatedFileNames,null, null));
}protected override voidCreateFile(String fileName, String content) {if(IsFileContentDifferent(fileName, content)) {
CheckoutFileIfRequired(fileName);
File.WriteAllText(fileName, content);
}
}internalVSManager(ITextTemplatingEngineHost host, StringBuilder template)
:base(host, template) {var hostServiceProvider =(IServiceProvider)host;if (hostServiceProvider == null)throw new ArgumentNullException("Could not obtain IServiceProvider");
dte= (EnvDTE.DTE) hostServiceProvider.GetService(typeof(EnvDTE.DTE));if (dte == null)throw new ArgumentNullException("Could not obtain DTE from host");
templateProjectItem=dte.Solution.FindProjectItem(host.TemplateFile);
checkOutAction= fileName =>dte.SourceControl.CheckOutItem(fileName);
projectSyncAction= keepFileNames =>ProjectSync(templateProjectItem, keepFileNames);
}private static void ProjectSync(EnvDTE.ProjectItem templateProjectItem, ListkeepFileNames) {var keepFileNameSet = new HashSet(keepFileNames);var projectFiles = new Dictionary();var originalFilePrefix = Path.GetFileNameWithoutExtension(templateProjectItem.FileNames[0]) + ".";foreach (EnvDTE.ProjectItem projectItem intemplateProjectItem.ProjectItems)
projectFiles.Add(projectItem.FileNames[0], projectItem);//Remove unused items from the project
foreach (var pair inprojectFiles)if (!keepFileNames.Contains(pair.Key) && !(Path.GetFileNameWithoutExtension(pair.Key) + ".").StartsWith(originalFilePrefix))
pair.Value.Delete();//Add missing files to the project
foreach(String fileName inkeepFileNameSet)if (!projectFiles.ContainsKey(fileName))
templateProjectItem.ProjectItems.AddFromFile(fileName);
}private voidCheckoutFileIfRequired(String fileName) {var sc =dte.SourceControl;if (sc != null && sc.IsItemUnderSCC(fileName) && !sc.IsItemCheckedOut(fileName))
checkOutAction.EndInvoke(checkOutAction.BeginInvoke(fileName,null, null));
}
}
} #>