/// <summary> /// Applies the loaded templates to <paramref name="templateData"/>. /// </summary> /// <param name="templateData"> /// Instance of <see cref="TemplateData"/> containing the various input data needed. /// </param> public virtual TemplateOutput Generate(TemplateData templateData) { Stopwatch timer = Stopwatch.StartNew(); ParsedTemplate tmpl = this.PrepareTemplate(templateData); // collect all work that has to be done List<UnitOfWork> work = new List<UnitOfWork>(); // resource work units work.AddRange(this.DiscoverWork(templateData, tmpl.Resources)); // stylesheet work units { List<StylesheetApplication> stylesheetApplications = new List<StylesheetApplication>(); foreach (Stylesheet stylesheet in tmpl.Stylesheets) { stylesheetApplications.AddRange(this.DiscoverWork(templateData, stylesheet)); } var duplicates = stylesheetApplications.GroupBy(sa => sa.SaveAs, StringComparer.OrdinalIgnoreCase) .Where(g => g.Count() > 1); foreach (var group in duplicates) { TraceSources.TemplateSource.TraceError("Duplicate work unit target ({0}) generated from: {1}", group.Key, string.Join(", ", group.Select(sa => '\'' + sa.StylesheetName + '\''))); foreach (var workunit in group.Skip(1)) { stylesheetApplications.Remove(workunit); } } work.AddRange(stylesheetApplications); } TraceSources.TemplateSource.TraceInformation("Generating {0:N0} documents from {1:N0} stylesheets.", work.Count, tmpl.Stylesheets.Length); ConcurrentBag<WorkUnitResult> results = new ConcurrentBag<WorkUnitResult>(); // create context ITemplatingContext context = new TemplatingContext(this._cache, this._basePath, templateData, this._resolvers, this._fileProvider); // fill indices using (TraceSources.TemplateSource.TraceActivity("Indexing input document")) { var customXsltContext = CreateCustomXsltContext(templateData.IgnoredVersionComponent); foreach (var index in tmpl.Indices) { TraceSources.TemplateSource.TraceVerbose("Adding index {0} (match: '{1}', key: '{1}')", index.Name, index.MatchExpr, index.KeyExpr); context.DocumentIndex.AddKey(index.Name, index.MatchExpr, index.KeyExpr, customXsltContext); } TraceSources.TemplateSource.TraceInformation("Indexing..."); context.DocumentIndex.BuildIndexes(); } int totalCount = work.Count; long lastProgress = Stopwatch.GetTimestamp(); int processed = 0; // process all units of work ParallelOptions parallelOptions = new ParallelOptions { //MaxDegreeOfParallelism = 1 }; Parallel.ForEach(work, parallelOptions, uow => { results.Add(uow.Execute(context)); int c = Interlocked.Increment(ref processed); long lp = Interlocked.Read(ref lastProgress); if ((Stopwatch.GetTimestamp() - lp) / (double)Stopwatch.Frequency > 5.0) { if (Interlocked.CompareExchange(ref lastProgress, Stopwatch.GetTimestamp(), lp) == lp) { TraceSources.TemplateSource.TraceInformation( "Progress: {0:P1} ({1:N0}/{2:N0})", c / (double)totalCount, c, totalCount); } } }); // stop timing timer.Stop(); // prepare stats Dictionary<Type, WorkUnitResult[]> resultGroups = results.GroupBy(ps => ps.WorkUnit.GetType()).ToDictionary(g => g.Key, g => g.ToArray()); var stylesheetStats = resultGroups[typeof(StylesheetApplication)] .GroupBy(r => ((StylesheetApplication)r.WorkUnit).StylesheetName); foreach (var statGroup in stylesheetStats) { TraceSources.TemplateSource.TraceInformation("Applied stylesheet '{0}' {1:N0} times in {2:N0} ms (min: {3:N0}, mean {4:N0}, max {5:N0}, avg: {6:N0})", statGroup.Key, statGroup.Count(), statGroup.Sum(ps => ps.Duration) / 1000.0, statGroup.Min(ps => ps.Duration) / 1000.0, statGroup.Skip(statGroup.Count() / 2).Take(1).Single().Duration / 1000.0, statGroup.Max(ps => ps.Duration) / 1000.0, statGroup.Average(ps => ps.Duration) / 1000.0); } var resourceStats = resultGroups[typeof(ResourceDeployment)]; foreach (var statGroup in resourceStats) { TraceSources.TemplateSource.TraceInformation("Deployed resource '{0}' in {1:N0} ms", ((ResourceDeployment)statGroup.WorkUnit).ResourcePath, statGroup.Duration); } TraceSources.TemplateSource.TraceInformation("Documentation generated in {0:N1} seconds (processing time: {1:N1} seconds)", timer.Elapsed.TotalSeconds, results.Sum(ps => ps.Duration) / 1000000.0); return new TemplateOutput(results.ToArray()); }
/// <summary> /// Applies the loaded templates to <paramref name="templateData"/>. /// </summary> /// <param name="templateData"> /// Instance of <see cref="TemplateData"/> containing the various input data needed. /// </param> public virtual TemplateOutput Generate(TemplateData templateData) { Stopwatch timer = Stopwatch.StartNew(); ParsedTemplate tmpl = this.PrepareTemplate(templateData); // collect all work that has to be done List<UnitOfWork> work = new List<UnitOfWork>(); // resource work units work.AddRange(this.DiscoverWork(templateData, tmpl.Resources)); // stylesheet work units { List<StylesheetApplication> stylesheetApplications = new List<StylesheetApplication>(); foreach (Stylesheet stylesheet in tmpl.Stylesheets) { stylesheetApplications.AddRange(this.DiscoverWork(templateData, stylesheet)); } var duplicates = stylesheetApplications.GroupBy(sa => sa.SaveAs, StringComparer.OrdinalIgnoreCase) .Where(g => g.Count() > 1); foreach (var group in duplicates) { TraceSources.TemplateSource.TraceCritical("Duplicate work unit target ({0}) generated from: {1}", group.Key, string.Join(", ", group.Select( sa => '\'' + sa.StylesheetName + '\''))); // TODO replace this with something more specific // throw new Exception("Critical error, continuing is not safe."); } work.AddRange(stylesheetApplications); } TraceSources.TemplateSource.TraceInformation("Generating {0:N0} documents from {1:N0} stylesheets.", work.Count, tmpl.Stylesheets.Length); ConcurrentBag<WorkUnitResult> results = new ConcurrentBag<WorkUnitResult>(); // create context ITemplatingContext context = new TemplatingContext(this._basePath, templateData, this._resolvers, this._fileProvider); // process all units of work Parallel.ForEach(work, uow => results.Add(uow.Execute(context))); // stop timing timer.Stop(); // prepare stats Dictionary<Type, WorkUnitResult[]> resultGroups = results.GroupBy(ps => ps.WorkUnit.GetType()).ToDictionary(g => g.Key, g => g.ToArray()); var stylesheetStats = resultGroups[typeof(StylesheetApplication)] .GroupBy(r => ((StylesheetApplication)r.WorkUnit).StylesheetName); foreach (var statGroup in stylesheetStats) { TraceSources.TemplateSource.TraceInformation("Applied stylesheet '{0}' {1:N0} times in {2:N0} ms (min: {3:N0}, mean {4:N0}, max {5:N0}, avg: {6:N0})", statGroup.Key, statGroup.Count(), statGroup.Sum(ps => ps.Duration) / 1000.0, statGroup.Min(ps => ps.Duration) / 1000.0, statGroup.Skip(statGroup.Count() / 2).Take(1).Single().Duration / 1000.0, statGroup.Max(ps => ps.Duration) / 1000.0, statGroup.Average(ps => ps.Duration) / 1000.0); } var resourceStats = resultGroups[typeof(ResourceDeployment)]; foreach (var statGroup in resourceStats) { TraceSources.TemplateSource.TraceInformation("Deployed resource '{0}' in {1:N0} ms", ((ResourceDeployment)statGroup.WorkUnit).ResourcePath, statGroup.Duration); } TraceSources.TemplateSource.TraceInformation("Documentation generated in {0:N1} seconds (processing time: {1:N1} seconds)", timer.Elapsed.TotalSeconds, results.Sum(ps => ps.Duration) / 1000000.0); return new TemplateOutput(results.ToArray()); }