public TemplatingContext(string basePath, TemplateData data, IEnumerable<IAssetUriResolver> resolvers, IFileProvider fileProvider)
 {
     this.BasePath = basePath;
     this.TemplateData = data;
     this.AssetUriResolvers = resolvers.ToArray();
     this.FileProvider = fileProvider;
 }
        public TemplatingContext(ObjectCache cache, string basePath, TemplateData data, IEnumerable<IAssetUriResolver> resolvers, IFileProvider fileProvider)
        {
            this.BasePath = basePath;
            this.TemplateData = data;
            this.AssetUriResolvers = resolvers.ToArray();
            this.FileProvider = fileProvider;
            this.Cache = cache;

            XPathDocument xpathDoc;
            using (var reader = data.XDocument.CreateReader(ReaderOptions.OmitDuplicateNamespaces))
                xpathDoc = new XPathDocument(reader);
            this.Document = xpathDoc.CreateNavigator();
            this.DocumentIndex = new XPathNavigatorIndex(this.Document.Clone());
        }
Example #3
0
        /// <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");

            DirectoryInfo htmlDir = Directory.CreateDirectory(htmlRoot);
            DirectoryInfo indexDir = Directory.CreateDirectory(indexRoot);
            DirectoryInfo sourceDir = Directory.CreateDirectory(sourceRoot);

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

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

            // generate output
            var templateData = new TemplateData
                                   {
                                       AssetRedirects = assetRedirects,
                                       Document = mergedDoc,
                                       IgnoredVersionComponent = this.IgnoreVersionComponent,
                                       TargetDirectory = htmlDir.FullName
                                   };

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


            this.OnStateChanged(State.Indexing);
            // 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_29, stopWordsReader);
                Analyzer titleAnalyzer = new TitleAnalyzer();
                IDictionary fieldAnalyzers = new Dictionary<string, Analyzer>
                                                 {
                                                     { "title", titleAnalyzer } 
                                                 };
                
                PerFieldAnalyzerWrapper analyzerWrapper = new PerFieldAnalyzerWrapper(analyzer, fieldAnalyzers);
                
                using (var writer = new IndexWriter(directory, analyzerWrapper, IndexWriter.MaxFieldLength.UNLIMITED))
                {
                    foreach (WorkUnitResult result in templateOutput.Results)
                    {
                        //string absPath = Path.Combine(htmlDir.FullName, result.SavedAs);

                        //HtmlDocument htmlDoc = new HtmlDocument();
                        //htmlDoc.Load(absPath);

                        //string htmlTitle = string.Empty;
                        //var titleNode = htmlDoc.DocumentNode.SelectSingleNode("/html/head/title");

                        //if (titleNode != null)
                        //    htmlTitle = HtmlEntity.DeEntitize(titleNode.InnerText);
                        //        //.Replace('.', ' ')
                        //        //.Replace('<', ' ')
                        //        //.Replace('>', ' ')
                        //        //.Replace('[', ' ')
                        //        //.Replace(']', ' ')
                        //        //.Replace('(', ' ')
                        //        //.Replace(')', ' ');

                        //HtmlNode contentNode = htmlDoc.GetElementbyId("content");

                        //HtmlNode summaryNode = contentNode.SelectSingleNode(".//p[@class='summary']");

                        //string summary = string.Empty;

                        //if (summaryNode != null && summaryNode.SelectSingleNode("span[@class='error']") == null)
                        //    summary = HtmlEntity.DeEntitize(summaryNode.InnerText);

                        //string body = HtmlEntity.DeEntitize(contentNode.InnerText);

                        //var doc = new Document();

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

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

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

                    writer.Optimize();
                    writer.Commit();
                    writer.Close();
                }
                analyzerWrapper.Close();
                analyzer.Close();
                directory.Close();
            }
            this.OnStateChanged(State.Finalizing);

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

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

            this.OnStateChanged(State.Idle);
        }
Example #4
0
        public void Invoke()
        {
            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.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.");

                var xmlSettings = new XmlReaderSettings
                {
                    CheckCharacters = false
                };

                foreach (FileInfo file in includedFiles)
                {
                    TraceSources.TemplateSource.TraceEvent(TraceEventType.Information, 0, "Path: {0}", file.Name);
                    XDocument fileDoc;
                    using (var reader = XmlReader.Create(file.FullName, xmlSettings))
                        fileDoc = XDocument.Load(reader);

                    bundle.Add(fileDoc);
                }

                // find template
                string appDir = Assembly.GetExecutingAssembly().Location;
                string cwDir = Directory.GetCurrentDirectory();


                IFileProvider fsProvider = new DirectoryFileProvider();
                IFileProvider resourceProvider = new ResourceFileProvider("LBi.LostDoc.ConsoleApplication.Templates");

                IFileProvider selectedFileProvider = null;
                string templatePath = null;

                if (System.IO.Path.IsPathRooted(this.Template) &&
                    fsProvider.FileExists(System.IO.Path.Combine(this.Template, "template.xml")))
                {
                    selectedFileProvider = fsProvider;
                    templatePath = this.Template;
                }
                else if (!System.IO.Path.IsPathRooted(this.Template))
                {
                    string tmp = System.IO.Path.Combine(cwDir, this.Template, "template.xml");
                    if (fsProvider.FileExists(tmp))
                    {
                        selectedFileProvider = fsProvider;
                        templatePath = tmp;
                    }
                    else
                    {
                        tmp = System.IO.Path.Combine(appDir, this.Template, "template.xml");
                        if (fsProvider.FileExists(tmp))
                        {
                            selectedFileProvider = fsProvider;
                            templatePath = tmp;
                        }
                        else
                        {
                            tmp = System.IO.Path.Combine(this.Template, "template.xml");
                            if (resourceProvider.FileExists(tmp))
                            {
                                selectedFileProvider = resourceProvider;
                                templatePath = tmp;
                            }
                        }
                    }
                }

                if (templatePath == null)
                    throw new FileNotFoundException(this.Template);

                string outputDir = this.Output
                                   ?? (Directory.Exists(this.Path)
                                           ? this.Path
                                           : System.IO.Path.GetDirectoryName(this.Path));

                Template template = new Template(selectedFileProvider);

                template.Load(templatePath);
                AssetRedirectCollection assetRedirects;
                XDocument mergedDoc = bundle.Merge(out assetRedirects);
                var templateData = new TemplateData
                                       {
                                           OverwriteExistingFiles = this.Force.IsPresent,
                                           AssetRedirects = assetRedirects,
                                           Document = mergedDoc,
                                           IgnoredVersionComponent = this.IgnoreVersionComponent,
                                           Arguments = this.Arguments,
                                           TargetDirectory = outputDir
                                       };

                template.Generate(templateData);
            }
            finally
            {
                TraceSources.TemplateSource.Listeners.Remove(traceListener);
                TraceSources.AssetResolverSource.Listeners.Remove(traceListener);
            }
        }
Example #5
0
        /// <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
            using (TextWriterTraceListener traceListener =
                new TextWriterTraceListener(Path.Combine(logDir.FullName,
                                                         string.Format(
                                                             "template_{0:yyyy'_'MM'_'dd'__'HH'_'mm'_'ss}.log",
                                                             DateTime.Now))))
            {

                // log everything
                traceListener.Filter = new EventTypeFilter(SourceLevels.All);
                LBi.LostDoc.Core.Diagnostics.TraceSources.TemplateSource.Switch.Level = SourceLevels.All;
                LBi.LostDoc.Core.Diagnostics.TraceSources.BundleSource.Switch.Level = SourceLevels.All;
                LBi.LostDoc.Core.Diagnostics.TraceSources.AssetResolverSource.Switch.Level = SourceLevels.All;
                LBi.LostDoc.Core.Diagnostics.TraceSources.TemplateSource.Listeners.Add(traceListener);
                LBi.LostDoc.Core.Diagnostics.TraceSources.BundleSource.Listeners.Add(traceListener);
                LBi.LostDoc.Core.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,
                                           TargetDirectory = htmlDir.FullName,
                                           Arguments = new Dictionary<string, object> {{"SearchUri", "/search/"}}
                                       };

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

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

            this.OnStateChanged(State.Indexing);

            using (TextWriterTraceListener traceListener =
                new TextWriterTraceListener(Path.Combine(logDir.FullName,
                                                         string.Format(
                                                             "index_{0:yyyy'_'MM'_'dd'__'HH'_'mm'_'ss}.log",
                                                             DateTime.Now))))
            {
                // 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(ConvertToXml)));

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

            this.OnStateChanged(State.Idle);
        }
Example #6
0
        private IEnumerable<UnitOfWork> DiscoverWork(TemplateData templateData, Resource[] resources)
        {
            for (int i = 0; i < resources.Length; i++)
            {
                CustomXsltContext xpathContext = CreateCustomXsltContext(templateData.IgnoredVersionComponent);
                if (EvalCondition(xpathContext, templateData.XDocument.Root, resources[i].ConditionExpression))
                    yield return new ResourceDeployment(resources[i].Path);
            }

        }
Example #7
0
        /// <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());
        }
Example #8
0
        protected virtual ParsedTemplate PrepareTemplate(TemplateData templateData)
        {
            // clone orig doc
            XDocument workingDoc = new XDocument(this._templateDefinition);

            // start by loading any parameters as they are needed for meta-template evaluation

            Dictionary<string, XPathVariable> globalParams = new Dictionary<string, XPathVariable>();

            XElement[] paramNodes = workingDoc.Root.Elements("parameter").ToArray();
            foreach (XElement paramNode in paramNodes)
            {
                var tmplVar = new XPathVariable
                                  {
                                      Name = paramNode.Attribute("name").Value,
                                      ValueExpression =
                                          paramNode.Attribute("select") == null
                                              ? null
                                              : paramNode.Attribute("select").Value,
                                  };
                globalParams.Add(tmplVar.Name, tmplVar);
            }


            CustomXsltContext customContext = new CustomXsltContext();
            Func<string, object> onFailedResolve =
                s =>
                {
                    throw new InvalidOperationException(
                        String.Format("Parameter '{0}' could not be resolved.", s));
                };

            customContext.OnResolveVariable +=
                s =>
                {
                    XPathVariable var;

                    // if it's defined
                    if (globalParams.TryGetValue(s, out var))
                    {
                        // see if the user provided a value
                        object value;
                        if (templateData.Arguments.TryGetValue(s, out value))
                            return value;

                        // evaluate default value
                        if (!String.IsNullOrWhiteSpace(var.ValueExpression))
                            return workingDoc.XPathEvaluate(var.ValueExpression,
                                                            customContext);
                    }

                    return onFailedResolve(s);
                };

            // check for meta-template directives and expand
            XElement metaNode = workingDoc.Root.Elements("meta-template").FirstOrDefault();

            // we're going to need this later
            XmlFileProviderResolver fileResolver = new XmlFileProviderResolver(this._fileProvider, this._basePath);

            while (metaNode != null)
            {
                if (EvalCondition(customContext, metaNode, this.GetAttributeValueOrDefault(metaNode, "condition")))
                {
                    #region Debug conditional

#if DEBUG
                    const bool debugEnabled = true;
#else
                    const bool debugEnabled = false;
#endif

                    #endregion

                    XslCompiledTransform metaTransform = this.LoadStylesheet(metaNode.Attribute("stylesheet").Value);

                    XsltArgumentList xsltArgList = new XsltArgumentList();

                    // TODO this is a quick fix/hack
                    xsltArgList.AddExtensionObject("urn:lostdoc-core", new TemplateXsltExtensions(null, null));

                    var metaParamNodes = metaNode.Elements("with-param");

                    foreach (XElement paramNode in metaParamNodes)
                    {
                        string pName = paramNode.Attribute("name").Value;
                        string pExpr = paramNode.Attribute("select").Value;

                        xsltArgList.AddParam(pName,
                                             string.Empty,
                                             workingDoc.XPathEvaluate(pExpr, customContext));
                    }

                    XDocument outputDoc = new XDocument();
                    using (XmlWriter outputWriter = outputDoc.CreateWriter())
                    {
                        metaTransform.Transform(workingDoc.CreateNavigator(),
                                                xsltArgList,
                                                outputWriter,
                                                fileResolver);
                    }


                    TraceSources.TemplateSource.TraceVerbose("Template after transformation by {0}",
                                                             metaNode.Attribute("stylesheet").Value);

                    TraceSources.TemplateSource.TraceData(TraceEventType.Verbose, 1, outputDoc.CreateNavigator());

                    workingDoc = outputDoc;
                }
                else
                {
                    // didn't process, so remove it
                    metaNode.Remove();
                }


                // select next template
                metaNode = workingDoc.Root.Elements("meta-template").FirstOrDefault();
            }

            // 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(stylesheets, elem));
                }
                else if (elem.Name.LocalName == "index")
                {
                    indices.Add(this.ParseIndex(elem));
                }
                else if (elem.Name.LocalName == "include-resource")
                {
                    resources.Add(new Resource
                                      {
                                          Path = elem.Attribute("path").Value,
                                          ConditionExpression = this.GetAttributeValueOrDefault(elem, "condition")
                                      });
                }
                else
                {
                    throw new Exception("Unknown element: " + elem.Name.LocalName);
                }
            }

            return new ParsedTemplate
                       {
                           Resources = resources.ToArray(),
                           Stylesheets = stylesheets.ToArray(),
                           Indices = indices.ToArray()
                       };
        }
Example #9
0
        protected virtual IEnumerable<StylesheetApplication> DiscoverWork(TemplateData templateData, Stylesheet stylesheet)
        {
            TraceSources.TemplateSource.TraceInformation("Processing stylesheet instructions: {0}",
                                                         (object)stylesheet.Name);

            CustomXsltContext xpathContext = CreateCustomXsltContext(templateData.IgnoredVersionComponent);

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

            foreach (XElement inputElement in inputElements)
            {
                // create resolver
                // ReSharper disable AccessToModifiedClosure
                Func<string, object> ssResolver = v => EvalVariable(stylesheet.Variables, xpathContext, inputElement, v);
                // ReSharper restore AccessToModifiedClosure

                // attach resolver
                xpathContext.OnResolveVariable += ssResolver;

                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);
                    //detach resolver
                    xpathContext.OnResolveVariable -= ssResolver;
                    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());

                // detach resolver
                xpathContext.OnResolveVariable -= ssResolver;

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

                    foreach (XElement aliasInputElement in aliasInputElements)
                    {
                        // ReSharper disable AccessToForEachVariableInClosure
                        Func<string, object> aliasResolver =
                            v =>
                            {
                                if (HasVariable(alias, v))
                                    return EvalVariable(alias.Variables, xpathContext, aliasInputElement, v);

                                return EvalVariable(stylesheet.Variables, xpathContext, inputElement, v);
                            };
                        // ReSharper restore AccessToForEachVariableInClosure

                        xpathContext.OnResolveVariable += aliasResolver;

                        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.OnResolveVariable -= aliasResolver;
                    }
                }

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

                    foreach (XElement sectionInputElement in sectionInputElements)
                    {
                        // ReSharper disable AccessToForEachVariableInClosure
                        // ReSharper disable AccessToModifiedClosure
                        Func<string, object> sectionResolver =
                            v =>
                            {

                                if (HasVariable(section, v))

                                    return EvalVariable(section.Variables, xpathContext, sectionInputElement, v);

                                return EvalVariable(stylesheet.Variables, xpathContext, inputElement, v);
                            };
                        // ReSharper enable AccessToModifiedClosure
                        // ReSharper restore AccessToForEachVariableInClosure

                        xpathContext.OnResolveVariable += sectionResolver;

                        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.OnResolveVariable -= sectionResolver;

                    }
                }


                xpathContext.OnResolveVariable += ssResolver;
                var xsltParams = ResolveXsltParams(stylesheet.XsltParams, inputElement, xpathContext).ToArray();
                xpathContext.OnResolveVariable -= ssResolver;

                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
                                 };
            }
        }
Example #10
0
        /// <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());
        }