Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions src/OneScript.Core/Exceptions/RuntimeException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,22 +175,36 @@ public static RuntimeException InvalidEncoding(string encoding)
return new RuntimeException(
$"Неправильное имя кодировки '{encoding}'",
$"Invalid encoding name '{encoding}'");
}
}

public static RuntimeException IncorrectOffset()
{
return new RuntimeException(
"Неправильное смещение внутри коллекции",
"Incorrect offset within collection");
}
}

public static RuntimeException IndexOutOfRange()
{
return new RuntimeException(
"Значение индекса выходит за пределы диапазона",
"Index is out of range");
}

public static RuntimeException ClosedStream()
{
return new RuntimeException(
"Ошибка обращения к закрытому потоку",
"Cannot access a closed stream");
}

public static RuntimeException NonWritableStream()
{
return new RuntimeException(
"Попытка записи в поток не поддерживающий запись",
"Cannot write to a stream that does not support writing");
}

#endregion
}
}
203 changes: 159 additions & 44 deletions src/OneScript.StandardLibrary/Text/TextWriteImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ This Source Code Form is subject to the terms of the
using OneScript.Execution;
using OneScript.Types;
using OneScript.Values;
using OneScript.StandardLibrary.Binary;
using ScriptEngine.Machine;
using ScriptEngine.Machine.Contexts;

Expand All @@ -22,52 +23,75 @@ namespace OneScript.StandardLibrary.Text
public class TextWriteImpl : AutoContext<TextWriteImpl>, IDisposable
{
StreamWriter _writer;
IStreamWrapper _streamWrapper;
Encoding _encoding;
string _lineDelimiter = "";
string _eolReplacement = "";

public TextWriteImpl()
{

}

public TextWriteImpl(string path, IValue encoding)
{
Open(path, encoding);
Open(ValueFactory.Create(path), encoding);
}

public TextWriteImpl(string path, IValue encoding, bool append)
{
Open(path, encoding, null, append);
Open(ValueFactory.Create(path), encoding, null, ValueFactory.Create(append));
}

public TextWriteImpl(IValue stream, IValue encoding, IValue writeBom)
{
Open(stream, encoding, null, null, writeBom);
}

/// <summary>
/// Открывает файл для записи.
/// Открывает файл или устанавливает поток для записи.
/// </summary>
/// <param name="path">Путь к файлу</param>
/// <param name="fileOrStream">Имя файла или поток, в который будет выполнена запись.</param>
/// <param name="encoding">Кодировка (необязательный). По умолчанию используется utf-8</param>
/// <param name="lineDelimiter">Разделитель строк (необязательный).</param>
/// <param name="append">Признак добавления в конец файла (необязательный)</param>
/// <param name="eolReplacement">Разделитель строк в файле (необязательный).</param>
/// <param name="lineDelimiter">
/// Определяет строку, разделяющую строки в файле/потоке (необязательный).
/// Значение по умолчанию: ПС.</param>
/// <param name="param4">
/// Для файла:
/// Признак добавления в конец файла (необязательный).
/// Значение по умолчанию: Ложь.
/// Для потока:
/// Определяет разделение строк в потоке для конвертации в стандартный перевод строк ПС (необязательный).
/// Значение по умолчанию: ВК + ПС.</param>
/// <param name="param5">
/// Для файла:
/// Определяет разделение строк в файле для конвертации в стандартный перевод строк ПС (необязательный).
/// Значение по умолчанию: ВК + ПС.
/// Для потока:
/// Если в начало потока требуется записать метку порядка байтов (BOM) для используемой кодировки текста,
/// то данный параметр должен иметь значение Истина.
/// Значение по умолчанию: Ложь.</param>
[ContextMethod("Открыть", "Open")]
public void Open(string path, IValue encoding = null, string lineDelimiter = null, bool append = false, string eolReplacement = null)
public void Open(IValue fileOrStream, IValue encoding = null, string lineDelimiter = null, IValue param4 = null, IValue param5 = null)
{
_lineDelimiter = lineDelimiter ?? "\n";
_eolReplacement = eolReplacement ?? "\r\n";

Encoding enc;
if (encoding == null)
if (fileOrStream is IStreamWrapper streamWrapper)
{
enc = new UTF8Encoding(true);
OpenStream(
streamWrapper,
encoding,
lineDelimiter,
ContextValuesMarshaller.ConvertValueStrict<string>(param4),
ContextValuesMarshaller.ConvertValueStrict<bool>(param5));
}
else
{
enc = TextEncodingEnum.GetEncoding(encoding);
if (enc.WebName == "utf-8" && append == true)
enc = new UTF8Encoding(false);
OpenFile(
fileOrStream.ToString(),
encoding,
lineDelimiter,
ContextValuesMarshaller.ConvertValueStrict<bool>(param4),
ContextValuesMarshaller.ConvertValueStrict<string>(param5));
}

_writer = new StreamWriter(path, append, enc);
_writer.AutoFlush = true;
}

[ContextMethod("Закрыть","Close")]
Expand All @@ -83,8 +107,8 @@ public void Close()
[ContextMethod("Записать", "Write")]
public void Write(string what)
{
ThrowIfNotOpened();

PrepareForWriting();
var stringToOutput = what.Replace ("\n", _eolReplacement);

_writer.Write(stringToOutput);
Expand All @@ -98,58 +122,149 @@ public void Write(string what)
[ContextMethod("ЗаписатьСтроку", "WriteLine")]
public void WriteLine(IBslProcess process, string what, BslValue delimiter = null)
{
ThrowIfNotOpened();

Write (what);
Write(what);

var sDelimiter = _lineDelimiter;
if (delimiter != null && delimiter.SystemType != BasicTypes.Undefined)
sDelimiter = delimiter.ToString(process);

Write (sDelimiter);
Write(sDelimiter);
}

public void ThrowIfNotOpened()
{
if (_writer == null)
throw new RuntimeException("Файл не открыт");
}

}
public void Dispose()
{
if (_writer != null)
{
_writer.Dispose();
_writer = null;
}

_streamWrapper = null;
}

private void PrepareForWriting()
{
ThrowIfClosedStream();
ThrowIfNonWritableStream();

if (_writer == null && _streamWrapper != null)
{
_writer = new StreamWriter(_streamWrapper.GetUnderlyingStream(), _encoding, -1, true);
_writer.AutoFlush = true;
}

ThrowIfNotOpened();
}

private void OpenFile(string path, IValue encoding = null, string lineDelimiter = null, bool append = false, string eolReplacement = null)
{
Dispose();

_lineDelimiter = lineDelimiter ?? "\n";
_eolReplacement = eolReplacement ?? "\r\n";
_encoding = ResolveEncodingForFile(encoding, append);
_streamWrapper = null;

_writer = new StreamWriter(path, append, _encoding);
_writer.AutoFlush = true;
}

private void OpenStream(IStreamWrapper streamWrapper, IValue encoding = null, string lineDelimiter = null, string eolReplacement = null, bool writeBom = false)
{
Dispose();

_lineDelimiter = lineDelimiter ?? "\n";
_eolReplacement = eolReplacement ?? "\r\n";
_encoding = ResolveEncodingForStream(encoding, writeBom);
_streamWrapper = streamWrapper;
}

private Encoding ResolveEncodingForFile(IValue encoding, bool append)
{
Encoding enc;
if (encoding == null)
{
enc = new UTF8Encoding(true);
}
else
{
enc = TextEncodingEnum.GetEncoding(encoding);
if (enc.WebName == "utf-8" && append == true)
enc = new UTF8Encoding(false);
}
return enc;
}

private Encoding ResolveEncodingForStream(IValue encoding, bool writeBom)
{
if (encoding == null)
{
return new UTF8Encoding(writeBom);
}
else
{
return TextEncodingEnum.GetEncoding(encoding, writeBom);
}
}

private void ThrowIfClosedStream()
{
if (_streamWrapper != null)
{
var stream = _streamWrapper.GetUnderlyingStream();
if (stream is { CanWrite: false, CanRead: false })
throw RuntimeException.ClosedStream();
}
}

private void ThrowIfNonWritableStream()
{
if (_streamWrapper != null && _streamWrapper.IsReadOnly)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

На днях починили поток только для чтения, чтобы сам выбрасывал исключения af962b1

Наверное имеет смысл обновиться с develop и убрать эту проверку, чтобы не дублировать тексты исключения

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Без этого возникнет исключение с родным сообщением от .NET, поскольку запись будет происходить напрямую в Stream из StreamWriter. А с этим подходом текст исключения будет таким же, как в 1С.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не очень понял... Мы используем тут не родной стрим .net, а наш враппер стримов. В коммите af962b1 сделана проверка на ReadOnly в наших стримах с текстом исключения, как в 1С.

Или я не прав?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

На данный момент просто реализовано так, что запись (Записать, ЗаписатьСтроку) идёт не через врапер потоков, а через StreamWriter, который работает напрямую с потоком .NET из врапера. Поэтому исключения будут дотнетовские.

throw RuntimeException.NonWritableStream();
}

/// <summary>
/// Создает объект с начальными значениями имени файла и кодировки.
/// Создает объект для записи текста в файл или поток.
/// </summary>
/// <param name="path">Имя файла</param>
/// <param name="encoding">Кодировка в виде строки</param>
/// <param name="lineDelimiter">Символ - разделитель строк</param>
/// <param name="append">Признак добавления в конец файла (необязательный)</param>
/// <param name="eolReplacement">Разделитель строк в файле (необязательный).</param>
/// <param name="fileOrStream">Имя файла или поток, в который будет выполнена запись.</param>
/// <param name="encoding">Кодировка (необязательный). По умолчанию используется utf-8</param>
/// <param name="lineDelimiter">
/// Определяет строку, разделяющую строки в файле/потоке (необязательный).
/// Значение по умолчанию: ПС.</param>
/// <param name="param4">
/// Для файла:
/// Признак добавления в конец файла (необязательный).
/// Значение по умолчанию: Ложь.
/// Для потока:
/// Определяет разделение строк в потоке для конвертации в стандартный перевод строк ПС (необязательный).
/// Значение по умолчанию: ВК + ПС.</param>
/// <param name="param5">
/// Для файла:
/// Определяет разделение строк в файле для конвертации в стандартный перевод строк ПС (необязательный).
/// Значение по умолчанию: ВК + ПС.
/// Для потока:
/// Если в начало потока требуется записать метку порядка байтов (BOM) для используемой кодировки текста,
/// то данный параметр должен иметь значение Истина.
/// Значение по умолчанию: Ложь.</param>
[ScriptConstructor(Name = "По имени файла")]
public static TextWriteImpl Constructor(string path, IValue encoding = null, string lineDelimiter = null, bool append = false, string eolReplacement = null)
public static TextWriteImpl Constructor(IValue fileOrStream, IValue encoding = null, string lineDelimiter = null, IValue param4 = null, IValue param5 = null)
{
var result = new TextWriteImpl();

string sLineDelimiter = lineDelimiter ?? "\n";
string sEolReplacement = eolReplacement ?? "\r\n";

result.Open (path, encoding, sLineDelimiter, append, sEolReplacement);

result.Open(fileOrStream, encoding, lineDelimiter, param4, param5);
return result;
}

[ScriptConstructor(Name = "Формирование неинициализированного объекта")]
public static TextWriteImpl Constructor()
{
return new TextWriteImpl();
}

}
}
}
Loading