//
// 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
}
}