4using System.Diagnostics;
8using System.Threading.Tasks;
29using static
System.Environment;
33 internal enum ResultType
39 internal class GraphInfo
41 public string BaseFileName;
42 public string TxtFileName;
43 public string ImageFileName;
44 public bool ImageExists;
45 public string PlantUmlFolder;
47 public string AsyncId;
57 private const int chartGenerationTimeout = 30000;
60 private static readonly Random rnd =
new Random();
61 private static Scheduler scheduler =
null;
62 private static string jarPath =
null;
63 private static string javaPath =
null;
64 private static string plantUmlFolder =
null;
65 private static string contentRootFolder =
null;
80 public static void Init(
string ContentRootFolder)
84 contentRootFolder = ContentRootFolder;
86 if (scheduler is
null)
94 Log.Terminating += (Sender, e) =>
98 return Task.CompletedTask;
105 if (
string.IsNullOrEmpty(JarPath))
106 Log.
Warning(
"PlantUML not found. PlantUML support will not be available in Markdown.");
107 else if (
string.IsNullOrEmpty(JavaPath))
108 Log.
Warning(
"Java not found. PlantUML support will not be available in Markdown.");
114 new KeyValuePair<string, object>(
"Path", jarPath),
115 new KeyValuePair<string, object>(
"Java", javaPath));
135 await queue.CloseForTermination(
true, chartGenerationTimeout);
136 await queue.DisposeAsync();
151 public static void SetPath(
string JarPath,
string JavaPath)
153 if (!
string.IsNullOrEmpty(jarPath) && JarPath != jarPath)
154 throw new Exception(
"PlantUML an Java paths have already been set.");
158 plantUmlFolder = Path.Combine(contentRootFolder,
"PlantUML");
160 if (!Directory.Exists(plantUmlFolder))
161 Directory.CreateDirectory(plantUmlFolder);
163 DeleteOldFiles(TimeSpan.FromDays(7));
166 private static void DeleteOldFiles(
object P)
168 if (P is TimeSpan MaxAge)
169 DeleteOldFiles(MaxAge,
true);
179 if (
string.IsNullOrEmpty(plantUmlFolder))
182 DateTime Limit = DateTime.Now - MaxAge;
185 DirectoryInfo PlantUmlFolder =
new DirectoryInfo(plantUmlFolder);
186 FileInfo[]
Files = PlantUmlFolder.GetFiles(
"*.*");
188 foreach (FileInfo FileInfo
in Files)
190 if (FileInfo.LastAccessTime < Limit)
194 File.Delete(FileInfo.FullName);
199 Log.
Error(
"Unable to delete old file: " + ex.Message, FileInfo.FullName);
205 Log.
Informational(Count.ToString() +
" old file(s) deleted.", plantUmlFolder);
211 scheduler.
Add(DateTime.Now.AddDays(rnd.NextDouble() * 2), DeleteOldFiles, MaxAge);
227 SpecialFolder.ProgramFiles,
228 SpecialFolder.ProgramFilesX86
233 Folders.
Add(Path.Combine(RuntimeFolder, SpecialFolder.ProgramFiles.ToString()));
234 Folders.
Add(Path.Combine(RuntimeFolder, SpecialFolder.ProgramFilesX86.ToString()));
237 string PathVar = Environment.GetEnvironmentVariable(
"PATH");
238 if (!
string.IsNullOrEmpty(PathVar))
240 string[] Paths = PathVar.Split(Path.PathSeparator);
244 string[] Folders2 = Folders.
ToArray();
257 if (!
string.IsNullOrEmpty(jarPath) && !
string.IsNullOrEmpty(javaPath))
259 int i = Language.IndexOf(
':');
261 Language = Language.Substring(0, i).TrimEnd();
263 switch (Language.ToLower())
265 case "uml":
return Grade.Excellent;
266 case "plantuml":
return Grade.Perfect;
270 return Grade.NotAtAll;
299 bool GenerateIfNotExists = asyncHtmlOutput is
null;
300 GraphInfo Info = await GetGraphInfo(Language, Rows, ResultType.Svg, GenerateIfNotExists);
302 if (GenerateIfNotExists || Info.ImageExists)
304 this.GenerateHTML(Output, Info);
310 foreach (KeyValuePair<AsyncMarkdownProcessing, object> P
in Document.AsyncTasks)
312 if (P.Value is AsyncState AsyncState && AsyncState.Type == ResultType.Svg && AsyncState.GraphInfos.Count < 10)
314 AsyncState.GraphInfos.Add(Info);
319 Document.QueueAsyncTask(this.ExecutePlantUml,
new AsyncState(ResultType.Svg, Info));
324 private class AsyncState
327 public readonly ResultType Type;
329 public AsyncState(ResultType Type, GraphInfo Info)
339 private async Task ExecutePlantUml(
object State)
343 XmlEntitiesOnly =
true
346 AsyncState AsyncState = (AsyncState)State;
350 await ExecutePlantUml(AsyncState.Type, AsyncState.GraphInfos.ToArray());
352 foreach (GraphInfo Info
in AsyncState.GraphInfos)
368 foreach (GraphInfo Info
in AsyncState.GraphInfos)
377 private static async Task ExecutePlantUml(ResultType Type, params GraphInfo[]
Files)
379 ChartRecord Rec =
new ChartRecord(Type,
Files);
382 if (!await Rec.Wait())
383 throw new Exception(
"Unable to process PlantUML chart.");
388 private readonly TaskCompletionSource<bool> result =
new TaskCompletionSource<bool>();
390 private readonly GraphInfo[] files;
392 public ChartRecord(ResultType Type, params GraphInfo[]
Files)
401 public Task Execute()
403 return this.Execute(CancellationToken.None);
410 public async Task Execute(CancellationToken Cancel)
412 StringBuilder Arguments =
new StringBuilder();
413 Arguments.Append(
"-jar \"");
414 Arguments.Append(jarPath);
415 Arguments.Append(
"\" -quiet -charset UTF-8 -t");
416 Arguments.Append(this.type.ToString().ToLower());
418 foreach (GraphInfo Info
in this.files)
420 Arguments.Append(
" \"");
421 Arguments.Append(Info.TxtFileName);
422 Arguments.Append(
'"');
425 ProcessStartInfo ProcessInformation =
new ProcessStartInfo()
428 Arguments = Arguments.ToString(),
429 UseShellExecute =
false,
430 RedirectStandardError =
true,
431 RedirectStandardOutput =
true,
432 RedirectStandardInput =
false,
433 WorkingDirectory = this.files[0].PlantUmlFolder,
434 CreateNoWindow =
true,
435 WindowStyle = ProcessWindowStyle.Hidden
438 Process P =
new Process();
439 TaskCompletionSource<int> ExitSource =
new TaskCompletionSource<int>();
441 P.Exited += (Sender, e) =>
443 ExitSource.TrySetResult(P.ExitCode);
446 Task
_ = Task.Delay(chartGenerationTimeout).ContinueWith(Prev =>
452 foreach (GraphInfo Info
in this.files)
454 if (File.Exists(Info.ImageFileName))
455 File.Delete(Info.ImageFileName);
464 ExitSource.TrySetException(
new TimeoutException(
"PlantUML process did not terminate properly."));
467 return Task.CompletedTask;
470 P.StartInfo = ProcessInformation;
471 P.EnableRaisingEvents =
true;
474 int ExitCode = await ExitSource.Task;
478 string Error = P.StandardError.ReadToEnd();
479 this.result.TrySetException(
new Exception(Error));
487 public void Processed(
bool Result)
489 this.result.TrySetResult(Result);
496 public Task<bool> Wait()
498 return this.result.Task;
506 public Task<bool> Wait(CancellationToken Cancel)
508 if (Cancel.CanBeCanceled)
509 Cancel.Register(() => this.result.TrySetException(
new OperationCanceledException(Cancel)));
511 return this.result.Task;
515 private void GenerateHTML(StringBuilder Output, GraphInfo Info)
517 Info.ImageFileName = Info.ImageFileName.Substring(contentRootFolder.Length).Replace(Path.DirectorySeparatorChar,
'/');
518 if (!Info.ImageFileName.StartsWith(
"/"))
519 Info.ImageFileName =
"/" + Info.ImageFileName;
521 Output.Append(
"<figure>");
522 Output.Append(
"<img src=\"");
525 if (!
string.IsNullOrEmpty(Info.Title))
527 Output.Append(
"\" alt=\"");
530 Output.Append(
"\" title=\"");
534 Output.Append(
"\" alt=\"PlantUML graph");
536 Output.Append(
"\" class=\"aloneUnsized\"/>");
538 if (!
string.IsNullOrEmpty(Info.Title))
540 Output.Append(
"<figcaption>");
542 Output.Append(
"</figcaption>");
545 Output.AppendLine(
"</figure>");
548 internal static Task<GraphInfo> GetGraphInfo(
string Language,
string[] Rows, ResultType Type)
553 internal static async Task<GraphInfo> GetGraphInfo(
string Language,
string Graph, ResultType Type)
556 string PlantUmlFolder = Path.Combine(contentRootFolder,
"PlantUML");
558 GraphInfo Result =
new GraphInfo()
560 BaseFileName = Path.Combine(PlantUmlFolder, FileName),
561 PlantUmlFolder = PlantUmlFolder
563 int i = Language.IndexOf(
':');
566 Result.Title = Language.Substring(i + 1).Trim();
568 Result.Title =
string.Empty;
570 Result.TxtFileName = Result.BaseFileName +
".txt";
571 if (!File.Exists(Result.TxtFileName))
586 Result.ImageExists = File.Exists(Result.ImageFileName);
587 if (Result.ImageExists)
589 FileInfo Info =
new FileInfo(Result.ImageFileName);
590 if (Info.Length == 0)
591 Result.ImageExists =
false;
597 internal static Task<GraphInfo> GetGraphInfo(
string Language,
string[] Rows, ResultType Type,
bool GenerateIfNotExists)
602 internal static async Task<GraphInfo> GetGraphInfo(
string Language,
string GraphDefinition, ResultType Type,
bool GenerateIfNotExists)
604 GraphInfo Result = await GetGraphInfo(Language, GraphDefinition, Type);
606 if (GenerateIfNotExists && !Result.ImageExists)
607 await ExecutePlantUml(Type, Result);
623 GraphInfo Info = await GetGraphInfo(Language, Rows, ResultType.Png,
true);
642 GraphInfo Info = await GetGraphInfo(Language, Rows, ResultType.Svg,
true);
662 GraphInfo Info = await GetGraphInfo(Language, Rows, ResultType.Png,
true);
666 XmlWriter Output =
Renderer.XmlOutput;
668 Output.WriteStartElement(
"Image");
669 Output.WriteAttributeString(
"Source", Info.ImageFileName);
670 Output.WriteAttributeString(
"Stretch",
"None");
672 if (!
string.IsNullOrEmpty(Info.Title))
673 Output.WriteAttributeString(
"ToolTip", Info.Title);
675 Output.WriteEndElement();
690 GraphInfo Info = await GetGraphInfo(Language, Rows, ResultType.Png,
true);
694 XmlWriter Output =
Renderer.XmlOutput;
696 Output.WriteStartElement(
"Image");
697 Output.WriteAttributeString(
"Source", Info.ImageFileName);
698 Output.WriteEndElement();
714 GraphInfo Info = await GetGraphInfo(Language, Rows, ResultType.Png,
true);
717 Output.AppendLine(
"\\begin{figure}[h]");
718 Output.AppendLine(
"\\centering");
720 Output.Append(
"\\fbox{\\includegraphics{");
721 Output.Append(Info.ImageFileName.Replace(
'\\',
'/'));
722 Output.AppendLine(
"}}");
724 if (!
string.IsNullOrEmpty(Info.Title))
726 Output.Append(
"\\caption{");
728 Output.AppendLine(
"}");
731 Output.AppendLine(
"\\end{figure}");
746 GraphInfo Info = await GetGraphInfo(Language, Rows, ResultType.Png,
true);
750 byte[] Data = await Runtime.IO.Files.ReadAllBytesAsync(Info.ImageFileName);
752 using (SKBitmap Bitmap = SKBitmap.Decode(Data))
771 GraphInfo Info = await GetGraphInfo(Language, Rows, ResultType.Png,
true);
775 byte[] Data = await Runtime.IO.Files.ReadAllBytesAsync(Info.ImageFileName);
782 XmlWriter Output =
Renderer.XmlOutput;
783 int Width = Image.Width;
784 int Height = Image.Height;
786 Output.WriteStartElement(
"imageStandalone");
788 Output.WriteAttributeString(
"contentType", ContentType);
789 Output.WriteAttributeString(
"width", Width.ToString());
790 Output.WriteAttributeString(
"height", Height.ToString());
792 Output.WriteStartElement(
"binary");
793 Output.WriteValue(Convert.ToBase64String(Data));
794 Output.WriteEndElement();
796 Output.WriteStartElement(
"caption");
797 if (
string.IsNullOrEmpty(Info.Title))
798 Output.WriteElementString(
"text",
"Graph");
800 Output.WriteElementString(
"text", Info.Title);
802 Output.WriteEndElement();
803 Output.WriteEndElement();
Contains information about a response to a content request.
bool HasError
If an error occurred.
object Decoded
Decoded object.
const string FileExtensionPng
png
const string ContentTypePng
image/png
const string FileExtensionSvg
svg
Static class managing encoding and decoding of internet content.
static Task< ContentResponse > DecodeAsync(string ContentType, byte[] Data, Encoding Encoding, KeyValuePair< string, string >[] Fields, Uri BaseUri)
Decodes an object.
Renders Contracts XML from a Markdown document.
Renders LaTeX from a Markdown document.
static string EscapeLaTeX(string s)
Escapes text for output in a LaTeX document.
Contains a markdown document. This markdown document class supports original markdown,...
static string AppendRows(string[] Rows)
Appends a set of rows into a single string with newlines between rows.
Base64-encoded image content.
static async Task< bool > GenerateMarkdownFromFile(StringBuilder Output, string FileName, string Title)
Generates Markdown embedding an image available in a file.
Class managing PlantUML integration into Markdown documents.
async Task< PixelInformation > GenerateImage(string[] Rows, string Language, MarkdownDocument Document)
Generates an image of the contents.
Grade Supports(string Language)
Checks how well the handler supports multimedia content of a given type.
async Task< bool > RenderMarkdown(MarkdownRenderer Renderer, string[] Rows, string Language, int Indent, MarkdownDocument Document)
Generates Markdown for the code content.
static void DeleteOldFiles(TimeSpan MaxAge, bool Reschedule)
Deletes generated files older than MaxAge .
static void Init(string ContentRootFolder)
Initializes the PlantUML-Markdown integration.
static void SetPath(string JarPath, string JavaPath)
Sets the full path of PlantUML.
async Task< bool > RenderWpfXaml(WpfXamlRenderer Renderer, string[] Rows, string Language, int Indent, MarkdownDocument Document)
Generates WPF XAML for the code content.
async Task< bool > RenderXamarinFormsXaml(XamarinFormsXamlRenderer Renderer, string[] Rows, string Language, int Indent, MarkdownDocument Document)
Generates Xamarin.Forms XAML for the code content.
async Task< bool > RenderHtml(HtmlRenderer Renderer, string[] Rows, string Language, int Indent, MarkdownDocument Document)
Generates HTML for the code content.
static async Task Terminate()
Terminates PlantUML processing.
async Task< bool > RenderText(TextRenderer Renderer, string[] Rows, string Language, int Indent, MarkdownDocument Document)
Generates plain text for the code content.
bool EvaluatesScript
If script is evaluated for this type of code block.
async Task< bool > RenderLatex(LatexRenderer Renderer, string[] Rows, string Language, int Indent, MarkdownDocument Document)
Generates LaTeX for the code content.
static void SearchForInstallationFolder(out string JarPath, out string JavaPath)
Searches for the installation folder on the local machine.
void Register(MarkdownDocument Document)
Is called on the object when an instance of the element has been created in a document.
PlantUml()
Class managing PlantUML integration into Markdown documents.
async Task< bool > RenderContractXml(ContractsRenderer Renderer, string[] Rows, string Language, int Indent, MarkdownDocument Document)
Generates smart contract XML for the code content.
Renders HTML from a Markdown document.
Contains settings that the HTML export uses to customize HTML output.
Renders portable Markdown from a Markdown document.
Abstract base class for Markdown renderers.
readonly StringBuilder Output
Renderer output.
void Clear()
Clears the underlying StringBuilder.
override string ToString()
Returns the renderer output.
Renders plain text from a Markdown document.
Renders XAML (WPF flavour) from a Markdown document.
Renders XAML (Xamarin.Forms flavour) from a Markdown document.
Static class helping modules to find files installed on the system.
static string[] GetFolders(Environment.SpecialFolder[] Folders, params string[] AppendWith)
Gets the physical locations of special folders.
static string ExecutableExtension
Extension used by executable files on the platform.
static string FindLatestFile(Environment.SpecialFolder[] Folders, string Pattern, bool IncludeSubfolders)
Finds the latest file matching a search pattern, by searching in a set of folders,...
Helps with common XML-related tasks.
static string HtmlValueEncode(string s)
Differs from Encode(String), in that it does not encode the aposotrophe or the quote.
static string HtmlAttributeEncode(string s)
Differs from Encode(String), in that it does not encode the aposotrophe.
Static class managing the application event log. Applications and services log events on this static ...
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
static void Warning(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a warning event.
static void Error(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an error event.
static void Informational(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an informational event.
A chunked list is a linked list of chunks of objects of type T .
void AddRange(IEnumerable< T > Collection)
Adds a range of elements (last) to the list.
void Add(T Item)
Adds an item to the collection.
T[] ToArray()
Returns an array containing all elements of the collection.
static Task WriteAllTextAsync(string FileName, string Text)
Creates a text file asynchronously.
Static class that dynamically manages types and interfaces available in the runtime environment.
static bool TryGetModuleParameter(string Name, out object Value)
Tries to get a module parameter value.
Processes work tasks, in an asynchronous manner.
Class that can be used to schedule events in time. It uses a timer to execute tasks at the appointed ...
void Dispose()
IDisposable.Dispose
DateTime Add(DateTime When, Action< object > Callback, object State)
Adds an event.
Contains methods for simple hash calculations.
static string ComputeSHA256HashString(byte[] Data)
Computes the SHA-256 hash of a block of binary data.
Interface for code content contract renderers.
Interface for classes that help output asynchronous markdown output.
Task< string > GenerateStub(MarkdownOutputType Type, StringBuilder Output, string Title, MarkdownDocument Document)
Generates a stub in the output, that will be filled with the asynchronously generated content,...
Task ReportResult(MarkdownOutputType Type, string Id, string Result)
Method called when asynchronous result has been generated in a Markdown document.
Interface for code content LaTeX renderers.
Interface for all markdown handlers of code content that generates an image output.
Interface for code content HTML renderers.
Interface for code content Markdown renderers.
Interface for code content plain text renderers.
Interface for code content WPF XAML renderers.
Interface for code content Xamarin.Forms XAML renderers.
Interface for asynchronous operations.
ResultType
Image types supported by GraphViz
MarkdownOutputType
Markdown output type.