diff --git a/wizards/source/scriptforge/SF_Array.xba b/wizards/source/scriptforge/SF_Array.xba index d28bc7d4541c..636739a0b16b 100644 --- a/wizards/source/scriptforge/SF_Array.xba +++ b/wizards/source/scriptforge/SF_Array.xba @@ -30,9 +30,14 @@ Const ARRAYINDEX1ERROR = "ARRAYINDEX1ERROR" ' Given index doe Const ARRAYINDEX2ERROR = "ARRAYINDEX2ERROR" ' Given indexes do not fit in array bounds Const CSVPARSINGERROR = "CSVPARSINGERROR" ' Parsing error detected while parsing a csv file Const CSVOVERFLOWWARNING = "CSVOVERFLOWWARNING" ' Array becoming too big, import process of csv file is interrupted +Const NOSCRIPTERROR = "NOSCRIPTERROR" ' Script could not be located +Const SCRIPTEXECERROR = "SCRIPTEXECERROR" ' Exception during script execution REM ============================================================ MODULE CONSTANTS +' CallByName arguments +Const vbGet = 2, vbLet = 4, vbMethod = 1, vbSet = 8 + Const MAXREPR = 50 ' Maximum length to represent an array in the console REM ===================================================== CONSTRUCTOR/DESTRUCTOR @@ -1078,6 +1083,124 @@ CatchIndex: GoTo Finally End Function ' ScriptForge.SF_Array.ExtractRow +REM ----------------------------------------------------------------------------- +Public Function Filter(Optional ByVal Module As Variant _ + , Optional ByVal Method As Variant _ + , Optional ByRef Params As Variant _ + , ParamArray Args() As Variant _ + ) As Variant +''' Execute the given script passing as argument consecutively each item of the Params array. +''' The script must be written in Basic. +''' Filter() returns a 1D array listing the items for which the called method has returned True. +''' Module: a Basic object referring either to a module, or to an instance of a Basic class. +''' Method: the method to execute, as a string +''' When Module is a ScriptForge object, Method may also be a property (get). +''' Params: a 1D array where each item represents the argument to pass for a given iteration. +''' Args: a list of positional arguments (maximum 8 items). +''' Return: +''' A 1D array listing the items for which the called method has returned True. +''' If an iteration causes an exception, the Filter() function raises an error. +''' Exceptions: +''' NOSCRIPTERROR The script could not be found +''' SCRIPTEXECERROR Script execution failed +''' Example: +''' SF_Array.Filter(Module1, "POSITIVE", Array(1, 2, -3, 4, -5, 0)) ' (1, 2, 4, 0) +''' '---- +''' Function POSITIVE(a) As Boolean +''' POSITIVE = ( a >= 0 ) +''' End Function + +Dim vReturn As Variant ' Return value +Dim lMin As Long ' LBound1 of input array +Dim lMax As Long ' UBound1 of input array +Dim vParamsItem As Variant ' A single item or item of Params +Dim iModuleType As Integer ' BASICOBJECT or SFOBJECT +Dim iCallType As Integer ' One of the vbXXX constants +Dim oModule As Object ' A SF_Utils._ObjectDescriptor type +Dim sScript As String ' Library.Module.Method +Dim bReturn As Boolean ' Value returned from called method +Dim lCount As Long ' Number of times the called method has returned True +Dim i As Long + +Const cstThisSub = "Array.Filter" +Const cstSubArgs = "Module, Method, Params, [Arg0, Arg1, ...]" + + If SF_Utils._ErrorHandling() Then On Local Error GoTo Finally + vReturn = Array() + +Check: + If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then + If Not SF_Utils._Validate(Module, "Module", V_OBJECT) Then GoTo Finally + If Not SF_Utils._Validate(Method, "Method", V_STRING) Then GoTo Finally + If Not SF_Utils._ValidateArray(Params, "Params", 1) Then GoTo Finally + End If + + Set oModule = SF_Utils._VarTypeObj(Module) + ' Prepare error messages method/script names + With oModule + iModuleType = .iVarType + If iModuleType = V_SFOBJECT Then + sScript = .sServiceName + ElseIf iModuleType = V_BASICOBJECT Then + sScript = Module.Name + Else + GoTo CatchNotFound + End If + End With + +Try: + ' Initialize the resulting array + lMin = LBound(Params, 1) : lMax = UBound(Params, 1) + ReDim vReturn(lMin To lMax) + lCount = -1 + + ' For Basic dynamic calls, the choice is made to use the - much faster than script provider techniques - CallByName(). + If SF_Utils._ErrorHandling() Then On Local Error GoTo CatchNotFound + ' Set the right call type: get (property) or method + iCallType = vbMethod + If iModuleType = V_SFOBJECT Then + If SF_Array.Contains(Module.Properties, Method, CaseSensitive := False) Then iCallType = vbGet + End If + For i = lMin To lMax + vParamsItem = Params(i) + Select Case UBound(Args) + 1 ' Number of arguments to pass to the called function + Case 0 : bReturn = CBool(CallByName(Module, Method, iCallType, vParamsItem)) + Case 1 : bReturn = CBool(CallByName(Module, Method, iCallType, vParamsItem, Args(0))) + Case 2 : bReturn = CBool(CallByName(Module, Method, iCallType, vParamsItem, Args(0), Args(1))) + Case 3 : bReturn = CBool(CallByName(Module, Method, iCallType, vParamsItem, Args(0), Args(1), Args(2))) + Case 4 : bReturn = CBool(CallByName(Module, Method, iCallType, vParamsItem, Args(0), Args(1), Args(2), Args(3))) + Case 5 : bReturn = CBool(CallByName(Module, Method, iCallType, vParamsItem, Args(0), Args(1), Args(2), Args(3), Args(4))) + Case 6 : bReturn = CBool(CallByName(Module, Method, iCallType, vParamsItem, Args(0), Args(1), Args(2), Args(3), Args(4), Args(5))) + Case 7 : bReturn = CBool(CallByName(Module, Method, iCallType, vParamsItem, Args(0), Args(1), Args(2), Args(3), Args(4), Args(5), Args(6))) + Case 8 : bReturn = CBool(CallByName(Module, Method, iCallType, vParamsItem, Args(0), Args(1), Args(2), Args(3), Args(4), Args(5), Args(6), Args(7))) + End Select + ' Retain the output only when True + If bReturn Then + lCount = lCount + 1 + vReturn(lMin + lCount) = Params(i) + End If + Next i + + ' Cut items in excess + If lCount < 0 Then + vReturn = Array() + Else + ReDim Preserve vReturn(lMin To lMin + lCount) + End If + +Finally: + Filter = vReturn + SF_Utils._ExitFunction(cstThisSub) + Exit Function +CatchExec: + SF_Exception.RaiseFatal(SCRIPTEXECERROR, "Script", sScript & "." & Method, Error$) + GoTo Finally +CatchNotFound: + If Err > 0 And Err <> 35 Then GoTo CatchExec ' Error 35 = Sub-procedure or function procedure not defined + SF_Exception.RaiseFatal(NOSCRIPTERROR, "Basic", "Module", sScript, "Method", Method) + GoTo Finally +End Function ' ScriptForge.SF_Array.Filter + REM ----------------------------------------------------------------------------- Public Function Flatten(Optional ByRef Array_1D As Variant) As Variant ''' Stack all items and all items in subarrays into one array without subarrays @@ -1631,10 +1754,10 @@ Public Function Join2D(Optional ByRef Array_2D As Variant _ Dim sJoin As String ' The return value Dim sItem As String ' The string representation of a single item Dim vItem As Variant ' Single item -Dim lMin1 As Long ' LBound1 of input array -Dim lMax1 As Long ' UBound1 of input array -Dim lMin2 As Long ' LBound2 of input array -Dim lMax2 As Long ' UBound2 of input array +Dim lMin1 As Long ' LBound1 of input array +Dim lMax1 As Long ' UBound1 of input array +Dim lMin2 As Long ' LBound2 of input array +Dim lMax2 As Long ' UBound2 of input array Dim i As Long Dim j As Long Const cstThisSub = "Array.Join2D" @@ -1680,6 +1803,124 @@ Catch: GoTo Finally End Function ' ScriptForge.SF_Array.Join2D +REM ----------------------------------------------------------------------------- +Public Function Map(Optional ByVal Module As Variant _ + , Optional ByVal Method As Variant _ + , Optional ByRef Params As Variant _ + , ParamArray Args() As Variant _ + ) As Variant +''' Execute the given method passing as argument consecutively +''' - a list of specific arguments, i.e. the Params() array; +''' - a list of fixed arguments, i.e. the Args() array. +''' The script must be written in Basic. +''' Map() returns a 1D array of the got results. +''' Args: +''' Module: a Basic object referring either to a module, or to an instance of a Basic class. +''' Method: the method to execute, as a string +''' When Module is a ScriptForge object, Method may also be a property (get). +''' Params: a 1D or 2D array where each row represents the set of variable arguments to pass for a given iteration. +''' Args: a list of positional arguments (maximum 8 items). +''' Return: +''' A 1D array of the results obtained in each iteration. Its size is equal to the number of rows in the input array. +''' If an iteration causes an exception, the Map() function raises an error. +''' Exceptions: +''' NOSCRIPTERROR The script could not be found +''' SCRIPTEXECERROR Script execution failed +''' Example: +''' | 1, 2| +''' SF_Array.Map(Module1, "SUM", | 6, 7|) +''' ' (3, 13) +''' '---- +''' Function SUM(a) +''' SUM = a(0) + a(1) +''' End Function +''' '==================================== +''' fs = CreateScriptService("FileSystem") +''' ui = CreateScriptService("UI") +''' folder = "C:\MyFolder" +''' opencalcdocs = SF_Array.Map(ui, "OpenDocument", fs.Files(folder, filter := "*.ods"), "", True) +''' ' Open read-only all Calc documents stored in folder + +Dim vReturn As Variant ' Return value +Dim iCountDims As Integer ' Number of dimensions of the input array +Dim lMin As Long ' LBound1 of input array +Dim lMax As Long ' UBound1 of input array +Dim vParamsRow As Variant ' A single row or item of Params +Dim iModuleType As Integer ' BASICOBJECT or SFOBJECT +Dim iCallType As Integer ' One of the vbXXX constants +Dim oModule As Object ' A SF_Utils._ObjectDescriptor type +Dim sScript As String ' Library.Module.Method +Dim i As Long + +Const cstThisSub = "Array.Map" +Const cstSubArgs = "Module, Method, Params, [Arg0, Arg1, ...]" + + If SF_Utils._ErrorHandling() Then On Local Error GoTo Finally + vReturn = Array() + +Check: + If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then + If Not SF_Utils._Validate(Module, "Module", V_OBJECT) Then GoTo Finally + If Not SF_Utils._Validate(Method, "Method", V_STRING) Then GoTo Finally + If Not SF_Utils._ValidateArray(Params, "Params") Then GoTo Finally + End If + + Set oModule = SF_Utils._VarTypeObj(Module) + ' Prepare error messages method/script names + With oModule + iModuleType = .iVarType + If iModuleType = V_SFOBJECT Then + sScript = .sServiceName + ElseIf iModuleType = V_BASICOBJECT Then + sScript = Module.Name + Else + GoTo CatchNotFound + End If + End With + + iCountDims = SF_Array.CountDims(Params) + If iCountDims <= 0 Or iCountDims > 2 Then GoTo Finally + +Try: + ' Initialize the resulting array + lMin = LBound(Params, 1) : lMax = UBound(Params, 1) + ReDim vReturn(lMin To lMax) + + ' For Basic dynamic calls, the choice is made to use the - much faster than script provider techniques - CallByName(). + If SF_Utils._ErrorHandling() Then On Local Error GoTo CatchNotFound + ' Set the right call type: get (property) or method + iCallType = vbMethod + If iModuleType = V_SFOBJECT Then + If SF_Array.Contains(Module.Properties, Method, CaseSensitive := False) Then iCallType = vbGet + End If + For i = lMin To lMax + If iCountDims = 1 Then vParamsRow = Params(i) Else vParamsRow = SF_Array.ExtractRow(Params, i) + Select Case UBound(Args) + 1 ' Number of arguments to pass to the called function + Case 0 : vReturn (i) = CallByName(Module, Method, iCallType, vParamsRow) + Case 1 : vReturn (i) = CallByName(Module, Method, iCallType, vParamsRow, Args(0)) + Case 2 : vReturn (i) = CallByName(Module, Method, iCallType, vParamsRow, Args(0), Args(1)) + Case 3 : vReturn (i) = CallByName(Module, Method, iCallType, vParamsRow, Args(0), Args(1), Args(2)) + Case 4 : vReturn (i) = CallByName(Module, Method, iCallType, vParamsRow, Args(0), Args(1), Args(2), Args(3)) + Case 5 : vReturn (i) = CallByName(Module, Method, iCallType, vParamsRow, Args(0), Args(1), Args(2), Args(3), Args(4)) + Case 6 : vReturn (i) = CallByName(Module, Method, iCallType, vParamsRow, Args(0), Args(1), Args(2), Args(3), Args(4), Args(5)) + Case 7 : vReturn (i) = CallByName(Module, Method, iCallType, vParamsRow, Args(0), Args(1), Args(2), Args(3), Args(4), Args(5), Args(6)) + Case 8 : vReturn (i) = CallByName(Module, Method, iCallType, vParamsRow, Args(0), Args(1), Args(2), Args(3), Args(4), Args(5), Args(6), Args(7)) + End Select + Next i + +Finally: + Map = vReturn + SF_Utils._ExitFunction(cstThisSub) + Exit Function +CatchExec: + SF_Exception.RaiseFatal(SCRIPTEXECERROR, "Script", sScript & "." & Method, Error$) + GoTo Finally +CatchNotFound: + If Err > 0 And Err <> 35 Then GoTo CatchExec ' Error 35 = Sub-procedure or function procedure not defined + SF_Exception.RaiseFatal(NOSCRIPTERROR, "Basic", "Module", sScript, "Method", Method) + GoTo Finally +End Function ' ScriptForge.SF_Array.Map + REM ----------------------------------------------------------------------------- Public Function Methods() As Variant ''' Return the list of public methods of the Array service as an array @@ -1699,6 +1940,7 @@ Public Function Methods() As Variant , "ExportToTextFile" _ , "ExtractColumn" _ , "ExtractRow" _ + , "Filter" _ , "Flatten" _ , "ImportFromCSVFile" _ , "IndexOf" _ @@ -1706,10 +1948,12 @@ Public Function Methods() As Variant , "InsertSorted" _ , "Intersection" _ , "Join2D" _ + , "Map" _ , "Prepend" _ , "PrependColumn" _ , "PrependRow" _ , "RangeInit" _ + , "Reduce" _ , "Reverse" _ , "Shuffle" _ , "Slice" _ @@ -1999,6 +2243,102 @@ CatchSequence: GoTo Finally End Function ' ScriptForge.SF_Array.RangeInit +REM ----------------------------------------------------------------------------- +Public Function Reduce(Optional ByVal Module As Variant _ + , Optional ByVal Method As Variant _ + , Optional ByRef Params As Variant _ + , Optional ByVal InitialValue As Variant _ + ) As Variant +''' Apply a function of two arguments cumulatively to the elements of an iterable, optionally starting with an initial argument. +''' The returned value is input as first argument in the next iteration. +''' The script must be written in Basic. +''' Reduce() returns the value returned by the last iteration. +''' Module: a Basic object referring either to a module, or to an instance of a Basic class. +''' Method: the method to execute, as a string +''' When Module is a ScriptForge object, Method may also be a property (get). +''' Params: a 1D array where each item represents the argument to pass for a given iteration. +''' InitialValue: when present, the first argument of the first iteration. +''' Return: +''' A scalar value as returned by the last iteration. +''' If the input array is empty, return the InitalValue or Null +''' If an iteration causes an exception, the Reduce() function raises an error. +''' Exceptions: +''' NOSCRIPTERROR The script could not be found +''' SCRIPTEXECERROR Script execution failed +''' Example: +''' SF_Array.Reduce(Module1, "SUM", Array(1, 2, -3, 4, -5, 0), 10) ' 9 +''' '---- +''' Function SUM(a, b) As Double +''' SUM = a + b +''' End Function + +Dim vReturn As Variant ' Return value +Dim lMin As Long ' LBound1 of input array +Dim lMax As Long ' UBound1 of input array +Dim iModuleType As Integer ' BASICOBJECT or SFOBJECT +Dim oModule As Object ' A SF_Utils._ObjectDescriptor type +Dim sScript As String ' Library.Module.Method +Dim i As Long + +Const cstThisSub = "Array.Reduce" +Const cstSubArgs = "Module, Method, Params, [Arg0, Arg1, ...], [InitialValue]" + + If SF_Utils._ErrorHandling() Then On Local Error GoTo Finally + vReturn = Null + +Check: + If IsMissing(InitialValue) Or IsEmpty(InitialValue) Then InitialValue = Null + If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then + If Not SF_Utils._Validate(Module, "Module", V_OBJECT) Then GoTo Finally + If Not SF_Utils._Validate(Method, "Method", V_STRING) Then GoTo Finally + If Not SF_Utils._ValidateArray(Params, "Params", 1) Then GoTo Finally + End If + + Set oModule = SF_Utils._VarTypeObj(Module) + ' Prepare error messages method/script names + With oModule + iModuleType = .iVarType + If iModuleType = V_SFOBJECT Then + sScript = .sServiceName + ElseIf iModuleType = V_BASICOBJECT Then + sScript = Module.Name + Else + GoTo CatchNotFound + End If + End With + +Try: + lMin = LBound(Params, 1) : lMax = UBound(Params, 1) + + ' For Basic dynamic calls, the choice is made to use the - much faster than script provider techniques - CallByName(). + If SF_Utils._ErrorHandling() Then On Local Error GoTo CatchNotFound + If lMin > lMax Then ' Empt array + vReturn = InitialValue + Else + If Not IsNull(InitialValue) Then ' The 1st iteration groups initial value + lMin-th item + vReturn = InitialValue + Else ' The 1st iteration groups the first 2 items + vreturn = Params(lMin) + lMin = lMin + 1 + End If + For i = lMin To lMax + vReturn = CallByName(Module, Method, vbMethod, vReturn, Params(i)) + Next i + End If + +Finally: + Reduce = vReturn + SF_Utils._ExitFunction(cstThisSub) + Exit Function +CatchExec: + SF_Exception.RaiseFatal(SCRIPTEXECERROR, "Script", sScript & "." & Method, Error$) + GoTo Finally +CatchNotFound: + If Err > 0 And Err <> 35 Then GoTo CatchExec ' Error 35 = Sub-procedure or function procedure not defined + SF_Exception.RaiseFatal(NOSCRIPTERROR, "Basic", "Module", sScript, "Method", Method) + GoTo Finally +End Function ' ScriptForge.SF_Array.Reduce + REM ----------------------------------------------------------------------------- Public Function Reverse(Optional ByRef Array_1D As Variant) As Variant ''' Return the reversed 1D input array diff --git a/wizards/source/scriptforge/SF_Session.xba b/wizards/source/scriptforge/SF_Session.xba index da8f7da7676b..842436811405 100644 --- a/wizards/source/scriptforge/SF_Session.xba +++ b/wizards/source/scriptforge/SF_Session.xba @@ -146,6 +146,7 @@ Public Function ExecuteBasicScript(Optional ByVal Scope As Variant _ ''' The value returned by the call to the script ''' Exceptions: ''' NOSCRIPTERROR The script could not be found +''' SCRIPTEXECERROR Script execution failed ''' Examples: ''' session.ExecuteBasicScript(, "XrayTool._Main.Xray", someuno) ' Sub: no return expected @@ -1115,16 +1116,14 @@ Dim sScope As String ' The scope part of the script URI Dim sLanguage As String ' The language part of the script URI Dim sScript As String ' The script part of the script URI Dim vStrings As Variant ' Array of strings: (script, language, scope) -Const cstComma = "," + _ExecuteScript = Empty + Try: If ScriptForge.SF_String.StartsWith(psScript, cstScript1) Then ' Parse script - vStrings = Split( _ - Replace( _ - Replace(Mid(psScript, Len(cstScript1) + 1), cstScript2, cstComma) _ - , cstScript3, cstComma) _ - , cstComma) + vStrings = SF_Session._ParseScriptURI(psScript) + If UBound(vStrings) <> 2 Then Exit Function sScript = vStrings(0) : sLanguage = vStrings(1) : sScope = vStrings(2) ' Execute script If UCase(sLanguage) = "BASIC" Then @@ -1136,13 +1135,50 @@ Try: End Function ' ScriptForge.SF_Session._ExecuteScript +REM ----------------------------------------------------------------------------- +Private Function _FindModule(psLibrary As String _ + , psModule As String _ + ) As Object +''' Identify the given library + module to prepare a CallByName() +''' - only GlobalScope is considered +''' - the library must be loaded (ScriptForge will not load it) +''' - the Standard library is excluded +''' - library and module must exist +''' - implement a limited number of arguments (8) +''' Args: +''' psLibrary: a Basic library +''' psModule: a Basic module +''' Returns: +''' A module as a Basic object or Nothing + +Dim oModule As Object ' com.sun.star.script.NativeObjectWrapper + + On Local Error GoTo Finally + Set oModule = Nothing + +Try: + With GlobalScope.BasicLibraries + If psLibrary <> "Standard" Then + If .hasByName(psLibrary) Then + If .isLibraryLoaded(psLibrary) Then + Set oModule = FindPropertyObject(FindObject(psLibrary), psModule) + End If + End If + End If + End With + +Finally: + Set _FindModule = oModule + Exit Function +End Function ' ScriptForge.SF_Session._FindModule + REM ----------------------------------------------------------------------------- Private Function _GetScript(ByVal psLanguage As String _ , ByVal psScope As String _ , ByVal psScript As String _ ) As Object ''' Get the adequate script provider and from there the requested script -''' Called by ExecuteBasicScript() and ExecutePythonScript() +''' Called a.o. by ExecuteBasicScript() and ExecutePythonScript() ''' The execution of the script is done by the caller ''' Args: ''' psLanguage: Basic or Python @@ -1172,6 +1208,7 @@ Try: Set oScript = oScriptProvider.getScript(sScript) End If ForAllUsers: + On Local Error GoTo 0 On Local Error GoTo CatchNotFound If IsNull(oScript) Then If psScope = SCRIPTISOXT Then psScope = SCRIPTISSHAROXT @@ -1187,5 +1224,35 @@ CatchNotFound: GoTo Finally End Function ' ScriptForge.SF_Session._GetScript +REM ----------------------------------------------------------------------------- +Private Function _ParseScriptURI(ByVal psScript As String) As Variant +''' Parse the script expressed in the scripting framework_URI notation +''' Args: +''' psScript: read https://wiki.documentfoundation.org/Documentation/DevGuide/Scripting_Framework#Scripting_Framework_URI_Specification +''' Returns: +''' An array of strings: (script, language, scope) + +Dim vStrings As Variant ' Array of strings: (script, language, scope) +Const cstComma = "," + +Try: + vStrings = Array() + If ScriptForge.SF_String.StartsWith(psScript, cstScript1) Then + ' Parse script + vStrings = Split( _ + Replace( _ + Replace(Mid(psScript, Len(cstScript1) + 1), cstScript2, cstComma) _ + , cstScript3, cstComma) _ + , cstComma) + End If + + ' Verify that all required components are present + If UBound(vStrings) <> 2 Then vStrings = Array() + +Finally: + _ParseScriptURI = vStrings + Exit Function +End Function ' ScriptForge.SF_Session._ParseScriptURI + REM =============================================== END OF SCRIPTFORGE.SF_SESSION \ No newline at end of file diff --git a/wizards/source/scriptforge/SF_Utils.xba b/wizards/source/scriptforge/SF_Utils.xba index 9ffe0f88739f..7da0499c5398 100644 --- a/wizards/source/scriptforge/SF_Utils.xba +++ b/wizards/source/scriptforge/SF_Utils.xba @@ -1102,8 +1102,10 @@ Try: If .sObjectType = cstBasicClass Then bUno = False ' Try if the Basic object has an ObjectType property - .sObjectType = oValue.ObjectType - .sServiceName = oValue.ServiceName + If Left(oValue.Name, 3) = "SF_" Then + .sObjectType = oValue.ObjectType + .sServiceName = oValue.ServiceName + End If End If ' Derive the return value from the object type Select Case True diff --git a/wizards/source/scriptforge/script.xlb b/wizards/source/scriptforge/script.xlb index bead89b8a66e..e4ac88f29e9e 100644 --- a/wizards/source/scriptforge/script.xlb +++ b/wizards/source/scriptforge/script.xlb @@ -1,24 +1,24 @@ - + + + + + + + + + + + + + - - + - - - - - - - - - - - - - + + \ No newline at end of file