summaryrefslogtreecommitdiffstats
path: root/wizards/source/scriptforge/SF_TextStream.xba
diff options
context:
space:
mode:
Diffstat (limited to 'wizards/source/scriptforge/SF_TextStream.xba')
-rw-r--r--wizards/source/scriptforge/SF_TextStream.xba702
1 files changed, 702 insertions, 0 deletions
diff --git a/wizards/source/scriptforge/SF_TextStream.xba b/wizards/source/scriptforge/SF_TextStream.xba
new file mode 100644
index 000000000..35f1b6fb2
--- /dev/null
+++ b/wizards/source/scriptforge/SF_TextStream.xba
@@ -0,0 +1,702 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
+<script:module xmlns:script="http://openoffice.org/2000/script" script:name="SF_TextStream" script:language="StarBasic" script:moduleType="normal">REM =======================================================================================================================
+REM === The ScriptForge library and its associated libraries are part of the LibreOffice project. ===
+REM === Full documentation is available on https://help.libreoffice.org/ ===
+REM =======================================================================================================================
+
+Option Compatible
+Option ClassModule
+
+Option Explicit
+
+&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
+&apos;&apos;&apos; SF_TextStream
+&apos;&apos;&apos; =============
+&apos;&apos;&apos; Class instantiated by the
+&apos;&apos;&apos; SF_FileSystem.CreateTextFile
+&apos;&apos;&apos; SF_FileSystem.OpenTextFile
+&apos;&apos;&apos; methods to facilitate the sequential processing of text files
+&apos;&apos;&apos; All open/read/write/close operations are presumed to happen during the same macro run
+&apos;&apos;&apos; The encoding to be used may be chosen by the user
+&apos;&apos;&apos; The list is in the Name column of https://www.iana.org/assignments/character-sets/character-sets.xhtml
+&apos;&apos;&apos; Note that probably not all values are available
+&apos;&apos;&apos; Line delimiters may be chosen by the user
+&apos;&apos;&apos; In input, CR, LF or CR+LF are supported
+&apos;&apos;&apos; In output, the default value is the usual newline on the actual operating system (see SF_FileSystem.sfNEWLINE)
+&apos;&apos;&apos;
+&apos;&apos;&apos; The design choices are largely inspired by
+&apos;&apos;&apos; https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/textstream-object
+&apos;&apos;&apos; The implementation is mainly based on the XTextInputStream and XTextOutputStream UNO interfaces
+&apos;&apos;&apos; https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1io_1_1XTextInputStream.html
+&apos;&apos;&apos; https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1io_1_1XTextOutputStream.html
+&apos;&apos;&apos;
+&apos;&apos;&apos; Instantiation example:
+&apos;&apos;&apos; Dim FSO As Object, myFile As Object
+&apos;&apos;&apos; Set FSO = CreateScriptService(&quot;FileSystem&quot;)
+&apos;&apos;&apos; Set myFile = FSO.OpenTextFile(&quot;C:\Temp\ThisFile.txt&quot;, FSO.ForReading) &apos; Once per file
+&apos;&apos;&apos;
+&apos;&apos;&apos; Detailed user documentation:
+&apos;&apos;&apos; https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03/sf_textstream.html?DbPAR=BASIC
+&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
+
+REM ================================================================== EXCEPTIONS
+
+Const FILENOTOPENERROR = &quot;FILENOTOPENERROR&quot; &apos; The file is already closed
+Const FILEOPENMODEERROR = &quot;FILEOPENMODEERROR&quot; &apos; The file is open in incompatible mode
+Const ENDOFFILEERROR = &quot;ENDOFFILEERROR&quot; &apos; When file was read, an end-of-file was encountered
+
+REM ============================================================= PRIVATE MEMBERS
+
+Private [Me] As Object
+Private [_Parent] As Object
+Private ObjectType As String &apos; Must be TEXTSTREAM
+Private ServiceName As String
+Private _FileName As String &apos; File where it is about
+Private _IOMode As Integer &apos; ForReading, ForWriting or ForAppending
+Private _Encoding As String &apos; https://www.iana.org/assignments/character-sets/character-sets.xhtml
+Private _NewLine As String &apos; Line break in write mode
+Private _FileExists As Boolean &apos; True if file exists before open
+Private _LineNumber As Long &apos; Number of lines read or written
+Private _FileHandler As Object &apos; com.sun.star.io.XInputStream or
+ &apos; com.sun.star.io.XOutputStream or
+ &apos; com.sun.star.io.XStream
+Private _InputStream As Object &apos; com.sun.star.io.TextInputStream
+Private _OutputStream As Object &apos; com.sun.star.io.TextOutputStream
+Private _ForceBlankLine As Boolean &apos; Workaround: XTextInputStream misses last line if file ends with newline
+
+REM ============================================================ MODULE CONSTANTS
+
+REM ===================================================== CONSTRUCTOR/DESTRUCTOR
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Initialize()
+ Set [Me] = Nothing
+ Set [_Parent] = Nothing
+ ObjectType = &quot;TEXTSTREAM&quot;
+ ServiceName = &quot;ScriptForge.TextStream&quot;
+ _FileName = &quot;&quot;
+ _IOMode = -1
+ _Encoding = &quot;&quot;
+ _NewLine = &quot;&quot;
+ _FileExists = False
+ _LineNumber = 0
+ Set _FileHandler = Nothing
+ Set _InputStream = Nothing
+ Set _OutputStream = Nothing
+ _ForceBlankLine = False
+End Sub &apos; ScriptForge.SF_TextStream Constructor
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Terminate()
+ Call Class_Initialize()
+End Sub &apos; ScriptForge.SF_TextStream Destructor
+
+REM -----------------------------------------------------------------------------
+Public Function Dispose() As Variant
+ Call Class_Terminate()
+ Set Dispose = Nothing
+End Function &apos; ScriptForge.SF_TextStream Explicit Destructor
+
+REM ================================================================== PROPERTIES
+
+REM -----------------------------------------------------------------------------
+Property Get AtEndOfStream() As Boolean
+&apos;&apos;&apos; In reading mode, True indicates that the end of the file has been reached
+&apos;&apos;&apos; In write and append modes, or if the file is not ready =&gt; always True
+&apos;&apos;&apos; The property should be invoked BEFORE each ReadLine() method:
+&apos;&apos;&apos; A ReadLine() executed while AtEndOfStream is True will raise an error
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; Dim sLine As String
+&apos;&apos;&apos; Do While Not myFile.AtEndOfStream
+&apos;&apos;&apos; sLine = myFile.ReadLine()
+&apos;&apos;&apos; &apos; ...
+&apos;&apos;&apos; Loop
+
+ AtEndOfStream = _PropertyGet(&quot;AtEndOfStream&quot;)
+
+End Property &apos; ScriptForge.SF_TextStream.AtEndOfStream
+
+REM -----------------------------------------------------------------------------
+Property Get Encoding() As String
+&apos;&apos;&apos; Returns the name of the text file either in url or in native operating system format
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; Dim myFile As Object
+&apos;&apos;&apos; FSO.FileNaming = &quot;SYS&quot;
+&apos;&apos;&apos; Set myFile = FSO.OpenTextFile(&quot;C:\Temp\myFile.txt&quot;)
+&apos;&apos;&apos; MsgBox myFile.Encoding &apos; UTF-8
+
+ Encoding = _PropertyGet(&quot;Encoding&quot;)
+
+End Property &apos; ScriptForge.SF_TextStream.Encoding
+
+REM -----------------------------------------------------------------------------
+Property Get FileName() As String
+&apos;&apos;&apos; Returns the name of the text file either in url or in native operating system format
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; Dim myFile As Object
+&apos;&apos;&apos; FSO.FileNaming = &quot;SYS&quot;
+&apos;&apos;&apos; Set myFile = FSO.OpenTextFile(&quot;C:\Temp\myFile.txt&quot;)
+&apos;&apos;&apos; MsgBox myFile.FileName &apos; C:\Temp\myFile.txt
+
+ FileName = _PropertyGet(&quot;FileName&quot;)
+
+End Property &apos; ScriptForge.SF_TextStream.FileName
+
+REM -----------------------------------------------------------------------------
+Property Get IOMode() As String
+&apos;&apos;&apos; Returns either &quot;READ&quot;, &quot;WRITE&quot; or &quot;APPEND&quot;
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; Dim myFile As Object
+&apos;&apos;&apos; FSO.FileNaming = &quot;SYS&quot;
+&apos;&apos;&apos; Set myFile = FSO.OpenTextFile(&quot;C:\Temp\myFile.txt&quot;)
+&apos;&apos;&apos; MsgBox myFile.IOMode &apos; READ
+
+ IOMode = _PropertyGet(&quot;IOMode&quot;)
+
+End Property &apos; ScriptForge.SF_TextStream.IOMode
+
+REM -----------------------------------------------------------------------------
+Property Get Line() As Long
+&apos;&apos;&apos; Returns the number of lines read or written so far
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; Dim myFile As Object
+&apos;&apos;&apos; FSO.FileNaming = &quot;SYS&quot;
+&apos;&apos;&apos; Set myFile = FSO.OpenTextFile(&quot;C:\Temp\myFile.txt&quot;, FSO.ForAppending)
+&apos;&apos;&apos; MsgBox myFile.Line &apos; The number of lines already present in myFile
+
+ Line = _PropertyGet(&quot;Line&quot;)
+
+End Property &apos; ScriptForge.SF_TextStream.Line
+
+REM -----------------------------------------------------------------------------
+Property Get NewLine() As Variant
+&apos;&apos;&apos; Returns the current character string to be inserted between 2 successive written lines
+&apos;&apos;&apos; The default value is the native line separator in the current operating system
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; MsgBox myFile.NewLine
+
+ NewLine = _PropertyGet(&quot;NewLine&quot;)
+
+End Property &apos; ScriptForge.SF_TextStream.NewLine (get)
+
+REM -----------------------------------------------------------------------------
+Property Let NewLine(ByVal pvLineBreak As Variant)
+&apos;&apos;&apos; Sets the current character string to be inserted between 2 successive written lines
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; myFile.NewLine = Chr(13) &amp; Chr(10)
+
+Const cstThisSub = &quot;TextStream.setNewLine&quot;
+
+ SF_Utils._EnterFunction(cstThisSub)
+ If VarType(pvLineBreak) = V_STRING Then _NewLine = pvLineBreak
+ SF_Utils._ExitFunction(cstThisSub)
+
+End Property &apos; ScriptForge.SF_TextStream.NewLine (let)
+
+REM ===================================================================== METHODS
+
+REM -----------------------------------------------------------------------------
+Public Function CloseFile() As Boolean
+&apos;&apos;&apos; Empties the output buffer if relevant. Closes the actual input or output stream
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True if the closure was successful
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; FILENOTOPENERROR Nothing found to close
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; myFile.CloseFile()
+
+Dim bClose As Boolean &apos; Return value
+Const cstThisSub = &quot;TextStream.CloseFile&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bClose = False
+
+Check:
+ SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+ If Not _IsFileOpen() Then GoTo Finally
+
+Try:
+ If Not IsNull(_InputStream) Then _InputStream.closeInput()
+ If Not IsNull(_OutputStream) Then
+ _OutputStream.flush()
+ _OutputStream.closeOutput()
+ End If
+ Set _InputStream = Nothing
+ Set _OutputStream = Nothing
+ Set _FileHandler = Nothing
+ bClose = True
+
+Finally:
+ CloseFile = bClose
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; ScriptForge.SF_TextStream.CloseFile
+
+REM -----------------------------------------------------------------------------
+Public Function GetProperty(Optional ByVal PropertyName As Variant) As Variant
+&apos;&apos;&apos; Return the actual value of the given property
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; PropertyName: the name of the property as a string
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The actual value of the property
+&apos;&apos;&apos; If the property does not exist, returns Null
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; see the exceptions of the individual properties
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; myModel.GetProperty(&quot;MyProperty&quot;)
+
+Const cstThisSub = &quot;TextStream.GetProperty&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ GetProperty = Null
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._Validate(PropertyName, &quot;PropertyName&quot;, V_STRING, Properties()) Then GoTo Catch
+ End If
+
+Try:
+ GetProperty = _PropertyGet(PropertyName)
+
+Finally:
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; ScriptForge.SF_TextStream.GetProperty
+
+REM -----------------------------------------------------------------------------
+Public Function Methods() As Variant
+&apos;&apos;&apos; Return the list of public methods of the Model service as an array
+
+ Methods = Array( _
+ &quot;CloseFile&quot; _
+ , &quot;ReadAll&quot; _
+ , &quot;readLine&quot; _
+ , &quot;SkipLine&quot; _
+ , &quot;WriteBlankLines&quot; _
+ , &quot;WriteLine&quot; _
+ )
+
+End Function &apos; ScriptForge.SF_TextStream.Methods
+
+REM -----------------------------------------------------------------------------
+Public Function Properties() As Variant
+&apos;&apos;&apos; Return the list or properties of the Timer class as an array
+
+ Properties = Array( _
+ &quot;AtEndOfStream&quot; _
+ , &quot;Encoding&quot; _
+ , &quot;FileName&quot; _
+ , &quot;IOMode&quot; _
+ , &quot;Line&quot; _
+ , &quot;NewLine&quot; _
+ )
+
+End Function &apos; ScriptForge.SF_TextStream.Properties
+
+REM -----------------------------------------------------------------------------
+Public Function ReadAll() As String
+&apos;&apos;&apos; Returns all the remaining lines in the text stream as one string. Line breaks are NOT removed
+&apos;&apos;&apos; The resulting string can be split in lines
+&apos;&apos;&apos; either by using the usual Split Basic builtin function if the line delimiter is known
+&apos;&apos;&apos; or with the SF_String.SplitLines method
+&apos;&apos;&apos; For large files, using the ReadAll method wastes memory resources.
+&apos;&apos;&apos; Other techniques should be used to input a file, such as reading a file line-by-line
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The read lines. The string may be empty.
+&apos;&apos;&apos; Note that the Line property in incremented only by 1
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; FILENOTOPENERROR File not open or already closed
+&apos;&apos;&apos; FILEOPENMODEERROR File opened in write or append modes
+&apos;&apos;&apos; ENDOFFILEERROR Previous reads already reached the end of the file
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; Dim a As String
+&apos;&apos;&apos; a = myFile.ReadAll()
+
+Dim sRead As String &apos; Return value
+Const cstThisSub = &quot;TextStream.ReadAll&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sRead = &quot;&quot;
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsFileOpen(&quot;READ&quot;) Then GoTo Finally
+ If _InputStream.isEOF() Then GoTo CatchEOF
+ End If
+
+Try:
+ sRead = _InputStream.readString(Array(), False)
+ _LineNumber = _LineNumber + 1
+
+Finally:
+ ReadAll = sRead
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchEOF:
+ SF_Exception.RaiseFatal(ENDOFFILEERROR, FileName)
+ GoTo Finally
+End Function &apos; ScriptForge.SF_TextStream.ReadAll
+
+REM -----------------------------------------------------------------------------
+Public Function ReadLine() As String
+&apos;&apos;&apos; Returns the next line in the text stream as a string. Line breaks are removed.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The read line. The string may be empty.
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; FILENOTOPENERROR File not open or already closed
+&apos;&apos;&apos; FILEOPENMODEERROR File opened in write or append modes
+&apos;&apos;&apos; ENDOFFILEERROR Previous reads already reached the end of the file
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; Dim a As String
+&apos;&apos;&apos; a = myFile.ReadLine()
+
+Dim sRead As String &apos; Return value
+Dim iRead As Integer &apos; Length of line break
+Const cstThisSub = &quot;TextStream.ReadLine&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sRead = &quot;&quot;
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsFileOpen(&quot;READ&quot;) Then GoTo Finally
+ If AtEndOfStream Then GoTo CatchEOF
+ End If
+
+Try:
+ &apos; When the text file ends with a line break,
+ &apos; XTextInputStream.readLine() returns the line break together with the last line
+ &apos; Hence the workaround to force a blank line at the end
+ If _ForceBlankLine Then
+ sRead = &quot;&quot;
+ _ForceBlankLine = False
+ Else
+ sRead = _InputStream.readLine()
+ &apos; The isEOF() is set immediately after having read the last line
+ If _InputStream.isEOF() And Len(sRead) &gt; 0 Then
+ iRead = 0
+ If SF_String.EndsWith(sRead, SF_String.sfCRLF) Then
+ iRead = 2
+ ElseIf SF_String.EndsWith(sRead, SF_String.sfLF) Or SF_String.EndsWith(sRead, SF_String.sfCR) Then
+ iRead = 1
+ End If
+ If iRead &gt; 0 Then
+ sRead = Left(sRead, Len(sRead) - iRead)
+ _ForceBlankLine = True &apos; Provision for a last empty line at the next read loop
+ End If
+ End If
+ End If
+ _LineNumber = _LineNumber + 1
+
+Finally:
+ ReadLine = sRead
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchEOF:
+ SF_Exception.RaiseFatal(ENDOFFILEERROR, FileName)
+ GoTo Finally
+End Function &apos; ScriptForge.SF_TextStream.ReadLine
+
+REM -----------------------------------------------------------------------------
+Public Function SetProperty(Optional ByVal PropertyName As Variant _
+ , Optional ByRef Value As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Set a new value to the given property
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; PropertyName: the name of the property as a string
+&apos;&apos;&apos; Value: its new value
+&apos;&apos;&apos; Exceptions
+&apos;&apos;&apos; ARGUMENTERROR The property does not exist
+
+Dim bSet As Boolean &apos; Return value
+Const cstThisSub = &quot;TextStream.SetProperty&quot;
+Const cstSubArgs = &quot;PropertyName, Value&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bSet = False
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._Validate(PropertyName, &quot;PropertyName&quot;, V_STRING, Properties()) Then GoTo Catch
+ End If
+
+Try:
+ bSet = True
+ Select Case UCase(PropertyName)
+ Case &quot;NEWLINE&quot;
+ If Not SF_Utils._Validate(Value, &quot;Value&quot;, V_STRING) Then GoTo Catch
+ NewLine = Value
+ Case Else
+ bSet = False
+ End Select
+
+Finally:
+ SetProperty = bSet
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; ScriptForge.SF_TextStream.SetProperty
+
+REM -----------------------------------------------------------------------------
+Public Sub SkipLine()
+&apos;&apos;&apos; Skips the next line when reading a TextStream file.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; FILENOTOPENERROR File not open or already closed
+&apos;&apos;&apos; FILEOPENMODEERROR File opened in write or append modes
+&apos;&apos;&apos; ENDOFFILEERROR Previous reads already reached the end of the file
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; myFile.SkipLine()
+
+Dim sRead As String &apos; Read buffer
+Const cstThisSub = &quot;TextStream.SkipLine&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsFileOpen(&quot;READ&quot;) Then GoTo Finally
+ If Not _ForceBlankLine Then &apos; The file ends with a newline =&gt; return one empty line more
+ If _InputStream.isEOF() Then GoTo CatchEOF
+ End If
+ End If
+
+Try:
+ sRead = ReadLine()
+
+Finally:
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Sub
+Catch:
+ GoTo Finally
+CatchEOF:
+ SF_Exception.RaiseFatal(ENDOFFILEERROR, FileName)
+ GoTo Finally
+End Sub &apos; ScriptForge.SF_TextStream.SkipLine
+
+REM -----------------------------------------------------------------------------
+Public Sub WriteBlankLines(Optional ByVal Lines As Variant)
+&apos;&apos;&apos; Writes a number of empty lines in the output stream
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Lines: the number of lines to write
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; FILENOTOPENERROR File not open or already closed
+&apos;&apos;&apos; FILEOPENMODEERROR File opened in read mode
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; myFile.WriteBlankLines(10)
+Dim i As Long
+Const cstThisSub = &quot;TextStream.WriteBlankLines&quot;
+Const cstSubArgs = &quot;Lines&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsFileOpen(&quot;WRITE&quot;) Then GoTo Finally
+ If Not SF_Utils._Validate(Lines, &quot;Lines&quot;, V_NUMERIC) Then GoTo Finally
+ End If
+
+Try:
+ For i = 1 To Lines
+ _OutputStream.writeString(_NewLine)
+ Next i
+ _LineNumber = _LineNumber + Lines
+
+Finally:
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Sub
+Catch:
+ GoTo Finally
+End Sub &apos; ScriptForge.SF_TextStream.WriteBlankLines
+
+REM -----------------------------------------------------------------------------
+Public Sub WriteLine(Optional ByVal Line As Variant)
+&apos;&apos;&apos; Writes the given line to the output stream. A newline is inserted if relevant
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Line: the line to write, may be empty
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; FILENOTOPENERROR File not open or already closed
+&apos;&apos;&apos; FILEOPENMODEERROR File opened in in read mode
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; myFile.WriteLine(&quot;Next line&quot;)
+Dim i As Long
+Const cstThisSub = &quot;TextStream.WriteLine&quot;
+Const cstSubArgs = &quot;Line&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsFileOpen(&quot;WRITE&quot;) Then GoTo Finally
+ If Not SF_Utils._Validate(Line, &quot;Line&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ _OutputStream.writeString(Iif(_LineNumber &gt; 0, _NewLine, &quot;&quot;) &amp; Line)
+ _LineNumber = _LineNumber + 1
+
+Finally:
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Sub
+Catch:
+ GoTo Finally
+End Sub &apos; ScriptForge.SF_TextStream.WriteLine
+
+REM =========================================================== PRIVATE FUNCTIONS
+
+REM -----------------------------------------------------------------------------
+Public Sub _Initialize()
+&apos;&apos;&apos; Opens file and setup input and/or output streams (ForAppending requires both)
+
+Dim oSfa As Object &apos; com.sun.star.ucb.SimpleFileAccess
+
+ &apos; Default newline related to current operating system
+ _NewLine = SF_String.sfNEWLINE
+
+ Set oSfa = SF_Utils._GetUNOService(&quot;FileAccess&quot;)
+
+ &apos; Setup input and/or output streams based on READ/WRITE/APPEND IO modes
+ Select Case _IOMode
+ Case SF_FileSystem.ForReading
+ Set _FileHandler = oSfa.openFileRead(_FileName)
+ Set _InputStream = CreateUnoService(&quot;com.sun.star.io.TextInputStream&quot;)
+ _InputStream.setInputStream(_FileHandler)
+ Case SF_FileSystem.ForWriting
+ &apos; Output file is deleted beforehand
+ If _FileExists Then oSfa.kill(_FileName)
+ Set _FileHandler = oSfa.openFileWrite(_FileName)
+ Set _OutputStream = CreateUnoService(&quot;com.sun.star.io.TextOutputStream&quot;)
+ _OutputStream.setOutputStream(_FileHandler)
+ Case SF_FileSystem.ForAppending
+ Set _FileHandler = oSfa.openFileReadWrite(_FileName)
+ Set _InputStream = CreateUnoService(&quot;com.sun.star.io.TextInputStream&quot;)
+ Set _OutputStream = CreateUnoService(&quot;com.sun.star.io.TextOutputStream&quot;)
+ _InputStream.setInputStream(_FileHandler)
+ &apos; Position at end of file: Skip and count existing lines
+ _LineNumber = 0
+ Do While Not _InputStream.isEOF()
+ _InputStream.readLine()
+ _LineNumber = _LineNumber + 1
+ Loop
+ _OutputStream.setOutputStream(_FileHandler)
+ End Select
+
+ If _Encoding = &quot;&quot; Then _Encoding = &quot;UTF-8&quot;
+ If Not IsNull(_InputStream) Then _InputStream.setEncoding(_Encoding)
+ If Not IsNull(_OutputStream) Then _OutputStream.setEncoding(_Encoding)
+
+End Sub &apos; ScriptForge.SF_TextStream._Initialize
+
+REM -----------------------------------------------------------------------------
+Private Function _IsFileOpen(Optional ByVal psMode As String) As Boolean
+&apos;&apos;&apos; Checks if file is open with the right mode (READ or WRITE)
+&apos;&apos;&apos; Raises an exception if the file is not open at all or not in the right mode
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; psMode: READ or WRITE or zero-length string
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; FILENOTOPENERROR File not open or already closed
+&apos;&apos;&apos; FILEOPENMODEERROR File opened in incompatible mode
+
+ _IsFileOpen = False
+ If IsMissing(psMode) Then psMode = &quot;&quot;
+ If IsNull(_InputStream) And IsNull(_OutputStream) Then GoTo CatchNotOpen
+ Select Case psMode
+ Case &quot;READ&quot;
+ If IsNull(_InputStream) Then GoTo CatchOpenMode
+ If _IOMode &lt;&gt; SF_FileSystem.ForReading Then GoTo CatchOpenMode
+ Case &quot;WRITE&quot;
+ If IsNull(_OutputStream) Then GoTo CatchOpenMode
+ If _IOMode = SF_FileSystem.ForReading Then GoTo CatchOpenMode
+ Case Else
+ End Select
+ _IsFileOpen = True
+
+Finally:
+ Exit Function
+CatchNotOpen:
+ SF_Exception.RaiseFatal(FILENOTOPENERROR, FileName)
+ GoTo Finally
+CatchOpenMode:
+ SF_Exception.RaiseFatal(FILEOPENMODEERROR, FileName, IOMode)
+ GoTo Finally
+End Function &apos; ScriptForge.SF_TextStream._IsFileOpen
+
+REM -----------------------------------------------------------------------------
+Private Function _PropertyGet(Optional ByVal psProperty As String)
+&apos;&apos;&apos; Return the value of the named property
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; psProperty: the name of the property
+
+Dim cstThisSub As String
+Dim cstSubArgs As String
+
+ cstThisSub = &quot;TextStream.get&quot; &amp; psProperty
+ cstSubArgs = &quot;&quot;
+ SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+
+ Select Case UCase(psProperty)
+ Case UCase(&quot;AtEndOfStream&quot;)
+ Select Case _IOMode
+ Case SF_FileSystem.ForReading
+ If IsNull(_InputStream) Then _PropertyGet = True Else _PropertyGet = CBool(_InputStream.isEOF() And Not _ForceBlankLine)
+ Case Else : _PropertyGet = True
+ End Select
+ Case UCase(&quot;Encoding&quot;)
+ _PropertyGet = _Encoding
+ Case UCase(&quot;FileName&quot;)
+ _PropertyGet = SF_FileSystem._ConvertFromUrl(_FileName) &apos; Depends on FileNaming
+ Case UCase(&quot;IOMode&quot;)
+ With SF_FileSystem
+ Select Case _IOMode
+ Case .ForReading : _PropertyGet = &quot;READ&quot;
+ Case .ForWriting : _PropertyGet = &quot;WRITE&quot;
+ Case .ForAppending : _PropertyGet = &quot;APPEND&quot;
+ Case Else : _PropertyGet = &quot;&quot;
+ End Select
+ End With
+ Case UCase(&quot;Line&quot;)
+ _PropertyGet = _LineNumber
+ Case UCase(&quot;NewLine&quot;)
+ _PropertyGet = _NewLine
+ Case Else
+ _PropertyGet = Null
+ End Select
+
+Finally:
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+End Function &apos; ScriptForge.SF_TextStream._PropertyGet
+
+REM -----------------------------------------------------------------------------
+Private Function _Repr() As String
+&apos;&apos;&apos; Convert the TextStream instance to a readable string, typically for debugging purposes (DebugPrint ...)
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Return:
+&apos;&apos;&apos; &quot;[TextStream]: File name, IOMode, LineNumber&quot;
+
+ _Repr = &quot;[TextStream]: &quot; &amp; FileName &amp; &quot;,&quot; &amp; IOMode &amp; &quot;,&quot; &amp; CStr(Line)
+
+End Function &apos; ScriptForge.SF_TextStream._Repr
+
+REM ============================================ END OF SCRIPTFORGE.SF_TextStream
+</script:module> \ No newline at end of file