summaryrefslogtreecommitdiffstats
path: root/wizards/source/scriptforge/SF_Dictionary.xba
diff options
context:
space:
mode:
Diffstat (limited to 'wizards/source/scriptforge/SF_Dictionary.xba')
-rw-r--r--wizards/source/scriptforge/SF_Dictionary.xba959
1 files changed, 959 insertions, 0 deletions
diff --git a/wizards/source/scriptforge/SF_Dictionary.xba b/wizards/source/scriptforge/SF_Dictionary.xba
new file mode 100644
index 000000000..22ada5148
--- /dev/null
+++ b/wizards/source/scriptforge/SF_Dictionary.xba
@@ -0,0 +1,959 @@
+<?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; Detailed user documentation:
+&apos;&apos;&apos; https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03/sf_dictionary.html?DbPAR=BASIC
+&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
+
+REM ================================================================== EXCEPTIONS
+
+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 IsArray(Value) Then
+ If Not SF_Utils._ValidateArray(Value, &quot;Value&quot;) Then GoTo Catch
+ Else
+ If Not SF_Utils._Validate(Value, &quot;Value&quot;) Then GoTo Catch
+ End If
+ 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> \ No newline at end of file