<?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_Timer" 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_Timer
&apos;&apos;&apos;	========
&apos;&apos;&apos;		Class for management of scripts execution performance
&apos;&apos;&apos;		A Timer measures durations. It can be suspended, resumed, restarted
&apos;&apos;&apos;		Duration properties are expressed in seconds with a precision of 3 decimal digits
&apos;&apos;&apos;
&apos;&apos;&apos;		Service invocation example:
&apos;&apos;&apos;			Dim myTimer As Variant
&apos;&apos;&apos;			myTimer = CreateScriptService(&quot;Timer&quot;)
&apos;&apos;&apos;			myTimer = CreateScriptService(&quot;Timer&quot;, True)	&apos;	=&gt; To start timer immediately
&apos;&apos;&apos;
&apos;&apos;&apos;		Detailed user documentation:
&apos;&apos;&apos;			https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03/sf_timer.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

REM ============================================================= PRIVATE MEMBERS

Private [Me]				As Object
Private [_Parent]			As Object
Private ObjectType			As String		&apos; Must be &quot;TIMER&quot;
Private ServiceName			As String
Private _TimerStatus		As Integer		&apos; inactive, started, suspended or stopped
Private _StartTime			As Double		&apos; Moment when timer started, restarted
Private _EndTime			As Double		&apos; Moment when timer stopped
Private _SuspendTime		As Double		&apos; Moment when timer suspended
Private _SuspendDuration	As Double		&apos; Duration of suspended status as a difference of times

REM ============================================================ MODULE CONSTANTS

Private Const STATUSINACTIVE	= 0
Private Const STATUSSTARTED		= 1
Private Const STATUSSUSPENDED	= 2
Private Const STATUSSTOPPED		= 3

Private Const DSECOND As Double = 1 / (24 * 60 * 60)	&apos;	Duration of 1 second as compared to 1.0 = 1 day

REM ===================================================== CONSTRUCTOR/DESTRUCTOR

REM -----------------------------------------------------------------------------
Private Sub Class_Initialize()
	Set [Me] = Nothing
	Set [_Parent] = Nothing
	ObjectType = &quot;TIMER&quot;
	ServiceName = &quot;ScriptForge.Timer&quot;
	_TimerStatus = STATUSINACTIVE
	_StartTime = 0
	_EndTime = 0
	_SuspendTime = 0
	_SuspendDuration = 0
End Sub		&apos;	ScriptForge.SF_Timer Constructor

REM -----------------------------------------------------------------------------
Private Sub Class_Terminate()
	Call Class_Initialize()
End Sub		&apos;	ScriptForge.SF_Timer Destructor

REM -----------------------------------------------------------------------------
Public Function Dispose() As Variant
	Call Class_Terminate()
	Set Dispose = Nothing
End Function	&apos;	ScriptForge.SF_Timer Explicit destructor

REM ================================================================== PROPERTIES

REM -----------------------------------------------------------------------------
Public Function Duration() As Double
&apos;&apos;&apos;	Returns the actual (out of suspensions) time elapsed since start or between start and stop
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		A Double expressing the duration in seconds
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		myTimer.Duration returns 1.234 (1 sec, 234 ms)

	Duration = _PropertyGet(&quot;Duration&quot;)

End Function	&apos;	ScriptForge.SF_Timer.Duration

REM -----------------------------------------------------------------------------
Property Get IsStarted() As Boolean
&apos;&apos;&apos;	Returns True if timer is started or suspended
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		myTimer.IsStarted

	IsStarted = _PropertyGet(&quot;IsStarted&quot;)

End Property	&apos;	ScriptForge.SF_Timer.IsStarted

REM -----------------------------------------------------------------------------
Property Get IsSuspended() As Boolean
&apos;&apos;&apos;	Returns True if timer is started and suspended
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		myTimer.IsSuspended

	IsSuspended = _PropertyGet(&quot;IsSuspended&quot;)

End Property	&apos;	ScriptForge.SF_Timer.IsSuspended

REM -----------------------------------------------------------------------------
Public Function SuspendDuration() As Double
&apos;&apos;&apos;	Returns the actual time elapsed while suspended since start or between start and stop
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		A Double expressing the duration in seconds
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		myTimer.SuspendDuration returns 1.234 (1 sec, 234 ms)

	SuspendDuration = _PropertyGet(&quot;SuspendDuration&quot;)

End Function	&apos;	ScriptForge.SF_Timer.SuspendDuration

REM -----------------------------------------------------------------------------
Public Function TotalDuration() As Double
&apos;&apos;&apos;	Returns the actual time elapsed (including suspensions) since start or between start and stop
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		A Double expressing the duration in seconds
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		myTimer.TotalDuration returns 1.234 (1 sec, 234 ms)

	TotalDuration = _PropertyGet(&quot;TotalDuration&quot;)

End Function	&apos;	ScriptForge.SF_Timer.TotalDuration

REM ===================================================================== METHODS

REM -----------------------------------------------------------------------------
Public Function Continue() As Boolean
&apos;&apos;&apos;	Halt suspension of a running timer
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		True if successful, False if the timer is not suspended
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		myTimer.Continue()

Const cstThisSub = &quot;Timer.Continue&quot;
Const cstSubArgs = &quot;&quot;

Check:
	Continue = False
	SF_Utils._EnterFunction(cstThisSub, cstSubArgs)

Try:
	If _TimerStatus = STATUSSUSPENDED Then
		_TimerStatus = STATUSSTARTED
		_SuspendDuration = _SuspendDuration + _Now() - _SuspendTime
		_SuspendTime = 0
		Continue = True
	End If

Finally:
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
End Function	&apos;	ScriptForge.SF_Timer.Continue

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;	Exceptions
&apos;&apos;&apos;		ARGUMENTERROR		The property does not exist
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		myTimer.GetProperty(&quot;Duration&quot;)

Const cstThisSub = &quot;Timer.GetProperty&quot;
Const cstSubArgs = &quot;PropertyName&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_Timer.Properties

REM -----------------------------------------------------------------------------
Public Function Methods() As Variant
&apos;&apos;&apos;	Return the list or methods of the Timer class as an array

	Methods = Array( _
					&quot;Continue&quot; _
					, &quot;Restart&quot; _
					, &quot;Start&quot; _
					, &quot;Suspend&quot; _
					, &quot;Terminate&quot; _
					)

End Function	&apos;	ScriptForge.SF_Timer.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;Duration&quot; _
					, &quot;IsStarted&quot; _
					, &quot;IsSuspended&quot; _
					, &quot;SuspendDuration&quot; _
					, &quot;TotalDuration&quot; _
					)

End Function	&apos;	ScriptForge.SF_Timer.Properties

REM -----------------------------------------------------------------------------
Public Function Restart() As Boolean
&apos;&apos;&apos;	Terminate the timer and restart a new clean timer
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		True if successful, False if the timer is inactive
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		myTimer.Restart()

Const cstThisSub = &quot;Timer.Restart&quot;
Const cstSubArgs = &quot;&quot;

Check:
	Restart = False
	SF_Utils._EnterFunction(cstThisSub, cstSubArgs)

Try:
	If _TimerStatus &lt;&gt; STATUSINACTIVE Then
		If _TimerStatus &lt;&gt; STATUSSTOPPED Then Terminate()
		Start()
		Restart = True
	End If

Finally:
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
End Function	&apos;	ScriptForge.SF_Timer.Restart

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

Const cstThisSub = &quot;Timer.SetProperty&quot;
Const cstSubArgs = &quot;PropertyName, Value&quot;

	If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	SetProperty = 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:
	Select Case UCase(PropertyName)
		Case Else
	End Select

Finally:
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function	&apos;	ScriptForge.SF_Timer.SetProperty

REM -----------------------------------------------------------------------------
Public Function Start() As Boolean
&apos;&apos;&apos;	Start a new clean timer
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		True if successful, False if the timer is already started
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		myTimer.Start()

Const cstThisSub = &quot;Timer.Start&quot;
Const cstSubArgs = &quot;&quot;

Check:
	Start = False
	SF_Utils._EnterFunction(cstThisSub, cstSubArgs)

Try:
	If _TimerStatus = STATUSINACTIVE Or _TimerStatus = STATUSSTOPPED Then
		_TimerStatus = STATUSSTARTED
		_StartTime = _Now()
		_EndTime = 0
		_SuspendTime = 0
		_SuspendDuration = 0
		Start = True
	End If

Finally:
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
End Function	&apos;	ScriptForge.SF_Timer.Start

REM -----------------------------------------------------------------------------
Public Function Suspend() As Boolean
&apos;&apos;&apos;	Suspend a running timer
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		True if successful, False if the timer is not started or already suspended
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		myTimer.Suspend()

Const cstThisSub = &quot;Timer.Suspend&quot;
Const cstSubArgs = &quot;&quot;

Check:
	Suspend = False
	SF_Utils._EnterFunction(cstThisSub, cstSubArgs)

Try:
	If _TimerStatus = STATUSSTARTED Then
		_TimerStatus = STATUSSUSPENDED
		_SuspendTime = _Now()
		Suspend = True
	End If

Finally:
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
End Function	&apos;	ScriptForge.SF_Timer.Suspend

REM -----------------------------------------------------------------------------
Public Function Terminate() As Boolean
&apos;&apos;&apos;	Terminate a running timer
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		True if successful, False if the timer is neither started nor suspended
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		myTimer.Terminate()

Const cstThisSub = &quot;Timer.Terminate&quot;
Const cstSubArgs = &quot;&quot;

Check:
	Terminate = False
	SF_Utils._EnterFunction(cstThisSub, cstSubArgs)

Try:
	If _TimerStatus = STATUSSTARTED Or _TimerStatus = STATUSSUSPENDED Then
		If _TimerSTatus = STATUSSUSPENDED Then Continue()
		_TimerStatus = STATUSSTOPPED
		_EndTime = _Now()
		Terminate = True
	End If

Finally:
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
End Function	&apos;	ScriptForge.SF_Timer.Terminate

REM =========================================================== PRIVATE FUNCTIONS

REM -----------------------------------------------------------------------------
Private Function _Now() As Double
&apos;&apos;&apos;	Returns the current date and time with a required precision of at least the millisecond.
&apos;&apos;&apos;	Uses the Basic NOW() (i.o. Calc&apos;s) function as it provides the required precision since LO 25.2 (tdf#161469).
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		The actual time as a number
&apos;&apos;&apos;		The integer part represents the date, the decimal part represents the time

	_Now = CDbl(Now())

End Function	&apos;	ScriptForge.SF_Timer._Now

REM -----------------------------------------------------------------------------
Private Function _PropertyGet(Optional ByVal psProperty As String)
&apos;&apos;&apos;	Return the named property
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		psProperty: the name of the property

Dim dDuration As Double			&apos;	Computed duration
Dim cstThisSub As String
Dim cstSubArgs As String

	cstThisSub = &quot;Timer.get&quot; &amp; psProperty
	cstSubArgs = &quot;&quot;
	SF_Utils._EnterFunction(cstThisSub, cstSubArgs)

	Select Case UCase(psProperty)
		Case UCase(&quot;Duration&quot;)
			Select Case _TimerStatus
				Case STATUSINACTIVE		:	dDuration = 0.0
				Case STATUSSTARTED
					dDuration = _Now() - _StartTime - _SuspendDuration
				Case STATUSSUSPENDED
					dDuration = _SuspendTime - _StartTime - _SuspendDuration
				Case STATUSSTOPPED
					dDuration = _EndTime - _StartTime - _SuspendDuration
			End Select
			_PropertyGet = Fix(dDuration * 1000 / DSECOND) / 1000
		Case UCase(&quot;IsStarted&quot;)
			_PropertyGet = CBool( _TimerStatus = STATUSSTARTED Or _TimerStatus = STATUSSUSPENDED )
		Case UCase(&quot;IsSuspended&quot;)
			_PropertyGet = CBool( _TimerStatus = STATUSSUSPENDED )
		Case UCase(&quot;SuspendDuration&quot;)
			Select Case _TimerStatus
				Case STATUSINACTIVE		:	dDuration = 0.0
				Case STATUSSTARTED, STATUSSTOPPED
					dDuration = _SuspendDuration
				Case STATUSSUSPENDED
					dDuration = _Now() - _SuspendTime + _SuspendDuration
			End Select
			_PropertyGet = Fix(dDuration * 1000 / DSECOND) / 1000
		Case UCase(&quot;TotalDuration&quot;)
			Select Case _TimerStatus
				Case STATUSINACTIVE		:	dDuration = 0.0
				Case STATUSSTARTED, STATUSSUSPENDED
					dDuration = _Now() - _StartTime
				Case STATUSSTOPPED
					dDuration = _EndTime - _StartTime
			End Select
			_PropertyGet = Fix(dDuration * 1000 / DSECOND) / 1000
	End Select

Finally:
	SF_Utils._ExitFunction(cstThisSub)
	Exit Function
End Function	&apos;	ScriptForge.SF_Timer._PropertyGet

REM -----------------------------------------------------------------------------
Private Function _Repr() As String
&apos;&apos;&apos;	Convert the Timer instance to a readable string, typically for debugging purposes (DebugPrint ...)
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;	Return:
&apos;&apos;&apos;		&quot;[Timer] Duration:xxx.yyy

Const cstTimer = &quot;[Timer] Duration: &quot;
Const cstMaxLength = 50	&apos;	Maximum length for items

	_Repr = cstTimer &amp; Replace(SF_Utils._Repr(Duration), &quot;.&quot;, &quot;&quot;&quot;&quot;)

End Function	&apos;	ScriptForge.SF_Timer._Repr

REM ============================================ END OF SCRIPTFORGE.SF_TIMER
</script:module>