SF_TextStream.xba 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
  3. <script:module xmlns:script="http://openoffice.org/2000/script" script:name="SF_TextStream" script:language="StarBasic" script:moduleType="normal">REM =======================================================================================================================
  4. REM === The ScriptForge library and its associated libraries are part of the LibreOffice project. ===
  5. REM === Full documentation is available on https://help.libreoffice.org/ ===
  6. REM =======================================================================================================================
  7. Option Compatible
  8. Option ClassModule
  9. Option Explicit
  10. &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;
  11. &apos;&apos;&apos; SF_TextStream
  12. &apos;&apos;&apos; =============
  13. &apos;&apos;&apos; Class instantiated by the
  14. &apos;&apos;&apos; SF_FileSystem.CreateTextFile
  15. &apos;&apos;&apos; SF_FileSystem.OpenTextFile
  16. &apos;&apos;&apos; methods to facilitate the sequential processing of text files
  17. &apos;&apos;&apos; All open/read/write/close operations are presumed to happen during the same macro run
  18. &apos;&apos;&apos; The encoding to be used may be chosen by the user
  19. &apos;&apos;&apos; The list is in the Name column of https://www.iana.org/assignments/character-sets/character-sets.xhtml
  20. &apos;&apos;&apos; Note that probably not all values are available
  21. &apos;&apos;&apos; Line delimiters may be chosen by the user
  22. &apos;&apos;&apos; In input, CR, LF or CR+LF are supported
  23. &apos;&apos;&apos; In output, the default value is the usual newline on the actual operating system (see SF_FileSystem.sfNEWLINE)
  24. &apos;&apos;&apos;
  25. &apos;&apos;&apos; The design choices are largely inspired by
  26. &apos;&apos;&apos; https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/textstream-object
  27. &apos;&apos;&apos; The implementation is mainly based on the XTextInputStream and XTextOutputStream UNO interfaces
  28. &apos;&apos;&apos; https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1io_1_1XTextInputStream.html
  29. &apos;&apos;&apos; https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1io_1_1XTextOutputStream.html
  30. &apos;&apos;&apos;
  31. &apos;&apos;&apos; Instantiation example:
  32. &apos;&apos;&apos; Dim FSO As Object, myFile As Object
  33. &apos;&apos;&apos; Set FSO = CreateScriptService(&quot;FileSystem&quot;)
  34. &apos;&apos;&apos; Set myFile = FSO.OpenTextFile(&quot;C:\Temp\ThisFile.txt&quot;, FSO.ForReading) &apos; Once per file
  35. &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;
  36. REM ================================================================== EXCEPTIONS
  37. Const FILENOTOPENERROR = &quot;FILENOTOPENERROR&quot; &apos; The file is already closed
  38. Const FILEOPENMODEERROR = &quot;FILEOPENMODEERROR&quot; &apos; The file is open in incompatible mode
  39. REM ============================================================= PRIVATE MEMBERS
  40. Private [Me] As Object
  41. Private [_Parent] As Object
  42. Private ObjectType As String &apos; Must be TEXTSTREAM
  43. Private ServiceName As String
  44. Private _FileName As String &apos; File where it is about
  45. Private _IOMode As Integer &apos; ForReading, ForWriting or ForAppending
  46. Private _Encoding As String &apos; https://www.iana.org/assignments/character-sets/character-sets.xhtml
  47. Private _NewLine As String &apos; Line break in write mode
  48. Private _FileExists As Boolean &apos; True if file exists before open
  49. Private _LineNumber As Long &apos; Number of lines read or written
  50. Private _FileHandler As Object &apos; com.sun.star.io.XInputStream or
  51. &apos; com.sun.star.io.XOutputStream or
  52. &apos; com.sun.star.io.XStream
  53. Private _InputStream As Object &apos; com.sun.star.io.TextInputStream
  54. Private _OutputStream As Object &apos; com.sun.star.io.TextOutputStream
  55. Private _ForceBlankLine As Boolean &apos; Workaround: XTextInputStream misses last line if file ends with newline
  56. REM ============================================================ MODULE CONSTANTS
  57. REM ===================================================== CONSTRUCTOR/DESTRUCTOR
  58. REM -----------------------------------------------------------------------------
  59. Private Sub Class_Initialize()
  60. Set [Me] = Nothing
  61. Set [_Parent] = Nothing
  62. ObjectType = &quot;TEXTSTREAM&quot;
  63. ServiceName = &quot;ScriptForge.TextStream&quot;
  64. _FileName = &quot;&quot;
  65. _IOMode = -1
  66. _Encoding = &quot;&quot;
  67. _NewLine = &quot;&quot;
  68. _FileExists = False
  69. _LineNumber = 0
  70. Set _FileHandler = Nothing
  71. Set _InputStream = Nothing
  72. Set _OutputStream = Nothing
  73. _ForceBlankLine = False
  74. End Sub &apos; ScriptForge.SF_TextStream Constructor
  75. REM -----------------------------------------------------------------------------
  76. Private Sub Class_Terminate()
  77. Call Class_Initialize()
  78. End Sub &apos; ScriptForge.SF_TextStream Destructor
  79. REM -----------------------------------------------------------------------------
  80. Public Function Dispose() As Variant
  81. Call Class_Terminate()
  82. Set Dispose = Nothing
  83. End Function &apos; ScriptForge.SF_TextStream Explicit Destructor
  84. REM ================================================================== PROPERTIES
  85. REM -----------------------------------------------------------------------------
  86. Property Get AtEndOfStream() As Boolean
  87. &apos;&apos;&apos; In reading mode, True indicates that the end of the file has been reached
  88. &apos;&apos;&apos; In write and append modes, or if the file is not ready =&gt; always True
  89. &apos;&apos;&apos; The property should be invoked BEFORE each ReadLine() method:
  90. &apos;&apos;&apos; A ReadLine() executed while AtEndOfStream is True will raise an error
  91. &apos;&apos;&apos; Example:
  92. &apos;&apos;&apos; Dim sLine As String
  93. &apos;&apos;&apos; Do While Not myFile.AtEndOfStream
  94. &apos;&apos;&apos; sLine = myFile.ReadLine()
  95. &apos;&apos;&apos; &apos; ...
  96. &apos;&apos;&apos; Loop
  97. AtEndOfStream = _PropertyGet(&quot;AtEndOfStream&quot;)
  98. End Property &apos; ScriptForge.SF_TextStream.AtEndOfStream
  99. REM -----------------------------------------------------------------------------
  100. Property Get Encoding() As String
  101. &apos;&apos;&apos; Returns the name of the text file either in url or in native operating system format
  102. &apos;&apos;&apos; Example:
  103. &apos;&apos;&apos; Dim myFile As Object
  104. &apos;&apos;&apos; FSO.FileNaming = &quot;SYS&quot;
  105. &apos;&apos;&apos; Set myFile = FSO.OpenTextFile(&quot;C:\Temp\myFile.txt&quot;)
  106. &apos;&apos;&apos; MsgBox myFile.Encoding &apos; UTF-8
  107. Encoding = _PropertyGet(&quot;Encoding&quot;)
  108. End Property &apos; ScriptForge.SF_TextStream.Encoding
  109. REM -----------------------------------------------------------------------------
  110. Property Get FileName() As String
  111. &apos;&apos;&apos; Returns the name of the text file either in url or in native operating system format
  112. &apos;&apos;&apos; Example:
  113. &apos;&apos;&apos; Dim myFile As Object
  114. &apos;&apos;&apos; FSO.FileNaming = &quot;SYS&quot;
  115. &apos;&apos;&apos; Set myFile = FSO.OpenTextFile(&quot;C:\Temp\myFile.txt&quot;)
  116. &apos;&apos;&apos; MsgBox myFile.FileName &apos; C:\Temp\myFile.txt
  117. FileName = _PropertyGet(&quot;FileName&quot;)
  118. End Property &apos; ScriptForge.SF_TextStream.FileName
  119. REM -----------------------------------------------------------------------------
  120. Property Get IOMode() As String
  121. &apos;&apos;&apos; Returns either &quot;READ&quot;, &quot;WRITE&quot; or &quot;APPEND&quot;
  122. &apos;&apos;&apos; Example:
  123. &apos;&apos;&apos; Dim myFile As Object
  124. &apos;&apos;&apos; FSO.FileNaming = &quot;SYS&quot;
  125. &apos;&apos;&apos; Set myFile = FSO.OpenTextFile(&quot;C:\Temp\myFile.txt&quot;)
  126. &apos;&apos;&apos; MsgBox myFile.IOMode &apos; READ
  127. IOMode = _PropertyGet(&quot;IOMode&quot;)
  128. End Property &apos; ScriptForge.SF_TextStream.IOMode
  129. REM -----------------------------------------------------------------------------
  130. Property Get Line() As Long
  131. &apos;&apos;&apos; Returns the number of lines read or written so far
  132. &apos;&apos;&apos; Example:
  133. &apos;&apos;&apos; Dim myFile As Object
  134. &apos;&apos;&apos; FSO.FileNaming = &quot;SYS&quot;
  135. &apos;&apos;&apos; Set myFile = FSO.OpenTextFile(&quot;C:\Temp\myFile.txt&quot;, FSO.ForAppending)
  136. &apos;&apos;&apos; MsgBox myFile.Line &apos; The number of lines already present in myFile
  137. Line = _PropertyGet(&quot;Line&quot;)
  138. End Property &apos; ScriptForge.SF_TextStream.Line
  139. REM -----------------------------------------------------------------------------
  140. Property Get NewLine() As Variant
  141. &apos;&apos;&apos; Returns the current character string to be inserted between 2 successive written lines
  142. &apos;&apos;&apos; The default value is the native line separator in the current operating system
  143. &apos;&apos;&apos; Example:
  144. &apos;&apos;&apos; MsgBox myFile.NewLine
  145. NewLine = _PropertyGet(&quot;NewLine&quot;)
  146. End Property &apos; ScriptForge.SF_TextStream.NewLine (get)
  147. REM -----------------------------------------------------------------------------
  148. Property Let NewLine(ByVal pvLineBreak As Variant)
  149. &apos;&apos;&apos; Sets the current character string to be inserted between 2 successive written lines
  150. &apos;&apos;&apos; Example:
  151. &apos;&apos;&apos; myFile.NewLine = Chr(13) &amp; Chr(10)
  152. Const cstThisSub = &quot;TextStream.setNewLine&quot;
  153. SF_Utils._EnterFunction(cstThisSub)
  154. If VarType(pvLineBreak) = V_STRING Then _NewLine = pvLineBreak
  155. SF_Utils._ExitFunction(cstThisSub)
  156. End Property &apos; ScriptForge.SF_TextStream.NewLine (let)
  157. REM ===================================================================== METHODS
  158. REM -----------------------------------------------------------------------------
  159. Public Function CloseFile() As Boolean
  160. &apos;&apos;&apos; Empties the output buffer if relevant. Closes the actual input or output stream
  161. &apos;&apos;&apos; Args:
  162. &apos;&apos;&apos; Returns:
  163. &apos;&apos;&apos; True if the closure was successful
  164. &apos;&apos;&apos; Exceptions:
  165. &apos;&apos;&apos; FILENOTOPENERROR Nothing found to close
  166. &apos;&apos;&apos; Examples:
  167. &apos;&apos;&apos; myFile.CloseFile()
  168. Dim bClose As Boolean &apos; Return value
  169. Const cstThisSub = &quot;TextStream.CloseFile&quot;
  170. Const cstSubArgs = &quot;&quot;
  171. If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
  172. bClose = False
  173. Check:
  174. SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
  175. If Not _IsFileOpen() Then GoTo Finally
  176. Try:
  177. If Not IsNull(_InputStream) Then _InputStream.closeInput()
  178. If Not IsNull(_OutputStream) Then
  179. _OutputStream.flush()
  180. _OutputStream.closeOutput()
  181. End If
  182. Set _InputStream = Nothing
  183. Set _OutputStream = Nothing
  184. Set _FileHandler = Nothing
  185. bClose = True
  186. Finally:
  187. CloseFile = bClose
  188. SF_Utils._ExitFunction(cstThisSub)
  189. Exit Function
  190. Catch:
  191. GoTo Finally
  192. End Function &apos; ScriptForge.SF_TextStream.CloseFile
  193. REM -----------------------------------------------------------------------------
  194. Public Function GetProperty(Optional ByVal PropertyName As Variant) As Variant
  195. &apos;&apos;&apos; Return the actual value of the given property
  196. &apos;&apos;&apos; Args:
  197. &apos;&apos;&apos; PropertyName: the name of the property as a string
  198. &apos;&apos;&apos; Returns:
  199. &apos;&apos;&apos; The actual value of the property
  200. &apos;&apos;&apos; If the property does not exist, returns Null
  201. &apos;&apos;&apos; Exceptions:
  202. &apos;&apos;&apos; see the exceptions of the individual properties
  203. &apos;&apos;&apos; Examples:
  204. &apos;&apos;&apos; myModel.GetProperty(&quot;MyProperty&quot;)
  205. Const cstThisSub = &quot;TextStream.GetProperty&quot;
  206. Const cstSubArgs = &quot;&quot;
  207. If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
  208. GetProperty = Null
  209. Check:
  210. If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
  211. If Not SF_Utils._Validate(PropertyName, &quot;PropertyName&quot;, V_STRING, Properties()) Then GoTo Catch
  212. End If
  213. Try:
  214. GetProperty = _PropertyGet(PropertyName)
  215. Finally:
  216. SF_Utils._ExitFunction(cstThisSub)
  217. Exit Function
  218. Catch:
  219. GoTo Finally
  220. End Function &apos; ScriptForge.SF_TextStream.GetProperty
  221. REM -----------------------------------------------------------------------------
  222. Public Function Methods() As Variant
  223. &apos;&apos;&apos; Return the list of public methods of the Model service as an array
  224. Methods = Array( _
  225. &quot;CloseFile&quot; _
  226. , &quot;ReadAll&quot; _
  227. , &quot;readLine&quot; _
  228. , &quot;SkipLine&quot; _
  229. , &quot;WriteBlankLines&quot; _
  230. , &quot;WriteLine&quot; _
  231. )
  232. End Function &apos; ScriptForge.SF_TextStream.Methods
  233. REM -----------------------------------------------------------------------------
  234. Public Function Properties() As Variant
  235. &apos;&apos;&apos; Return the list or properties of the Timer class as an array
  236. Properties = Array( _
  237. &quot;AtEndOfStream&quot; _
  238. , &quot;Encoding&quot; _
  239. , &quot;FileName&quot; _
  240. , &quot;IOMode&quot; _
  241. , &quot;Line&quot; _
  242. , &quot;NewLine&quot; _
  243. )
  244. End Function &apos; ScriptForge.SF_TextStream.Properties
  245. REM -----------------------------------------------------------------------------
  246. Public Function ReadAll() As String
  247. &apos;&apos;&apos; Returns all the remaining lines in the text stream as one string. Line breaks are NOT removed
  248. &apos;&apos;&apos; The resulting string can be split in lines
  249. &apos;&apos;&apos; either by using the usual Split Basic builtin function if the line delimiter is known
  250. &apos;&apos;&apos; or with the SF_String.SplitLines method
  251. &apos;&apos;&apos; For large files, using the ReadAll method wastes memory resources.
  252. &apos;&apos;&apos; Other techniques should be used to input a file, such as reading a file line-by-line
  253. &apos;&apos;&apos; Args:
  254. &apos;&apos;&apos; Returns:
  255. &apos;&apos;&apos; The read lines. The string may be empty.
  256. &apos;&apos;&apos; Note that the Line property in incremented only by 1
  257. &apos;&apos;&apos; Exceptions:
  258. &apos;&apos;&apos; FILENOTOPENERROR File not open or already closed
  259. &apos;&apos;&apos; FILEOPENMODEERROR File opened in write or append modes
  260. &apos;&apos;&apos; ENDOFFILEERROR Previous reads already reached the end of the file
  261. &apos;&apos;&apos; Examples:
  262. &apos;&apos;&apos; Dim a As String
  263. &apos;&apos;&apos; a = myFile.ReadAll()
  264. Dim sRead As String &apos; Return value
  265. Const cstThisSub = &quot;TextStream.ReadAll&quot;
  266. Const cstSubArgs = &quot;&quot;
  267. If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
  268. sRead = &quot;&quot;
  269. Check:
  270. If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
  271. If Not _IsFileOpen(&quot;READ&quot;) Then GoTo Finally
  272. If _InputStream.isEOF() Then GoTo CatchEOF
  273. End If
  274. Try:
  275. sRead = _InputStream.readString(Array(), False)
  276. _LineNumber = _LineNumber + 1
  277. Finally:
  278. ReadAll = sRead
  279. SF_Utils._ExitFunction(cstThisSub)
  280. Exit Function
  281. Catch:
  282. GoTo Finally
  283. CatchEOF:
  284. &apos;TODO: SF_Exception.RaiseFatal(FILEWRITEMODEERROR, cstThisSub)
  285. MsgBox &quot;END OF FILE ERROR !!&quot;
  286. GoTo Finally
  287. End Function &apos; ScriptForge.SF_TextStream.ReadAll
  288. REM -----------------------------------------------------------------------------
  289. Public Function ReadLine() As String
  290. &apos;&apos;&apos; Returns the next line in the text stream as a string. Line breaks are removed.
  291. &apos;&apos;&apos; Args:
  292. &apos;&apos;&apos; Returns:
  293. &apos;&apos;&apos; The read line. The string may be empty.
  294. &apos;&apos;&apos; Exceptions:
  295. &apos;&apos;&apos; FILENOTOPENERROR File not open or already closed
  296. &apos;&apos;&apos; FILEOPENMODEERROR File opened in write or append modes
  297. &apos;&apos;&apos; ENDOFFILEERROR Previous reads already reached the end of the file
  298. &apos;&apos;&apos; Examples:
  299. &apos;&apos;&apos; Dim a As String
  300. &apos;&apos;&apos; a = myFile.ReadLine()
  301. Dim sRead As String &apos; Return value
  302. Dim iRead As Integer &apos; Length of line break
  303. Const cstThisSub = &quot;TextStream.ReadLine&quot;
  304. Const cstSubArgs = &quot;&quot;
  305. If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
  306. sRead = &quot;&quot;
  307. Check:
  308. If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
  309. If Not _IsFileOpen(&quot;READ&quot;) Then GoTo Finally
  310. If AtEndOfStream Then GoTo CatchEOF
  311. End If
  312. Try:
  313. &apos; When the text file ends with a line break,
  314. &apos; XTextInputStream.readLine() returns the line break together with the last line
  315. &apos; Hence the workaround to force a blank line at the end
  316. If _ForceBlankLine Then
  317. sRead = &quot;&quot;
  318. _ForceBlankLine = False
  319. Else
  320. sRead = _InputStream.readLine()
  321. &apos; The isEOF() is set immediately after having read the last line
  322. If _InputStream.isEOF() And Len(sRead) &gt; 0 Then
  323. iRead = 0
  324. If SF_String.EndsWith(sRead, SF_String.sfCRLF) Then
  325. iRead = 2
  326. ElseIf SF_String.EndsWith(sRead, SF_String.sfLF) Or SF_String.EndsWith(sRead, SF_String.sfCR) Then
  327. iRead = 1
  328. End If
  329. If iRead &gt; 0 Then
  330. sRead = Left(sRead, Len(sRead) - iRead)
  331. _ForceBlankLine = True &apos; Provision for a last empty line at the next read loop
  332. End If
  333. End If
  334. End If
  335. _LineNumber = _LineNumber + 1
  336. Finally:
  337. ReadLine = sRead
  338. SF_Utils._ExitFunction(cstThisSub)
  339. Exit Function
  340. Catch:
  341. GoTo Finally
  342. CatchEOF:
  343. &apos;TODO: SF_Exception.RaiseFatal(FILEWRITEMODEERROR, cstThisSub)
  344. MsgBox &quot;END OF FILE ERROR !!&quot;
  345. GoTo Finally
  346. End Function &apos; ScriptForge.SF_TextStream.ReadLine
  347. REM -----------------------------------------------------------------------------
  348. Public Function SetProperty(Optional ByVal PropertyName As Variant _
  349. , Optional ByRef Value As Variant _
  350. ) As Boolean
  351. &apos;&apos;&apos; Set a new value to the given property
  352. &apos;&apos;&apos; Args:
  353. &apos;&apos;&apos; PropertyName: the name of the property as a string
  354. &apos;&apos;&apos; Value: its new value
  355. &apos;&apos;&apos; Exceptions
  356. &apos;&apos;&apos; ARGUMENTERROR The property does not exist
  357. Dim bSet As Boolean &apos; Return value
  358. Const cstThisSub = &quot;TextStream.SetProperty&quot;
  359. Const cstSubArgs = &quot;PropertyName, Value&quot;
  360. If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
  361. bSet = False
  362. Check:
  363. If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
  364. If Not SF_Utils._Validate(PropertyName, &quot;PropertyName&quot;, V_STRING, Properties()) Then GoTo Catch
  365. End If
  366. Try:
  367. bSet = True
  368. Select Case UCase(PropertyName)
  369. Case &quot;NEWLINE&quot;
  370. If Not SF_Utils._Validate(Value, &quot;Value&quot;, V_STRING) Then GoTo Catch
  371. NewLine = Value
  372. Case Else
  373. bSet = False
  374. End Select
  375. Finally:
  376. SetProperty = bSet
  377. SF_Utils._ExitFunction(cstThisSub)
  378. Exit Function
  379. Catch:
  380. GoTo Finally
  381. End Function &apos; ScriptForge.SF_TextStream.SetProperty
  382. REM -----------------------------------------------------------------------------
  383. Public Sub SkipLine()
  384. &apos;&apos;&apos; Skips the next line when reading a TextStream file.
  385. &apos;&apos;&apos; Args:
  386. &apos;&apos;&apos; Exceptions:
  387. &apos;&apos;&apos; FILENOTOPENERROR File not open or already closed
  388. &apos;&apos;&apos; FILEOPENMODEERROR File opened in write or append modes
  389. &apos;&apos;&apos; ENDOFFILEERROR Previous reads already reached the end of the file
  390. &apos;&apos;&apos; Examples:
  391. &apos;&apos;&apos; myFile.SkipLine()
  392. Dim sRead As String &apos; Read buffer
  393. Const cstThisSub = &quot;TextStream.SkipLine&quot;
  394. Const cstSubArgs = &quot;&quot;
  395. If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
  396. Check:
  397. If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
  398. If Not _IsFileOpen(&quot;READ&quot;) Then GoTo Finally
  399. If Not _ForceBlankLine Then &apos; The file ends with a newline =&gt; return one empty line more
  400. If _InputStream.isEOF() Then GoTo CatchEOF
  401. End If
  402. End If
  403. Try:
  404. sRead = ReadLine()
  405. Finally:
  406. SF_Utils._ExitFunction(cstThisSub)
  407. Exit Sub
  408. Catch:
  409. GoTo Finally
  410. CatchEOF:
  411. &apos;TODO: SF_Exception.RaiseFatal(FILEWRITEMODEERROR, cstThisSub)
  412. MsfBox &quot;END OF FILE ERROR !!&quot;
  413. GoTo Finally
  414. End Sub &apos; ScriptForge.SF_TextStream.SkipLine
  415. REM -----------------------------------------------------------------------------
  416. Public Sub WriteBlankLines(Optional ByVal Lines As Variant)
  417. &apos;&apos;&apos; Writes a number of empty lines in the output stream
  418. &apos;&apos;&apos; Args:
  419. &apos;&apos;&apos; Lines: the number of lines to write
  420. &apos;&apos;&apos; Returns:
  421. &apos;&apos;&apos; Exceptions:
  422. &apos;&apos;&apos; FILENOTOPENERROR File not open or already closed
  423. &apos;&apos;&apos; FILEOPENMODEERROR File opened in in read mode
  424. &apos;&apos;&apos; Examples:
  425. &apos;&apos;&apos; myFile.WriteBlankLines(10)
  426. Dim i As Long
  427. Const cstThisSub = &quot;TextStream.WriteBlankLines&quot;
  428. Const cstSubArgs = &quot;Lines&quot;
  429. If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
  430. Check:
  431. If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
  432. If Not _IsFileOpen(&quot;WRITE&quot;) Then GoTo Finally
  433. If Not SF_Utils._Validate(Lines, &quot;Lines&quot;, V_NUMERIC) Then GoTo Finally
  434. End If
  435. Try:
  436. For i = 1 To Lines
  437. _OutputStream.writeString(_NewLine)
  438. Next i
  439. _LineNumber = _LineNumber + Lines
  440. Finally:
  441. SF_Utils._ExitFunction(cstThisSub)
  442. Exit Sub
  443. Catch:
  444. GoTo Finally
  445. End Sub &apos; ScriptForge.SF_TextStream.WriteBlankLines
  446. REM -----------------------------------------------------------------------------
  447. Public Sub WriteLine(Optional ByVal Line As Variant)
  448. &apos;&apos;&apos; Writes the given line to the output stream. A newline is inserted if relevant
  449. &apos;&apos;&apos; Args:
  450. &apos;&apos;&apos; Line: the line to write, may be empty
  451. &apos;&apos;&apos; Returns:
  452. &apos;&apos;&apos; Exceptions:
  453. &apos;&apos;&apos; FILENOTOPENERROR File not open or already closed
  454. &apos;&apos;&apos; FILEOPENMODEERROR File opened in in read mode
  455. &apos;&apos;&apos; Examples:
  456. &apos;&apos;&apos; myFile.WriteLine(&quot;Next line&quot;)
  457. Dim i As Long
  458. Const cstThisSub = &quot;TextStream.WriteLine&quot;
  459. Const cstSubArgs = &quot;Line&quot;
  460. If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
  461. Check:
  462. If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
  463. If Not _IsFileOpen(&quot;WRITE&quot;) Then GoTo Finally
  464. If Not SF_Utils._Validate(Line, &quot;Line&quot;, V_STRING) Then GoTo Finally
  465. End If
  466. Try:
  467. _OutputStream.writeString(Iif(_LineNumber &gt; 0, _NewLine, &quot;&quot;) &amp; Line)
  468. _LineNumber = _LineNumber + 1
  469. Finally:
  470. SF_Utils._ExitFunction(cstThisSub)
  471. Exit Sub
  472. Catch:
  473. GoTo Finally
  474. End Sub &apos; ScriptForge.SF_TextStream.WriteLine
  475. REM =========================================================== PRIVATE FUNCTIONS
  476. REM -----------------------------------------------------------------------------
  477. Public Sub _Initialize()
  478. &apos;&apos;&apos; Opens file and setup input and/or output streams (ForAppending requires both)
  479. Dim oSfa As Object &apos; com.sun.star.ucb.SimpleFileAccess
  480. &apos; Default newline related to current operating system
  481. _NewLine = SF_String.sfNEWLINE
  482. Set oSfa = SF_Utils._GetUNOService(&quot;FileAccess&quot;)
  483. &apos; Setup input and/or output streams based on READ/WRITE/APPEND IO modes
  484. Select Case _IOMode
  485. Case SF_FileSystem.ForReading
  486. Set _FileHandler = oSfa.openFileRead(_FileName)
  487. Set _InputStream = CreateUnoService(&quot;com.sun.star.io.TextInputStream&quot;)
  488. _InputStream.setInputStream(_FileHandler)
  489. Case SF_FileSystem.ForWriting
  490. &apos; Output file is deleted beforehand
  491. If _FileExists Then oSfa.kill(_FileName)
  492. Set _FileHandler = oSfa.openFileWrite(_FileName)
  493. Set _OutputStream = CreateUnoService(&quot;com.sun.star.io.TextOutputStream&quot;)
  494. _OutputStream.setOutputStream(_FileHandler)
  495. Case SF_FileSystem.ForAppending
  496. Set _FileHandler = oSfa.openFileReadWrite(_FileName)
  497. Set _InputStream = CreateUnoService(&quot;com.sun.star.io.TextInputStream&quot;)
  498. Set _OutputStream = CreateUnoService(&quot;com.sun.star.io.TextOutputStream&quot;)
  499. _InputStream.setInputStream(_FileHandler)
  500. &apos; Position at end of file: Skip and count existing lines
  501. _LineNumber = 0
  502. Do While Not _InputStream.isEOF()
  503. _InputStream.readLine()
  504. _LineNumber = _LineNumber + 1
  505. Loop
  506. _OutputStream.setOutputStream(_FileHandler)
  507. End Select
  508. If _Encoding = &quot;&quot; Then _Encoding = &quot;UTF-8&quot;
  509. If Not IsNull(_InputStream) Then _InputStream.setEncoding(_Encoding)
  510. If Not IsNull(_OutputStream) Then _OutputStream.setEncoding(_Encoding)
  511. End Sub &apos; ScriptForge.SF_TextStream._Initialize
  512. REM -----------------------------------------------------------------------------
  513. Private Function _IsFileOpen(Optional ByVal psMode As String) As Boolean
  514. &apos;&apos;&apos; Checks if file is open with the right mode (READ or WRITE)
  515. &apos;&apos;&apos; Raises an exception if the file is not open at all or not in the right mode
  516. &apos;&apos;&apos; Args:
  517. &apos;&apos;&apos; psMode: READ or WRITE or zero-length string
  518. &apos;&apos;&apos; Exceptions:
  519. &apos;&apos;&apos; FILENOTOPENERROR File not open or already closed
  520. &apos;&apos;&apos; FILEOPENMODEERROR File opened in incompatible mode
  521. _IsFileOpen = False
  522. If IsMissing(psMode) Then psMode = &quot;&quot;
  523. If IsNull(_InputStream) And IsNull(_OutputStream) Then GoTo CatchNotOpen
  524. Select Case psMode
  525. Case &quot;READ&quot;
  526. If IsNull(_InputStream) Then GoTo CatchOpenMode
  527. If _IOMode &lt;&gt; SF_FileSystem.ForReading Then GoTo CatchOpenMode
  528. Case &quot;WRITE&quot;
  529. If IsNull(_OutputStream) Then GoTo CatchOpenMode
  530. If _IOMode = SF_FileSystem.ForReading Then GoTo CatchOpenMode
  531. Case Else
  532. End Select
  533. _IsFileOpen = True
  534. Finally:
  535. Exit Function
  536. CatchNotOpen:
  537. SF_Exception.RaiseFatal(FILENOTOPENERROR, FileName)
  538. GoTo Finally
  539. CatchOpenMode:
  540. SF_Exception.RaiseFatal(FILEOPENMODEERROR, FileName, IOMode)
  541. GoTo Finally
  542. End Function &apos; ScriptForge.SF_TextStream._IsFileOpen
  543. REM -----------------------------------------------------------------------------
  544. Private Function _PropertyGet(Optional ByVal psProperty As String)
  545. &apos;&apos;&apos; Return the value of the named property
  546. &apos;&apos;&apos; Args:
  547. &apos;&apos;&apos; psProperty: the name of the property
  548. Dim cstThisSub As String
  549. Dim cstSubArgs As String
  550. cstThisSub = &quot;TextStream.get&quot; &amp; psProperty
  551. cstSubArgs = &quot;&quot;
  552. SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
  553. Select Case UCase(psProperty)
  554. Case UCase(&quot;AtEndOfStream&quot;)
  555. Select Case _IOMode
  556. Case SF_FileSystem.ForReading
  557. If IsNull(_InputStream) Then _PropertyGet = True Else _PropertyGet = _InputStream.isEOF() And Not _ForceBlankLine
  558. Case Else : _PropertyGet = True
  559. End Select
  560. Case UCase(&quot;Encoding&quot;)
  561. _PropertyGet = _Encoding
  562. Case UCase(&quot;FileName&quot;)
  563. _PropertyGet = SF_FileSystem._ConvertFromUrl(_FileName) &apos; Depends on FileNaming
  564. Case UCase(&quot;IOMode&quot;)
  565. With SF_FileSystem
  566. Select Case _IOMode
  567. Case .ForReading : _PropertyGet = &quot;READ&quot;
  568. Case .ForWriting : _PropertyGet = &quot;WRITE&quot;
  569. Case .ForAppending : _PropertyGet = &quot;APPEND&quot;
  570. Case Else : _PropertyGet = &quot;&quot;
  571. End Select
  572. End With
  573. Case UCase(&quot;Line&quot;)
  574. _PropertyGet = _LineNumber
  575. Case UCase(&quot;NewLine&quot;)
  576. _PropertyGet = _NewLine
  577. Case Else
  578. _PropertyGet = Null
  579. End Select
  580. Finally:
  581. SF_Utils._ExitFunction(cstThisSub)
  582. Exit Function
  583. End Function &apos; ScriptForge.SF_TextStream._PropertyGet
  584. REM -----------------------------------------------------------------------------
  585. Private Function _Repr() As String
  586. &apos;&apos;&apos; Convert the TextStream instance to a readable string, typically for debugging purposes (DebugPrint ...)
  587. &apos;&apos;&apos; Args:
  588. &apos;&apos;&apos; Return:
  589. &apos;&apos;&apos; &quot;[TextStream]: File name, IOMode, LineNumber&quot;
  590. _Repr = &quot;[TextStream]: &quot; &amp; FileName &amp; &quot;,&quot; &amp; IOMode &amp; &quot;,&quot; &amp; CStr(Line)
  591. End Function &apos; ScriptForge.SF_TextStream._Repr
  592. REM ============================================ END OF SCRIPTFORGE.SF_TextStream
  593. </script:module>