// // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // #nullable disable using System; using System.Collections.Generic; using System.IO; using System.Text; using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts; using Microsoft.SqlTools.Utility; namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage { /// /// Abstract class for implementing writers that save results to file. Stores some basic info /// that all save as writer would need. /// public abstract class SaveAsStreamWriter : IFileStreamWriter { /// /// Stores the internal state for the writer that will be necessary for any writer. /// /// The stream that will be written to /// The SaveAs request parameters /// /// The entire list of columns for the result set. Used to determine which columns to /// output. /// protected SaveAsStreamWriter(Stream stream, SaveResultsRequestParams requestParams, IReadOnlyList columns) { Validate.IsNotNull(nameof(stream), stream); Validate.IsNotNull(nameof(columns), columns); FileStream = stream; if (requestParams.IsSaveSelection) { // ReSharper disable PossibleInvalidOperationException IsSaveSelection verifies these values exist ColumnStartIndex = requestParams.ColumnStartIndex.Value; ColumnEndIndex = requestParams.ColumnEndIndex.Value; // ReSharper restore PossibleInvalidOperationException } else { // Save request was for the entire result set, use default start/end ColumnStartIndex = 0; ColumnEndIndex = columns.Count - 1; } ColumnCount = ColumnEndIndex - ColumnStartIndex + 1; } #region Properties /// /// Index of the first column to write to the output file /// protected int ColumnStartIndex { get; } /// /// Number of columns to write to the output file /// protected int ColumnCount { get; } /// /// Index of the last column to write to the output file (inclusive). /// protected int ColumnEndIndex { get; } /// /// The file stream to use to write the output file /// protected Stream FileStream { get; } #endregion /// /// Not implemented, do not use. /// [Obsolete] public int WriteRow(StorageDataReader dataReader) { throw new InvalidOperationException("This type of writer is meant to write values from a list of cell values only."); } /// /// Indicates whether columns should be measured based on whether the output format supports it and if the caller wants the columns automatically sized /// public virtual bool ShouldMeasureRowColumns => false; /// /// Measures the columns in a row of data as part of determining automatic column widths /// /// The row of data to measure public virtual void MeasureRowColumns(IList row) { } /// /// Writes a row of data to the output file using the format provided by the implementing class. /// /// The row of data to output /// The list of columns to output public abstract void WriteRow(IList row, IReadOnlyList columns); /// /// Not implemented, do not use. /// [Obsolete] public void Seek(long offset) { throw new InvalidOperationException("SaveAs writers are meant to be written once contiguously."); } /// /// Flushes the file stream buffer /// public void FlushBuffer() { FileStream.Flush(); } /// /// Attempts to parse the provided and return an encoding that /// matches the encoding name or codepage number. /// /// Encoding name or codepage number to parse. /// /// Encoding to return if no encoding of provided name/codepage number exists. /// /// /// Desired encoding object or the if the desired /// encoding could not be found. /// protected static Encoding ParseEncoding(string encoding, Encoding fallbackEncoding) { // If the encoding is a number, we try to look up a codepage encoding using the // parsed number as a codepage. If it is not a number, attempt to look up an // encoding with the provided encoding name. If getting the encoding fails in // either case, we will return the fallback encoding. Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); try { return int.TryParse(encoding, out int codePage) ? Encoding.GetEncoding(codePage) : Encoding.GetEncoding(encoding); } catch { return fallbackEncoding; } } #region IDisposable Implementation private bool disposed; /// /// Disposes the instance by flushing and closing the file stream /// /// protected virtual void Dispose(bool disposing) { if (disposed) return; if (disposing) { FileStream.Dispose(); } disposed = true; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } #endregion } }