file-online-preview/office-plugin/windows-office/share/basic/ScriptForge/SF_Dictionary.xba

953 lines
36 KiB
Java

<?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_Dictionary" 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_Dictionary
&apos;&apos;&apos; =============
&apos;&apos;&apos; Class for management of dictionaries
&apos;&apos;&apos; A dictionary is a collection of key-item pairs
&apos;&apos;&apos; The key is a not case-sensitive string
&apos;&apos;&apos; Items may be of any type
&apos;&apos;&apos; Keys, items can be retrieved, counted, etc.
&apos;&apos;&apos;
&apos;&apos;&apos; The implementation is based on
&apos;&apos;&apos; - one collection mapping keys and entries in the array
&apos;&apos;&apos; - one 1-column array: key + data
&apos;&apos;&apos;
&apos;&apos;&apos; Why a Dictionary class beside the builtin Collection class ?
&apos;&apos;&apos; A standard Basic collection does not support the retrieval of the keys
&apos;&apos;&apos; Additionally it may contain only simple data (strings, numbers, ...)
&apos;&apos;&apos;
&apos;&apos;&apos; Service instantiation example:
&apos;&apos;&apos; Dim myDict As Variant
&apos;&apos;&apos; myDict = CreateScriptService(&quot;Dictionary&quot;) &apos; Once per dictionary
&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
Const DUPLICATEKEYERROR = &quot;DUPLICATEKEYERROR&quot; &apos; Key exists already
Const UNKNOWNKEYERROR = &quot;UNKNOWNKEYERROR&quot; &apos; Key not found
Const INVALIDKEYERROR = &quot;INVALIDKEYERROR&quot; &apos; Key contains only spaces
REM ============================================================= PRIVATE MEMBERS
&apos; Defines an entry in the MapItems array
Type ItemMap
Key As String
Value As Variant
End Type
Private [Me] As Object
Private [_Parent] As Object
Private ObjectType As String &apos; Must be &quot;DICTIONARY&quot;
Private ServiceName As String
Private MapKeys As Variant &apos; To retain the original keys
Private MapItems As Variant &apos; Array of ItemMaps
Private _MapSize As Long &apos; Total number of entries in the dictionary
Private _MapRemoved As Long &apos; Number of inactive entries in the dictionary
REM ===================================================== CONSTRUCTOR/DESTRUCTOR
REM -----------------------------------------------------------------------------
Private Sub Class_Initialize()
Set [Me] = Nothing
Set [_Parent] = Nothing
ObjectType = &quot;DICTIONARY&quot;
ServiceName = &quot;ScriptForge.Dictionary&quot;
Set MapKeys = New Collection
Set MapItems = Array()
_MapSize = 0
_MapRemoved = 0
End Sub &apos; ScriptForge.SF_Dictionary Constructor
REM -----------------------------------------------------------------------------
Private Sub Class_Terminate()
Call Class_Initialize()
End Sub &apos; ScriptForge.SF_Dictionary Destructor
REM -----------------------------------------------------------------------------
Public Function Dispose() As Variant
RemoveAll()
Set Dispose = Nothing
End Function &apos; ScriptForge.SF_Dictionary Explicit destructor
REM ================================================================== PROPERTIES
REM -----------------------------------------------------------------------------
Property Get Count() As Long
&apos;&apos;&apos; Actual number of entries in the dictionary
&apos;&apos;&apos; Example:
&apos;&apos;&apos; myDict.Count
Count = _PropertyGet(&quot;Count&quot;)
End Property &apos; ScriptForge.SF_Dictionary.Count
REM -----------------------------------------------------------------------------
Public Function Item(Optional ByVal Key As Variant) As Variant
&apos;&apos;&apos; Return the value of the item related to Key
&apos;&apos;&apos; Args:
&apos;&apos;&apos; Key: the key value (string)
&apos;&apos;&apos; Returns:
&apos;&apos;&apos; Empty if not found, otherwise the found value
&apos;&apos;&apos; Example:
&apos;&apos;&apos; myDict.Item(&quot;ThisKey&quot;)
&apos;&apos;&apos; NB: defined as a function to not disrupt the Basic IDE debugger
Item = _PropertyGet(&quot;Item&quot;, Key)
End Function &apos; ScriptForge.SF_Dictionary.Item
REM -----------------------------------------------------------------------------
Property Get Items() as Variant
&apos;&apos;&apos; Return the list of Items as a 1D array
&apos;&apos;&apos; The Items and Keys properties return their respective contents in the same order
&apos;&apos;&apos; The order is however not necessarily identical to the creation sequence
&apos;&apos;&apos; Returns:
&apos;&apos;&apos; The array is empty if the dictionary is empty
&apos;&apos;&apos; Examples
&apos;&apos;&apos; a = myDict.Items
&apos;&apos;&apos; For Each b In a ...
Items = _PropertyGet(&quot;Items&quot;)
End Property &apos; ScriptForge.SF_Dictionary.Items
REM -----------------------------------------------------------------------------
Property Get Keys() as Variant
&apos;&apos;&apos; Return the list of keys as a 1D array
&apos;&apos;&apos; The Keys and Items properties return their respective contents in the same order
&apos;&apos;&apos; The order is however not necessarily identical to the creation sequence
&apos;&apos;&apos; Returns:
&apos;&apos;&apos; The array is empty if the dictionary is empty
&apos;&apos;&apos; Examples
&apos;&apos;&apos; a = myDict.Keys
&apos;&apos;&apos; For each b In a ...
Keys = _PropertyGet(&quot;Keys&quot;)
End Property &apos; ScriptForge.SF_Dictionary.Keys
REM ===================================================================== METHODS
REM -----------------------------------------------------------------------------
Public Function Add(Optional ByVal Key As Variant _
, Optional ByVal Item As Variant _
) As Boolean
&apos;&apos;&apos; Add a new key-item pair into the dictionary
&apos;&apos;&apos; Args:
&apos;&apos;&apos; Key: must not yet exist in the dictionary
&apos;&apos;&apos; Item: any value, including an array, a Basic object, a UNO object, ...
&apos;&apos;&apos; Returns: True if successful
&apos;&apos;&apos; Exceptions:
&apos;&apos;&apos; DUPLICATEKEYERROR: such a key exists already
&apos;&apos;&apos; INVALIDKEYERROR: zero-length string or only spaces
&apos;&apos;&apos; Examples:
&apos;&apos;&apos; myDict.Add(&quot;NewKey&quot;, NewValue)
Dim oItemMap As ItemMap &apos; New entry in the MapItems array
Const cstThisSub = &quot;Dictionary.Add&quot;
Const cstSubArgs = &quot;Key, Item&quot;
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
Add = False
Check:
If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
If Not SF_Utils._Validate(Key, &quot;Key&quot;, V_STRING) Then GoTo Catch
If IsArray(Item) Then
If Not SF_Utils._ValidateArray(Item, &quot;Item&quot;) Then GoTo Catch
Else
If Not SF_Utils._Validate(Item, &quot;Item&quot;) Then GoTo Catch
End If
End If
If Key = Space(Len(Key)) Then GoTo CatchInvalid
If Exists(Key) Then GoTo CatchDuplicate
Try:
_MapSize = _MapSize + 1
MapKeys.Add(_MapSize, Key)
oItemMap.Key = Key
oItemMap.Value = Item
ReDim Preserve MapItems(1 To _MapSize)
MapItems(_MapSize) = oItemMap
Add = True
Finally:
SF_Utils._ExitFunction(cstThisSub)
Exit Function
Catch:
GoTo Finally
CatchDuplicate:
SF_Exception.RaiseFatal(DUPLICATEKEYERROR, &quot;Key&quot;, Key)
GoTo Finally
CatchInvalid:
SF_Exception.RaiseFatal(INVALIDKEYERROR, &quot;Key&quot;)
GoTo Finally
End Function &apos; ScriptForge.SF_Dictionary.Add
REM -----------------------------------------------------------------------------
Public Function ConvertToArray() As Variant
&apos;&apos;&apos; Store the content of the dictionary in a 2-columns array:
&apos;&apos;&apos; Key stored in 1st column, Item stored in 2nd
&apos;&apos;&apos; Args:
&apos;&apos;&apos; Returns:
&apos;&apos;&apos; a zero-based 2D array(0:Count - 1, 0:1)
&apos;&apos;&apos; an empty array if the dictionary is empty
Dim vArray As Variant &apos; Return value
Dim sKey As String &apos; Tempry key
Dim vKeys As Variant &apos; Array of keys
Dim lCount As Long &apos; Counter
Const cstThisSub = &quot;Dictionary.ConvertToArray&quot;
Const cstSubArgs = &quot;&quot;
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
Check:
SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
Try:
vArray = Array()
If Count = 0 Then
Else
ReDim vArray(0 To Count - 1, 0 To 1)
lCount = -1
vKeys = Keys
For Each sKey in vKeys
lCount = lCount + 1
vArray(lCount, 0) = sKey
vArray(lCount, 1) = Item(sKey)
Next sKey
End If
Finally:
ConvertToArray = vArray()
SF_Utils._ExitFunction(cstThisSub)
Exit Function
Catch:
GoTo Finally
End Function &apos; ScriptForge.SF_Dictionary.ConvertToArray
REM -----------------------------------------------------------------------------
Public Function ConvertToJson(ByVal Optional Indent As Variant) As Variant
&apos;&apos;&apos; Convert the content of the dictionary to a JSON string
&apos;&apos;&apos; JSON = JavaScript Object Notation: https://en.wikipedia.org/wiki/JSON
&apos;&apos;&apos; Limitations
&apos;&apos;&apos; Allowed item types: String, Boolean, numbers, Null and Empty
&apos;&apos;&apos; Arrays containing above types are allowed
&apos;&apos;&apos; Dates are converted into strings (not within arrays)
&apos;&apos;&apos; Other types are converted to their string representation (cfr. SF_String.Represent)
&apos;&apos;&apos; Args:
&apos;&apos;&apos; Indent:
&apos;&apos;&apos; If indent is a non-negative integer or string, then JSON array elements and object members will be pretty-printed with that indent level.
&apos;&apos;&apos; An indent level &lt;= 0 will only insert newlines.
&apos;&apos;&apos; &quot;&quot;, (the default) selects the most compact representation.
&apos;&apos;&apos; Using a positive integer indent indents that many spaces per level.
&apos;&apos;&apos; If indent is a string (such as Chr(9)), that string is used to indent each level.
&apos;&apos;&apos; Returns:
&apos;&apos;&apos; the JSON string
&apos;&apos;&apos; Example:
&apos;&apos;&apos; myDict.Add(&quot;p0&quot;, 12.5)
&apos;&apos;&apos; myDict.Add(&quot;p1&quot;, &quot;a string àé&quot;&quot;ê&quot;)
&apos;&apos;&apos; myDict.Add(&quot;p2&quot;, DateSerial(2020,9,28))
&apos;&apos;&apos; myDict.Add(&quot;p3&quot;, True)
&apos;&apos;&apos; myDict.Add(&quot;p4&quot;, Array(1,2,3))
&apos;&apos;&apos; MsgBox a.ConvertToJson() &apos; {&quot;p0&quot;: 12.5, &quot;p1&quot;: &quot;a string \u00e0\u00e9\&quot;\u00ea&quot;, &quot;p2&quot;: &quot;2020-09-28&quot;, &quot;p3&quot;: true, &quot;p4&quot;: [1, 2, 3]}
Dim sJson As String &apos; Return value
Dim vArray As Variant &apos; Array of property values
Dim oPropertyValue As Object &apos; com.sun.star.beans.PropertyValue
Dim sKey As String &apos; Tempry key
Dim vKeys As Variant &apos; Array of keys
Dim vItem As Variant &apos; Tempry item
Dim iVarType As Integer &apos; Extended VarType
Dim lCount As Long &apos; Counter
Dim vIndent As Variant &apos; Python alias of Indent
Const cstPyHelper = &quot;$&quot; &amp; &quot;_SF_Dictionary__ConvertToJson&quot;
Const cstThisSub = &quot;Dictionary.ConvertToJson&quot;
Const cstSubArgs = &quot;[Indent=Null]&quot;
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
Check:
If IsMissing(Indent) Or IsEmpty(INDENT) Then Indent = &quot;&quot;
If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
If Not SF_Utils._Validate(Indent, &quot;Indent&quot;, Array(V_STRING, V_NUMERIC)) Then GoTo Finally
End If
sJson = &quot;&quot;
Try:
vArray = Array()
If Count = 0 Then
Else
ReDim vArray(0 To Count - 1)
lCount = -1
vKeys = Keys
For Each sKey in vKeys
&apos; Check item type
vItem = Item(sKey)
iVarType = SF_Utils._VarTypeExt(vItem)
Select Case iVarType
Case V_STRING, V_BOOLEAN, V_NUMERIC, V_NULL, V_EMPTY
Case V_DATE
vItem = SF_Utils._CDateToIso(vItem)
Case &gt;= V_ARRAY
Case Else
vItem = SF_Utils._Repr(vItem)
End Select
&apos; Build in each array entry a (Name, Value) pair
Set oPropertyValue = SF_Utils._MakePropertyValue(sKey, vItem)
lCount = lCount + 1
Set vArray(lCount) = oPropertyValue
Next sKey
End If
&apos;Pass array to Python script for the JSON conversion
With ScriptForge.SF_Session
vIndent = Indent
If VarType(Indent) = V_STRING Then
If Len(Indent) = 0 Then vIndent = Null
End If
sJson = .ExecutePythonScript(.SCRIPTISSHARED, _SF_.PythonHelper &amp; cstPyHelper, vArray, vIndent)
End With
Finally:
ConvertToJson = sJson
SF_Utils._ExitFunction(cstThisSub)
Exit Function
Catch:
GoTo Finally
End Function &apos; ScriptForge.SF_Dictionary.ConvertToJson
REM -----------------------------------------------------------------------------
Public Function ConvertToPropertyValues() As Variant
&apos;&apos;&apos; Store the content of the dictionary in an array of PropertyValues
&apos;&apos;&apos; Key stored in Name, Item stored in Value
&apos;&apos;&apos; Args:
&apos;&apos;&apos; Returns:
&apos;&apos;&apos; a zero-based 1D array(0:Count - 1). Each entry is a com.sun.star.beans.PropertyValue
&apos;&apos;&apos; Name: the key in the dictionary
&apos;&apos;&apos; Value:
&apos;&apos;&apos; Dates are converted to UNO dates
&apos;&apos;&apos; Empty arrays are replaced by Null
&apos;&apos;&apos; an empty array if the dictionary is empty
Dim vArray As Variant &apos; Return value
Dim oPropertyValue As Object &apos; com.sun.star.beans.PropertyValue
Dim sKey As String &apos; Tempry key
Dim vKeys As Variant &apos; Array of keys
Dim lCount As Long &apos; Counter
Const cstThisSub = &quot;Dictionary.ConvertToPropertyValues&quot;
Const cstSubArgs = &quot;&quot;
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
Check:
SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
Try:
vArray = Array()
If Count = 0 Then
Else
ReDim vArray(0 To Count - 1)
lCount = -1
vKeys = Keys
For Each sKey in vKeys
&apos; Build in each array entry a (Name, Value) pair
Set oPropertyValue = SF_Utils._MakePropertyValue(sKey, Item(sKey))
lCount = lCount + 1
Set vArray(lCount) = oPropertyValue
Next sKey
End If
Finally:
ConvertToPropertyValues = vArray()
SF_Utils._ExitFunction(cstThisSub)
Exit Function
Catch:
GoTo Finally
End Function &apos; ScriptForge.SF_Dictionary.ConvertToPropertyValues
REM -----------------------------------------------------------------------------
Public Function Exists(Optional ByVal Key As Variant) As Boolean
&apos;&apos;&apos; Determine if a key exists in the dictionary
&apos;&apos;&apos; Args:
&apos;&apos;&apos; Key: the key value (string)
&apos;&apos;&apos; Returns: True if key exists
&apos;&apos;&apos; Examples:
&apos;&apos;&apos; If myDict.Exists(&quot;SomeKey&quot;) Then &apos; don&apos;t add again
Dim vItem As Variant &apos; Item part in MapKeys
Const cstThisSub = &quot;Dictionary.Exists&quot;
Const cstSubArgs = &quot;Key&quot;
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
Exists = False
Check:
If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
If Not SF_Utils._Validate(Key, &quot;Key&quot;, V_STRING) Then GoTo Catch
End If
Try:
&apos; Dirty but preferred to go through whole collection
On Local Error GoTo NotFound
vItem = MapKeys(Key)
NotFound:
Exists = ( Not ( Err = 5 ) And vItem &gt; 0 )
On Local Error GoTo 0
Finally:
SF_Utils._ExitFunction(cstThisSub)
Exit Function
Catch:
GoTo Finally
End Function &apos; ScriptForge.SF_Dictionary.Exists
REM -----------------------------------------------------------------------------
Public Function GetProperty(Optional ByVal PropertyName As Variant _
, Optional ByVal Key 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; Key: mandatory if PropertyName = &quot;Item&quot;, ignored otherwise
&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; myDict.GetProperty(&quot;Count&quot;)
Const cstThisSub = &quot;Dictionary.GetProperty&quot;
Const cstSubArgs = &quot;PropertyName, [Key]&quot;
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
GetProperty = Null
Check:
If IsMissing(Key) Or IsEmpty(Key) Then Key = &quot;&quot;
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, Key)
Finally:
SF_Utils._ExitFunction(cstThisSub)
Exit Function
Catch:
GoTo Finally
End Function &apos; ScriptForge.SF_Dictionary.GetProperty
REM -----------------------------------------------------------------------------
Public Function ImportFromJson(Optional ByVal InputStr As Variant _
, Optional Byval Overwrite As Variant _
) As Boolean
&apos;&apos;&apos; Adds the content of a Json string into the current dictionary
&apos;&apos;&apos; JSON = JavaScript Object Notation: https://en.wikipedia.org/wiki/JSON
&apos;&apos;&apos; Limitations
&apos;&apos;&apos; The JSON string may contain numbers, strings, booleans, null values and arrays containing those types
&apos;&apos;&apos; It must not contain JSON objects, i.e. sub-dictionaries
&apos;&apos;&apos; An attempt is made to convert strings to dates if they fit one of next patterns:
&apos;&apos;&apos; YYYY-MM-DD, HH:MM:SS or YYYY-MM-DD HH:MM:SS
&apos;&apos;&apos; Args:
&apos;&apos;&apos; InputStr: the json string to import
&apos;&apos;&apos; Overwrite: when True entries with same name may exist in the dictionary and their values are overwritten
&apos;&apos;&apos; Default = False
&apos;&apos;&apos; Returns:
&apos;&apos;&apos; True if successful
&apos;&apos;&apos; Exceptions:
&apos;&apos;&apos; DUPLICATEKEYERROR: such a key exists already
&apos;&apos;&apos; INVALIDKEYERROR: zero-length string or only spaces
&apos;&apos;&apos; Example:
&apos;&apos;&apos; Dim s As String
&apos;&apos;&apos; s = &quot;{&apos;firstName&apos;: &apos;John&apos;,&apos;lastName&apos;: &apos;Smith&apos;,&apos;isAlive&apos;: true,&apos;age&apos;: 66, &apos;birth&apos;: &apos;1954-09-28 20:15:00&apos;&quot; _
&apos;&apos;&apos; &amp; &quot;,&apos;address&apos;: {&apos;streetAddress&apos;: &apos;21 2nd Street&apos;,&apos;city&apos;: &apos;New York&apos;,&apos;state&apos;: &apos;NY&apos;,&apos;postalCode&apos;: &apos;10021-3100&apos;}&quot; _
&apos;&apos;&apos; &amp; &quot;,&apos;phoneNumbers&apos;: [{&apos;type&apos;: &apos;home&apos;,&apos;number&apos;: &apos;212 555-1234&apos;},{&apos;type&apos;: &apos;office&apos;,&apos;number&apos;: &apos;646 555-4567&apos;}]&quot; _
&apos;&apos;&apos; &amp; &quot;,&apos;children&apos;: [&apos;Q&apos;,&apos;M&apos;,&apos;G&apos;,&apos;T&apos;],&apos;spouse&apos;: null}&quot;
&apos;&apos;&apos; s = Replace(s, &quot;&apos;&quot;, &quot;&quot;&quot;&quot;)
&apos;&apos;&apos; myDict.ImportFromJson(s, OverWrite := True)
&apos;&apos;&apos; &apos; The (sub)-dictionaries &quot;address&quot; and &quot;phoneNumbers(0) and (1) are reduced to Empty
Dim bImport As Boolean &apos; Return value
Dim vArray As Variant &apos; JSON string converted to array
Dim vArrayEntry As Variant &apos; A single entry in vArray
Dim vKey As Variant &apos; Tempry key
Dim vItem As Variant &apos; Tempry item
Dim bExists As Boolean &apos; True when an entry exists
Dim dDate As Date &apos; String converted to Date
Const cstPyHelper = &quot;$&quot; &amp; &quot;_SF_Dictionary__ImportFromJson&quot;
Const cstThisSub = &quot;Dictionary.ImportFromJson&quot;
Const cstSubArgs = &quot;InputStr, [Overwrite=False]&quot;
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
bImport = False
Check:
If IsMissing(Overwrite) Or IsEmpty(Overwrite) Then Overwrite = False
If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
If Not SF_Utils._Validate(InputStr, &quot;InputStr&quot;, V_STRING) Then GoTo Finally
If Not SF_Utils._Validate(Overwrite, &quot;Overwrite&quot;, V_BOOLEAN) Then GoYo Finally
End If
Try:
With ScriptForge.SF_Session
vArray = .ExecutePythonScript(.SCRIPTISSHARED, _SF_.PythonHelper &amp; cstPyHelper, InputStr)
End With
If Not IsArray(vArray) Then GoTo Finally &apos; Conversion error or nothing to do
&apos; vArray = Array of subarrays = 2D DataArray (cfr. Calc)
For Each vArrayEntry In vArray
vKey = vArrayEntry(0)
If VarType(vKey) = V_STRING Then &apos; Else skip
vItem = vArrayEntry(1)
If Overwrite Then bExists = Exists(vKey) Else bExists = False
&apos; When the item matches a date pattern, convert it to a date
If VarType(vItem) = V_STRING Then
dDate = SF_Utils._CStrToDate(vItem)
If dDate &gt; -1 Then vItem = dDate
End If
If bExists Then
ReplaceItem(vKey, vItem)
Else
Add(vKey, vItem) &apos; Key controls are done in Add
End If
End If
Next vArrayEntry
bImport = True
Finally:
ImportFromJson = bImport
SF_Utils._ExitFunction(cstThisSub)
Exit Function
Catch:
GoTo Finally
End Function &apos; ScriptForge.SF_Dictionary.ImportFromJson
REM -----------------------------------------------------------------------------
Public Function ImportFromPropertyValues(Optional ByVal PropertyValues As Variant _
, Optional Byval Overwrite As Variant _
) As Boolean
&apos;&apos;&apos; Adds the content of an array of PropertyValues into the current dictionary
&apos;&apos;&apos; Names contain Keys, Values contain Items
&apos;&apos;&apos; UNO dates are replaced by Basic dates
&apos;&apos;&apos; Args:
&apos;&apos;&apos; PropertyValues: a zero-based 1D array. Each entry is a com.sun.star.beans.PropertyValue
&apos;&apos;&apos; Overwrite: when True entries with same name may exist in the dictionary and their values are overwritten
&apos;&apos;&apos; Default = False
&apos;&apos;&apos; Returns:
&apos;&apos;&apos; True if successful
&apos;&apos;&apos; Exceptions:
&apos;&apos;&apos; DUPLICATEKEYERROR: such a key exists already
&apos;&apos;&apos; INVALIDKEYERROR: zero-length string or only spaces
Dim bImport As Boolean &apos; Return value
Dim oPropertyValue As Object &apos; com.sun.star.beans.PropertyValue
Dim vItem As Variant &apos; Tempry item
Dim sObjectType As String &apos; UNO object type of dates
Dim bExists As Boolean &apos; True when an entry exists
Const cstThisSub = &quot;Dictionary.ImportFromPropertyValues&quot;
Const cstSubArgs = &quot;PropertyValues, [Overwrite=False]&quot;
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
bImport = False
Check:
If IsMissing(Overwrite) Or IsEmpty(Overwrite) Then Overwrite = False
If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
If IsArray(PropertyValues) Then
If Not SF_Utils._ValidateArray(PropertyValues, &quot;PropertyValues&quot;, 1, V_OBJECT, True) Then GoTo Finally
Else
If Not SF_Utils._Validate(PropertyValues, &quot;PropertyValues&quot;, V_OBJECT) Then GoTo Finally
End If
If Not SF_Utils._Validate(Overwrite, &quot;Overwrite&quot;, V_BOOLEAN) Then GoYo Finally
End If
Try:
If Not IsArray(PropertyValues) Then PropertyValues = Array(PropertyValues)
With oPropertyValue
For Each oPropertyValue In PropertyValues
If Overwrite Then bExists = Exists(.Name) Else bExists = False
If SF_Session.UnoObjectType(oPropertyValue) = &quot;com.sun.star.beans.PropertyValue&quot; Then
If IsUnoStruct(.Value) Then
sObjectType = SF_Session.UnoObjectType(.Value)
Select Case sObjectType
Case &quot;com.sun.star.util.DateTime&quot; : vItem = CDateFromUnoDateTime(.Value)
Case &quot;com.sun.star.util.Date&quot; : vItem = CDateFromUnoDate(.Value)
Case &quot;com.sun.star.util.Time&quot; : vItem = CDateFromUnoTime(.Value)
Case Else : vItem = .Value
End Select
Else
vItem = .Value
End If
If bExists Then
ReplaceItem(.Name, vItem)
Else
Add(.Name, vItem) &apos; Key controls are done in Add
End If
End If
Next oPropertyValue
End With
bImport = True
Finally:
ImportFromPropertyValues = bImport
SF_Utils._ExitFunction(cstThisSub)
Exit Function
Catch:
GoTo Finally
End Function &apos; ScriptForge.SF_Dictionary.ImportFromPropertyValues
REM -----------------------------------------------------------------------------
Public Function Methods() As Variant
&apos;&apos;&apos; Return the list or methods of the Dictionary class as an array
Methods = Array( _
&quot;Add&quot; _
, &quot;ConvertToArray&quot; _
, &quot;ConvertToJson&quot; _
, &quot;ConvertToPropertyValues&quot; _
, &quot;Exists&quot; _
, &quot;ImportFromJson&quot; _
, &quot;ImportFromPropertyValues&quot; _
, &quot;Remove&quot; _
, &quot;RemoveAll&quot; _
, &quot;ReplaceItem&quot; _
, &quot;ReplaceKey&quot; _
)
End Function &apos; ScriptForge.SF_Dictionary.Methods
REM -----------------------------------------------------------------------------
Public Function Properties() As Variant
&apos;&apos;&apos; Return the list or properties of the Dictionary class as an array
Properties = Array( _
&quot;Count&quot; _
, &quot;Item&quot; _
, &quot;Items&quot; _
, &quot;Keys&quot; _
)
End Function &apos; ScriptForge.SF_Dictionary.Properties
REM -----------------------------------------------------------------------------
Public Function Remove(Optional ByVal Key As Variant) As Boolean
&apos;&apos;&apos; Remove an existing dictionary entry based on its key
&apos;&apos;&apos; Args:
&apos;&apos;&apos; Key: must exist in the dictionary
&apos;&apos;&apos; Returns: True if successful
&apos;&apos;&apos; Exceptions:
&apos;&apos;&apos; UNKNOWNKEYERROR: the key does not exist
&apos;&apos;&apos; Examples:
&apos;&apos;&apos; myDict.Remove(&quot;OldKey&quot;)
Dim lIndex As Long &apos; To remove entry in the MapItems array
Const cstThisSub = &quot;Dictionary.Remove&quot;
Const cstSubArgs = &quot;Key&quot;
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
Remove = False
Check:
If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
If Not SF_Utils._Validate(Key, &quot;Key&quot;, V_STRING) Then GoTo Catch
End If
If Not Exists(Key) Then GoTo CatchUnknown
Try:
lIndex = MapKeys.Item(Key)
MapKeys.Remove(Key)
Erase MapItems(lIndex) &apos; Is now Empty
_MapRemoved = _MapRemoved + 1
Remove = True
Finally:
SF_Utils._ExitFunction(cstThisSub)
Exit Function
Catch:
GoTo Finally
CatchUnknown:
SF_Exception.RaiseFatal(UNKNOWNKEYERROR, &quot;Key&quot;, Key)
GoTo Finally
End Function &apos; ScriptForge.SF_Dictionary.Remove
REM -----------------------------------------------------------------------------
Public Function RemoveAll() As Boolean
&apos;&apos;&apos; Remove all the entries from the dictionary
&apos;&apos;&apos; Args:
&apos;&apos;&apos; Returns: True if successful
&apos;&apos;&apos; Examples:
&apos;&apos;&apos; myDict.RemoveAll()
Dim vKeys As Variant &apos; Array of keys
Dim sColl As String &apos; A collection key in MapKeys
Const cstThisSub = &quot;Dictionary.RemoveAll&quot;
Const cstSubArgs = &quot;&quot;
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
RemoveAll = False
Check:
SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
Try:
vKeys = Keys
For Each sColl In vKeys
MapKeys.Remove(sColl)
Next sColl
Erase MapKeys
Erase MapItems
&apos; Make dictionary ready to receive new entries
Call Class_Initialize()
RemoveAll = True
Finally:
SF_Utils._ExitFunction(cstThisSub)
Exit Function
Catch:
GoTo Finally
End Function &apos; ScriptForge.SF_Dictionary.RemoveAll
REM -----------------------------------------------------------------------------
Public Function ReplaceItem(Optional ByVal Key As Variant _
, Optional ByVal Value As Variant _
) As Boolean
&apos;&apos;&apos; Replace the item value
&apos;&apos;&apos; Args:
&apos;&apos;&apos; Key: must exist in the dictionary
&apos;&apos;&apos; Returns: True if successful
&apos;&apos;&apos; Exceptions:
&apos;&apos;&apos; UNKNOWNKEYERROR: the old key does not exist
&apos;&apos;&apos; Examples:
&apos;&apos;&apos; myDict.ReplaceItem(&quot;Key&quot;, NewValue)
Dim oItemMap As ItemMap &apos; Content to update in the MapItems array
Dim lIndex As Long &apos; Entry in the MapItems array
Const cstThisSub = &quot;Dictionary.ReplaceItem&quot;
Const cstSubArgs = &quot;Key, Value&quot;
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
ReplaceItem = False
Check:
If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
If Not SF_Utils._Validate(Key, &quot;Key&quot;, V_STRING) Then GoTo Catch
If Not SF_Utils._Validate(Value, &quot;Value&quot;) Then GoTo Catch
End If
If Not Exists(Key) Then GoTo CatchUnknown
Try:
&apos; Find entry in MapItems and update it with the new value
lIndex = MapKeys.Item(Key)
oItemMap = MapItems(lIndex)
oItemMap.Value = Value
ReplaceItem = True
Finally:
SF_Utils._ExitFunction(cstThisSub)
Exit Function
Catch:
GoTo Finally
CatchUnknown:
SF_Exception.RaiseFatal(UNKNOWNKEYERROR, &quot;Key&quot;, Key)
GoTo Finally
End Function &apos; ScriptForge.SF_Dictionary.ReplaceItem
REM -----------------------------------------------------------------------------
Public Function ReplaceKey(Optional ByVal Key As Variant _
, Optional ByVal Value As Variant _
) As Boolean
&apos;&apos;&apos; Replace existing key
&apos;&apos;&apos; Args:
&apos;&apos;&apos; Key: must exist in the dictionary
&apos;&apos;&apos; Value: must not exist in the dictionary
&apos;&apos;&apos; Returns: True if successful
&apos;&apos;&apos; Exceptions:
&apos;&apos;&apos; UNKNOWNKEYERROR: the old key does not exist
&apos;&apos;&apos; DUPLICATEKEYERROR: the new key exists
&apos;&apos;&apos; Examples:
&apos;&apos;&apos; myDict.ReplaceKey(&quot;OldKey&quot;, &quot;NewKey&quot;)
Dim oItemMap As ItemMap &apos; Content to update in the MapItems array
Dim lIndex As Long &apos; Entry in the MapItems array
Const cstThisSub = &quot;Dictionary.ReplaceKey&quot;
Const cstSubArgs = &quot;Key, Value&quot;
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
ReplaceKey = False
Check:
If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
If Not SF_Utils._Validate(Key, &quot;Key&quot;, V_STRING) Then GoTo Catch
If Not SF_Utils._Validate(Value, &quot;Value&quot;, V_STRING) Then GoTo Catch
End If
If Not Exists(Key) Then GoTo CatchUnknown
If Value = Space(Len(Value)) Then GoTo CatchInvalid
If Exists(Value) Then GoTo CatchDuplicate
Try:
&apos; Remove the Key entry and create a new one in MapKeys
With MapKeys
lIndex = .Item(Key)
.Remove(Key)
.Add(lIndex, Value)
End With
oItemMap = MapItems(lIndex)
oItemMap.Key = Value
ReplaceKey = True
Finally:
SF_Utils._ExitFunction(cstThisSub)
Exit Function
Catch:
GoTo Finally
CatchUnknown:
SF_Exception.RaiseFatal(UNKNOWNKEYERROR, &quot;Key&quot;, Key)
GoTo Finally
CatchDuplicate:
SF_Exception.RaiseFatal(DUPLICATEKEYERROR, &quot;Value&quot;, Value)
GoTo Finally
CatchInvalid:
SF_Exception.RaiseFatal(INVALIDKEYERROR, &quot;Key&quot;)
GoTo Finally
End Function &apos; ScriptForge.SF_Dictionary.ReplaceKey
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;Dictionary.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_Dictionary.SetProperty
REM =========================================================== PRIVATE FUNCTIONS
REM -----------------------------------------------------------------------------
Private Function _PropertyGet(Optional ByVal psProperty As String _
, Optional pvKey As Variant _
)
&apos;&apos;&apos; Return the named property
&apos;&apos;&apos; Args:
&apos;&apos;&apos; psProperty: the name of the property
&apos;&apos;&apos; pvKey: the key to retrieve, numeric or string
Dim vItemMap As Variant &apos; Entry in the MapItems array
Dim vArray As Variant &apos; To get Keys or Values
Dim i As Long
Dim cstThisSub As String
Dim cstSubArgs As String
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
cstThisSub = &quot;SF_Dictionary.get&quot; &amp; psProperty
If IsMissing(pvKey) Then cstSubArgs = &quot;&quot; Else cstSubArgs = &quot;[Key]&quot;
SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
Select Case UCase(psProperty)
Case UCase(&quot;Count&quot;)
_PropertyGet = _MapSize - _MapRemoved
Case UCase(&quot;Item&quot;)
If Not SF_Utils._Validate(pvKey, &quot;Key&quot;, V_STRING) Then GoTo Catch
If Exists(pvKey) Then _PropertyGet = MapItems(MapKeys(pvKey)).Value Else _PropertyGet = Empty
Case UCase(&quot;Keys&quot;), UCase(&quot;Items&quot;)
vArray = Array()
If _MapSize - _MapRemoved - 1 &gt;= 0 Then
ReDim vArray(0 To (_MapSize - _MapRemoved - 1))
i = -1
For each vItemMap In MapItems()
If Not IsEmpty(vItemMap) Then
i = i + 1
If UCase(psProperty) = &quot;KEYS&quot; Then vArray(i) = vItemMap.Key Else vArray(i) = vItemMap.Value
End If
Next vItemMap
End If
_PropertyGet = vArray
End Select
Finally:
SF_Utils._ExitFunction(cstThisSub)
Exit Function
Catch:
GoTo Finally
End Function &apos; ScriptForge.SF_Dictionary._PropertyGet
REM -----------------------------------------------------------------------------
Private Function _Repr() As String
&apos;&apos;&apos; Convert the Dictionary instance to a readable string, typically for debugging purposes (DebugPrint ...)
&apos;&apos;&apos; Args:
&apos;&apos;&apos; Return:
&apos;&apos;&apos; &quot;[Dictionary] (key1:value1, key2:value2, ...)
Dim sDict As String &apos; Return value
Dim vKeys As Variant &apos; Array of keys
Dim sKey As String &apos; Tempry key
Dim vItem As Variant &apos; Tempry item
Const cstDictEmpty = &quot;[Dictionary] ()&quot;
Const cstDict = &quot;[Dictionary]&quot;
Const cstMaxLength = 50 &apos; Maximum length for items
Const cstSeparator = &quot;, &quot;
_Repr = &quot;&quot;
If Count = 0 Then
sDict = cstDictEmpty
Else
sDict = cstDict &amp; &quot; (&quot;
vKeys = Keys
For Each sKey in vKeys
vItem = Item(sKey)
sDict = sDict &amp; sKey &amp; &quot;:&quot; &amp; SF_Utils._Repr(vItem, cstMaxLength) &amp; cstSeparator
Next sKey
sDict = Left(sDict, Len(sDict) - Len(cstSeparator)) &amp; &quot;)&quot; &apos; Suppress last comma
End If
_Repr = sDict
End Function &apos; ScriptForge.SF_Dictionary._Repr
REM ============================================ END OF SCRIPTFORGE.SF_DICTIONARY
</script:module>