diff --git a/Morestachio.Examples/CustomFormatterExample/DataGeneration.cs b/Morestachio.Examples/CustomFormatterExample/DataGeneration.cs index 963f593d..88f96e03 100644 --- a/Morestachio.Examples/CustomFormatterExample/DataGeneration.cs +++ b/Morestachio.Examples/CustomFormatterExample/DataGeneration.cs @@ -14,30 +14,36 @@ public class DataGeneration : MorestachioExampleBase //this method can be used to obtain a new ParserOptions //here you could add custom formatters //this method is optional you can safely remove it - public override ParserOptions Configurate(string templateText, Encoding encoding, bool shouldEscape) + public override ParserOptions Configure( + string templateText, + Encoding encoding, + bool shouldEscape, + IServiceProvider serviceProvider + ) { var options = ParserOptionsBuilder.New() .WithTemplate(templateText) .WithEncoding(encoding) .WithDisableContentEscaping(shouldEscape) .WithTimeout(TimeSpan.FromSeconds(5)) + .WithServiceProvider(serviceProvider) .WithFormatters(); return options.Build(); } [MorestachioFormatter("ToBase64", "")] - public static string ToBase64(byte[] data){ + public static string ToBase64(byte[] data) + { return Convert.ToBase64String(data); } [MorestachioGlobalFormatter("HttpGet", "Gets an string value from the url")] - public static async Task GetHttpValue(string source) + public static async Task GetHttpValue(string source, [ExternalData] HttpClient httpClient) { - var httpClient = new HttpClient(); return await httpClient.GetByteArrayAsync(source); } - + //there must be always a method in the Program class that will be called to obtain the data public override object GetData() { diff --git a/Morestachio.Examples/MorestachioExampleBase.cs b/Morestachio.Examples/MorestachioExampleBase.cs index 88e5f022..a382146c 100644 --- a/Morestachio.Examples/MorestachioExampleBase.cs +++ b/Morestachio.Examples/MorestachioExampleBase.cs @@ -1,101 +1,277 @@ using System; +using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using System.Xml; using System.Xml.Serialization; using Morestachio.Document.Contracts; using Morestachio.Document.Items; +using Morestachio.Parsing.ParserErrors; +using Morestachio.Rendering; using Newtonsoft.Json; using Formatting = Newtonsoft.Json.Formatting; -namespace Morestachio.Example.Base +// ReSharper disable once CheckNamespace +namespace Morestachio.Example.Base; + +/// +/// DTO object used to communicate with the UI +/// +public class MorestachioRunResult +{ + /// + /// When set will indicate errors in the template and should be displayed in UI + /// + public IEnumerable Errors { get; set; } + + /// + /// The stringified version of the generated template + /// + public string TemplateResult { get; set; } + + /// + /// Tracking information + /// + public IEnumerable> Times { get; set; } + + /// + /// The document as an Xml document + /// + public string XmlDocument { get; set; } + + /// + /// The document as an Xml document + /// + public string JsonDocument { get; set; } +} + +/// +/// Base class for generating Templates with the use of the Morestachio Demo +/// +public abstract class MorestachioExampleBase { - public abstract partial class MorestachioExampleBase + /// + /// External call point. This is where the UI will enter the Custom document generation + /// + /// The Template as provided by the UI + /// Currently expected to be + /// The toggle from UI "Html Escaped" + /// The runtimes service provider + /// + public ValueTask Run(string templateText, + Encoding encoding, + bool shouldEscape, + IServiceProvider serviceProvider) { - public virtual ParserOptions Configurate(string templateText, Encoding encoding, bool shouldEscape) + return RunCore(templateText, encoding, shouldEscape, serviceProvider); + } + + protected virtual async ValueTask RunCore( + string templateText, + Encoding encoding, + bool shouldEscape, + IServiceProvider serviceProvider + ) + { + //for debugging purposes + var times = new Dictionary(); + + //Helper method for profiling a step in template execution + T Evaluate(Func fnc, string name) { - var options = ParserOptionsBuilder.New() - .WithTemplate(templateText) - .WithEncoding(encoding) - .WithDisableContentEscaping(shouldEscape) - .WithTimeout(TimeSpan.FromSeconds(5)); + var sw = Stopwatch.StartNew(); - return options.Build(); + try + { + return fnc(); + } + finally + { + sw.Stop(); + times[name] = sw.Elapsed; + } } - public abstract object GetData(); + //Helper method for profiling a step in template execution + async Task EvaluateAsync(Func> fnc, string name) + { + var sw = Stopwatch.StartNew(); + + try + { + return await fnc(); + } + finally + { + sw.Stop(); + times[name] = sw.Elapsed; + } + } + + //Call the Configure method to get either a user custom defined parser options object or a default one + var options = Evaluate(() => Configure(templateText, encoding, shouldEscape, serviceProvider), "Configure"); - public virtual async Task Parse(ParserOptions parserOptions) + //parse the template + var documentInfo = await EvaluateAsync(async () => await Parse(options), "Parse"); + + if (documentInfo.Errors.Any()) { - return await Parser.ParseWithOptionsAsync(parserOptions); + //when there are any errors just return them there is no point in further progressing with the template + return new MorestachioRunResult() + { + Errors = documentInfo.Errors + }; } - public virtual string SerializeToXmlText(IDocumentItem obj) + //get the custom data object + var data = Evaluate(GetData, "GetData"); + + //create a renderer. This might be changed to support an compiled renderer + var renderer = Evaluate(() => CreateRenderer(documentInfo), "Create Render"); + + //render the template + var result = await EvaluateAsync(async () => (await renderer.RenderAndStringifyAsync(data, CancellationToken.None)), "Render"); + + //serialization + var jsonResult = Evaluate(() => SerializeToJsonText(documentInfo.Document), "Json Serialization"); + var xmlResult = Evaluate(() => SerializeToXmlText(documentInfo.Document), "Xml Serialization"); + + return new MorestachioRunResult() { - var devidedTypes = typeof(MorestachioDocument).Assembly - .GetTypes() - .Where(e => e.IsClass) - .Where(e => typeof(IDocumentItem) - .IsAssignableFrom(e)) - .ToArray(); - var xmlSerializer = new XmlSerializer(obj.GetType(), devidedTypes); - - using (var ms = new MemoryStream()) + JsonDocument = jsonResult, + XmlDocument = xmlResult, + TemplateResult = result, + Times = times + }; + } + + /// + /// Gets a renderer for an + /// + /// + /// + protected virtual IRenderer CreateRenderer(MorestachioDocumentInfo morestachioDocumentInfo) + { + return morestachioDocumentInfo.CreateRenderer(); + } + + /// + /// Gets a Parser options object that can be used to process + /// + /// + /// + /// + /// + /// + public virtual ParserOptions Configure( + string templateText, + Encoding encoding, + bool shouldEscape, + IServiceProvider serviceProvider + ) + { + return ParserOptionsBuilder.New() + .WithTemplate(templateText) + .WithEncoding(encoding) + .WithDisableContentEscaping(shouldEscape) + .WithTimeout(TimeSpan.FromSeconds(5)) + .WithServiceProvider(serviceProvider) + .Build(); + } + + /// + /// Gets the data object used to process the template + /// + /// + public abstract object GetData(); + + /// + /// Parses the Morestachio template in a processable form + /// + /// + /// + protected virtual async Task Parse(ParserOptions parserOptions) + { + return await Parser.ParseWithOptionsAsync(parserOptions); + } + + /// + /// Serializes the to a XML document + /// + /// + /// + protected virtual string SerializeToXmlText(IDocumentItem obj) + { + var documentItemTypes = typeof(MorestachioDocument).Assembly + .GetTypes() + .Where(e => e.IsClass) + .Where(e => typeof(IDocumentItem) + .IsAssignableFrom(e)) + .ToArray(); + var xmlSerializer = new XmlSerializer(obj.GetType(), documentItemTypes); + + using (var ms = new MemoryStream()) + { + xmlSerializer.Serialize(ms, obj); + ms.Position = 0; + using var reader = new StreamReader(ms, true); + var xmlText = reader.ReadToEnd(); // no exception on `LoadXml` + + return PrettifyXML(xmlText); + } + + static string PrettifyXML(string xml) + { + using var mStream = new MemoryStream(); + using var writer = new XmlTextWriter(mStream, Encoding.UTF8); + var document = new XmlDocument(); + try { - xmlSerializer.Serialize(ms, obj); - ms.Position = 0; - using var reader = new StreamReader(ms, true); - var xmlText = reader.ReadToEnd(); // no exception on `LoadXml` + // Load the XmlDocument with the XML. + document.LoadXml(xml); - return PrettifyXML(xmlText); - } + writer.Formatting = System.Xml.Formatting.Indented; + + // Write the XML into a formatting XmlTextWriter + document.WriteContentTo(writer); + + writer.Flush(); + mStream.Flush(); + + // Have to rewind the MemoryStream in order to read + // its contents. + mStream.Position = 0; + + // Read MemoryStream contents into a StreamReader. + var sReader = new StreamReader(mStream); - static string PrettifyXML(string xml) + // Extract the text from the StreamReader. + var formattedXml = sReader.ReadToEnd(); + + return formattedXml; + } + catch (XmlException e) { - using var mStream = new MemoryStream(); - using var writer = new XmlTextWriter(mStream, Encoding.UTF8); - var document = new XmlDocument(); - try - { - // Load the XmlDocument with the XML. - document.LoadXml(xml); - - writer.Formatting = System.Xml.Formatting.Indented; - - // Write the XML into a formatting XmlTextWriter - document.WriteContentTo(writer); - - writer.Flush(); - mStream.Flush(); - - // Have to rewind the MemoryStream in order to read - // its contents. - mStream.Position = 0; - - // Read MemoryStream contents into a StreamReader. - var sReader = new StreamReader(mStream); - - // Extract the text from the StreamReader. - var formattedXml = sReader.ReadToEnd(); - - return formattedXml; - } - catch (XmlException e) - { - return e.Message; - // Handle the exception - } + return e.Message; + // Handle the exception } } + } - public virtual string SerializeToJsonText(IDocumentItem obj) - { - var jsonSerializerSettings = new JsonSerializerSettings(); - jsonSerializerSettings.Formatting = Formatting.Indented; - jsonSerializerSettings.TypeNameHandling = TypeNameHandling.Objects; - return JsonConvert.SerializeObject(obj, jsonSerializerSettings); - } + /// + /// Serializes the to a Json Document + /// + /// + /// + protected virtual string SerializeToJsonText(IDocumentItem obj) + { + var jsonSerializerSettings = new JsonSerializerSettings(); + jsonSerializerSettings.Formatting = Formatting.Indented; + jsonSerializerSettings.TypeNameHandling = TypeNameHandling.Objects; + return JsonConvert.SerializeObject(obj, jsonSerializerSettings); } -} +} \ No newline at end of file diff --git a/Morestachio.Examples/ResultBuilder.cs b/Morestachio.Examples/ResultBuilder.cs deleted file mode 100644 index 7971b873..00000000 --- a/Morestachio.Examples/ResultBuilder.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Morestachio.Parsing.ParserErrors; -using Morestachio.Rendering; - -namespace Morestachio.Example.Base -{ - public static class ResultBuilder - { - public const string RESULT_TIMES = "RESULT_TIMES"; - public const string RESULT_NAME = "COMPILED_RESULT"; - public const string ERRORS_NAME = "ERRORS"; - public const string JSONTEXT_NAME = "JSON"; - public const string XMLTEXT_NAME = "XML"; - } - - public partial class MorestachioExampleBase - { - private IDictionary FailResultBuilder(params IMorestachioError[] errors) - { - var result = new Dictionary - { - //[ResultBuilder.ERRORS_NAME] = JsonConvert.SerializeObject(errors) - }; - return result; - } - - private IDictionary OkResult(string result, string jsonResult, string xmlResult, KeyValuePair[] times) - { - var resultBuilder = new Dictionary(); - resultBuilder[ResultBuilder.RESULT_NAME] = result; - resultBuilder[ResultBuilder.JSONTEXT_NAME] = jsonResult; - resultBuilder[ResultBuilder.XMLTEXT_NAME] = xmlResult; - resultBuilder[ResultBuilder.RESULT_TIMES] = times; - return resultBuilder; - } - - public IDictionary Run(string templateText, Encoding encoding, bool shouldEscape) - { - return (RunCore(templateText, encoding, shouldEscape)).GetAwaiter().GetResult(); - } - - public async Task> RunCore(string templateText, Encoding encoding, bool shouldEscape) - { - var times = new Dictionary(); - - T Evaluate(Func fnc, string name) - { - var sw = Stopwatch.StartNew(); - - try - { - return fnc(); - } - finally - { - sw.Stop(); - times[name] = sw.Elapsed; - } - } - - async Task EvaluateAsync(Func> fnc, string name) - { - var sw = Stopwatch.StartNew(); - - try - { - return await fnc(); - } - finally - { - sw.Stop(); - times[name] = sw.Elapsed; - } - } - - var options = Evaluate(() => Configurate(templateText, encoding, shouldEscape), "Configurate"); - var documentInfo = await EvaluateAsync(async () => await Parse(options), "Parse"); - - if (documentInfo.Errors.Any()) - { - return FailResultBuilder(documentInfo.Errors.ToArray()); - } - - var data = Evaluate(GetData, "GetData"); - var result = await EvaluateAsync(async () => - { - return (await documentInfo.CreateRenderer().RenderAndStringifyAsync(data, CancellationToken.None)); - }, "Render"); - - var jsonResult = Evaluate(() => SerializeToJsonText(documentInfo.Document), "Json Serialization"); - var xmlResult = Evaluate(() => SerializeToXmlText(documentInfo.Document), "Xml Serialization"); - - return OkResult(result, jsonResult, xmlResult, times.ToArray()); - } - - - } -} \ No newline at end of file diff --git a/Morestachio.sln b/Morestachio.sln index b9547817..0ec82887 100644 --- a/Morestachio.sln +++ b/Morestachio.sln @@ -59,7 +59,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Integrations", "Integration EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Morestachio.System.Xml.Linq", "Morestachio.System.Xml.Linq\Morestachio.System.Xml.Linq.csproj", "{E514EE96-F61E-4584-9617-09B51DE2F5CA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Morestachio.Extensions.Logging", "Morestachio.Extensions.Logging\Morestachio.Extensions.Logging.csproj", "{E78C69C0-2537-48E9-A35E-C8578F971C38}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Morestachio.Extensions.Logging", "Morestachio.Extensions.Logging\Morestachio.Extensions.Logging.csproj", "{E78C69C0-2537-48E9-A35E-C8578F971C38}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -88,9 +88,7 @@ Global {B3692F2F-B295-46BC-B0F1-D4679C172142}.Release|Any CPU.ActiveCfg = Release|Any CPU {B3692F2F-B295-46BC-B0F1-D4679C172142}.Release|Any CPU.Build.0 = Release|Any CPU {B38420AB-6681-4A6D-B9C1-BBA67DD2307E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B38420AB-6681-4A6D-B9C1-BBA67DD2307E}.Debug|Any CPU.Build.0 = Debug|Any CPU {B38420AB-6681-4A6D-B9C1-BBA67DD2307E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B38420AB-6681-4A6D-B9C1-BBA67DD2307E}.Release|Any CPU.Build.0 = Release|Any CPU {3D97A54D-A724-40B8-9155-3B8FE28CA80E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3D97A54D-A724-40B8-9155-3B8FE28CA80E}.Debug|Any CPU.Build.0 = Debug|Any CPU {3D97A54D-A724-40B8-9155-3B8FE28CA80E}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/Morestachio/Formatter/Framework/ServiceCollection.cs b/Morestachio/Formatter/Framework/ServiceCollection.cs index 5e89a11e..d01b9737 100644 --- a/Morestachio/Formatter/Framework/ServiceCollection.cs +++ b/Morestachio/Formatter/Framework/ServiceCollection.cs @@ -9,6 +9,7 @@ public class ServiceCollection : IServiceContainer { private readonly IDictionary _localSource; private readonly ServiceCollection _parentProvider; + private readonly IList _subContainer; /// /// Creates a new Top level Service collection @@ -22,7 +23,7 @@ public ServiceCollection() : this(null) public ServiceCollection(ServiceCollection parentProvider) { _parentProvider = parentProvider; - + _subContainer = new List(); _localSource = new Dictionary { { typeof(ServiceCollection), this }, @@ -73,7 +74,6 @@ public void RemoveService(Type serviceType, bool promote) _localSource.Remove(serviceType); } - /// /// Enumerates all services known /// @@ -141,6 +141,15 @@ public void AddService(Func serviceFactory) AddService(typeof(T), serviceFactory); } + /// + /// Adds an to the Collection of services. + /// + /// + public void AddSubProvider(IServiceProvider subContainer) + { + _subContainer.Add(subContainer); + } + /// /// Gets the service if present /// @@ -204,18 +213,22 @@ public bool TryGetService(Type serviceType, out object service) return true; } - if (_parentProvider == null) + if (_parentProvider != null) { - return false; - } + service = _parentProvider.GetService(serviceType); - service = _parentProvider.GetService(serviceType); + if (service is Delegate factory) + { + service = factory.DynamicInvoke(); + } - if (service is Delegate factory) - { - service = factory.DynamicInvoke(); + if (service != null) + { + return true; + } } + service = _subContainer.Select(c => c.GetService(serviceType)).FirstOrDefault(); return service != null; } diff --git a/Morestachio/ParserOptionsBuilderFormatterExtensions.cs b/Morestachio/ParserOptionsBuilderFormatterExtensions.cs index 83cca38e..2fcbda8c 100644 --- a/Morestachio/ParserOptionsBuilderFormatterExtensions.cs +++ b/Morestachio/ParserOptionsBuilderFormatterExtensions.cs @@ -1,4 +1,5 @@ -using Morestachio.Formatter.Framework; +using System.ComponentModel.Design; +using Morestachio.Formatter.Framework; using Morestachio.Formatter.Framework.Converter; using Morestachio.Framework; @@ -14,10 +15,7 @@ public static class ParserOptionsDefaultPartialStoreExtensions /// public static IParserOptionsBuilder WithDefaultPartialStore(this IParserOptionsBuilder builder, Func config) { - return builder.WithPartialsStore(() => - { - return config(new DefaultPartialsStore()); - }); + return builder.WithPartialsStore(() => config(new DefaultPartialsStore())); } /// @@ -128,6 +126,19 @@ public static IParserOptionsBuilder WithService( }); } + /// + /// Adds a new + /// + public static IParserOptionsBuilder WithServiceProvider(this IParserOptionsBuilder builder, + IServiceProvider subContainer) + { + return builder.WithConfig(c => + { + c.Formatters.Services.AddSubProvider(subContainer); + return c; + }); + } + /// /// Adds formatters from an type ///