public TemplatingContext(ObjectCache cache, CompositionContainer container, IFileProvider outputFileProvider, TemplateData data, IEnumerable<IAssetUriResolver> resolvers, IFileProvider templateFileProvider)
        {
            this.Container = container;
            this.OutputFileProvider = outputFileProvider;
            this.TemplateData = data;
            this.AssetUriResolvers = resolvers.ToArray();
            this.TemplateFileProvider = templateFileProvider;
            this.Cache = cache;

            XPathDocument xpathDoc;
            using (var reader = data.Document.CreateReader(ReaderOptions.OmitDuplicateNamespaces))
                xpathDoc = new XPathDocument(reader);
            this.Document = xpathDoc.CreateNavigator();
            this.DocumentIndex = new XPathNavigatorIndex(this.Document.Clone());
        }
        private IEnumerable<UnitOfWork> DiscoverWork(TemplateData templateData, XPathVariable[] parameters, Resource[] resources)
        {
            CustomXsltContext xpathContext = CreateCustomXsltContext(templateData.IgnoredVersionComponent);
            xpathContext.PushVariableScope(templateData.Document.Root, parameters);
            for (int i = 0; i < resources.Length; i++)
            {
                xpathContext.PushVariableScope(templateData.Document.Root, resources[i].Variables);

                if (EvalCondition(xpathContext, templateData.Document.Root, resources[i].ConditionExpression))
                {
                    string expandedSource = EvalValue(xpathContext, templateData.Document.Root, resources[i].Source);
                    string expandedOutput = EvalValue(xpathContext, templateData.Document.Root, resources[i].Output);

                    List<IResourceTransform> transforms = new List<IResourceTransform>();

                    foreach (var resourceTransform in resources[i].Transforms)
                    {
                        using (CompositionContainer localContainer = new CompositionContainer(this._container.Catalog))
                        {
                            string dirName = Path.GetDirectoryName(expandedSource);
                            CompositionBatch batch = new CompositionBatch();
                            var exportMetadata = new Dictionary<string, object>();

                            exportMetadata.Add(CompositionConstants.ExportTypeIdentityMetadataName,
                                               AttributedModelServices.GetTypeIdentity(typeof(IFileProvider)));

                            exportMetadata.Add(CompositionConstants.PartCreationPolicyMetadataName,
                                               CreationPolicy.Shared);

                            batch.AddExport(new Export(ContractNames.ResourceFileProvider,
                                                       exportMetadata,
                                                       () => new ScopedFileProvider(resources[i].FileProvider, dirName)));

                            // TODO export resourceTransform.Parameters into localContainer using CompositionBatch

                            localContainer.Compose(batch);

                            var requiredMetadata = new[]
                                                   {
                                                       new Tuple<string, object, IEqualityComparer>("Name",
                                                                                                    resourceTransform.Name,
                                                                                                    StringComparer.OrdinalIgnoreCase)
                                                   };

                            ImportDefinition importDefinition =
                                new MetadataContractBasedImportDefinition(
                                    typeof(IResourceTransform),
                                    null,
                                    requiredMetadata,
                                    ImportCardinality.ExactlyOne,
                                    false,
                                    true,
                                    CreationPolicy.NonShared);

                            Export transformExport = localContainer.GetExports(importDefinition).Single();

                            transforms.Add((IResourceTransform)transformExport.Value);
                        }
                    }

                    yield return
                        new ResourceDeployment(resources[i].FileProvider,
                                               expandedSource,
                                               expandedOutput, // TODO this needs a 'writable' file provider
                                               transforms.ToArray());
                }
                xpathContext.PopVariableScope();
            }
            xpathContext.PopVariableScope();

        }
        /// <summary>
        /// This method will construct a three folder structure inside <paramref name="targetDirectory"/> containing: Html, Index, and Source
        /// </summary>
        /// <param name="sourceDirectory">
        /// Directory containing ldoc files
        /// </param>
        /// <param name="targetDirectory">
        /// Output directory
        /// </param>
        public void Build(string sourceDirectory, string targetDirectory)
        {
            if (Directory.Exists(targetDirectory) && Directory.EnumerateFileSystemEntries(targetDirectory).Any())
                throw new InvalidOperationException("Target path is not empty.");

            this.OnStateChanged(State.Preparing);

            string htmlRoot = Path.Combine(targetDirectory, "Html");
            string indexRoot = Path.Combine(targetDirectory, "Index");
            string sourceRoot = Path.Combine(targetDirectory, "Source");
            string logRoot = Path.Combine(targetDirectory, "Logs");

            DirectoryInfo htmlDir = Directory.CreateDirectory(htmlRoot);
            DirectoryInfo indexDir = Directory.CreateDirectory(indexRoot);
            DirectoryInfo sourceDir = Directory.CreateDirectory(sourceRoot);
            DirectoryInfo logDir = Directory.CreateDirectory(logRoot);
            var sourceFiles = Directory.EnumerateFiles(sourceDirectory, "*.ldoc", SearchOption.TopDirectoryOnly);

            // copy all source files to output directory and add to bundle
            Bundle bundle = new Bundle(this.IgnoreVersionComponent);
            foreach (var sourceFile in sourceFiles)
            {
                string targetFile = Path.Combine(sourceDir.FullName, Path.GetFileName(sourceFile));
                File.Copy(sourceFile, targetFile);
                bundle.Add(XDocument.Load(targetFile));
            }

            TemplateOutput templateOutput;

            // wire up logging
            string templateLogFile = Path.Combine(logDir.FullName, 
                                                  string.Format("template_{0:yyyy'_'MM'_'dd'__'HH'_'mm'_'ss}.log", DateTime.Now));
            using (TextWriterTraceListener traceListener = new TextWriterTraceListener(templateLogFile))
            {
                // log everything
                traceListener.Filter = new EventTypeFilter(SourceLevels.All);
                LostDoc.Diagnostics.TraceSources.TemplateSource.Switch.Level = SourceLevels.All;
                LostDoc.Diagnostics.TraceSources.BundleSource.Switch.Level = SourceLevels.All;
                LostDoc.Diagnostics.TraceSources.AssetResolverSource.Switch.Level = SourceLevels.All;
                LostDoc.Diagnostics.TraceSources.TemplateSource.Listeners.Add(traceListener);
                LostDoc.Diagnostics.TraceSources.BundleSource.Listeners.Add(traceListener);
                LostDoc.Diagnostics.TraceSources.AssetResolverSource.Listeners.Add(traceListener);

                // merge ldoc files
                this.OnStateChanged(State.Merging);
                AssetRedirectCollection assetRedirects;
                var mergedDoc = bundle.Merge(out assetRedirects);

                // generate output
                var templateData = new TemplateData(mergedDoc)
                                       {
                                           AssetRedirects = assetRedirects, 
                                           IgnoredVersionComponent = this.IgnoreVersionComponent, 
                                           OutputFileProvider =
                                               new ScopedFileProvider(new DirectoryFileProvider(), htmlDir.FullName), 
                                           
                                           //TargetDirectory = htmlDir.FullName,
                                           Arguments = new Dictionary<string, object> { { "SearchUri", "/search/" } }, 
                                           KeepTemporaryFiles = true, 
                                           TemporaryFilesPath = Path.Combine(logDir.FullName, "temp")
                                       };

                this.OnStateChanged(State.Templating);
                templateOutput = this.Template.Generate(templateData);

                LostDoc.Diagnostics.TraceSources.TemplateSource.Listeners.Remove(traceListener);
                LostDoc.Diagnostics.TraceSources.BundleSource.Listeners.Remove(traceListener);
                LostDoc.Diagnostics.TraceSources.AssetResolverSource.Listeners.Remove(traceListener);
            }

            this.OnStateChanged(State.Indexing);

            string indexLogFile = Path.Combine(logDir.FullName, 
                                               string.Format("index_{0:yyyy'_'MM'_'dd'__'HH'_'mm'_'ss}.log", DateTime.Now));
            using (TextWriterTraceListener traceListener = new TextWriterTraceListener(indexLogFile))
            {
                // log everything
                traceListener.Filter = new EventTypeFilter(SourceLevels.All);
                TraceSources.ContentBuilderSource.Switch.Level = SourceLevels.All;
                TraceSources.ContentBuilderSource.Listeners.Add(traceListener);

                // one stop-word per line
                StringReader stopWordsReader = new StringReader(@"missing");

                // index output
                using (var directory = FSDirectory.Open(indexDir))
                using (stopWordsReader)
                {
                    Analyzer analyzer = new StandardAnalyzer(global::Lucene.Net.Util.Version.LUCENE_30, stopWordsReader);
                    Analyzer titleAnalyzer = new TitleAnalyzer();
                    IDictionary<string, Analyzer> fieldAnalyzers = new Dictionary<string, Analyzer>
                                                                       {
                                                                           { "title", titleAnalyzer }
                                                                       };

                    PerFieldAnalyzerWrapper analyzerWrapper = new PerFieldAnalyzerWrapper(analyzer, fieldAnalyzers);

                    using (
                        var writer = new IndexWriter(directory, analyzerWrapper, IndexWriter.MaxFieldLength.UNLIMITED))
                    {
                        var saResults =
                            templateOutput.Results.Select(wur => wur.WorkUnit).OfType<StylesheetApplication>();

                        var saDict = saResults.ToDictionary(sa => sa.Asset);

                        var indexResults = saDict.Values.Where(sa => sa.SaveAs.EndsWith(".xml"));

                        foreach (var sa in indexResults)
                        {
                            string absPath = Path.Combine(htmlDir.FullName, sa.SaveAs);

                            XDocument indexDoc = XDocument.Load(absPath);

                            string assetId = indexDoc.Root.Attribute("assetId").Value;
                            string title = indexDoc.Root.Element("title").Value.Trim();
                            string summary = indexDoc.Root.Element("summary").Value.Trim();
                            string text = indexDoc.Root.Element("text").Value.Trim();

                            var ssApplication = saDict[AssetIdentifier.Parse(assetId)];

                            var doc = new Document();

                            doc.Add(new Field("uri", 
                                              new Uri(ssApplication.SaveAs, UriKind.Relative).ToString(), 
                                              Field.Store.YES, 
                                              Field.Index.NO));
                            doc.Add(new Field("aid", ssApplication.Asset, Field.Store.YES, Field.Index.NOT_ANALYZED));
                            foreach (AssetIdentifier aid in ssApplication.Aliases)
                                doc.Add(new Field("alias", aid, Field.Store.NO, Field.Index.NOT_ANALYZED));

                            foreach (var section in ssApplication.Sections)
                            {
                                doc.Add(new Field("section", 
                                                  section.AssetIdentifier, 
                                                  Field.Store.NO, 
                                                  Field.Index.NOT_ANALYZED));
                            }

                            doc.Add(new Field("title", title, Field.Store.YES, Field.Index.ANALYZED));
                            doc.Add(new Field("summary", summary, Field.Store.YES, Field.Index.ANALYZED));
                            doc.Add(new Field("content", text, Field.Store.NO, Field.Index.ANALYZED));
                            TraceSources.ContentBuilderSource.TraceVerbose("Indexing document: {0}", doc.ToString());
                            writer.AddDocument(doc);
                        }

                        writer.Optimize();
                        writer.Commit();
                    }

                    analyzerWrapper.Close();
                    analyzer.Close();
                }

                TraceSources.ContentBuilderSource.Listeners.Remove(traceListener);
            }

            this.OnStateChanged(State.Finalizing);

            var infoDoc = new XDocument(
                new XElement("content", 
                             new XAttribute("created", 
                                            XmlConvert.ToString(DateTime.UtcNow, XmlDateTimeSerializationMode.Utc)), 
                             templateOutput.Results.Select(this.ConvertToXml)));

            infoDoc.Save(Path.Combine(targetDirectory, "info.xml"));

            this.OnStateChanged(State.Idle);
        }
        protected virtual ParsedTemplate PrepareTemplate(TemplateData templateData, Stack<IFileProvider> providers = null)
        {
            // set up temp file container
            TempFileCollection tempFiles = new TempFileCollection(templateData.TemporaryFilesPath,
                                                                  templateData.KeepTemporaryFiles);

            if (!Directory.Exists(tempFiles.TempDir))
                Directory.CreateDirectory(tempFiles.TempDir);

            if (providers == null)
            {
                providers = new Stack<IFileProvider>();
                providers.Push(new HttpFileProvider());
                providers.Push(new DirectoryFileProvider());
            }

            // clone orig doc
            XDocument workingDoc;

            // this is required to preserve the line information 
            using (var xmlReader = this._templateDefinition.CreateReader())
                workingDoc = XDocument.Load(xmlReader, LoadOptions.SetLineInfo);

            // template inheritence
            //XAttribute templateInheritsAttr = workingDoc.Root.Attribute("inherits");
            if (this._templateInfo.Inherits != null)
            {

                int depth = providers.Count + 1;
                Template inheritedTemplate = _templateInfo.Inherits.Load(this._container);
                ParsedTemplate parsedTemplate = inheritedTemplate.PrepareTemplate(templateData, providers);

                providers.Push(inheritedTemplate.GetScopedFileProvider());

                // a little hacky but it should work with the Reverse()/AddFirst()
                foreach (XElement elem in parsedTemplate.Source.Root.Elements().Reverse())
                {
                    workingDoc.Root.AddFirst(new XElement(elem));
                }

                // create and register temp file (this can be overriden later if there are meta-template directives
                // in the template
                this._templateSourcePath = this.SaveTempFile(tempFiles, workingDoc, "inherited." + depth + '.' + _templateInfo.Name);
            }

            // add our file provider to the top of the stack
            providers.Push(this.GetScopedFileProvider());

            // create stacked provider
            IFileProvider provider = new StackedFileProvider(providers);

            // start by loading any parameters as they are needed for meta-template evaluation
            CustomXsltContext customContext = CreateCustomXsltContext(templateData.IgnoredVersionComponent);

            XElement[] paramNodes = workingDoc.Root.Elements("parameter").ToArray();
            List<XPathVariable> globalParams = new List<XPathVariable>();

            foreach (XElement paramNode in paramNodes)
            {
                string name = this.GetAttributeValue(paramNode, "name");
                object argValue;
                if (templateData.Arguments.TryGetValue(name, out argValue))
                    globalParams.Add(new ConstantXPathVariable(name, argValue));
                else
                {
                    string expr = this.GetAttributeValueOrDefault(paramNode, "select");
                    globalParams.Add(new ExpressionXPathVariable(name, expr));
                }
            }

            customContext.PushVariableScope(workingDoc, globalParams.ToArray());

            var arguments = templateData.Arguments
                                        .Select(argument => new ConstantXPathVariable(argument.Key, argument.Value))
                                        .ToArray();

            customContext.PushVariableScope(workingDoc, arguments);

            // expand any meta-template directives
            workingDoc = ApplyMetaTransforms(workingDoc, customContext, provider, tempFiles);

            // there was neither inheretance, nor any meta-template directives
            if (this._templateSourcePath == null)
            {
                // save current template to disk
                this._templateSourcePath = this.SaveTempFile(tempFiles, workingDoc);
            }

            // loading template
            List<Stylesheet> stylesheets = new List<Stylesheet>();
            List<Resource> resources = new List<Resource>();
            List<Index> indices = new List<Index>();
            foreach (XElement elem in workingDoc.Root.Elements())
            {
                // we alread proessed the parameters
                if (elem.Name.LocalName == "parameter")
                    continue;

                if (elem.Name.LocalName == "apply-stylesheet")
                {
                    stylesheets.Add(this.ParseStylesheet(provider, stylesheets, elem));
                }
                else if (elem.Name.LocalName == "index")
                {
                    indices.Add(this.ParseIndexDefinition(elem));
                }
                else if (elem.Name.LocalName == "include-resource")
                {
                    resources.Add(ParseResouceDefinition(provider, elem));
                }
                else
                {
                    throw new Exception("Unknown element: " + elem.Name.LocalName);
                }
            }

            return new ParsedTemplate
                       {
                           Parameters = globalParams.ToArray(),
                           Source = workingDoc,
                           Resources = resources.ToArray(),
                           Stylesheets = stylesheets.ToArray(),
                           Indices = indices.ToArray(),
                           TemporaryFiles = tempFiles,
                       };
        }
        /// <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();

            this._fileResolver.Clear();

            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.Parameters, tmpl.Resources));

            // stylesheet work units
            {
                List<StylesheetApplication> stylesheetApplications = new List<StylesheetApplication>();
                foreach (Stylesheet stylesheet in tmpl.Stylesheets)
                {
                    stylesheetApplications.AddRange(this.DiscoverWork(templateData, tmpl.Parameters, 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._container,
                                                               templateData.OutputFileProvider, // TODO fix this (this._basePath)
                                                               templateData,
                                                               this._resolvers,
                                                               this._templateInfo.Source);


            // 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
                                                  };


            IEnumerable<UnitOfWork> unitsOfWork = work;
            if (templateData.Filter != null)
            {
                unitsOfWork = unitsOfWork
                    .Where(uow =>
                               {
                                   if (templateData.Filter(uow))
                                       return true;

                                   TraceSources.TemplateSource.TraceInformation("Filtered unit of work: [{0}] {1}",
                                                                                uow.GetType().Name,
                                                                                uow.ToString());
                                   return false;
                               });
            }


            Parallel.ForEach(unitsOfWork,
                             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();


            Stopwatch statsTimer = new Stopwatch();
            // 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)
            {
                long min = statGroup.Min(ps => ps.Duration);
                long max = statGroup.Max(ps => ps.Duration);
                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,
                                                             min / 1000.0,
                                                             statGroup.Skip(statGroup.Count() / 2).Take(1).Single().Duration / 1000.0,
                                                             max / 1000.0,
                                                             statGroup.Average(ps => ps.Duration) / 1000.0);


                // TODO this is quick and dirty, should be cleaned up 
                long[] buckets = new long[20];
                int rows = 6;
                /* 
┌────────────────────┐ ◄ 230
│█                  █│
│█                  █│
│█                  █│
│█                  █│
│█                  █│
│█__________________█│
└────────────────────┘ ◄ 0
▲ 12ms               ▲ 12ms
                 */
                // this is a little hacky, but it will do for now
                WorkUnitResult[] sortedResults = statGroup.OrderBy(r => r.Duration).ToArray();
                double bucketSize = (max - min) / (double)buckets.Length;
                int bucketNum = 0;
                long bucketMax = 0;
                foreach (WorkUnitResult result in sortedResults)
                {
                    while ((result.Duration - min) > (bucketNum + 1) * bucketSize)
                        bucketNum++;

                    buckets[bucketNum] += 1;
                    bucketMax = Math.Max(buckets[bucketNum], bucketMax);
                }


                double rowHeight = bucketMax / (double)rows;

                StringBuilder graph = new StringBuilder();
                graph.AppendLine("Graph:");
                const int gutter = 2;
                int columnWidth = graph.Length;
                graph.Append('┌').Append('─', buckets.Length).Append('┐').Append('◄').Append(' ').Append(bucketMax.ToString("N0"));
                int firstLineLength = graph.Length - columnWidth;
                columnWidth = graph.Length - columnWidth + gutter;
                StringBuilder lastLine = new StringBuilder();
                lastLine.Append('▲').Append(' ').Append((min / 1000.0).ToString("N0")).Append("ms");
                lastLine.Append(' ', (buckets.Length + 2) - lastLine.Length - 1);
                lastLine.Append('▲').Append(' ').Append((max / 1000.0).ToString("N0")).Append("ms");
                columnWidth = Math.Max(columnWidth, lastLine.Length + gutter);

                if (columnWidth > firstLineLength)
                    graph.Append(' ', columnWidth - firstLineLength);
                graph.AppendLine("Percentage of the applications processed within a certain time (ms)");

                for (int row = 0; row < rows; row++)
                {
                    // │┌┐└┘─
                    graph.Append('│');
                    for (int col = 0; col < buckets.Length; col++)
                    {
                        if (buckets[col] > (rowHeight * (rows - (row + 1)) + rowHeight / 2.0))
                            graph.Append('█');
                        else if (buckets[col] > rowHeight * (rows - (row + 1)))
                            graph.Append('▄');
                        else if (row == rows - 1)
                            graph.Append('_');
                        else
                            graph.Append(' ');
                    }
                    graph.Append('│');

                    graph.Append(' ', columnWidth - (buckets.Length + 2));
                    switch (row)
                    {
                        case 0:
                            graph.Append(" 100% ").Append((max / 1000.0).ToString("N0"));
                            break;
                        case 1:
                            graph.Append("  95% ").Append((sortedResults[((int)Math.Floor(sortedResults.Length * 0.95))].Duration / 1000.0).ToString("N0"));
                            break;
                        case 2:
                            graph.Append("  90% ").Append((sortedResults[((int)Math.Floor(sortedResults.Length * .9))].Duration / 1000.0).ToString("N0"));
                            break;
                        case 3:
                            graph.Append("  80% ").Append((sortedResults[((int)Math.Floor(sortedResults.Length * 0.8))].Duration / 1000.0).ToString("N0"));
                            break;
                        case 4:
                            graph.Append("  70% ").Append((sortedResults[((int)Math.Floor(sortedResults.Length * 0.7))].Duration / 1000.0).ToString("N0"));
                            break;
                        case 5:
                            graph.Append("  50% ").Append((sortedResults[((int)Math.Floor(sortedResults.Length * 0.5))].Duration / 1000.0).ToString("N0"));
                            break;
                    }

                    graph.AppendLine();
                }
                int len = graph.Length;
                graph.Append('└').Append('─', buckets.Length).Append('┘').Append('◄').Append(" 0");
                len = graph.Length - len;
                if (columnWidth > len)
                    graph.Append(' ', columnWidth - len);

                graph.Append("  10% ").Append((sortedResults[((int)Math.Floor(sortedResults.Length * .1))].Duration / 1000.0).ToString("N0"));

                graph.AppendLine();

                lastLine.Append(' ', columnWidth - lastLine.Length);
                lastLine.Append("   1% ").Append((sortedResults[((int)Math.Floor(sortedResults.Length * .01))].Duration / 1000.0).ToString("N0"));
                graph.Append(lastLine.ToString());

                TraceSources.TemplateSource.TraceVerbose(graph.ToString());

            }

            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);

            TraceSources.TemplateSource.TraceInformation("Statistics generated in {0:N1} seconds",
                                                         statsTimer.Elapsed.TotalSeconds);

            return new TemplateOutput(results.ToArray(), tmpl.TemporaryFiles);
        }
        protected virtual IEnumerable<StylesheetApplication> DiscoverWork(TemplateData templateData, XPathVariable[] parameters, Stylesheet stylesheet)
        {
            TraceSources.TemplateSource.TraceInformation("Processing stylesheet instructions: {0}",
                                                         (object)stylesheet.Name);

            CustomXsltContext xpathContext = CreateCustomXsltContext(templateData.IgnoredVersionComponent);

            xpathContext.PushVariableScope(templateData.Document.Root, parameters); // 1

            XElement[] inputElements =
                templateData.Document.XPathSelectElements(stylesheet.SelectExpression, xpathContext).ToArray();

            foreach (XElement inputElement in inputElements)
            {
                xpathContext.PushVariableScope(inputElement, stylesheet.Variables); // 2

                string saveAs = ResultToString(inputElement.XPathEvaluate(stylesheet.OutputExpression, xpathContext));
                string version = ResultToString(inputElement.XPathEvaluate(stylesheet.VersionExpression, xpathContext));
                string assetId = ResultToString(inputElement.XPathEvaluate(stylesheet.AssetIdExpression, xpathContext));
                List<AssetIdentifier> aliases = new List<AssetIdentifier>();
                List<AssetSection> sections = new List<AssetSection>();

                // eval condition, shortcut and log instead of wrapping entire loop in if
                if (!EvalCondition(xpathContext, inputElement, stylesheet.ConditionExpression))
                {
                    TraceSources.TemplateSource.TraceVerbose("{0}, {1} => Condition not met", assetId, version);
                    xpathContext.PopVariableScope(); // 2
                    continue;
                }

                Uri newUri = new Uri(saveAs, UriKind.RelativeOrAbsolute);

                // register url
                this._fileResolver.Add(assetId, new Version(version), ref newUri);

                TraceSources.TemplateSource.TraceVerbose("{0}, {1} => {2}", assetId, version, newUri.ToString());

                // aliases
                foreach (AliasRegistration alias in stylesheet.AssetAliases)
                {
                    XElement[] aliasInputElements =
                        inputElement.XPathSelectElements(alias.SelectExpression, xpathContext).ToArray();

                    foreach (XElement aliasInputElement in aliasInputElements)
                    {
                        xpathContext.PushVariableScope(aliasInputElement, alias.Variables); // 3

                        string aliasVersion =
                            ResultToString(aliasInputElement.XPathEvaluate(alias.VersionExpression, xpathContext));
                        string aliasAssetId =
                            ResultToString(aliasInputElement.XPathEvaluate(alias.AssetIdExpression, xpathContext));

                        // eval condition
                        if (EvalCondition(xpathContext, aliasInputElement, alias.ConditionExpression))
                        {
                            this._fileResolver.Add(aliasAssetId, new Version(aliasVersion), newUri);
                            aliases.Add(AssetIdentifier.Parse(aliasAssetId));
                            TraceSources.TemplateSource.TraceVerbose("{0}, {1} (Alias) => {2}", aliasAssetId,
                                                                     aliasVersion,
                                                                     newUri.ToString());
                        }
                        else
                        {
                            TraceSources.TemplateSource.TraceVerbose("{0}, {1} (Alias) => Condition not met",
                                                                     assetId,
                                                                     version);
                        }
                        xpathContext.PopVariableScope(); // 3
                    }
                }

                // sections
                foreach (SectionRegistration section in stylesheet.Sections)
                {
                    XElement[] sectionInputElements = inputElement.XPathSelectElements(section.SelectExpression, xpathContext).ToArray();

                    foreach (XElement sectionInputElement in sectionInputElements)
                    {
                        xpathContext.PushVariableScope(sectionInputElement, section.Variables); // 4

                        string sectionName =
                            ResultToString(sectionInputElement.XPathEvaluate(section.NameExpression, xpathContext));
                        string sectionVersion =
                            ResultToString(sectionInputElement.XPathEvaluate(section.VersionExpression, xpathContext));
                        string sectionAssetId =
                            ResultToString(sectionInputElement.XPathEvaluate(section.AssetIdExpression, xpathContext));

                        // eval condition
                        if (EvalCondition(xpathContext, sectionInputElement, section.ConditionExpression))
                        {
                            Uri sectionUri = new Uri(newUri + "#" + sectionName, UriKind.Relative);
                            this._fileResolver.Add(sectionAssetId, new Version(sectionVersion), sectionUri);
                            TraceSources.TemplateSource.TraceVerbose("{0}, {1}, (Section: {2}) => {3}",
                                                                     sectionAssetId,
                                                                     sectionVersion,
                                                                     sectionName,
                                                                     sectionUri.ToString());

                            sections.Add(new AssetSection(AssetIdentifier.Parse(sectionAssetId), sectionName, sectionUri));
                        }
                        else
                        {
                            TraceSources.TemplateSource.TraceVerbose("{0}, {1}, (Section: {2}) => Condition not met",
                                                                       sectionAssetId,
                                                                       sectionVersion,
                                                                       sectionName);
                        }

                        xpathContext.PopVariableScope(); // 4
                    }
                }

                var xsltParams = ResolveXsltParams(stylesheet.XsltParams, inputElement, xpathContext).ToArray();

                xpathContext.PopVariableScope(); // 2

                yield return new StylesheetApplication
                                 {
                                     StylesheetName = stylesheet.Name,
                                     Asset = new AssetIdentifier(assetId, new Version(version)),
                                     Aliases = aliases, /* list of AssetIdentifiers */
                                     Sections = sections, /* list of AssetSection */
                                     SaveAs = newUri.ToString(),
                                     Transform = stylesheet.Transform,
                                     InputElement = inputElement,
                                     XsltParams = xsltParams
                                 };
            }

            xpathContext.PopVariableScope(); // 1
        }
        public override void Invoke(CompositionContainer container)
        {
            var traceListener = new ConsolidatedConsoleTraceListener
                                {
                                    { TraceSources.TemplateSource, "Template" },
                                    { TraceSources.BundleSource, "Bundle" },
                                    { TraceSources.AssetResolverSource, "Resolve" }
                                };

            using (traceListener)
            {
                this.ConfigureTraceLevels(traceListener);

                LinkedList<FileInfo> includedFiles = new LinkedList<FileInfo>();

                if (File.Exists(this.Path))
                    includedFiles.AddLast(new FileInfo(this.Path));
                else if (Directory.Exists(this.Path))
                {
                    Directory.GetFiles(this.Path, "*.ldoc", SearchOption.AllDirectories)
                             .Aggregate(includedFiles, (l, f) => l.AddLast(new FileInfo(f)).List);
                }
                else
                    throw new FileNotFoundException(System.IO.Path.GetFullPath(this.Path));


                Bundle bundle = new Bundle(this.IgnoreVersionComponent);

                TraceSources.TemplateSource.TraceInformation("Merging LostDoc files into bundle.");

                foreach (FileInfo file in includedFiles)
                {
                    TraceSources.TemplateSource.TraceEvent(TraceEventType.Information, 0, "Source: {0}", file.Name);
                    XDocument fileDoc = XDocument.Load(file.FullName);

                    bundle.Add(fileDoc);
                }

                var lazyProviders = container.GetExports<IFileProvider>(ContractNames.TemplateProvider);
                var realProviders = lazyProviders.Select(lazy => lazy.Value);
                TemplateResolver templateResolver = new TemplateResolver(realProviders.ToArray());
                TemplateInfo templateInfo = templateResolver.Resolve(this.Template);
                Template template = templateInfo.Load(container);

                string outputDir = this.Output
                                   ?? (Directory.Exists(this.Path)
                                           ? this.Path
                                           : System.IO.Path.GetDirectoryName(this.Path));
                AssetRedirectCollection assetRedirects;
                XDocument mergedDoc = bundle.Merge(out assetRedirects);

                var templateData = new TemplateData(mergedDoc)
                                       {
                                           AssetRedirects = assetRedirects,
                                           OverwriteExistingFiles = this.Force.IsPresent,
                                           IgnoredVersionComponent = this.IgnoreVersionComponent,
                                           Arguments = this.Arguments,
                                           OutputFileProvider = new ScopedFileProvider(new DirectoryFileProvider(), outputDir)
                                       };

                template.Generate(templateData);
            }

        }
        public void Invoke(CompositionContainer container)
        {
            var traceListener = new ConsolidatedConsoleTraceListener(
                new Dictionary<string, string>
                    {
                        {
                            "LostDoc.Core.Template",
                            "Template"
                        },
                        {
                            "LostDoc.Core.Bundle",
                            "Bundle"
                        },
                        {
                            "LostDoc.Core.Template.AssetResolver",
                            "Resolve"
                        }
                    });

            
            TraceSources.TemplateSource.Listeners.Add(traceListener);
            TraceSources.AssetResolverSource.Listeners.Add(traceListener);
            try
            {
                if (this.Quiet.IsPresent)
                {
                    const SourceLevels quietLevel = SourceLevels.Error | SourceLevels.Warning | SourceLevels.Critical;
                    TraceSources.TemplateSource.Switch.Level = quietLevel;
                    TraceSources.AssetResolverSource.Switch.Level = quietLevel;
                    TraceSources.BundleSource.Listeners.Add(traceListener);
                }
                else if (this.Verbose.IsPresent)
                {
                    const SourceLevels verboseLevel = SourceLevels.All;
                    TraceSources.TemplateSource.Switch.Level = verboseLevel;
                    TraceSources.AssetResolverSource.Switch.Level = verboseLevel;
                    TraceSources.BundleSource.Listeners.Add(traceListener);
                }
                else
                {
                    const SourceLevels normalLevel = SourceLevels.Information | SourceLevels.Warning | SourceLevels.Error | SourceLevels.ActivityTracing;
                    TraceSources.TemplateSource.Switch.Level = normalLevel;
                    TraceSources.AssetResolverSource.Switch.Level = normalLevel;
                }

                LinkedList<FileInfo> includedFiles = new LinkedList<FileInfo>();

                if (File.Exists(this.Path))
                    includedFiles.AddLast(new FileInfo(this.Path));
                else if (Directory.Exists(this.Path))
                {
                    Directory.GetFiles(this.Path, "*.ldoc", SearchOption.AllDirectories)
                             .Aggregate(includedFiles,
                                        (l, f) => l.AddLast(new FileInfo(f)).List);
                }
                else
                    throw new FileNotFoundException(System.IO.Path.GetFullPath(this.Path));


                Bundle bundle = new Bundle(this.IgnoreVersionComponent);

                TraceSources.TemplateSource.TraceInformation("Merging LostDoc files into bundle.");

                foreach (FileInfo file in includedFiles)
                {
                    TraceSources.TemplateSource.TraceEvent(TraceEventType.Information, 0, "Source: {0}", file.Name);
                    XDocument fileDoc = XDocument.Load(file.FullName);

                    bundle.Add(fileDoc);
                }

                var lazyProviders = container.GetExports<IFileProvider>(ContractNames.TemplateProvider);
                var realProviders = lazyProviders.Select(lazy => lazy.Value);
                TemplateResolver templateResolver = new TemplateResolver(realProviders.ToArray());

                Template template = new Template(container);
                template.Load(templateResolver, this.Template);

                string outputDir = this.Output
                                   ?? (Directory.Exists(this.Path)
                                           ? this.Path
                                           : System.IO.Path.GetDirectoryName(this.Path));
                AssetRedirectCollection assetRedirects;
                XDocument mergedDoc = bundle.Merge(out assetRedirects);

                var templateData = new TemplateData(mergedDoc)
                                       {
                                           AssetRedirects = assetRedirects,
                                           OverwriteExistingFiles = this.Force.IsPresent,
                                           IgnoredVersionComponent = this.IgnoreVersionComponent,
                                           Arguments = this.Arguments,
                                           OutputFileProvider = new ScopedFileProvider(new DirectoryFileProvider(), outputDir)
                                       };

                template.Generate(templateData);
            }
            finally
            {
                TraceSources.TemplateSource.Listeners.Remove(traceListener);
                TraceSources.AssetResolverSource.Listeners.Remove(traceListener);
            }
        }
Exemple #9
0
        private IEnumerable<UnitOfWork> DiscoverWork(TemplateData templateData, XPathVariable[] parameters, Resource[] resources)
        {
            CustomXsltContext xpathContext = CreateCustomXsltContext(templateData.IgnoredVersionComponent);
            xpathContext.PushVariableScope(templateData.Document.Root, parameters);
            for (int i = 0; i < resources.Length; i++)
            {
                xpathContext.PushVariableScope(templateData.Document.Root, resources[i].Variables);

                if (EvalCondition(xpathContext, templateData.Document.Root, resources[i].ConditionExpression))
                {
                    List<IResourceTransform> transforms = new List<IResourceTransform>();

                    foreach (var resourceTransform in resources[i].Transforms)
                    {
                        CompositionContainer paramContainer = new CompositionContainer(this._container);

                        // TODO export resourceTransform.Parameters into paramContainer
                        ImportDefinition importDefinition =
                            new MetadataContractBasedImportDefinition(
                                typeof(IResourceTransform),
                                null,
                                new[] {new Tuple<string, object, IEqualityComparer>("Name", resourceTransform.Name, StringComparer.OrdinalIgnoreCase) },
                                ImportCardinality.ExactlyOne,
                                false,
                                false,
                                CreationPolicy.NonShared);

                        Export transformExport = paramContainer.GetExports(importDefinition).Single();

                        transforms.Add((IResourceTransform)transformExport.Value);
                    }

                    yield return
                        new ResourceDeployment(resources[i].FileProvider,
                                               resources[i].Source,
                                               resources[i].Output, // TODO this needs a 'writable' file provider
                                               transforms.ToArray());
                }
                xpathContext.PopVariableScope();
            }
            xpathContext.PopVariableScope();

        }
        public TemplatingContext(ObjectCache cache, CompositionContainer container, IFileProvider outputFileProvider, TemplateData data, IEnumerable <IAssetUriResolver> resolvers, IFileProvider templateFileProvider)
        {
            this.Container            = container;
            this.OutputFileProvider   = outputFileProvider;
            this.TemplateData         = data;
            this.AssetUriResolvers    = resolvers.ToArray();
            this.TemplateFileProvider = templateFileProvider;
            this.Cache = cache;

            XPathDocument xpathDoc;

            using (var reader = data.Document.CreateReader(ReaderOptions.OmitDuplicateNamespaces))
                xpathDoc = new XPathDocument(reader);
            this.Document      = xpathDoc.CreateNavigator();
            this.DocumentIndex = new XPathNavigatorIndex(this.Document.Clone());
        }