diff --git a/src/PAModel/PAConvert/Yaml/YamlWriter.cs b/src/PAModel/PAConvert/Yaml/YamlWriter.cs
index d2900e92..62a68247 100644
--- a/src/PAModel/PAConvert/Yaml/YamlWriter.cs
+++ b/src/PAModel/PAConvert/Yaml/YamlWriter.cs
@@ -1,192 +1,215 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
-using System;
using System.IO;
-namespace Microsoft.PowerPlatform.Formulas.Tools.Yaml
+namespace Microsoft.PowerPlatform.Formulas.Tools.Yaml;
+
+///
+/// Helper to write out a safe subset of Yaml.
+/// Notably, property values are always either multi-line escaped or
+/// prefixed with '=' to block yaml from treating it as a yaml expression.
+///
+public class YamlWriter : IDisposable
{
- ///
- /// Helper to write out a safe subset of Yaml.
- /// Notably, property values are always either multi-line escaped or
- /// prefixed with '=' to block yaml from treating it as a yaml expression.
- ///
- public class YamlWriter
- {
- private readonly TextWriter _text;
- private int _currentIndent;
+ private readonly TextWriter _textWriter;
+ private int _currentIndent;
+ private bool _isDisposed;
+ private const string Indent = " ";
- private const string Indent = " ";
+ public YamlWriter(TextWriter text)
+ {
+ _textWriter = text ?? throw new ArgumentNullException(nameof(text));
+ }
- public YamlWriter(TextWriter text)
- {
- _text = text;
- }
+ public void WriteStartObject(string propertyName)
+ {
+ WriteIndent();
- public void WriteStartObject(string propertyName)
- {
- WriteIndent();
+ bool needsEscape = propertyName.IndexOfAny(new char[] { '\"', '\'' }) != -1;
+ if (needsEscape)
+ propertyName = $"\"{propertyName.Replace("\"", "\\\"")}\"";
- bool needsEscape = propertyName.IndexOfAny(new char[] { '\"', '\'' }) != -1;
- if (needsEscape)
- propertyName = $"\"{propertyName.Replace("\"", "\\\"")}\"";
+ _textWriter.Write(propertyName);
+ _textWriter.WriteLine(":");
- _text.Write(propertyName);
- _text.WriteLine(":");
+ _currentIndent++;
+ }
- _currentIndent++;
+ public void WriteEndObject()
+ {
+ _currentIndent--;
+ if (_currentIndent < 0)
+ {
+ throw new InvalidOperationException("No matching start object");
}
- public void WriteEndObject()
+ }
+
+ public void WriteProperty(string propertyName, bool value, bool includeEquals = true)
+ {
+ WriteIndent();
+ _textWriter.Write(propertyName);
+ _textWriter.Write(": ");
+ if (includeEquals)
{
- _currentIndent--;
- if (_currentIndent < 0)
- {
- throw new InvalidOperationException("No matching start object");
- }
+ _textWriter.Write("=");
}
+ _textWriter.WriteLine(value ? "true" : "false");
+ }
- public void WriteProperty(string propertyName, bool value, bool includeEquals = true)
+ public void WriteProperty(string propertyName, int value, bool includeEquals = true)
+ {
+ WriteIndent();
+ _textWriter.Write(propertyName);
+ _textWriter.Write(": ");
+ if (includeEquals)
{
- WriteIndent();
- _text.Write(propertyName);
- _text.Write(": ");
- if (includeEquals)
- {
- _text.Write("=");
- }
- _text.WriteLine(value ? "true" : "false");
+ _textWriter.Write("=");
}
+ _textWriter.WriteLine(value);
+ }
- public void WriteProperty(string propertyName, int value, bool includeEquals = true)
+ public void WriteProperty(string propertyName, double value, bool includeEquals = true)
+ {
+ WriteIndent();
+ _textWriter.Write(propertyName);
+ _textWriter.Write(": ");
+ if (includeEquals)
{
- WriteIndent();
- _text.Write(propertyName);
- _text.Write(": ");
- if (includeEquals)
- {
- _text.Write("=");
- }
- _text.WriteLine(value);
+ _textWriter.Write("=");
}
+ _textWriter.WriteLine(value);
+ }
- public void WriteProperty(string propertyName, double value, bool includeEquals = true)
+ ///
+ /// Safely write a property. Based on the value, will chose whether single-line (and prefix with an '=')
+ /// or multi-line and pick the right the escape.
+ ///
+ ///
+ ///
+ public void WriteProperty(string propertyName, string value, bool includeEquals = true)
+ {
+ if (value == null)
{
WriteIndent();
- _text.Write(propertyName);
- _text.Write(": ");
- if (includeEquals)
- {
- _text.Write("=");
- }
- _text.WriteLine(value);
+ _textWriter.Write(propertyName);
+ _textWriter.WriteLine(":");
+ return;
}
- ///
- /// Safely write a property. Based on the value, will chose whether single-line (and prefix with an '=')
- /// or multi-line and pick the right the escape.
- ///
- ///
- ///
- public void WriteProperty(string propertyName, string value, bool includeEquals = true)
- {
- if (value == null)
- {
- WriteIndent();
- _text.Write(propertyName);
- _text.WriteLine(":");
- return;
- }
+ value = NormalizeNewlines(value);
- value = NormalizeNewlines(value);
+ bool isSingleLine = value.IndexOfAny(new char[] { '#', '\n', ':' }) == -1;
- bool isSingleLine = value.IndexOfAny(new char[] { '#', '\n', ':' }) == -1;
+ // For consistency, both single and multiline PA properties prefix with '='.
+ // Only single-line actually needs this - to avoid yaml's regular expression escaping.
+ if (includeEquals)
+ {
+ value = '=' + value;
+ }
- // For consistency, both single and multiline PA properties prefix with '='.
- // Only single-line actually needs this - to avoid yaml's regular expression escaping.
- if (includeEquals)
- {
- value = '=' + value;
- }
+ if (isSingleLine)
+ {
+ WriteIndent();
+ _textWriter.Write(propertyName);
+ _textWriter.Write(": ");
+ _textWriter.WriteLine(value);
+ }
+ else
+ {
+ WriteIndent();
+ _textWriter.Write(propertyName);
+ _textWriter.Write(": ");
- if (isSingleLine)
+ int numNewlines = 0;
+ for (int i = value.Length - 1; i > 0; i--)
{
- WriteIndent();
- _text.Write(propertyName);
- _text.Write(": ");
- _text.WriteLine(value);
- }
- else
- {
- WriteIndent();
- _text.Write(propertyName);
- _text.Write(": ");
-
- int numNewlines = 0;
- for (int i = value.Length - 1; i > 0; i--)
+ if (value[i] == '\n')
{
- if (value[i] == '\n')
- {
- numNewlines++;
- }
- else
- {
- break;
- }
+ numNewlines++;
}
- switch (numNewlines)
+ else
{
- case 0: _text.WriteLine("|-"); break;
- case 1: _text.WriteLine("|"); break;
- default: _text.WriteLine("|+"); break;
+ break;
}
+ }
+ switch (numNewlines)
+ {
+ case 0: _textWriter.WriteLine("|-"); break;
+ case 1: _textWriter.WriteLine("|"); break;
+ default: _textWriter.WriteLine("|+"); break;
+ }
- _currentIndent++;
+ _currentIndent++;
- bool needIndent = true;
- foreach (var ch in value)
+ bool needIndent = true;
+ foreach (var ch in value)
+ {
+ if (needIndent)
{
- if (needIndent)
- {
- WriteIndent();
- needIndent = false;
- }
-
- if (ch == '\n')
- {
- _text.WriteLine();
- needIndent = true;
- continue;
- }
- _text.Write(ch);
+ WriteIndent();
+ needIndent = false;
}
- if (numNewlines == 0)
+ if (ch == '\n')
{
- _text.WriteLine();
+ _textWriter.WriteLine();
+ needIndent = true;
+ continue;
}
+ _textWriter.Write(ch);
+ }
- _currentIndent--;
+ if (numNewlines == 0)
+ {
+ _textWriter.WriteLine();
}
+
+ _currentIndent--;
}
+ }
+
+ // Write a newline, for aesthics. since this is inbetween properties,
+ // it should get ignored on parse.
+ public void WriteNewline()
+ {
+ _textWriter.WriteLine();
+ }
- // Write a newline, for aesthics. since this is inbetween properties,
- // it should get ignored on parse.
- public void WriteNewline()
+ private void WriteIndent()
+ {
+ for (int i = 0; i < _currentIndent; i++)
{
- _text.WriteLine();
+ _textWriter.Write(Indent);
}
+ }
- private void WriteIndent()
+ private string NormalizeNewlines(string x)
+ {
+ return x.Replace("\r\n", "\n").Replace("\r", "\n");
+ }
+
+ #region IDisposable
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_isDisposed)
{
- for (int i = 0; i < _currentIndent; i++)
+ if (disposing)
{
- _text.Write(Indent);
+ _textWriter?.Dispose();
}
- }
- private string NormalizeNewlines(string x)
- {
- return x.Replace("\r\n", "\n").Replace("\r", "\n");
+ _isDisposed = true;
}
}
+
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
+ Dispose(disposing: true);
+ GC.SuppressFinalize(this);
+ }
+
+ #endregion
}