Skip to content

Commit 0a18df3

Browse files
authored
Merge pull request #1639 from Stivo182/feature/textwriter-stream-support
Поддержка потока в ЗаписьТекста
2 parents 50cfeb6 + 96cc743 commit 0a18df3

File tree

3 files changed

+444
-98
lines changed

3 files changed

+444
-98
lines changed

src/OneScript.Core/Exceptions/RuntimeException.cs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,22 +175,36 @@ public static RuntimeException InvalidEncoding(string encoding)
175175
return new RuntimeException(
176176
$"Неправильное имя кодировки '{encoding}'",
177177
$"Invalid encoding name '{encoding}'");
178-
}
179-
178+
}
179+
180180
public static RuntimeException IncorrectOffset()
181181
{
182182
return new RuntimeException(
183183
"Неправильное смещение внутри коллекции",
184184
"Incorrect offset within collection");
185-
}
186-
185+
}
186+
187187
public static RuntimeException IndexOutOfRange()
188188
{
189189
return new RuntimeException(
190190
"Значение индекса выходит за пределы диапазона",
191191
"Index is out of range");
192192
}
193193

194+
public static RuntimeException ClosedStream()
195+
{
196+
return new RuntimeException(
197+
"Ошибка обращения к закрытому потоку",
198+
"Cannot access a closed stream");
199+
}
200+
201+
public static RuntimeException NonWritableStream()
202+
{
203+
return new RuntimeException(
204+
"Попытка записи в поток не поддерживающий запись",
205+
"Cannot write to a stream that does not support writing");
206+
}
207+
194208
#endregion
195209
}
196210
}

src/OneScript.StandardLibrary/Text/TextWriteImpl.cs

Lines changed: 159 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ This Source Code Form is subject to the terms of the
1313
using OneScript.Execution;
1414
using OneScript.Types;
1515
using OneScript.Values;
16+
using OneScript.StandardLibrary.Binary;
1617
using ScriptEngine.Machine;
1718
using ScriptEngine.Machine.Contexts;
1819

@@ -22,52 +23,75 @@ namespace OneScript.StandardLibrary.Text
2223
public class TextWriteImpl : AutoContext<TextWriteImpl>, IDisposable
2324
{
2425
StreamWriter _writer;
26+
IStreamWrapper _streamWrapper;
27+
Encoding _encoding;
2528
string _lineDelimiter = "";
2629
string _eolReplacement = "";
2730

2831
public TextWriteImpl()
2932
{
3033

3134
}
32-
35+
3336
public TextWriteImpl(string path, IValue encoding)
3437
{
35-
Open(path, encoding);
38+
Open(ValueFactory.Create(path), encoding);
3639
}
3740

3841
public TextWriteImpl(string path, IValue encoding, bool append)
3942
{
40-
Open(path, encoding, null, append);
43+
Open(ValueFactory.Create(path), encoding, null, ValueFactory.Create(append));
4144
}
4245

46+
public TextWriteImpl(IValue stream, IValue encoding, IValue writeBom)
47+
{
48+
Open(stream, encoding, null, null, writeBom);
49+
}
50+
4351
/// <summary>
44-
/// Открывает файл для записи.
52+
/// Открывает файл или устанавливает поток для записи.
4553
/// </summary>
46-
/// <param name="path">Путь к файлу</param>
54+
/// <param name="fileOrStream">Имя файла или поток, в который будет выполнена запись.</param>
4755
/// <param name="encoding">Кодировка (необязательный). По умолчанию используется utf-8</param>
48-
/// <param name="lineDelimiter">Разделитель строк (необязательный).</param>
49-
/// <param name="append">Признак добавления в конец файла (необязательный)</param>
50-
/// <param name="eolReplacement">Разделитель строк в файле (необязательный).</param>
56+
/// <param name="lineDelimiter">
57+
/// Определяет строку, разделяющую строки в файле/потоке (необязательный).
58+
/// Значение по умолчанию: ПС.</param>
59+
/// <param name="param4">
60+
/// Для файла:
61+
/// Признак добавления в конец файла (необязательный).
62+
/// Значение по умолчанию: Ложь.
63+
/// Для потока:
64+
/// Определяет разделение строк в потоке для конвертации в стандартный перевод строк ПС (необязательный).
65+
/// Значение по умолчанию: ВК + ПС.</param>
66+
/// <param name="param5">
67+
/// Для файла:
68+
/// Определяет разделение строк в файле для конвертации в стандартный перевод строк ПС (необязательный).
69+
/// Значение по умолчанию: ВК + ПС.
70+
/// Для потока:
71+
/// Если в начало потока требуется записать метку порядка байтов (BOM) для используемой кодировки текста,
72+
/// то данный параметр должен иметь значение Истина.
73+
/// Значение по умолчанию: Ложь.</param>
5174
[ContextMethod("Открыть", "Open")]
52-
public void Open(string path, IValue encoding = null, string lineDelimiter = null, bool append = false, string eolReplacement = null)
75+
public void Open(IValue fileOrStream, IValue encoding = null, string lineDelimiter = null, IValue param4 = null, IValue param5 = null)
5376
{
54-
_lineDelimiter = lineDelimiter ?? "\n";
55-
_eolReplacement = eolReplacement ?? "\r\n";
56-
57-
Encoding enc;
58-
if (encoding == null)
77+
if (fileOrStream is IStreamWrapper streamWrapper)
5978
{
60-
enc = new UTF8Encoding(true);
79+
OpenStream(
80+
streamWrapper,
81+
encoding,
82+
lineDelimiter,
83+
ContextValuesMarshaller.ConvertValueStrict<string>(param4),
84+
ContextValuesMarshaller.ConvertValueStrict<bool>(param5));
6185
}
6286
else
6387
{
64-
enc = TextEncodingEnum.GetEncoding(encoding);
65-
if (enc.WebName == "utf-8" && append == true)
66-
enc = new UTF8Encoding(false);
88+
OpenFile(
89+
fileOrStream.ToString(),
90+
encoding,
91+
lineDelimiter,
92+
ContextValuesMarshaller.ConvertValueStrict<bool>(param4),
93+
ContextValuesMarshaller.ConvertValueStrict<string>(param5));
6794
}
68-
69-
_writer = new StreamWriter(path, append, enc);
70-
_writer.AutoFlush = true;
7195
}
7296

7397
[ContextMethod("Закрыть","Close")]
@@ -83,8 +107,8 @@ public void Close()
83107
[ContextMethod("Записать", "Write")]
84108
public void Write(string what)
85109
{
86-
ThrowIfNotOpened();
87-
110+
PrepareForWriting();
111+
88112
var stringToOutput = what.Replace ("\n", _eolReplacement);
89113

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

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

109-
Write (sDelimiter);
131+
Write(sDelimiter);
110132
}
111133

112134
public void ThrowIfNotOpened()
113135
{
114136
if (_writer == null)
115137
throw new RuntimeException("Файл не открыт");
116-
}
117-
138+
}
139+
118140
public void Dispose()
119141
{
120142
if (_writer != null)
121143
{
122144
_writer.Dispose();
123145
_writer = null;
124146
}
147+
148+
_streamWrapper = null;
149+
}
150+
151+
private void PrepareForWriting()
152+
{
153+
ThrowIfClosedStream();
154+
ThrowIfNonWritableStream();
155+
156+
if (_writer == null && _streamWrapper != null)
157+
{
158+
_writer = new StreamWriter(_streamWrapper.GetUnderlyingStream(), _encoding, -1, true);
159+
_writer.AutoFlush = true;
160+
}
161+
162+
ThrowIfNotOpened();
163+
}
164+
165+
private void OpenFile(string path, IValue encoding = null, string lineDelimiter = null, bool append = false, string eolReplacement = null)
166+
{
167+
Dispose();
168+
169+
_lineDelimiter = lineDelimiter ?? "\n";
170+
_eolReplacement = eolReplacement ?? "\r\n";
171+
_encoding = ResolveEncodingForFile(encoding, append);
172+
_streamWrapper = null;
173+
174+
_writer = new StreamWriter(path, append, _encoding);
175+
_writer.AutoFlush = true;
125176
}
126177

178+
private void OpenStream(IStreamWrapper streamWrapper, IValue encoding = null, string lineDelimiter = null, string eolReplacement = null, bool writeBom = false)
179+
{
180+
Dispose();
181+
182+
_lineDelimiter = lineDelimiter ?? "\n";
183+
_eolReplacement = eolReplacement ?? "\r\n";
184+
_encoding = ResolveEncodingForStream(encoding, writeBom);
185+
_streamWrapper = streamWrapper;
186+
}
187+
188+
private Encoding ResolveEncodingForFile(IValue encoding, bool append)
189+
{
190+
Encoding enc;
191+
if (encoding == null)
192+
{
193+
enc = new UTF8Encoding(true);
194+
}
195+
else
196+
{
197+
enc = TextEncodingEnum.GetEncoding(encoding);
198+
if (enc.WebName == "utf-8" && append == true)
199+
enc = new UTF8Encoding(false);
200+
}
201+
return enc;
202+
}
203+
204+
private Encoding ResolveEncodingForStream(IValue encoding, bool writeBom)
205+
{
206+
if (encoding == null)
207+
{
208+
return new UTF8Encoding(writeBom);
209+
}
210+
else
211+
{
212+
return TextEncodingEnum.GetEncoding(encoding, writeBom);
213+
}
214+
}
215+
216+
private void ThrowIfClosedStream()
217+
{
218+
if (_streamWrapper != null)
219+
{
220+
var stream = _streamWrapper.GetUnderlyingStream();
221+
if (stream is { CanWrite: false, CanRead: false })
222+
throw RuntimeException.ClosedStream();
223+
}
224+
}
225+
226+
private void ThrowIfNonWritableStream()
227+
{
228+
if (_streamWrapper != null && _streamWrapper.IsReadOnly)
229+
throw RuntimeException.NonWritableStream();
230+
}
231+
127232
/// <summary>
128-
/// Создает объект с начальными значениями имени файла и кодировки.
233+
/// Создает объект для записи текста в файл или поток.
129234
/// </summary>
130-
/// <param name="path">Имя файла</param>
131-
/// <param name="encoding">Кодировка в виде строки</param>
132-
/// <param name="lineDelimiter">Символ - разделитель строк</param>
133-
/// <param name="append">Признак добавления в конец файла (необязательный)</param>
134-
/// <param name="eolReplacement">Разделитель строк в файле (необязательный).</param>
235+
/// <param name="fileOrStream">Имя файла или поток, в который будет выполнена запись.</param>
236+
/// <param name="encoding">Кодировка (необязательный). По умолчанию используется utf-8</param>
237+
/// <param name="lineDelimiter">
238+
/// Определяет строку, разделяющую строки в файле/потоке (необязательный).
239+
/// Значение по умолчанию: ПС.</param>
240+
/// <param name="param4">
241+
/// Для файла:
242+
/// Признак добавления в конец файла (необязательный).
243+
/// Значение по умолчанию: Ложь.
244+
/// Для потока:
245+
/// Определяет разделение строк в потоке для конвертации в стандартный перевод строк ПС (необязательный).
246+
/// Значение по умолчанию: ВК + ПС.</param>
247+
/// <param name="param5">
248+
/// Для файла:
249+
/// Определяет разделение строк в файле для конвертации в стандартный перевод строк ПС (необязательный).
250+
/// Значение по умолчанию: ВК + ПС.
251+
/// Для потока:
252+
/// Если в начало потока требуется записать метку порядка байтов (BOM) для используемой кодировки текста,
253+
/// то данный параметр должен иметь значение Истина.
254+
/// Значение по умолчанию: Ложь.</param>
135255
[ScriptConstructor(Name = "По имени файла")]
136-
public static TextWriteImpl Constructor(string path, IValue encoding = null, string lineDelimiter = null, bool append = false, string eolReplacement = null)
256+
public static TextWriteImpl Constructor(IValue fileOrStream, IValue encoding = null, string lineDelimiter = null, IValue param4 = null, IValue param5 = null)
137257
{
138258
var result = new TextWriteImpl();
139-
140-
string sLineDelimiter = lineDelimiter ?? "\n";
141-
string sEolReplacement = eolReplacement ?? "\r\n";
142-
143-
result.Open (path, encoding, sLineDelimiter, append, sEolReplacement);
144-
259+
result.Open(fileOrStream, encoding, lineDelimiter, param4, param5);
145260
return result;
146261
}
147-
262+
148263
[ScriptConstructor(Name = "Формирование неинициализированного объекта")]
149264
public static TextWriteImpl Constructor()
150265
{
151266
return new TextWriteImpl();
152267
}
153268

154269
}
155-
}
270+
}

0 commit comments

Comments
 (0)