using System; using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Semmle.Extraction.CSharp { /// /// A `TextWriter` object that wraps another `TextWriter` object, and which /// HTML escapes the characters &, {, }, ", @, and #, before /// writing to the underlying object. /// public sealed class EscapingTextWriter : TextWriter { private readonly TextWriter wrapped; private readonly bool disposeUnderlying; public EscapingTextWriter(TextWriter wrapped, bool disposeUnderlying = false) { this.wrapped = wrapped; this.disposeUnderlying = disposeUnderlying; } /// /// Creates a new instance with a new underlying `StringWriter` object. The /// underlying object is disposed of when this object is. /// public EscapingTextWriter() : this(new StringWriter(), true) { } public EscapingTextWriter(IFormatProvider? formatProvider) : base(formatProvider) => throw new NotImplementedException(); private void WriteEscaped(char c) { switch (c) { case '&': wrapped.Write("&"); break; case '{': wrapped.Write("{"); break; case '}': wrapped.Write("}"); break; case '"': wrapped.Write("""); break; case '@': wrapped.Write("@"); break; case '#': wrapped.Write("#"); break; default: wrapped.Write(c); break; } } public void WriteSubId(IEntity entity) { if (entity is null) { wrapped.Write(""); return; } WriteUnescaped('{'); wrapped.WriteLabel(entity); WriteUnescaped('}'); } public void WriteUnescaped(char c) => wrapped.Write(c); public void WriteUnescaped(string s) => wrapped.Write(s); #region overrides public override Encoding Encoding => wrapped.Encoding; public override IFormatProvider FormatProvider => wrapped.FormatProvider; public override string NewLine { get => wrapped.NewLine; } public override void Close() => throw new NotImplementedException(); public override ValueTask DisposeAsync() => throw new NotImplementedException(); public override bool Equals(object? obj) => wrapped.Equals(obj) && obj is EscapingTextWriter other && disposeUnderlying == other.disposeUnderlying; public override void Flush() => wrapped.Flush(); public override Task FlushAsync() => wrapped.FlushAsync(); public override int GetHashCode() => HashCode.Combine(wrapped, disposeUnderlying); public override string ToString() => wrapped.ToString() ?? ""; public override void Write(bool value) => wrapped.Write(value); public override void Write(char value) => WriteEscaped(value); public override void Write(char[]? buffer) { if (buffer is null) return; Write(buffer, 0, buffer.Length); } public override void Write(char[] buffer, int index, int count) { for (var i = index; i < buffer.Length && i < index + count; i++) { WriteEscaped(buffer[i]); } } public override void Write(decimal value) => wrapped.Write(value); public override void Write(double value) => wrapped.Write(value); public override void Write(int value) => wrapped.Write(value); public override void Write(long value) => wrapped.Write(value); public override void Write(object? value) => Write(value?.ToString()); public override void Write(ReadOnlySpan buffer) { foreach (var c in buffer) { WriteEscaped(c); } } public override void Write(float value) => wrapped.Write(value); public override void Write(string? value) { if (value is null) { wrapped.Write(value); } else { foreach (var c in value) { WriteEscaped(c); } } } public override void Write(string format, object? arg0) => Write(string.Format(format, arg0)); public override void Write(string format, object? arg0, object? arg1) => Write(string.Format(format, arg0, arg1)); public override void Write(string format, object? arg0, object? arg1, object? arg2) => Write(string.Format(format, arg0, arg1, arg2)); public override void Write(string format, params object?[] arg) => Write(string.Format(format, arg)); public override void Write(StringBuilder? value) { if (value is null) { wrapped.Write(value); } else { for (var i = 0; i < value.Length; i++) { WriteEscaped(value[i]); } } } public override void Write(uint value) => wrapped.Write(value); public override void Write(ulong value) => wrapped.Write(value); public override Task WriteAsync(char value) => throw new NotImplementedException(); public override Task WriteAsync(char[] buffer, int index, int count) => throw new NotImplementedException(); public override Task WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) => throw new NotImplementedException(); public override Task WriteAsync(string? value) => throw new NotImplementedException(); public override Task WriteAsync(StringBuilder? value, CancellationToken cancellationToken = default) => throw new NotImplementedException(); public override void WriteLine() => wrapped.WriteLine(); public override void WriteLine(bool value) => wrapped.WriteLine(value); public override void WriteLine(char value) { Write(value); WriteLine(); } public override void WriteLine(char[]? buffer) { Write(buffer); WriteLine(); } public override void WriteLine(char[] buffer, int index, int count) { Write(buffer, index, count); WriteLine(); } public override void WriteLine(decimal value) => wrapped.WriteLine(value); public override void WriteLine(double value) => wrapped.WriteLine(value); public override void WriteLine(int value) => wrapped.WriteLine(value); public override void WriteLine(long value) => wrapped.WriteLine(value); public override void WriteLine(object? value) { Write(value); WriteLine(); } public override void WriteLine(ReadOnlySpan buffer) { Write(buffer); WriteLine(); } public override void WriteLine(float value) => wrapped.WriteLine(value); public override void WriteLine(string? value) { Write(value); WriteLine(); } public override void WriteLine(string format, object? arg0) { Write(format, arg0); WriteLine(); } public override void WriteLine(string format, object? arg0, object? arg1) { Write(format, arg0, arg1); WriteLine(); } public override void WriteLine(string format, object? arg0, object? arg1, object? arg2) { Write(format, arg0, arg1, arg2); WriteLine(); } public override void WriteLine(string format, params object?[] arg) { Write(format, arg); WriteLine(); } public override void WriteLine(StringBuilder? value) { Write(value); WriteLine(); } public override void WriteLine(uint value) => wrapped.WriteLine(value); public override void WriteLine(ulong value) => wrapped.WriteLine(value); public override Task WriteLineAsync() => throw new NotImplementedException(); public override Task WriteLineAsync(char value) => throw new NotImplementedException(); public override Task WriteLineAsync(char[] buffer, int index, int count) => throw new NotImplementedException(); public override Task WriteLineAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) => throw new NotImplementedException(); public override Task WriteLineAsync(string? value) => throw new NotImplementedException(); public override Task WriteLineAsync(StringBuilder? value, CancellationToken cancellationToken = default) => throw new NotImplementedException(); protected override void Dispose(bool disposing) { if (disposing && disposeUnderlying) wrapped.Dispose(); } #endregion overrides } }