Skip to content

Commit

Permalink
Added SubContainer for Services in formatters
Browse files Browse the repository at this point in the history
Fixed examples state
  • Loading branch information
JPVenson committed Jun 15, 2022
1 parent 7d14700 commit d6acc93
Show file tree
Hide file tree
Showing 6 changed files with 296 additions and 196 deletions.
16 changes: 11 additions & 5 deletions Morestachio.Examples/CustomFormatterExample/DataGeneration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<DataGeneration>();

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<byte[]> GetHttpValue(string source)
public static async Task<byte[]> 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()
{
Expand Down
316 changes: 246 additions & 70 deletions Morestachio.Examples/MorestachioExampleBase.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// DTO object used to communicate with the UI
/// </summary>
public class MorestachioRunResult
{
/// <summary>
/// When set will indicate errors in the template and should be displayed in UI
/// </summary>
public IEnumerable<IMorestachioError> Errors { get; set; }

/// <summary>
/// The stringified version of the generated template
/// </summary>
public string TemplateResult { get; set; }

/// <summary>
/// Tracking information
/// </summary>
public IEnumerable<KeyValuePair<string, TimeSpan>> Times { get; set; }

/// <summary>
/// The document as an Xml document
/// </summary>
public string XmlDocument { get; set; }

/// <summary>
/// The document as an Xml document
/// </summary>
public string JsonDocument { get; set; }
}

/// <summary>
/// Base class for generating Templates with the use of the Morestachio Demo
/// </summary>
public abstract class MorestachioExampleBase
{
public abstract partial class MorestachioExampleBase
/// <summary>
/// External call point. This is where the UI will enter the Custom document generation
/// </summary>
/// <param name="templateText">The Template as provided by the UI</param>
/// <param name="encoding">Currently expected to be <see cref="Encoding.UTF8"/></param>
/// <param name="shouldEscape">The toggle from UI "Html Escaped" </param>
/// <param name="serviceProvider">The runtimes service provider</param>
/// <returns></returns>
public ValueTask<MorestachioRunResult> 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<MorestachioRunResult> RunCore(
string templateText,
Encoding encoding,
bool shouldEscape,
IServiceProvider serviceProvider
)
{
//for debugging purposes
var times = new Dictionary<string, TimeSpan>();

//Helper method for profiling a step in template execution
T Evaluate<T>(Func<T> 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<T> EvaluateAsync<T>(Func<Task<T>> 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<MorestachioDocumentInfo> 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
};
}

/// <summary>
/// Gets a renderer for an <see cref="MorestachioDocumentInfo"/>
/// </summary>
/// <param name="morestachioDocumentInfo"></param>
/// <returns></returns>
protected virtual IRenderer CreateRenderer(MorestachioDocumentInfo morestachioDocumentInfo)
{
return morestachioDocumentInfo.CreateRenderer();
}

/// <summary>
/// Gets a Parser options object that can be used to process <see cref="GetData"/>
/// </summary>
/// <param name="templateText"></param>
/// <param name="encoding"></param>
/// <param name="shouldEscape"></param>
/// <param name="serviceProvider"></param>
/// <returns></returns>
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();
}

/// <summary>
/// Gets the data object used to process the template
/// </summary>
/// <returns></returns>
public abstract object GetData();

/// <summary>
/// Parses the Morestachio template in a processable form
/// </summary>
/// <param name="parserOptions"></param>
/// <returns></returns>
protected virtual async Task<MorestachioDocumentInfo> Parse(ParserOptions parserOptions)
{
return await Parser.ParseWithOptionsAsync(parserOptions);
}

/// <summary>
/// Serializes the <see cref="IDocumentItem"/> to a XML document
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
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);
}
/// <summary>
/// Serializes the <see cref="IDocumentItem"/> to a Json Document
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
protected virtual string SerializeToJsonText(IDocumentItem obj)
{
var jsonSerializerSettings = new JsonSerializerSettings();
jsonSerializerSettings.Formatting = Formatting.Indented;
jsonSerializerSettings.TypeNameHandling = TypeNameHandling.Objects;
return JsonConvert.SerializeObject(obj, jsonSerializerSettings);
}
}
}
Loading

0 comments on commit d6acc93

Please sign in to comment.