diff options
Diffstat (limited to 'wizards/source/scriptforge/SF_PythonHelper.xba')
-rw-r--r-- | wizards/source/scriptforge/SF_PythonHelper.xba | 967 |
1 files changed, 967 insertions, 0 deletions
diff --git a/wizards/source/scriptforge/SF_PythonHelper.xba b/wizards/source/scriptforge/SF_PythonHelper.xba new file mode 100644 index 000000000..99d9f86c6 --- /dev/null +++ b/wizards/source/scriptforge/SF_PythonHelper.xba @@ -0,0 +1,967 @@ +<?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_PythonHelper" 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 Explicit + +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +''' SF_PythonHelper (aka Basic) +''' =============== +''' Singleton class implementing the "ScriptForge.Basic" service +''' Implemented as a usual Basic module +''' +''' The "Basic" service must be called ONLY from a PYTHON script +''' Service invocations: Next Python code lines are equivalent: +''' bas = CreateScriptService('ScriptForge.Basic') +''' bas = CreateScriptService('Basic') +''' +''' This service proposes a collection of methods to be executed in a Python context +''' to simulate the exact behaviour of the identical Basic builtin method. +''' Typical example: +''' bas.MsgBox('This has to be displayed in a message box') +''' +''' The service includes also an agnostic "Python Dispatcher" function. +''' It dispatches Python script requests to execute Basic services to the +''' appropriate properties and methods via dynamic call techniques +''' +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +REM ================================================================== EXCEPTIONS + +REM ============================================================ MODULE CONSTANTS + +REM ===================================================== CONSTRUCTOR/DESTRUCTOR + +REM ----------------------------------------------------------------------------- +Public Function Dispose() As Variant + Set Dispose = Nothing +End Function ' ScriptForge.SF_PythonHelper Explicit destructor + +REM ================================================================== PROPERTIES + +REM ----------------------------------------------------------------------------- +Property Get ObjectType As String +''' Only to enable object representation + ObjectType = "SF_PythonHelper" +End Property ' ScriptForge.SF_PythonHelper.ObjectType + +REM ----------------------------------------------------------------------------- +Property Get ServiceName As String +''' Internal use + ServiceName = "ScriptForge.Basic" +End Property ' ScriptForge.SF_PythonHelper.ServiceName + +REM ============================================================== PUBLIC METHODS + +REM ----------------------------------------------------------------------------- +Public Function PyCDate(ByVal DateArg As Variant) As Variant +''' Convenient function to replicate CDate() in Python scripts +''' Args: +''' DateArg: a date as a string or as a double +''' Returns: +''' The converted date as a UNO DateTime structure +''' If the input argument could not be recognized as a date, return the argument unchanged +''' Example: (Python code) +''' a = bas.CDate('2021-02-18') + +Dim vDate As Variant ' Return value +Const cstThisSub = "Basic.CDate" +Const cstSubArgs = "datearg" + + On Local Error GoTo Catch + vDate = Null + +Check: + SF_Utils._EnterFunction(cstThisSub, cstSubArgs) + +Try: + vDate = CDate(DateArg) + +Finally: + If VarType(vDate) = V_DATE Then PyCDate = CDateToUnoDateTime(vDate) Else PyCDate = DateArg + SF_Utils._ExitFunction(cstThisSub) + Exit Function +Catch: + On Local Error GoTo 0 + GoTo Finally +End Function ' ScriptForge.SF_PythonHelper.PyCDate + +REM ----------------------------------------------------------------------------- +Public Function PyConvertFromUrl(ByVal FileName As Variant) As String +''' Convenient function to replicate ConvertFromUrl() in Python scripts +''' Args: +''' FileName: a string representing a file in URL format +''' Returns: +''' The same file name in native operating system notation +''' Example: (Python code) +''' a = bas.ConvertFromUrl('file:////boot.sys') + +Dim sFileName As String ' Return value +Const cstThisSub = "Basic.ConvertFromUrl" +Const cstSubArgs = "filename" + + If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch + sFileName = "" + +Check: + SF_Utils._EnterFunction(cstThisSub, cstSubArgs) + +Try: + sFileName = ConvertFromUrl(FileName) + +Finally: + PyConvertFromUrl = sFileName + SF_Utils._ExitFunction(cstThisSub) + Exit Function +Catch: + GoTo Finally +End Function ' ScriptForge.SF_PythonHelper.PyConvertFromUrl + +REM ----------------------------------------------------------------------------- +Public Function PyConvertToUrl(ByVal FileName As Variant) As String +''' Convenient function to replicate ConvertToUrl() in Python scripts +''' Args: +''' FileName: a string representing a file in native operating system notation +''' Returns: +''' The same file name in URL format +''' Example: (Python code) +''' a = bas.ConvertToUrl('C:\boot.sys') + +Dim sFileName As String ' Return value +Const cstThisSub = "Basic.ConvertToUrl" +Const cstSubArgs = "filename" + + If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch + sFileName = "" + +Check: + SF_Utils._EnterFunction(cstThisSub, cstSubArgs) + +Try: + sFileName = ConvertToUrl(FileName) + +Finally: + PyConvertToUrl = sFileName + SF_Utils._ExitFunction(cstThisSub) + Exit Function +Catch: + GoTo Finally +End Function ' ScriptForge.SF_PythonHelper.PyConvertToUrl + +REM ----------------------------------------------------------------------------- +Public Function PyCreateUnoService(ByVal UnoService As Variant) As Variant +''' Convenient function to replicate CreateUnoService() in Python scripts +''' Args: +''' UnoService: a string representing the service to create +''' Returns: +''' A UNO object +''' Example: (Python code) +''' a = bas.CreateUnoService('com.sun.star.i18n.CharacterClassification') + +Dim vUno As Variant ' Return value +Const cstThisSub = "Basic.CreateUnoService" +Const cstSubArgs = "unoservice" + + If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch + Set vUno = Nothing + +Check: + SF_Utils._EnterFunction(cstThisSub, cstSubArgs) + +Try: + Set vUno = CreateUnoService(UnoService) + +Finally: + Set PyCreateUnoService = vUno + SF_Utils._ExitFunction(cstThisSub) + Exit Function +Catch: + GoTo Finally +End Function ' ScriptForge.SF_PythonHelper.PyCreateUnoService + +REM ----------------------------------------------------------------------------- +Public Function PyDateAdd(ByVal Add As Variant _ + , ByVal Count As Variant _ + , ByVal DateArg As Variant _ + ) As Variant +''' Convenient function to replicate DateAdd() in Python scripts +''' Args: +''' Add: The unit to add +''' Count: how many times to add (might be negative) +''' DateArg: a date as a com.sun.star.util.DateTime UNO structure +''' Returns: +''' The new date as a string in iso format +''' Example: (Python code) +''' a = bas.DateAdd('d', 1, bas.Now()) ' Tomorrow + +Dim vNewDate As Variant ' Return value +Dim vDate As Date ' Alias of DateArg +Const cstThisSub = "Basic.DateAdd" +Const cstSubArgs = "add, count, datearg" + + If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch + vNewDate = "" + +Check: + SF_Utils._EnterFunction(cstThisSub, cstSubArgs) + +Try: + If VarType(DateArg) = V_OBJECT Then + vDate = CDateFromUnoDateTime(DateArg) + Else + vDate = SF_Utils._CStrToDate(DateArg) + End If + vNewDate = DateAdd(Add, Count, vDate) + +Finally: + If VarType(vNewDate) = V_DATE Then PyDateAdd = CDateToUnoDateTime(vNewDate) Else PyDateAdd = vNewDate + SF_Utils._ExitFunction(cstThisSub) + Exit Function +Catch: + GoTo Finally +End Function ' ScriptForge.SF_PythonHelper.PyDateAdd + +REM ----------------------------------------------------------------------------- +Public Function PyDateDiff(ByVal Add As Variant _ + , ByVal Date1 As Variant _ + , ByVal Date2 As Variant _ + , ByVal WeekStart As Variant _ + , ByVal YearStart As Variant _ + ) As Long +''' Convenient function to replicate DateDiff() in Python scripts +''' Args: +''' Add: The unit of the date interval +''' Date1, Date2: the two dates to be compared +''' WeekStart: the starting day of a week +''' YearStart: the starting week of a year +''' Returns: +''' The number of intervals expressed in Adds +''' Example: (Python code) +''' a = bas.DateDiff('d', bas.DateAdd('d', 1, bas.Now()), bas.Now()) ' -1 day + +Dim lDiff As Long ' Return value +Dim vDate1 As Date ' Alias of Date1 +Dim vDate2 As Date ' Alias of Date2 +Const cstThisSub = "Basic.DateDiff" +Const cstSubArgs = "add, date1, date2, [weekstart=1], [yearstart=1]" + + If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch + lDiff = 0 + +Check: + SF_Utils._EnterFunction(cstThisSub, cstSubArgs) + +Try: + If VarType(Date1) = V_OBJECT Then + vDate1 = CDateFromUnoDateTime(Date1) + Else + vDate1 = SF_Utils._CStrToDate(Date1) + End If + If VarType(Date2) = V_OBJECT Then + vDate2 = CDateFromUnoDateTime(Date2) + Else + vDate2 = SF_Utils._CStrToDate(Date2) + End If + lDiff = DateDiff(Add, vDate1, vDate2, WeekStart, YearStart) + + +Finally: + PyDateDiff = lDiff + SF_Utils._ExitFunction(cstThisSub) + Exit Function +Catch: + GoTo Finally +End Function ' ScriptForge.SF_PythonHelper.PyDateDiff + +REM ----------------------------------------------------------------------------- +Public Function PyDatePart(ByVal Add As Variant _ + , ByVal DateArg As Variant _ + , ByVal WeekStart As Variant _ + , ByVal YearStart As Variant _ + ) As Long +''' Convenient function to replicate DatePart() in Python scripts +''' Args: +''' Add: The unit of the date interval +''' DateArg: The date from which to extract a part +''' WeekStart: the starting day of a week +''' YearStart: the starting week of a year +''' Returns: +''' The specified part of the date +''' Example: (Python code) +''' a = bas.DatePart('y', bas.Now()) ' day of year + +Dim lPart As Long ' Return value +Dim vDate As Date ' Alias of DateArg +Const cstThisSub = "Basic.DatePart" +Const cstSubArgs = "add, datearg, [weekstart=1], [yearstart=1]" + + If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch + lPart = 0 + +Check: + SF_Utils._EnterFunction(cstThisSub, cstSubArgs) + +Try: + If VarType(DateArg) = V_OBJECT Then + vDate = CDateFromUnoDateTime(DateArg) + Else + vDate = SF_Utils._CStrToDate(DateArg) + End If + lPart = DatePart(Add, vDate, WeekStart, YearStart) + + +Finally: + PyDatePart = lPart + SF_Utils._ExitFunction(cstThisSub) + Exit Function +Catch: + GoTo Finally +End Function ' ScriptForge.SF_PythonHelper.PyDatePart + +REM ----------------------------------------------------------------------------- +Public Function PyDateValue(ByVal DateArg As Variant) As Variant +''' Convenient function to replicate DateValue() in Python scripts +''' Args: +''' DateArg: a date as a string +''' Returns: +''' The converted date as a UNO DateTime structure +''' Example: (Python code) +''' a = bas.DateValue('2021-02-18') + +Dim vDate As Variant ' Return value +Const cstThisSub = "Basic.DateValue" +Const cstSubArgs = "datearg" + + If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch + vDate = "" + +Check: + SF_Utils._EnterFunction(cstThisSub, cstSubArgs) + +Try: + vDate = DateValue(DateArg) + +Finally: + If VarType(vDate) = V_DATE Then PyDateValue = CDateToUnoDateTime(vDate) Else PyDateValue = vDate + SF_Utils._ExitFunction(cstThisSub) + Exit Function +Catch: + GoTo Finally +End Function ' ScriptForge.SF_PythonHelper.PyDateValue + +REM ----------------------------------------------------------------------------- +Public Function PyFormat(ByVal Value As Variant _ + , ByVal Pattern As Variant _ + ) As String +''' Convenient function to replicate Format() in Python scripts +''' Args: +''' Value: a date or a number +''' Pattern: the format to apply +''' Returns: +''' The formatted value +''' Example: (Python code) +''' MsgBox bas.Format(6328.2, '##,##0.00') + +Dim sFormat As String ' Return value +Dim vValue As Variant ' Alias of Value +Const cstThisSub = "Basic.Format" +Const cstSubArgs = "value, pattern" + + If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch + sFormat = "" + +Check: + SF_Utils._EnterFunction(cstThisSub, cstSubArgs) + +Try: + If VarType(Value) = V_OBJECT Then vValue = CDateFromUnoDateTime(Value) ELse vValue = Value + If IsEmpty(Pattern) Or Len(Pattern) = 0 Then sFormat = Str(vValue) Else sFormat = Format(vValue, Pattern) + + +Finally: + PyFormat = sFormat + SF_Utils._ExitFunction(cstThisSub) + Exit Function +Catch: + GoTo Finally +End Function ' ScriptForge.SF_PythonHelper.PyFormat + +REM ----------------------------------------------------------------------------- +Public Function PyGetGuiType() As Integer +''' Convenient function to replicate GetGuiType() in Python scripts +''' Args: +''' Returns: +''' The GetGuiType value +''' Example: (Python code) +''' MsgBox bas.GetGuiType() + +Const cstThisSub = "Basic.GetGuiType" +Const cstSubArgs = "" + +Check: + SF_Utils._EnterFunction(cstThisSub, cstSubArgs) + +Try: + PyGetGuiType = GetGuiType() + + +Finally: + SF_Utils._ExitFunction(cstThisSub) + Exit Function +End Function ' ScriptForge.SF_PythonHelper.PyGetGuiType + +REM ----------------------------------------------------------------------------- +Public Function PyGetSystemTicks() As Long +''' Convenient function to replicate GetSystemTicks() in Python scripts +''' Args: +''' Returns: +''' The GetSystemTicks value +''' Example: (Python code) +''' MsgBox bas.GetSystemTicks() + +Const cstThisSub = "Basic.GetSystemTicks" +Const cstSubArgs = "" + +Check: + SF_Utils._EnterFunction(cstThisSub, cstSubArgs) + +Try: + PyGetSystemTicks = GetSystemTicks() + + +Finally: + SF_Utils._ExitFunction(cstThisSub) + Exit Function +End Function ' ScriptForge.SF_PythonHelper.PyGetSystemTicks + +REM ----------------------------------------------------------------------------- +Public Function PyGlobalScope(ByVal Library As Variant) As Object +''' Convenient function to replicate GlobalScope() in Python scripts +''' Args: +''' Library: "Basic" or "Dialog" +''' Returns: +''' The GlobalScope value +''' Example: (Python code) +''' MsgBox bas.GlobalScope.BasicLibraries() + +Const cstThisSub = "Basic.GlobalScope.BasicLibraries" ' or DialogLibraries +Const cstSubArgs = "" + +Check: + SF_Utils._EnterFunction(cstThisSub, cstSubArgs) + +Try: + Select Case Library + Case "Basic" + PyGlobalScope = GlobalScope.BasicLibraries() + Case "Dialog" + PyGlobalScope = GlobalScope.DialogLibraries() + Case Else + End Select + +Finally: + SF_Utils._ExitFunction(cstThisSub) + Exit Function +End Function ' ScriptForge.SF_PythonHelper.PyGlobalScope + +REM ----------------------------------------------------------------------------- +Public Function PyInputBox(ByVal Msg As Variant _ + , ByVal Title As Variant _ + , ByVal Default As Variant _ + , Optional ByVal XPosTwips As Variant _ + , Optional ByVal YPosTwips As Variant _ + ) As String +''' Convenient function to replicate InputBox() in Python scripts +''' Args: +''' Msg: String expression displayed as the message in the dialog box +''' Title: String expression displayed in the title bar of the dialog box +''' Default: String expression displayed in the text box as default if no other input is given +''' XPosTwips: Integer expression that specifies the horizontal position of the dialog +''' YPosTwips: Integer expression that specifies the vertical position of the dialog +''' If XPosTwips and YPosTwips are omitted, the dialog is centered on the screen +''' The position is specified in twips. +''' Returns: +''' The entered value or "" if the user pressed the Cancel button +''' Example: (Python code) +''' a = bas.InputBox ('Please enter a phrase:', 'Dear User') + +Dim sInput As String ' Return value +Const cstThisSub = "Basic.InputBox" +Const cstSubArgs = "msg, [title=''], [default=''], [xpostwips], [ypostwips]" + + If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch + sInput = "" + +Check: + If IsMissing(YPosTwips) Then YPosTwips = 1 + SF_Utils._EnterFunction(cstThisSub, cstSubArgs) + +Try: + If IsMissing(XPosTwips) Then + sInput = InputBox(Msg, Title, Default) + Else + sInput = InputBox(Msg, Title, Default, XPosTwips, YPosTwips) + End If + +Finally: + PyInputBox = sInput + SF_Utils._ExitFunction(cstThisSub) + Exit Function +Catch: + GoTo Finally +End Function ' ScriptForge.SF_PythonHelper.PyInputBox + +REM ----------------------------------------------------------------------------- +Public Function PyMsgBox(ByVal Text As Variant _ + , ByVal DialogType As Variant _ + , ByVal DialogTitle As Variant _ + ) As Integer +''' Convenient function to replicate MsgBox() in Python scripts +''' Args: +''' Text: String expression displayed as a message in the dialog box +''' DialogType: Any integer expression that defines the number and type of buttons or icons displayed +''' DialogTitle: String expression displayed in the title bar of the dialog +''' Returns: +''' The pressed button +''' Example: (Python code) +''' a = bas.MsgBox ('Please press a button:', bas.MB_EXCLAMATION, 'Dear User') + +Dim iMsg As Integer ' Return value +Const cstThisSub = "Basic.MsgBox" +Const cstSubArgs = "text, [dialogtype=0], [dialogtitle]" + + If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch + iMsg = -1 + +Check: + SF_Utils._EnterFunction(cstThisSub, cstSubArgs) + +Try: + iMsg = MsgBox(Text, DialogType, DialogTitle) + +Finally: + PyMsgBox = iMsg + SF_Utils._ExitFunction(cstThisSub) + Exit Function +Catch: + GoTo Finally +End Function ' ScriptForge.SF_PythonHelper.PyMsgBox + +REM ============================================================= PRIVATE METHODS + +REM ----------------------------------------------------------------------------- +Public Function _PythonDispatcher(ByRef BasicObject As Variant _ + , ByVal CallType As Variant _ + , ByVal Script As Variant _ + , ParamArray Args() As Variant _ + ) As Variant +''' Called from Python only +''' The method calls the method Script associated with the BasicObject class or module +''' with the given arguments +''' The invocation of the method can be a Property Get, Property Let or a usual call +''' NB: arguments and return values must not be 2D arrays +''' The implementation intends to be as AGNOSTIC as possible in terms of objects nature and methods called +''' Args: +''' BasicObject: a module or a class instance - May also be the reserved string: "SF_Services" +''' CallType: one of the constants applicable to a CallByName statement + optional protocol flags +''' Script: the name of the method or property +''' Args: the arguments to pass to the method. Input arguments can contain symbolic constants for Null, Missing, etc. +''' Returns: +''' A 1D array: +''' [0] The returned value - scalar, object or 1D array +''' [1] The VarType() of the returned value +''' Null, Empty and Nothing have different vartypes but return all None to Python +''' Additionally, when array: +''' [2] Number of dimensions in Basic +''' Additionally, when Basic object: +''' [2] Module (1), Class instance (2) or UNO (3) +''' [3] The object's ObjectType +''' [4] The object's service name +''' [5] The object's name +''' When an error occurs Python receives None as a scalar. This determines the occurrence of a failure + +Dim vReturn As Variant ' The value returned by the invoked property or method +Dim vReturnArray As Variant ' Return value +Dim vBasicObject As Variant ' Alias of BasicObject to avoid "Object reference not set" error +Dim iNbArgs As Integer ' Number of valid input arguments +Dim vArg As Variant ' Alias for a single argument +Dim vArgs() As Variant ' Alias for Args() +Dim sScript As String ' Argument of ExecuteBasicScript() +Dim vParams As Variant ' Array of arguments to pass to a ParamArray +Dim sObjectType As String ' Alias of object.ObjectType +Dim sServiceName As String ' Alias of BasicObject.ServiceName +Dim bBasicClass As Boolean ' True when BasicObject is a class +Dim sLibrary As String ' Library where the object belongs to +Dim bUno As Boolean ' Return value is a UNO object +Dim oObjDesc As Object ' _ObjectDescriptor type +Dim iDims As Integer ' # of dims of vReturn +Dim sess As Object : Set sess = ScriptForge.SF_Session +Dim i As Long, j As Long + +' Conventional special input or output values +Const cstNoArgs = "+++NOARGS+++", cstSymEmpty = "+++EMPTY+++", cstSymNull = "+++NULL+++", cstSymMissing = "+++MISSING+++" + +' https://support.office.com/en-us/article/CallByName-fonction-49ce9475-c315-4f13-8d35-e98cfe98729a +' Determines the CallType +Const vbGet = 2, vbLet = 4, vbMethod = 1, vbSet = 8 +' Protocol flags +Const cstDateArg = 64 ' May contain a date argument +Const cstDateRet = 128 ' Return value can be a date +Const cstUno = 256 ' Return value can be a UNO object +Const cstArgArray = 512 ' Any argument can be a 2D array +Const cstRetArray = 1024 ' Return value can be an array +Const cstObject = 2048 ' 1st argument is a Basic object when numeric +Const cstHardCode = 4096 ' Method must not be executed with CallByName() +' Object nature in returned array +Const objMODULE = 1, objCLASS = 2, objUNO = 3 + +Check: + If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch + _PythonDispatcher = Null + + ' Ignore Null basic objects (Null = Null or Nothing) + If IsNull(BasicObject) Or IsEmpty(BasicObject) Then GoTo Catch + + ' Reinterpret arguments one by one into vArgs, convert UNO date/times and conventional NoArgs/Empty/Null/Missing values + iNbArgs = -1 + vArgs = Array() + + If UBound(Args) >= 0 Then + For i = 0 To UBound(Args) + vArg = Args(i) + ' Are there arguments ? + If i = 0 And VarType(vArg) = V_STRING Then + If vArg = cstNoArgs Then Exit For + End If + ' Is 1st argument a reference to a Basic object ? + If i = 0 And (( CallType And cstObject ) = cstObject) And SF_Utils._VarTypeExt(vArg) = V_NUMERIC Then + If vArg < 0 Or Not IsArray(_SF_.PythonStorage) Then GoTo Catch + If vArg > UBound(_SF_.PythonStorage) Then GoTo Catch + vArg = _SF_.PythonStorage(vArg) + ' Is argument a symbolic constant for Null, Empty, ... , or a date? + ElseIf VarType(vArg) = V_STRING Then + If Len(vArg) = 0 Then + ElseIf vArg = cstSymEmpty Then + vArg = Empty + ElseIf vArg = cstSymNull Then + vArg = Null + ElseIf vArg = cstSymMissing Then + Exit For ' Next arguments must be missing also + End If + ElseIf VarType(vArg) = V_OBJECT Then + If ( CallType And cstDateArg ) = cstDateArg Then vArg = CDateFromUnoDateTime(vArg) + End If + iNbArgs = iNbArgs + 1 + + ReDim Preserve vArgs(iNbArgs) + vArgs(iNbArgs) = vArg + Next i + End If + +Try: + ' Dispatching strategy: based on next constraints + ' (1) Bug https://bugs.documentfoundation.org/show_bug.cgi?id=138155 + ' The CallByName function fails when returning an array + ' (2) Python has tuples and tuple of tuples, not 2D arrays + ' (3) Passing 2D arrays through a script provider always transform it into a sequence of sequences + ' (4) The CallByName function takes exclusive control on the targeted object up to its exit + ' 1. Methods in usual modules are called by ExecuteBasicScript() except if they use a ParamArray + ' 2. Properties in any service are got and set with obj.GetProperty/SetProperty(...) + ' 3. Methods in class modules are invoked with CallByName + ' 4. Methods in class modules using a 2D array or returning arrays, or methods using ParamArray, +''' are hardcoded as exceptions or are not implemented + ' 5. Due to constraint (4), a predefined list of method calls must be hardcoded to avoid blocking use of CallByName + ' The concerned methods are flagged with cstHardCode + + With _SF_ + ' Initialize Python persistent storage at 1st call + If IsEmpty(.PythonStorage) Then ._InitPythonStorage() + ' Reset any error + ._Stackreset() + ' Set Python trigger to manage signatures in error messages + .TriggeredByPython = True + End With + + Select case VarType(BasicObject) + Case V_STRING + ' Special entry for CreateScriptService() + vBasicObject = BasicObject + If vBasicObject = "SF_Services" Then + If UBound(vArgs) = 0 Then vParams = Array() Else vParams = SF_Array.Slice(vArgs, 1) + Select Case UBound(vParams) + Case -1 : vReturn = SF_Services.CreateScriptService(vArgs(0)) + Case 0 : vReturn = SF_Services.CreateScriptService(vArgs(0), vParams(0)) + Case 1 : vReturn = SF_Services.CreateScriptService(vArgs(0), vParams(0), vParams(1)) + Case 2 : vReturn = SF_Services.CreateScriptService(vArgs(0), vParams(0), vParams(1), vParams(2)) + Case 3 : vReturn = SF_Services.CreateScriptService(vArgs(0), vParams(0), vParams(1), vParams(2), vParams(3)) + Case 4 : vReturn = SF_Services.CreateScriptService(vArgs(0), vParams(0), vParams(1), vParams(2), vParams(3), vParams(4)) + End Select + End If + If VarType(vReturn) = V_OBJECT And Not IsNull(vReturn) Then + vBasicObject = vReturn + sObjectType = vBasicObject.ObjectType + bBasicClass = ( Left(sObjectType, 3) <> "SF_" ) + End If + + ' Implement dispatching strategy + Case V_INTEGER + If BasicObject < 0 Or Not IsArray(_SF_.PythonStorage) Then GoTo Catch + If BasicObject > UBound(_SF_.PythonStorage) Then GoTo Catch + vBasicObject = _SF_.PythonStorage(BasicObject) + sObjectType = vBasicObject.ObjectType + sServiceName = vBasicObject.ServiceName + + ' Basic modules have type = "SF_*" + bBasicClass = ( Left(sObjectType, 3) <> "SF_" ) + sLibrary = Split(sServiceName, ".")(0) + + ' Methods in standard modules returning/passing a date are hardcoded as exceptions + If Not bBasicClass And ((CallType And vbMethod) = vbMethod) _ + And (((CallType And cstDateRet) = cstDateRet) Or ((CallType And cstDateArg) = cstDateArg)) Then + Select Case sServiceName + Case "ScriptForge.FileSystem" + If Script = "GetFileModified" Then vReturn = SF_FileSystem.GetFileModified(vArgs(0)) + Case "ScriptForge.Region" + Select Case Script + Case "DSTOffset" : vReturn = SF_Region.DSTOffset(vArgs(0), vArgs(1), vArgs(2)) + Case "LocalDateTime" : vReturn = SF_Region.LocalDateTime(vArgs(0), vArgs(1), vArgs(2)) + Case "UTCDateTime" : vReturn = SF_Region.UTCDateTime(vArgs(0), vArgs(1), vArgs(2)) + Case "UTCNow" : vReturn = SF_Region.UTCNow(vArgs(0), vArgs(1)) + Case Else + End Select + End Select + + ' Methods in usual modules using a 2D array or returning arrays are hardcoded as exceptions + ElseIf Not bBasicClass And _ + (((CallType And vbMethod) + (CallType And cstArgArray)) = vbMethod + cstArgArray Or _ + ((CallType And vbMethod) + (CallType And cstRetArray)) = vbMethod + cstRetArray) Then + ' Not service related + If Script = "Methods" Then + vReturn = vBasicObject.Methods() + ElseIf Script = "Properties" Then + vReturn = vBasicObject.Properties() + Else + Select Case sServiceName + Case "ScriptForge.Array" + If Script = "ImportFromCSVFile" Then vReturn = SF_Array.ImportFromCSVFile(vArgs(0), vArgs(1), vArgs(2), True) + End Select + End If + + ' Methods in usual modules are called by ExecuteBasicScript() except if they use a ParamArray + ElseIf Not bBasicClass And (CallType And vbMethod) = vbMethod Then + sScript = sLibrary & "." & sObjectType & "." & Script + ' Force validation in targeted function, not in ExecuteBasicScript() + _SF_.StackLevel = -1 + Select Case UBound(vArgs) + Case -1 : vReturn = sess.ExecuteBasicScript(, sScript) + Case 0 : vReturn = sess.ExecuteBasicScript(, sScript, vArgs(0)) + Case 1 : vReturn = sess.ExecuteBasicScript(, sScript, vArgs(0), vArgs(1)) + Case 2 : vReturn = sess.ExecuteBasicScript(, sScript, vArgs(0), vArgs(1), vArgs(2)) + Case 3 : vReturn = sess.ExecuteBasicScript(, sScript, vArgs(0), vArgs(1), vArgs(2), vArgs(3)) + Case 4 : vReturn = sess.ExecuteBasicScript(, sScript, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4)) + Case 5 : vReturn = sess.ExecuteBasicScript(, sScript, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5)) + Case 6 : vReturn = sess.ExecuteBasicScript(, sScript, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6)) + Case 7 : vReturn = sess.ExecuteBasicScript(, sScript, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7)) + End Select + _SF_.StackLevel = 0 + + ' Properties in any service are got and set with obj.GetProperty/SetProperty(...) + ElseIf (CallType And vbGet) = vbGet Then ' In some cases (Calc ...) GetProperty may have an argument + If UBound(vArgs) < 0 Then vReturn = vBasicObject.GetProperty(Script) Else vReturn = vBasicObject.GetProperty(Script, vArgs(0)) + ElseIf (CallType And vbLet) = vbLet Then + vReturn = vBasicObject.SetProperty(Script, vArgs(0)) + + ' Methods in class modules using a 2D array or returning arrays are hardcoded as exceptions. Bug #138155 + ElseIf ((CallType And vbMethod) + (CallType And cstArgArray)) = vbMethod + cstArgArray Or _ + ((CallType And vbMethod) + (CallType And cstRetArray)) = vbMethod + cstRetArray Then + If Script = "Methods" Then + vReturn = vBasicObject.Methods() + ElseIf Script = "Properties" Then + vReturn = vBasicObject.Properties() + Else + Select Case sServiceName + Case "SFDatabases.Database" + If Script = "GetRows" Then vReturn = vBasicObject.GetRows(vArgs(0), vArgs(1), vArgs(2), vArgs(3)) + Case "SFDialogs.Dialog" + If Script = "Controls" Then vReturn = vBasicObject.Controls(vArgs(0)) + Case "SFDialogs.DialogControl" + If Script = "SetTableData" Then vReturn = vBasicObject.SetTableData(vArgs(0), vArgs(1), vArgs(2)) + Case "SFDocuments.Document" + If Script = "Forms" Then vReturn = vBasicObject.Forms(vArgs(0)) + Case "SFDocuments.Base" + Select Case Script + Case "FormDocuments" : vReturn = vBasicObject.FormDocuments() + Case "Forms" : vReturn = vBasicObject.Forms(vArgs(0), vArgs(1)) + End Select + Case "SFDocuments.Calc" + Select Case Script + Case "Charts" : vReturn = vBasicObject.Charts(vArgs(0), vArgs(1)) + Case "Forms" : vReturn = vBasicObject.Forms(vArgs(0), vArgs(1)) + Case "GetFormula" : vReturn = vBasicObject.GetFormula(vArgs(0)) + Case "GetValue" : vReturn = vBasicObject.GetValue(vArgs(0)) + Case "SetArray" : vReturn = vBasicObject.SetArray(vArgs(0), vArgs(1)) + Case "SetFormula" : vReturn = vBasicObject.SetFormula(vArgs(0), vArgs(1)) + Case "SetValue" : vReturn = vBasicObject.SetValue(vArgs(0), vArgs(1)) + End Select + Case "SFDocuments.Form" + Select Case Script + Case "Controls" : vReturn = vBasicObject.Controls(vArgs(0)) + Case "Subforms" : vReturn = vBasicObject.Subforms(vArgs(0)) + End Select + Case "SFDocuments.FormControl" + If Script = "Controls" Then vReturn = vBasicObject.Controls(vArgs(0)) + End Select + End If + + ' Methods in class modules may better not be executed with CallByName() + ElseIf bBasicClass And ((CallType And vbMethod) + (CallType And cstHardCode)) = vbMethod + cstHardCode Then + Select Case sServiceName + Case "SFDialogs.Dialog" + Select Case Script + Case "Activate" : vReturn = vBasicObject.Activate() + Case "Center" + If UBound(vArgs) < 0 Then vReturn = vBasicObject.Center() Else vReturn = vBasicObject.Center(vArgs(0)) + Case "EndExecute" : vReturn = vBasicObject.EndExecute(vArgs(0)) + Case "Execute" : vReturn = vBasicObject.Execute(vArgs(0)) + Case "Resize" : vReturn = vBasicObject.Resize(vArgs(0), vArgs(1), vArgs(2), vArgs(3)) + End Select + End Select + + ' Methods in class modules are invoked with CallByName + ElseIf bBasicClass And ((CallType And vbMethod) = vbMethod) Then + Select Case UBound(vArgs) + ' Dirty alternatives to process usual and ParamArray cases + ' But, up to ... how many ? + ' - The OFFSETADDRESSERROR has 12 arguments + ' - The ".uno:DataSort" command may have 14 property name-value pairs + Case -1 : vReturn = CallByName(vBasicObject, Script, vbMethod) + Case 0 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0)) + Case 1 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1)) + Case 2 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2)) + Case 3 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3)) + Case 4 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4)) + Case 5 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5)) + Case 6 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6)) + Case 7 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7)) + Case 8 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ + , vArgs(8)) + Case 9 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ + , vArgs(8), vArgs(9)) + Case 10 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ + , vArgs(8), vArgs(9), vArgs(10)) + Case 11 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ + , vArgs(8), vArgs(9), vArgs(10), vArgs(11)) + Case 12, 13 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ + , vArgs(8), vArgs(9), vArgs(10), vArgs(11), vArgs(12)) + Case 14, 15 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ + , vArgs(8), vArgs(9), vArgs(10), vArgs(11), vArgs(12), vArgs(13), vArgs(14)) + Case 16, 17 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ + , vArgs(8), vArgs(9), vArgs(10), vArgs(11), vArgs(12), vArgs(13), vArgs(14), vArgs(15), vArgs(16)) + Case 18, 19 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ + , vArgs(8), vArgs(9), vArgs(10), vArgs(11), vArgs(12), vArgs(13), vArgs(14), vArgs(15), vArgs(16), vArgs(17), vArgs(18)) + Case 20, 21 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ + , vArgs(8), vArgs(9), vArgs(10), vArgs(11), vArgs(12), vArgs(13), vArgs(14), vArgs(15), vArgs(16), vArgs(17), vArgs(18) _ + , vArgs(19), vArgs(20)) + Case 22, 23 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ + , vArgs(8), vArgs(9), vArgs(10), vArgs(11), vArgs(12), vArgs(13), vArgs(14), vArgs(15), vArgs(16), vArgs(17), vArgs(18) _ + , vArgs(19), vArgs(20), vArgs(21), vArgs(22)) + Case 24, 25 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ + , vArgs(8), vArgs(9), vArgs(10), vArgs(11), vArgs(12), vArgs(13), vArgs(14), vArgs(15), vArgs(16), vArgs(17), vArgs(18) _ + , vArgs(19), vArgs(20), vArgs(21), vArgs(22), vArgs(23), vArgs(24)) + Case 26, 27 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ + , vArgs(8), vArgs(9), vArgs(10), vArgs(11), vArgs(12), vArgs(13), vArgs(14), vArgs(15), vArgs(16), vArgs(17), vArgs(18) _ + , vArgs(19), vArgs(20), vArgs(21), vArgs(22), vArgs(23), vArgs(24), vArgs(25), vArgs(26)) + Case >= 28 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ + , vArgs(8), vArgs(9), vArgs(10), vArgs(11), vArgs(12), vArgs(13), vArgs(14), vArgs(15), vArgs(16), vArgs(17), vArgs(18) _ + , vArgs(19), vArgs(20), vArgs(21), vArgs(22), vArgs(23), vArgs(24), vArgs(25), vArgs(26), vArgs(27), vArgs(28)) + End Select + End If + + ' Post processing + If Script = "Dispose" Then + ' Special case: Dispose() must update the cache for class objects created in Python scripts + Set _SF_.PythonStorage(BasicObject) = Nothing + End If + Case Else + End Select + + ' Format the returned array + vReturnArray = Array() + ' Distinguish: Basic object + ' UNO object + ' Array + ' Scalar + If IsArray(vReturn) Then + ReDim vReturnArray(0 To 2) + iDims = SF_Array.CountDims(vReturn) + ' Replace dates by UNO format + If iDims = 1 Then + For i = LBound(vReturn) To UBound(vReturn) + If VarType(vReturn(i)) = V_DATE Then vReturn(i) = CDateToUnoDateTime(vReturn(i)) + Next i + ElseIf iDims = 2 Then + For i = LBound(vReturn, 1) To UBound(vReturn, 1) + For j = LBound(vReturn, 2) To UBound(vReturn, 2) + If VarType(vReturn(i, j)) = V_DATE Then vReturn(i, j) = CDateToUnoDateTime(vReturn(i, j)) + Next j + Next i + End If + vReturnArray(0) = vReturn ' 2D arrays are flattened by the script provider when returning to Python + vReturnArray(1) = VarType(vReturn) + vReturnArray(2) = iDims + ElseIf VarType(vReturn) = V_OBJECT And Not IsNull(vReturn) Then + ' Uno or not Uno ? + bUno = False + If (CallType And cstUno) = cstUno Then ' UNO considered only when pre-announced in CallType + Set oObjDesc = SF_Utils._VarTypeObj(vReturn) + bUno = ( oObjDesc.iVarType = V_UNOOBJECT ) + End If + If bUno Then + ReDim vReturnArray(0 To 2) + Set vReturnArray(0) = vReturn + Else + ReDim vReturnArray(0 To 5) + vReturnArray(0) = _SF_._AddToPythonSTorage(vReturn) + End If + vReturnArray(1) = V_OBJECT + vReturnArray(2) = Iif(bUno, objUNO, Iif(bBasicClass, objCLASS, objMODULE)) + If Not bUno Then + vReturnArray(3) = vReturn.ObjectType + vReturnArray(4) = vReturn.ServiceName + vReturnArray(5) = "" + If vReturn.ObjectType <> "SF_CalcReference" Then ' Calc references are implemented as a Type ... End Type data structure + If SF_Array.Contains(vReturn.Properties(), "Name", SortOrder := "ASC") Then vReturnArray(5) = vReturn.Name + End If + End If + Else ' Scalar or Nothing + ReDim vReturnArray(0 To 1) + If VarType(vReturn) = V_DATE Then vReturnArray(0) = CDateToUnoDateTime(vReturn) Else vReturnArray(0) = vReturn + vReturnArray(1) = VarType(vReturn) + End If + + _PythonDispatcher = vReturnArray + +Finally: + _SF_.TriggeredByPython = False ' Reset normal state + Exit Function +Catch: + GoTo Finally +End Function ' ScriptForge.SF_PythonHelper._PythonDispatcher + +REM ----------------------------------------------------------------------------- +Private Function _Repr() As String +''' Convert the Basic instance to a readable string, typically for debugging purposes (DebugPrint ...) +''' Args: +''' Return: +''' "[PythonHelper]" + + _Repr = "[PythonHelper]" + +End Function ' ScriptForge.SF_PythonHelper._Repr + +REM ================================================= END OF SCRIPTFORGE.SF_PythonHelper +</script:module>
\ No newline at end of file |