Module.xba 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  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="Module" script:language="StarBasic">
  4. REM =======================================================================================================================
  5. REM === The Access2Base library is a part of the LibreOffice project. ===
  6. REM === Full documentation is available on http://www.access2base.com ===
  7. REM =======================================================================================================================
  8. Option Compatible
  9. Option ClassModule
  10. Option Explicit
  11. REM -----------------------------------------------------------------------------------------------------------------------
  12. REM --- CLASS ROOT FIELDS ---
  13. REM -----------------------------------------------------------------------------------------------------------------------
  14. Private _Type As String &apos; Must be MODULE
  15. Private _This As Object &apos; Workaround for absence of This builtin function
  16. Private _Parent As Object
  17. Private _Name As String
  18. Private _Library As Object &apos; com.sun.star.container.XNameAccess
  19. Private _LibraryName As String
  20. Private _Storage As String &apos; GLOBAL or DOCUMENT
  21. Private _Script As String &apos; Full script (string with vbLf&apos;s)
  22. Private _Lines As Variant &apos; Array of script lines
  23. Private _CountOfLines As Long
  24. Private _ProcsParsed As Boolean &apos; To test before use of proc arrays
  25. Private _ProcNames() As Variant &apos; All procedure names
  26. Private _ProcDecPositions() As Variant &apos; All procedure declarations
  27. Private _ProcEndPositions() As Variant &apos; All end procedure statements
  28. Private _ProcTypes() As Variant &apos; One of the vbext_pk_* constants
  29. REM -----------------------------------------------------------------------------------------------------------------------
  30. REM --- CONSTRUCTORS / DESTRUCTORS ---
  31. REM -----------------------------------------------------------------------------------------------------------------------
  32. Private Sub Class_Initialize()
  33. _Type = OBJMODULE
  34. Set _This = Nothing
  35. Set _Parent = Nothing
  36. _Name = &quot;&quot;
  37. Set _Library = Nothing
  38. _LibraryName = &quot;&quot;
  39. _Storage = &quot;&quot;
  40. _Script = &quot;&quot;
  41. _Lines = Array()
  42. _CountOfLines = 0
  43. _ProcsParsed = False
  44. _ProcNames = Array()
  45. _ProcDecPositions = Array()
  46. _ProcEndPositions = Array()
  47. End Sub &apos; Constructor
  48. REM -----------------------------------------------------------------------------------------------------------------------
  49. Private Sub Class_Terminate()
  50. On Local Error Resume Next
  51. Call Class_Initialize()
  52. End Sub &apos; Destructor
  53. REM -----------------------------------------------------------------------------------------------------------------------
  54. Public Sub Dispose()
  55. Call Class_Terminate()
  56. End Sub &apos; Explicit destructor
  57. REM -----------------------------------------------------------------------------------------------------------------------
  58. REM --- CLASS GET/LET/SET PROPERTIES ---
  59. REM -----------------------------------------------------------------------------------------------------------------------
  60. REM -----------------------------------------------------------------------------------------------------------------------
  61. Property Get CountOfDeclarationLines() As Long
  62. CountOfDeclarationLines = _PropertyGet(&quot;CountOfDeclarationLines&quot;)
  63. End Property &apos; CountOfDeclarationLines (get)
  64. REM -----------------------------------------------------------------------------------------------------------------------
  65. Property Get CountOfLines() As Long
  66. CountOfLines = _PropertyGet(&quot;CountOfLines&quot;)
  67. End Property &apos; CountOfLines (get)
  68. REM -----------------------------------------------------------------------------------------------------------------------
  69. Property Get Name() As String
  70. Name = _PropertyGet(&quot;Name&quot;)
  71. End Property &apos; Name (get)
  72. REM -----------------------------------------------------------------------------------------------------------------------
  73. Property Get ObjectType() As String
  74. ObjectType = _PropertyGet(&quot;ObjectType&quot;)
  75. End Property &apos; ObjectType (get)
  76. REM -----------------------------------------------------------------------------------------------------------------------
  77. Public Function Lines(Optional ByVal pvLine As Variant, Optional ByVal pvNumLines As Variant) As String
  78. &apos; Returns a string containing the contents of a specified line or lines in a standard module or a class module
  79. Const cstThisSub = &quot;Module.Lines&quot;
  80. Utils._SetCalledSub(cstThisSub)
  81. Dim sLines As String, lLine As Long
  82. sLines = &quot;&quot;
  83. If IsMissing(pvLine) Or IsMissing(pvNumLines) Then Call _TraceArguments()
  84. If Not Utils._CheckArgument(pvLine, 1, _AddNumeric()) Then GoTo Exit_Function
  85. If Not Utils._CheckArgument(pvNumLines, 1, _AddNumeric()) Then GoTo Exit_Function
  86. lLine = pvLine
  87. Do While lLine &lt; _CountOfLines And lLine &lt; pvLine + pvNumLines
  88. sLines = sLines &amp; _Lines(lLine - 1) &amp; vbLf
  89. lLine = lLine + 1
  90. Loop
  91. If Len(sLines) &gt; 0 Then sLines = Left(sLines, Len(sLines) - 1)
  92. Exit_Function:
  93. Lines = sLines
  94. Utils._ResetCalledSub(cstThisSub)
  95. Exit Function
  96. End Function &apos; Lines
  97. REM -----------------------------------------------------------------------------------------------------------------------
  98. Public Function ProcBodyLine(Optional ByVal pvProc As Variant, Optional ByVal pvProcType As Variant) As Long
  99. &apos; Return the number of the line at which the body of a specified procedure begins
  100. Const cstThisSub = &quot;Module.ProcBodyLine&quot;
  101. Utils._SetCalledSub(cstThisSub)
  102. Dim iIndex As Integer
  103. If IsMissing(pvProc) Or IsMissing(pvProcType) Then Call _TraceArguments()
  104. If Not Utils._CheckArgument(pvProc, 1, vbString) Then GoTo Exit_Function
  105. If Not Utils._CheckArgument(pvProcType, 2, _AddNumeric()) Then GoTo Exit_Function
  106. iIndex = _FindProcIndex(pvProc, pvProcType)
  107. If iIndex &gt;= 0 Then ProcBodyLine = _LineOfPosition(_ProcDecPositions(iIndex)) Else ProcBodyLine = iIndex
  108. Exit_Function:
  109. Utils._ResetCalledSub(cstThisSub)
  110. Exit Function
  111. End Function &apos; ProcBodyline
  112. REM -----------------------------------------------------------------------------------------------------------------------
  113. Public Function ProcCountLines(Optional ByVal pvProc As Variant, Optional ByVal pvProcType As Variant) As Long
  114. &apos; Return the number of lines in the specified procedure
  115. Const cstThisSub = &quot;Module.ProcCountLines&quot;
  116. Utils._SetCalledSub(cstThisSub)
  117. Dim iIndex As Integer, lStart As Long, lEnd As Long
  118. If IsMissing(pvProc) Or IsMissing(pvProcType) Then Call _TraceArguments()
  119. If Not Utils._CheckArgument(pvProc, 1, vbString) Then GoTo Exit_Function
  120. If Not Utils._CheckArgument(pvProcType, 2, _AddNumeric()) Then GoTo Exit_Function
  121. iIndex = _FindProcIndex(pvProc, pvProcType)
  122. lStart = ProcStartLine(pvProc, pvProcType)
  123. lEnd = _LineOfPosition(_ProcEndPositions(iIndex))
  124. ProcCountLines = lEnd - lStart + 1
  125. Exit_Function:
  126. Utils._ResetCalledSub(cstThisSub)
  127. Exit Function
  128. End Function &apos; ProcCountLines
  129. REM -----------------------------------------------------------------------------------------------------------------------
  130. Public Function ProcOfLine(Optional ByVal pvLine As Variant, Optional ByRef pvProcType As Variant) As String
  131. &apos; Return the name and type of the procedure containing line pvLine
  132. Const cstThisSub = &quot;Module.ProcOfLine&quot;
  133. Utils._SetCalledSub(cstThisSub)
  134. Dim sProcedure As String, iProc As Integer, lLineDec As Long, lLineEnd As Long
  135. If IsMissing(pvLine) Or IsMissing(pvProcType) Then Call _TraceArguments()
  136. If Not Utils._CheckArgument(pvLine, 1, _AddNumeric()) Then GoTo Exit_Function
  137. If Not Utils._CheckArgument(pvProcType, 2, _AddNumeric()) Then GoTo Exit_Function
  138. If Not _ProcsParsed Then _ParseProcs()
  139. sProcedure = &quot;&quot;
  140. For iProc = 0 To UBound(_ProcNames)
  141. lLineEnd = _LineOfPosition(_ProcEndPositions(iProc))
  142. If pvLine &lt;= lLineEnd Then
  143. lLineDec = _LineOfPosition(_ProcDecPositions(iProc))
  144. If pvLine &lt; lLineDec Then &apos; Line between 2 procedures
  145. sProcedure = &quot;&quot;
  146. Else
  147. sProcedure = _ProcNames(iProc)
  148. pvProcType = _ProcTypes(iProc)
  149. End If
  150. Exit For
  151. End If
  152. Next iProc
  153. Exit_Function:
  154. ProcOfLine = sProcedure
  155. Utils._ResetCalledSub(cstThisSub)
  156. Exit Function
  157. End Function &apos; ProcOfline
  158. REM -----------------------------------------------------------------------------------------------------------------------
  159. Public Function ProcStartLine(Optional ByVal pvProc As Variant, Optional ByVal pvProcType As Variant) As Long
  160. &apos; Return the number of the line at which the specified procedure begins
  161. Const cstThisSub = &quot;Module.ProcStartLine&quot;
  162. Utils._SetCalledSub(cstThisSub)
  163. Dim lLine As Long, lIndex As Long, sLine As String
  164. If IsMissing(pvProc) Or IsMissing(pvProcType) Then Call _TraceArguments()
  165. If Not Utils._CheckArgument(pvProc, 1, vbString) Then GoTo Exit_Function
  166. If Not Utils._CheckArgument(pvProcType, 2, _AddNumeric()) Then GoTo Exit_Function
  167. lLine = ProcBodyLine(pvProc, pvProcType)
  168. &apos; Search baclIndexward for comment lines
  169. lIndex = lLine - 1
  170. Do While lIndex &gt; 0
  171. sLine = _Trim(_Lines(lIndex - 1))
  172. If UCase(Left(sLine, 4)) = &quot;REM &quot; Or Left(sLine, 1) = &quot;&apos;&quot; Then
  173. lLine = lIndex
  174. Else
  175. Exit Do
  176. End If
  177. lIndex = lIndex - 1
  178. Loop
  179. ProcStartLine = lLine
  180. Exit_Function:
  181. Utils._ResetCalledSub(cstThisSub)
  182. Exit Function
  183. End Function &apos; ProcStartLine
  184. REM -----------------------------------------------------------------------------------------------------------------------
  185. Public Function Properties(ByVal Optional pvIndex As Variant) As Variant
  186. &apos; Return
  187. &apos; a Collection object if pvIndex absent
  188. &apos; a Property object otherwise
  189. Const cstThisSub = &quot;Module.Properties&quot;
  190. Utils._SetCalledSub(cstThisSub)
  191. Dim vProperty As Variant, vPropertiesList() As Variant, sObject As String
  192. vPropertiesList = _PropertiesList()
  193. sObject = Utils._PCase(_Type)
  194. If IsMissing(pvIndex) Then
  195. vProperty = PropertiesGet._Properties(sObject, _This, vPropertiesList)
  196. Else
  197. vProperty = PropertiesGet._Properties(sObject, _This, vPropertiesList, pvIndex)
  198. vProperty._Value = _PropertyGet(vPropertiesList(pvIndex))
  199. End If
  200. Exit_Function:
  201. Set Properties = vProperty
  202. Utils._ResetCalledSub(cstThisSub)
  203. Exit Function
  204. End Function &apos; Properties
  205. REM -----------------------------------------------------------------------------------------------------------------------
  206. Property Get pType() As String
  207. pType = _PropertyGet(&quot;Type&quot;)
  208. End Property &apos; Type (get)
  209. REM -----------------------------------------------------------------------------------------------------------------------
  210. REM --- CLASS METHODS ---
  211. REM -----------------------------------------------------------------------------------------------------------------------
  212. REM -----------------------------------------------------------------------------------------------------------------------
  213. Public Function Find(Optional ByVal pvTarget As Variant _
  214. , Optional ByRef pvStartLine As Variant _
  215. , Optional ByRef pvStartColumn As Variant _
  216. , Optional ByRef pvEndLine As Variant _
  217. , Optional ByRef pvEndColumn As Variant _
  218. , Optional ByVal pvWholeWord As Boolean _
  219. , Optional ByVal pvMatchCase As Boolean _
  220. , Optional ByVal pvPatternSearch As Boolean _
  221. ) As Boolean
  222. &apos; Finds specified text in the module
  223. &apos; xxLine and xxColumn arguments are mainly to return the position of the found string
  224. &apos; If they are initialized but nonsense, the function returns False
  225. Const cstThisSub = &quot;Module.Find&quot;
  226. Utils._SetCalledSub(cstThisSub)
  227. If _ErrorHandler() Then On Local Error Goto Error_Function
  228. Dim bFound As Boolean, lPosition As Long, lStartLine As Long, lStartColumn As Long, lStartPosition As Long
  229. Dim lEndLine As Long, lEndColumn As Long, lEndPosition As Long
  230. Dim sMatch As String, vOptions As Variant, sPattern As String
  231. Dim i As Integer, sSpecChar As String
  232. Const cstSpecialCharacters = &quot;\[^$.|?*+()&quot;
  233. bFound = False
  234. If IsMissing(pvTarget) Or IsMissing(pvStartLine) Or IsMissing(pvStartColumn) Or IsMissing(pvEndLine) Or IsMissing(pvEndColumn) Then Call _TraceArguments()
  235. If Not Utils._CheckArgument(pvTarget, 1, vbString) Then GoTo Exit_Function
  236. If Len(pvTarget) = 0 Then GoTo Exit_Function
  237. If Not IsEmpty(pvStartLine) Then
  238. If Not Utils._CheckArgument(pvStartLine, 2, _AddNumeric()) Then GoTo Exit_Function
  239. End If
  240. If Not IsEmpty(pvStartColumn) Then
  241. If Not Utils._CheckArgument(pvStartColumn, 3, _AddNumeric()) Then GoTo Exit_Function
  242. End If
  243. If Not IsEmpty(pvEndLine) Then
  244. If Not Utils._CheckArgument(pvEndLine, 4, _AddNumeric()) Then GoTo Exit_Function
  245. End If
  246. If Not IsEmpty(pvEndColumn) Then
  247. If Not Utils._CheckArgument(pvEndColumn, 5, _AddNumeric()) Then GoTo Exit_Function
  248. End If
  249. If IsMissing(pvWholeWord) Then pvWholeWord = False
  250. If Not Utils._CheckArgument(pvWholeWord, 6, vbBoolean) Then GoTo Exit_Function
  251. If IsMissing(pvMatchCase) Then pvMatchCase = False
  252. If Not Utils._CheckArgument(pvMatchCase, 7, vbBoolean) Then GoTo Exit_Function
  253. If IsMissing(pvPatternSearch) Then pvPatternSearch = False
  254. If Not Utils._CheckArgument(pvPatternSearch, 8, vbBoolean) Then GoTo Exit_Function
  255. &apos; Initialize starting values
  256. If IsEmpty(pvStartLine) Then lStartLine = 1 Else lStartLine = pvStartLine
  257. If lStartLine &lt;= 0 Or lStartLine &gt; UBound(_Lines) + 1 Then GoTo Exit_Function
  258. If IsEmpty(pvStartColumn) Then lStartColumn = 1 Else lStartColumn = pvStartColumn
  259. If lStartColumn &lt;= 0 Then GoTo Exit_Function
  260. If lStartColumn &gt; 1 And lStartColumn &gt; Len(_Lines(lStartLine + 1)) Then GoTo Exit_Function
  261. lStartPosition = _PositionOfLine(lStartline) + lStartColumn - 1
  262. If IsEmpty(pvEndLine) Then lEndLine = UBound(_Lines) + 1 Else lEndLine = pvEndLine
  263. If lEndLine &lt; lStartLine Or lEndLine &gt; UBound(_Lines) + 1 Then GoTo Exit_Function
  264. If IsEmpty(pvEndColumn) Then lEndColumn = Len(_Lines(lEndLine - 1)) Else lEndColumn = pvEndColumn
  265. If lEndColumn &lt; 0 Then GoTo Exit_Function
  266. If lEndColumn = 0 Then lEndColumn = 1
  267. If lEndColumn &gt; Len(_Lines(lEndLine - 1)) + 1 Then GoTo Exit_Function
  268. lEndPosition = _PositionOfLine(lEndline) + lEndColumn - 1
  269. If pvMatchCase Then
  270. Set vOptions = _A2B_.SearchOptions
  271. vOptions.transliterateFlags = 0
  272. End If
  273. &apos; Define pattern to search for
  274. sPattern = pvTarget
  275. &apos; Protect special characters in regular expressions
  276. For i = 1 To Len(cstSpecialCharacters)
  277. sSpecChar = Mid(cstSpecialCharacters, i, 1)
  278. sPattern = Replace(sPattern, sSpecChar, &quot;\&quot; &amp; sSpecChar)
  279. Next i
  280. If pvPatternSearch Then sPattern = Replace(Replace(sPattern, &quot;\*&quot;, &quot;.*&quot;), &quot;\?&quot;, &quot;.&quot;)
  281. If pvWholeWord Then sPattern = &quot;\b&quot; &amp; sPattern &amp; &quot;\b&quot;
  282. lPosition = lStartPosition
  283. sMatch = Utils._RegexSearch(_Script, sPattern, lPosition)
  284. &apos; Re-establish default options for later searches
  285. If pvMatchCase Then vOptions.transliterateFlags = com.sun.star.i18n.TransliterationModules.IGNORE_CASE
  286. &apos; Found within requested bounds ?
  287. If sMatch &lt;&gt; &quot;&quot; And lPosition &gt;= lStartPosition And lPosition &lt;= lEndPosition Then
  288. pvStartLine = _LineOfPosition(lPosition)
  289. pvStartColumn = lPosition - _PositionOfLine(pvStartLine) + 1
  290. pvEndLine = _LineOfPosition(lPosition + Len(sMatch) - 1)
  291. If pvEndLine &gt; pvStartLine Then
  292. pvEndColumn = lPosition + Len(sMatch) - 1 - _PositionOfLine(pvEndLine)
  293. Else
  294. pvEndColumn = pvStartColumn + Len(sMatch) - 1
  295. End If
  296. bFound = True
  297. End If
  298. Exit_Function:
  299. Find = bFound
  300. Utils._ResetCalledSub(cstThisSub)
  301. Exit Function
  302. Error_Function:
  303. TraceError(TRACEABORT, Err, &quot;Module.Find&quot;, Erl)
  304. bFound = False
  305. GoTo Exit_Function
  306. End Function &apos; Find
  307. REM -----------------------------------------------------------------------------------------------------------------------
  308. Public Function getProperty(Optional ByVal pvProperty As Variant) As Variant
  309. &apos; Return property value of psProperty property name
  310. Const cstThisSub = &quot;Module.Properties&quot;
  311. Utils._SetCalledSub(cstThisSub)
  312. If IsMissing(pvProperty) Then Call _TraceArguments()
  313. getProperty = _PropertyGet(pvProperty)
  314. Utils._ResetCalledSub(cstThisSub)
  315. End Function &apos; getProperty
  316. REM --------------------------------Mid(a._Script, iCtl, 25)---------------------------------------------------------------------------------------
  317. Public Function hasProperty(ByVal Optional pvProperty As Variant) As Boolean
  318. &apos; Return True if object has a valid property called pvProperty (case-insensitive comparison !)
  319. Const cstThisSub = &quot;Module.hasProperty&quot;
  320. Utils._SetCalledSub(cstThisSub)
  321. If IsMissing(pvProperty) Then hasProperty = PropertiesGet._hasProperty(_Type, _PropertiesList()) Else hasProperty = PropertiesGet._hasProperty(_Type, _PropertiesList(), pvProperty)
  322. Utils._ResetCalledSub(cstThisSub)
  323. Exit Function
  324. End Function &apos; hasProperty
  325. REM -----------------------------------------------------------------------------------------------------------------------
  326. REM --- PRIVATE FUNCTIONS ---
  327. REM -----------------------------------------------------------------------------------------------------------------------
  328. REM -----------------------------------------------------------------------------------------------------------------------
  329. Private Function _BeginStatement(ByVal plStart As Long) As Long
  330. &apos; Return the position in _Script of the beginning of the current statement as defined by plStart
  331. Dim sProc As String, iProc As Integer, iType As Integer
  332. Dim lPosition As Long, lPrevious As Long, sFind As String
  333. sProc = ProcOfLine(_LineOfPosition(plStart), iType)
  334. iProc = _FindProcIndex(sProc, iType)
  335. If iProc &lt; 0 Then lPosition = 1 Else lPosition = _ProcDecPositions(iProc)
  336. sFind = &quot;Any&quot;
  337. Do While lPosition &lt; plStart And sFind &lt;&gt; &quot;&quot;
  338. lPrevious = lPosition
  339. sFind = _FindPattern(&quot;%^\w&quot;, lPosition)
  340. If sFind = &quot;&quot; Then Exit Do
  341. Loop
  342. _BeginStatement = lPrevious
  343. End Function &apos; _EndStatement
  344. REM -----------------------------------------------------------------------------------------------------------------------
  345. Private Function _EndStatement(ByVal plStart As Long) As Long
  346. &apos; Return the position in _Script of the end of the current statement as defined by plStart
  347. &apos; plStart is assumed not to be in the middle of a comment or a string
  348. Dim sMatch As String, lPosition As Long
  349. lPosition = plStart
  350. sMatch = _FindPattern(&quot;%$&quot;, lPosition)
  351. _EndStatement = lPosition
  352. End Function &apos; _EndStatement
  353. REM -----------------------------------------------------------------------------------------------------------------------
  354. Private Function _FindPattern(ByVal psPattern As Variant, Optional ByRef plStart As Long) As String
  355. &apos; Find first occurrence of any of the patterns in |-delimited string psPattern
  356. &apos; Special escapes
  357. &apos; - for word breaks: &quot;%B&quot; (f.i. for searching &quot;END%BFUNCTION&quot;)
  358. &apos; - for statement start: &quot;%^&quot; (f.i. for searching &quot;%^END%BFUNCTION&quot;). Necessarily first 2 characters of pattern
  359. &apos; - for statement end: &quot;%$&quot;. Pattern should not contain anything else
  360. &apos; If quoted string searched, pattern should start and end with a double quote
  361. &apos; Return &quot;&quot; if none found, otherwise returns the matching string
  362. &apos; plStart = start position of _Script to search (starts at 1)
  363. &apos; In output plStart contains the first position of the matching string or is left unchanged
  364. &apos; To search again the same or another pattern =&gt; plStart = plStart + Len(matching string)
  365. &apos; Comments and strings are skipped
  366. &apos; Common patterns
  367. Const cstComment = &quot;(&apos;|\bREM\b)[^\n]*$&quot;
  368. Const cstString = &quot;&quot;&quot;[^&quot;&quot;\n]*&quot;&quot;&quot;
  369. Const cstBeginStatement = &quot;(^|:|\bthen\b|\belse\b|\n)[ \t]*&quot;
  370. Const cstEndStatement = &quot;[ \t]*($|:|\bthen\b|\belse\b|\n)&quot;
  371. Const cstContinuation = &quot;[ \t]_\n&quot;
  372. Const cstWordBreak = &quot;\b[ \t]+(_\n[ \t]*)?\b&quot;
  373. Const cstAlt = &quot;|&quot;
  374. Dim sRegex As String, lStart As Long, bContinue As Boolean, sMatch As String
  375. Dim bEndStatement As Boolean, bQuote As Boolean
  376. If psPattern = &quot;%$&quot; Then
  377. sRegex = cstEndStatement
  378. Else
  379. sRegex = psPattern
  380. If Left(psPattern, 2) = &quot;%^&quot; Then sRegex = cstBeginStatement &amp; Right(sRegex, Len(sregex) - 2)
  381. sregex = Replace(sregex, &quot;%B&quot;, cstWordBreak)
  382. End If
  383. &apos; Add all to ignore patterns to regex. If pattern = quoted string do not add cstString
  384. If Len(psPattern) &gt; 2 And Left(psPattern, 1) = &quot;&quot;&quot;&quot; And Right(psPattern, 1) = &quot;&quot;&quot;&quot; Then
  385. bQuote = True
  386. sRegex = sRegex &amp; cstAlt &amp; cstComment &amp; cstAlt &amp; cstContinuation
  387. Else
  388. bQuote = False
  389. sRegex = sRegex &amp; cstAlt &amp; cstComment &amp; cstAlt &amp; cstString &amp; cstAlt &amp; cstContinuation
  390. End If
  391. If IsMissing(plStart) Then plStart = 1
  392. lStart = plStart
  393. bContinue = True
  394. Do While bContinue
  395. bEndStatement = False
  396. sMatch = Utils._RegexSearch(_Script, sRegex, lStart)
  397. Select Case True
  398. Case sMatch = &quot;&quot;
  399. bContinue = False
  400. Case Left(sMatch, 1) = &quot;&apos;&quot;
  401. bEndStatement = True
  402. Case Left(sMatch, 1) = &quot;&quot;&quot;&quot;
  403. If bQuote Then
  404. plStart = lStart
  405. bContinue = False
  406. End If
  407. Case Left(smatch, 1) = &quot;:&quot; Or Left(sMatch, 1) = vbLf
  408. If psPattern = &quot;%$&quot; Then
  409. bEndStatement = True
  410. Else
  411. bContinue = False
  412. plStart = lStart + 1
  413. sMatch = Right(sMatch, Len(sMatch) - 1)
  414. End If
  415. Case UCase(Left(sMatch, 4)) = &quot;REM &quot; Or UCase(Left(sMatch, 4)) = &quot;REM&quot; &amp; vbTab Or UCase(Left(sMatch, 4)) = &quot;REM&quot; &amp; vbNewLine
  416. bEndStatement = True
  417. Case UCase(Left(sMatch, 4)) = &quot;THEN&quot; Or UCase(Left(sMatch, 4)) = &quot;ELSE&quot;
  418. If psPattern = &quot;%$&quot; Then
  419. bEndStatement = True
  420. Else
  421. bContinue = False
  422. plStart = lStart + 4
  423. sMatch = Right(sMatch, Len(sMatch) - 4)
  424. End If
  425. Case sMatch = &quot; _&quot; &amp; vbLf
  426. Case Else &apos; Found
  427. plStart = lStart
  428. bContinue = False
  429. End Select
  430. If bEndStatement And psPattern = &quot;%$&quot; Then
  431. bContinue = False
  432. plStart = lStart - 1
  433. sMatch = &quot;&quot;
  434. End If
  435. lStart = lStart + Len(sMatch)
  436. Loop
  437. _FindPattern = sMatch
  438. End Function &apos; _FindPattern
  439. REM -----------------------------------------------------------------------------------------------------------------------
  440. Private Function _FindProcIndex(ByVal psProc As String, ByVal piType As Integer) As Integer
  441. &apos; Return index of entry in _Procnames corresponding with pvProc
  442. Dim i As Integer, iIndex As Integer
  443. If Not _ProcsParsed Then _ParseProcs
  444. iIndex = -1
  445. For i = 0 To UBound(_ProcNames)
  446. If UCase(psProc) = UCase(_ProcNames(i)) And piType = _ProcTypes(i) Then
  447. iIndex = i
  448. Exit For
  449. End If
  450. Next i
  451. If iIndex &lt; 0 Then TraceError(TRACEFATAL, ERRPROCEDURENOTFOUND, Utils._CalledSub(), 0, , Array(psProc, _Name))
  452. Exit_Function:
  453. _FindProcIndex = iIndex
  454. Exit Function
  455. End Function &apos; _FindProcIndex
  456. REM -----------------------------------------------------------------------------------------------------------------------
  457. Public Sub _Initialize()
  458. _Script = Replace(_Script, vbCr, &quot;&quot;)
  459. _Lines = Split(_Script, vbLf)
  460. _CountOfLines = UBound(_Lines) + 1
  461. End Sub &apos; _Initialize
  462. REM -----------------------------------------------------------------------------------------------------------------------
  463. Private Function _LineOfPosition(ByVal plPosition) As Long
  464. &apos; Return the line number of a position in _Script
  465. Dim lLine As Long, lLength As Long
  466. &apos; Start counting from start or end depending on how close position is
  467. If plPosition &lt;= Len(_Script) / 2 Then
  468. lLength = 0
  469. For lLine = 0 To UBound(_Lines)
  470. lLength = lLength + Len(_Lines(lLine)) + 1 &apos; + 1 for line feed
  471. If lLength &gt;= plPosition Then
  472. _LineOfPosition = lLine + 1
  473. Exit Function
  474. End If
  475. Next lLine
  476. Else
  477. If Right(_Script, 1) = vbLf Then lLength = Len(_Script) + 1 Else lLength = Len(_Script)
  478. For lLine = UBound(_Lines) To 0 Step -1
  479. lLength = lLength - Len(_Lines(lLine)) - 1 &apos; - 1 for line feed
  480. If lLength &lt;= plPosition Then
  481. _LineOfPosition = lLine + 1
  482. Exit Function
  483. End If
  484. Next lLine
  485. End If
  486. End Function &apos; _LineOfPosition
  487. REM -----------------------------------------------------------------------------------------------------------------------
  488. Private Sub _ParseProcs()
  489. &apos; Fills the Proc arrays: name, start and end position
  490. &apos; Executed at first request needing this processing
  491. Dim lPosition As Long, iProc As Integer, sDecProc As String, sEndProc As String, sNameProc As String, sType As String
  492. Const cstDeclaration = &quot;%^(private%B|public%B)?\b(property%Bget|property%Blet|property%Bset|function|sub)\b&quot;
  493. Const cstEnd = &quot;%^end%B(property|function|sub)\b&quot;
  494. Const cstName = &quot;\w*&quot; &apos;&quot;[A-Za-z_][A-Za-z_0-9]*&quot;
  495. If _ProcsParsed Then Exit Sub &apos; Do not redo if already done
  496. _ProcNames = Array()
  497. _ProcDecPositions = Array()
  498. _ProcEndPositions = Array()
  499. _ProcTypes = Array()
  500. lPosition = 1
  501. iProc = -1
  502. sDecProc = &quot;???&quot;
  503. Do While sDecProc &lt;&gt; &quot;&quot;
  504. &apos; Identify Function/Sub declaration string
  505. sDecProc = _FindPattern(cstDeclaration, lPosition)
  506. If sDecProc &lt;&gt; &quot;&quot; Then
  507. iProc = iProc + 1
  508. ReDim Preserve _ProcNames(0 To iProc)
  509. ReDim Preserve _ProcDecPositions(0 To iProc)
  510. ReDim Preserve _ProcEndPositions(0 To iProc)
  511. ReDim Preserve _ProcTypes(0 To iProc)
  512. _ProcDecPositions(iProc) = lPosition
  513. lPosition = lPosition + Len(sDecProc)
  514. &apos; Identify procedure type
  515. Select Case True
  516. Case InStr(UCase(sDecProc), &quot;FUNCTION&quot;) &gt; 0 : _ProcTypes(iProc) = vbext_pk_Proc
  517. Case InStr(UCase(sDecProc), &quot;SUB&quot;) &gt; 0 : _ProcTypes(iProc) = vbext_pk_Proc
  518. Case InStr(UCase(sDecProc), &quot;GET&quot;) &gt; 0 : _ProcTypes(iProc) = vbext_pk_Get
  519. Case InStr(UCase(sDecProc), &quot;LET&quot;) &gt; 0 : _ProcTypes(iProc) = vbext_pk_Let
  520. Case InStr(UCase(sDecProc), &quot;SET&quot;) &gt; 0 : _ProcTypes(iProc) = vbext_pk_Set
  521. End Select
  522. &apos; Identify name of Function/Sub
  523. sNameProc = _FindPattern(cstName, lPosition)
  524. If sNameProc = &quot;&quot; Then Exit Do &apos; Should never happen
  525. _ProcNames(iProc) = sNameProc
  526. lPosition = lPosition + Len(sNameProc)
  527. &apos; Identify End statement
  528. sEndProc = _FindPattern(cstEnd, lPosition)
  529. If sEndProc = &quot;&quot; Then Exit Do &apos; Should never happen
  530. _ProcEndPositions(iProc) = lPosition
  531. lPosition = lPosition + Len(sEndProc)
  532. End If
  533. Loop
  534. _ProcsParsed = True
  535. End Sub
  536. REM -----------------------------------------------------------------------------------------------------------------------
  537. Private Function _PositionOfLine(ByVal plLine) As Long
  538. &apos; Return the position of the first character of the given line in _Script
  539. Dim lLine As Long, lPosition As Long
  540. &apos; Start counting from start or end depending on how close line is
  541. If plLine &lt;= (UBound(_Lines) + 1) / 2 Then
  542. lPosition = 0
  543. For lLine = 0 To plLine - 1
  544. lPosition = lPosition + 1 &apos; + 1 for line feed
  545. If lLine &lt; plLine - 1 Then lPosition = lPosition + Len(_Lines(lLine))
  546. Next lLine
  547. Else
  548. lPosition = Len(_Script) + 2 &apos; Anticipate an ending null-string and a line feed
  549. For lLine = UBound(_Lines) To plLine - 1 Step -1
  550. lPosition = lPosition - Len(_Lines(lLine)) - 1 &apos; - 1 for line feed
  551. Next lLine
  552. End If
  553. _PositionOfLine = lPosition
  554. End Function &apos; _LineOfPosition
  555. REM -----------------------------------------------------------------------------------------------------------------------
  556. Private Function _PropertiesList() As Variant
  557. _PropertiesList = Array(&quot;CountOfDeclarationLines&quot;, &quot;CountOfLines&quot;, &quot;Name&quot;, &quot;ObjectType&quot;, &quot;Type&quot;)
  558. End Function &apos; _PropertiesList
  559. REM -----------------------------------------------------------------------------------------------------------------------
  560. Private Function _PropertyGet(ByVal psProperty As String) As Variant
  561. &apos; Return property value of the psProperty property name
  562. Dim cstThisSub As String
  563. Const cstDot = &quot;.&quot;
  564. Dim sText As String
  565. If _ErrorHandler() Then On Local Error Goto Error_Function
  566. cstThisSub = &quot;Module.get&quot; &amp; psProperty
  567. Utils._SetCalledSub(cstThisSub)
  568. _PropertyGet = Null
  569. Select Case UCase(psProperty)
  570. Case UCase(&quot;CountOfDeclarationLines&quot;)
  571. If Not _ProcsParsed Then _ParseProcs()
  572. If UBound(_ProcNames) &gt;= 0 Then
  573. _PropertyGet = ProcStartLine(_ProcNames(0), _ProcTypes(0)) - 1
  574. Else
  575. _PropertyGet = _CountOfLines
  576. End If
  577. Case UCase(&quot;CountOfLines&quot;)
  578. _PropertyGet = _CountOfLines
  579. Case UCase(&quot;Name&quot;)
  580. _PropertyGet = _Storage &amp; cstDot &amp; _LibraryName &amp; cstDot &amp; _Name
  581. Case UCase(&quot;ObjectType&quot;)
  582. _PropertyGet = _Type
  583. Case UCase(&quot;Type&quot;)
  584. &apos; Find option statement before any procedure declaration
  585. sText = _FindPattern(&quot;%^option%Bclassmodule\b|\bfunction\b|\bsub\b|\bproperty\b&quot;)
  586. If UCase(Left(sText, 6)) = &quot;OPTION&quot; Then _PropertyGet = acClassModule Else _PropertyGet = acStandardModule
  587. Case Else
  588. Goto Trace_Error
  589. End Select
  590. Exit_Function:
  591. Utils._ResetCalledSub(cstThisSub)
  592. Exit Function
  593. Trace_Error:
  594. TraceError(TRACEFATAL, ERRPROPERTY, Utils._CalledSub(), 0, 1, psProperty)
  595. _PropertyGet = Nothing
  596. Goto Exit_Function
  597. Error_Function:
  598. TraceError(TRACEABORT, Err, &quot;Module._PropertyGet&quot;, Erl)
  599. _PropertyGet = Null
  600. GoTo Exit_Function
  601. End Function &apos; _PropertyGet
  602. </script:module>