1
1
Fork 0
mirror of https://github.com/QB64-Phoenix-Edition/QB64pe.git synced 2024-07-03 12:21:20 +00:00
QB64-PE/source/qb_framework/qb_framework_methods.bas
2015-10-30 23:18:44 +11:00

1968 lines
72 KiB
QBasic

DEFLNG A-Z
'#################### NODESET: Methods ####################
FUNCTION QB_FRAMEWORK_leakInfo$
DIM QB__handlesets AS LONG
QB__handlesets = QB_HANDLE_count(1)
DIM QB__nodes AS LONG
QB__nodes = QB_HANDLE_count(__QB_NODE_handleSet)
DIM QB__datetimes AS LONG
QB__datetimes = QB_HANDLE_count(__QB_DATETIME_handleSet)
DIM QB__strings AS LONG
QB__strings = QB_HANDLE_count(__QB_STR_handleSet)
DIM QB__leakInfo AS LONG
QB__leakInfo = QB_NODE_newDictionary
QB_NODE_assign QB__leakInfo, QB_NODE_newValueWithLabel_long("HANDLE_set_count", QB__handlesets)
QB_NODE_assign QB__leakInfo, QB_NODE_newValueWithLabel_long("STR_count", QB__strings)
QB_NODE_assign QB__leakInfo, QB_NODE_newValueWithLabel_long("NODE_count", QB__nodes)
QB_NODE_assign QB__leakInfo, QB_NODE_newValueWithLabel_long("DATETIME_count", QB__datetimes)
QB_NODE_assign QB__leakInfo, QB_NODE_newValueWithLabel_long("global_hash_table_size", (UBOUND(__QB_NODE_hashLists) + 1) * 4)
QB_FRAMEWORK_leakInfo$ = QB_NODESET_serialize(QB__leakInfo, "json")
QB_NODE_destroy QB__leakInfo
END FUNCTION
SUB QB_NODESET_free (QB__selIn AS LONG)
IF QB__selIn < 0 THEN
QB_NODE_destroy -QB__selIn 'destroy this list/hashset of nodes
END IF
END SUB
FUNCTION QB_NODESET_count& (QB__selIn AS LONG)
QB_NODESET_count& = QB_NODESET_count_PRESERVE&(QB__selIn)
QB_NODESET_free QB__selIn
END FUNCTION
FUNCTION QB_NODESET_count_PRESERVE& (QB__selIn AS LONG)
IF QB__selIn < 0 THEN
QB_NODESET_count_PRESERVE& = __QB_NODE(-QB__selIn).count
ELSE
IF QB__selIn <> 0 THEN
QB_NODESET_count_PRESERVE& = 1
ELSE
QB_NODESET_count_PRESERVE& = 0
END IF
END IF
END FUNCTION
FUNCTION QB_NODESET_equal& (QB__selIn AS LONG, value AS STRING)
DIM QB__selOut AS LONG: QB__selOut = QB_NODE_new(QB_NODE_TYPE_HASHSET, 0)
DIM QB__SelInI AS LONG: DIM QB__SelInIterator AS LONG
DIM QB__sel AS LONG
DIM QB__newSel AS LONG
IF QB__selIn < 0 THEN
DO WHILE QB_NODE_each(QB__SelInI, -QB__selIn, QB__SelInIterator)
QB__sel = __QB_NODE(QB__SelInI).label
IF __QB_NODE(QB__sel).valueFormat = QB_NODE_FORMAT_STR THEN
IF QB_STR_get(__QB_NODE(QB__sel).value) = value THEN
QB__newSel = QB_NODE_newLabel_long(QB__sel)
QB_NODE_assign QB__selOut, QB__newSel
END IF
END IF
LOOP
ELSE
QB__sel = QB__selIn
IF __QB_NODE(QB__sel).valueFormat = QB_NODE_FORMAT_STR THEN
IF QB_STR_get(__QB_NODE(QB__sel).value) = value THEN
QB__newSel = QB_NODE_newLabel_long(QB__sel)
QB_NODE_assign QB__selOut, QB__newSel
END IF
END IF
END IF
QB_NODESET_equal& = -QB__selOut
QB_NODESET_free QB__selIn
END FUNCTION
FUNCTION QB_NODESET_label_equal& (QB__selIn AS LONG, value AS STRING)
DIM QB__selOut AS LONG: QB__selOut = QB_NODE_new(QB_NODE_TYPE_HASHSET, 0)
DIM QB__SelInI AS LONG: DIM QB__SelInIterator AS LONG
DIM QB__sel AS LONG
DIM QB__newSel AS LONG
IF QB__selIn < 0 THEN
DO WHILE QB_NODE_each(QB__SelInI, -QB__selIn, QB__SelInIterator)
QB__sel = __QB_NODE(QB__SelInI).label
IF __QB_NODE(QB__sel).labelFormat = QB_NODE_FORMAT_STR THEN
IF QB_STR_get(__QB_NODE(QB__sel).label) = value THEN
QB__newSel = QB_NODE_newLabel_long(QB__sel)
QB_NODE_assign QB__selOut, QB__newSel
END IF
END IF
LOOP
ELSE
QB__sel = QB__selIn
IF __QB_NODE(QB__sel).labelFormat = QB_NODE_FORMAT_STR THEN
IF QB_STR_get(__QB_NODE(QB__sel).label) = value THEN
QB__newSel = QB_NODE_newLabel_long(QB__sel)
QB_NODE_assign QB__selOut, QB__newSel
END IF
END IF
END IF
QB_NODESET_label_equal& = -QB__selOut
QB_NODESET_free QB__selIn
END FUNCTION
FUNCTION QB_NODESET_allChildren& (QB__selIn AS LONG) 'all decendants, all depths
DIM QB__selOut AS LONG: QB__selOut = QB_NODE_new(QB_NODE_TYPE_HASHSET, 0)
DIM QB__SelInI AS LONG: DIM QB__SelInIterator AS LONG
DIM QB__sel AS LONG
IF QB__selIn < 0 THEN
DO WHILE QB_NODE_each(QB__SelInI, -QB__selIn, QB__SelInIterator)
QB__sel = __QB_NODE(QB__SelInI).label
IF __QB_NODE(QB__sel).count THEN
__QB_NODESET_addChildren QB__sel, QB__selOut
END IF
LOOP
ELSE
QB__sel = QB__selIn
IF __QB_NODE(QB__sel).count THEN
__QB_NODESET_addChildren QB__sel, QB__selOut
END IF
END IF
QB_NODESET_allChildren& = -QB__selOut
QB_NODESET_free QB__selIn
END FUNCTION
FUNCTION QB_NODESET_children& (QB__selIn AS LONG) 'only 1st level decendants
DIM QB__selOut AS LONG: QB__selOut = QB_NODE_new(QB_NODE_TYPE_HASHSET, 0)
DIM QB__SelInI AS LONG: DIM QB__SelInIterator AS LONG
DIM QB__sel AS LONG
IF QB__selIn < 0 THEN
DO WHILE QB_NODE_each(QB__SelInI, -QB__selIn, QB__SelInIterator)
QB__sel = __QB_NODE(QB__SelInI).label
IF __QB_NODE(QB__sel).count THEN
__QB_NODESET_addChildrenWithDepth QB__sel, QB__selOut, 1, 1, 1
END IF
LOOP
ELSE
QB__sel = QB__selIn
IF __QB_NODE(QB__sel).count THEN
__QB_NODESET_addChildrenWithDepth QB__sel, QB__selOut, 1, 1, 1
END IF
END IF
QB_NODESET_children& = -QB__selOut
QB_NODESET_free QB__selIn
END FUNCTION
FUNCTION QB_NODESET_parent& (QB__selIn AS LONG)
DIM QB__selOut AS LONG: QB__selOut = QB_NODE_new(QB_NODE_TYPE_HASHSET, 0)
DIM QB__SelInI AS LONG: DIM QB__SelInIterator AS LONG
DIM QB__sel AS LONG
DIM QB__newSel AS LONG
IF QB__selIn < 0 THEN
DO WHILE QB_NODE_each(QB__SelInI, -QB__selIn, QB__SelInIterator)
QB__sel = __QB_NODE(QB__SelInI).label
IF __QB_NODE(QB__sel).parent THEN
QB__newSel = QB_NODE_newLabel_long(__QB_NODE(QB__sel).parent)
QB_NODE_assign QB__selOut, QB__newSel
END IF
LOOP
ELSE
QB__sel = QB__selIn
IF __QB_NODE(QB__sel).parent THEN
QB__newSel = QB_NODE_newLabel_long(__QB_NODE(QB__sel).parent)
QB_NODE_assign QB__selOut, QB__newSel
END IF
END IF
QB_NODESET_parent& = -QB__selOut
QB_NODESET_free QB__selIn
END FUNCTION
FUNCTION QB_NODESET_node& (QB__selIn AS LONG)
IF QB__selIn >= 0 THEN
QB_NODESET_node& = QB__selIn
'note: not a nodeset, no need to call nodeset free
EXIT FUNCTION
END IF
DIM QB__sel AS LONG
QB__sel = __QB_NODE(-QB__selIn).firstChild
IF QB__sel <> 0 THEN
QB__sel = __QB_NODE(QB__sel).label
END IF
QB_NODESET_node& = QB__sel
QB_NODESET_free QB__selIn
END FUNCTION
FUNCTION QB_NODESET_parents& (QB__selIn AS LONG)
DIM QB__selOut AS LONG: QB__selOut = QB_NODE_new(QB_NODE_TYPE_HASHSET, 0)
DIM QB__SelInI AS LONG: DIM QB__SelInIterator AS LONG
DIM QB__sel AS LONG
DIM QB__newSel AS LONG
IF QB__selIn < 0 THEN
DO WHILE QB_NODE_each(QB__SelInI, -QB__selIn, QB__SelInIterator)
QB__sel = __QB_NODE(QB__SelInI).label
QB__sel = __QB_NODE(QB__sel).parent
DO WHILE QB__sel
QB__newSel = QB_NODE_newLabel_long(QB__sel)
QB_NODE_assign QB__selOut, QB__newSel
QB__sel = __QB_NODE(QB__sel).parent
LOOP
LOOP
ELSE
QB__sel = QB__selIn
QB__sel = __QB_NODE(QB__sel).parent
DO WHILE QB__sel
QB__newSel = QB_NODE_newLabel_long(QB__sel)
QB_NODE_assign QB__selOut, QB__newSel
QB__sel = __QB_NODE(QB__sel).parent
LOOP
END IF
QB_NODESET_parents& = -QB__selOut
QB_NODESET_free QB__selIn
END FUNCTION
FUNCTION QB_NODESET_each& (QB__SelInI AS LONG, QB__selIn AS LONG, QB__selInIterator AS LONG)
DIM QB__ret AS LONG
QB__ret = QB_NODESET_each_PRESERVE(QB__SelInI, QB__selIn, QB__selInIterator)
IF QB__ret = 0 THEN QB_NODESET_free QB__selIn
QB_NODESET_each& = QB__ret
END FUNCTION
FUNCTION QB_NODESET_each_PRESERVE& (QB__SelInI AS LONG, QB__selIn AS LONG, QB__selInIterator AS LONG)
IF QB__selIn > 0 THEN
IF QB__selInIterator = 0 THEN
QB_NODESET_each_PRESERVE& = -1
QB__SelInI = QB__selIn
ELSE
QB__selInIterator = 1
QB_NODESET_each_PRESERVE& = 0
QB__SelInI = 0
END IF
ELSE
DIM QB__ret AS LONG
QB_NODESET_each_PRESERVE = QB_NODE_each(QB__SelInI, -QB__selIn, QB__selInIterator)
IF QB__SelInI <> 0 THEN QB__SelInI = __QB_NODE(QB__SelInI).label
END IF
END FUNCTION
FUNCTION QB_NODESET_deserialize& (json AS STRING, format AS STRING) 'only "json" is supported
'prepass to make deserializing by scanning (INSTR) for : { } [ ] , work
DIM json2 AS STRING
json2 = SPACE$(LEN(json) * 6) 'the maximum size preparsed content can grow is 6x
i2 = 0
inblock = 0
lastA = 0
lastLastA = 0
FOR i1 = 1 TO LEN(json)
a = ASC(json, i1)
IF inblock THEN
IF a = 58 OR a = 123 OR a = 125 OR a = 91 OR a = 93 OR a = 44 THEN 'escape... : { } [ ] ,
IF a = 58 THEN i2 = i2 + 6: MID$(json2, i2 - 5, 6) = "\u003A"
IF a = 123 THEN i2 = i2 + 6: MID$(json2, i2 - 5, 6) = "\u007B"
IF a = 125 THEN i2 = i2 + 6: MID$(json2, i2 - 5, 6) = "\u007D"
IF a = 91 THEN i2 = i2 + 6: MID$(json2, i2 - 5, 6) = "\u005B"
IF a = 93 THEN i2 = i2 + 6: MID$(json2, i2 - 5, 6) = "\u005D"
IF a = 44 THEN i2 = i2 + 6: MID$(json2, i2 - 5, 6) = "\u002C"
ELSE
i2 = i2 + 1: ASC(json2, i2) = a
END IF
IF a = inblock AND ((lastA <> 92) OR (lastA = 92 AND lastLastA = 92)) THEN inblock = 0 'note: we allow \'
ELSE
IF a = 34 THEN inblock = 34
IF a = 39 THEN inblock = 39
i2 = i2 + 1: ASC(json2, i2) = a
END IF
lastLastA = lastA
lastA = a
NEXT
json2 = LEFT$(json2, i2)
json2 = LTRIM$(RTRIM$(json2))
DIM QB__index AS LONG
QB__index = 1
QB_NODESET_deserialize& = __QB_JSON_deserialize(json2, QB__index, 0)
END FUNCTION
FUNCTION QB_NODESET_serialize$ (QB__selIn AS LONG, format AS STRING) 'only "json" is supported
QB_NODESET_serialize$ = QB_NODESET_serialize_PRESERVE$(QB__selIn, format)
QB_NODESET_free QB__selIn
END FUNCTION
FUNCTION QB_NODESET_serialize_PRESERVE$ (QB__selIn AS LONG, format AS STRING) 'only "json" is supported
DIM QB__ret AS STRING
DIM QB__SelInI AS LONG: DIM QB__SelInIterator AS LONG
DIM QB__sel AS LONG
IF QB__selIn < 0 THEN
DIM QB__n AS LONG
DO WHILE QB_NODE_each(QB__SelInI, -QB__selIn, QB__SelInIterator)
QB__sel = __QB_NODE(QB__SelInI).label
QB__n = QB__n + 1
__QB_JSON_serialize QB__ret, QB__sel, 0
IF __QB_NODE(-QB__selIn).count <> QB__n THEN QB__ret = QB__ret + ","
LOOP
ELSE
QB__sel = QB__selIn
__QB_JSON_serialize QB__ret, QB__sel, 0
END IF
QB_NODESET_serialize_PRESERVE$ = QB__ret
END FUNCTION
'########################################
'#################### DATETIME: Methods ####################
FUNCTION QB_DATETIME_new (dateTimeType AS LONG)
DIM QB__handle AS LONG
QB__handle = QB_HANDLE_new(__QB_DATETIME_handleSet)
IF QB__handle > __QB_DATETIME_ubound THEN
__QB_DATETIME_ubound = QB__handle * 2
REDIM _PRESERVE __QB_DATETIME(__QB_DATETIME_ubound) AS QB_DATETIME
END IF
__QB_DATETIME(QB__handle) = __QB_DATETIME_TYPE_EMPTY
__QB_DATETIME(QB__handle).reserved = 1
__QB_DATETIME(QB__handle).type = dateTimeType
QB_DATETIME_new& = QB__handle
END FUNCTION
SUB QB_DATETIME_get (QB__handle AS LONG, dateTimeToPopulate AS QB_DATETIME)
dateTimeToPopulate = __QB_DATETIME(QB__handle)
END SUB
SUB QB_DATETIME_set (QB__handle AS LONG, dateTimeToPopulate AS QB_DATETIME)
__QB_DATETIME(QB__handle) = dateTimeToPopulate
END SUB
SUB QB_DATETIME_free (QB__handle AS LONG)
IF QB__handle > __QB_DATETIME_ubound OR QB__handle <= 0 THEN ERROR 258: EXIT SUB 'invalid handle
IF __QB_DATETIME(QB__handle).reserved = 0 THEN ERROR 258: EXIT SUB
__QB_DATETIME(QB__handle).reserved = 0
QB_HANDLE_free QB__handle, __QB_DATETIME_handleSet
END SUB
FUNCTION QB_DATETIME_now
DIM QB__handle AS LONG
QB__handle = QB_DATETIME_new(QB_DATETIME_TYPE_LOCAL)
DIM QB__date AS STRING
QB__date = DATE$ 'mm-dd-yyyy
DIM QB__time AS STRING
QB__time = TIME$ 'hh:mm:ss
DIM QB__timer AS DOUBLE
QB__timer = TIMER(0.001)
__QB_DATETIME(QB__handle).months = VAL(MID$(QB__date, 1, 2))
__QB_DATETIME(QB__handle).days = VAL(MID$(QB__date, 4, 2))
__QB_DATETIME(QB__handle).years = VAL(MID$(QB__date, 7, 4))
__QB_DATETIME(QB__handle).hours = VAL(MID$(QB__time, 1, 2))
__QB_DATETIME(QB__handle).minutes = VAL(MID$(QB__time, 4, 2))
__QB_DATETIME(QB__handle).seconds = VAL(MID$(QB__time, 7, 2))
DIM QB__msStr AS STRING
DIM QB__ms AS LONG
QB__msStr = LTRIM$(STR$(QB__timer - INT(QB__timer)))
IF LEN(QB__msStr) > 4 THEN QB__msStr = LEFT$(QB__msStr, 4)
QB__ms = VAL(QB__msStr) * 1000
IF QB__ms >= 1000 THEN QB__ms = 0
__QB_DATETIME(QB__handle).milliseconds = QB__ms
QB_DATETIME_now& = QB__handle
END FUNCTION
FUNCTION QB_DATETIME_format$ (QB__handle AS LONG, format AS STRING)
'Example:
' PRINT QB_DATETIME_format(myDateHandle, "D/M/YYYY H:mm:ss {AM}") 'could print "31/3/2012 5:02:05 PM"
'
'YYYY - 4 digit year
'YY - 2 digit year
'MM - 2 digit month
'M - 1 or 2 digit month
'DD - 2 digit day
'D - 1 or 2 digit day
'{TH},{Th},{th}
'{JAN},{jan},{Jan}
'{JANUARY},{january},{January}
'{MONDAY},{Monday},{monday}
'hh - 2 digit hour (24 hour time)
'HH - 2 digit hour (12 hour time)
'h - 1 or 2 digit hour (24 hour time)
'H - 1 or 2 digit hour (12 hour time)
'mm - 2 digit minutes
'm - 1 or 2 digit minutes
'ss - 2 digit seconds
's - 1 or 2 digit seconds
'zzz - 3 digit milliseconds
'z - 1, 2 or 3 digit milliseconds
'{AM},{am}
DIM QB__out AS STRING
QB__out = ""
DIM QB__i AS LONG
DIM QB__fi AS LONG
DIM QB__s AS STRING
DIM QB__i1 AS LONG
DIM QB__minDigits AS LONG
DIM QB__value AS STRING
DIM QB__rhs AS STRING
DIM QB__n AS LONG
DIM QB__x AS LONG
DIM QB__smartCase AS LONG
FOR QB__fi = 1 TO LEN(format)
FOR QB__i = 1 TO 100
QB__minDigits = -1 'N/A
QB__smartCase = 0 'match case exactly
QB__s = ""
QB__n = 0
QB__n = QB__n + 1: IF QB__i = QB__n THEN
QB__s = "{am}"
QB__smartCase = 1
IF __QB_DATETIME(QB__handle).hours > 11 THEN QB__value = "pm" ELSE QB__value = "am"
END IF
QB__n = QB__n + 1: IF QB__i = QB__n THEN
QB__s = "{th}"
QB__smartCase = 1
QB__value = "th"
IF __QB_DATETIME(QB__handle).days MOD 10 = 1 THEN QB__value = "st"
IF __QB_DATETIME(QB__handle).days MOD 10 = 2 THEN QB__value = "nd"
IF __QB_DATETIME(QB__handle).days MOD 10 = 3 THEN QB__value = "rd"
IF __QB_DATETIME(QB__handle).days > 10 AND __QB_DATETIME(QB__handle).days < 14 THEN QB__value = "th"
END IF
QB__n = QB__n + 1: IF QB__i = QB__n THEN
QB__s = "{monday}"
QB__smartCase = 1
DIM QB__month AS LONG
DIM QB__year AS LONG
DIM QB__day AS LONG
DIM QB__newYear AS STRING
DIM QB__century AS LONG
DIM QB__dmy AS LONG
'http://brisray.com/qbasic/qdate.htm
QB__day = __QB_DATETIME(QB__handle).days
QB__month = __QB_DATETIME(QB__handle).months
QB__year = __QB_DATETIME(QB__handle).years
IF QB__month < 3 THEN
QB__month = QB__month + 12
QB__year = QB__year - 1
END IF
'*** Add 1 to the month and multiply by 2.61
'*** Drop the fraction (not round) afterwards
QB__month = QB__month + 1
QB__month = FIX(QB__month * 2.61)
'*** Add Day, Month and the last two digits of the year
QB__newYear = LTRIM$(STR$(QB__year))
QB__year = VAL(RIGHT$(QB__newYear, 2))
QB__dmy = QB__day + QB__month + QB__year
QB__century = VAL(LEFT$(QB__newYear, 2))
'*** Add a quarter of the last two digits of the year
'*** (truncated not rounded)
QB__year = FIX(QB__year / 4)
QB__dmy = QB__dmy + QB__year
'*** Add the following factors for the year
IF QB__century = 18 THEN QB__century = 2
IF QB__century = 19 THEN QB__century = 0
IF QB__century = 20 THEN QB__century = 6
IF QB__century = 21 THEN QB__century = 4
QB__dmy = QB__dmy + QB__century
'*** The day of the week is the modulus of DMY divided by 7
QB__dmy = QB__dmy MOD 7
IF QB__dmy = 0 THEN QB__value = "sunday"
IF QB__dmy = 1 THEN QB__value = "monday"
IF QB__dmy = 2 THEN QB__value = "tuesday"
IF QB__dmy = 3 THEN QB__value = "wednesday"
IF QB__dmy = 4 THEN QB__value = "thursday"
IF QB__dmy = 5 THEN QB__value = "friday"
IF QB__dmy = 6 THEN QB__value = "saturday"
END IF
QB__n = QB__n + 1: IF QB__i = QB__n THEN
QB__s = "{mon}"
QB__smartCase = 1
'http://brisray.com/qbasic/qdate.htm
QB__day = __QB_DATETIME(QB__handle).days
QB__month = __QB_DATETIME(QB__handle).months
QB__year = __QB_DATETIME(QB__handle).years
IF QB__month < 3 THEN
QB__month = QB__month + 12
QB__year = QB__year - 1
END IF
'*** Add 1 to the month and multiply by 2.61
'*** Drop the fraction (not round) afterwards
QB__month = QB__month + 1
QB__month = FIX(QB__month * 2.61)
'*** Add Day, Month and the last two digits of the year
QB__newYear = LTRIM$(STR$(QB__year))
QB__year = VAL(RIGHT$(QB__newYear, 2))
QB__dmy = QB__day + QB__month + QB__year
QB__century = VAL(LEFT$(QB__newYear, 2))
'*** Add a quarter of the last two digits of the year
'*** (truncated not rounded)
QB__year = FIX(QB__year / 4)
QB__dmy = QB__dmy + QB__year
'*** Add the following factors for the year
IF QB__century = 18 THEN QB__century = 2
IF QB__century = 19 THEN QB__century = 0
IF QB__century = 20 THEN QB__century = 6
IF QB__century = 21 THEN QB__century = 4
QB__dmy = QB__dmy + QB__century
'*** The day of the week is the modulus of DMY divided by 7
QB__dmy = QB__dmy MOD 7
IF QB__dmy = 0 THEN QB__value = "sun"
IF QB__dmy = 1 THEN QB__value = "mon"
IF QB__dmy = 2 THEN QB__value = "tue"
IF QB__dmy = 3 THEN QB__value = "wed"
IF QB__dmy = 4 THEN QB__value = "thu"
IF QB__dmy = 5 THEN QB__value = "fri"
IF QB__dmy = 6 THEN QB__value = "sat"
END IF
QB__n = QB__n + 1: IF QB__i = QB__n THEN
QB__s = "{jan}"
QB__smartCase = 1
IF __QB_DATETIME(QB__handle).months = 1 THEN QB__value = "jan"
IF __QB_DATETIME(QB__handle).months = 2 THEN QB__value = "feb"
IF __QB_DATETIME(QB__handle).months = 3 THEN QB__value = "mar"
IF __QB_DATETIME(QB__handle).months = 4 THEN QB__value = "apr"
IF __QB_DATETIME(QB__handle).months = 5 THEN QB__value = "may"
IF __QB_DATETIME(QB__handle).months = 6 THEN QB__value = "jun"
IF __QB_DATETIME(QB__handle).months = 7 THEN QB__value = "jul"
IF __QB_DATETIME(QB__handle).months = 8 THEN QB__value = "aug"
IF __QB_DATETIME(QB__handle).months = 9 THEN QB__value = "sep"
IF __QB_DATETIME(QB__handle).months = 10 THEN QB__value = "oct"
IF __QB_DATETIME(QB__handle).months = 11 THEN QB__value = "nov"
IF __QB_DATETIME(QB__handle).months = 12 THEN QB__value = "dec"
END IF
QB__n = QB__n + 1: IF QB__i = QB__n THEN
QB__s = "{january}"
QB__smartCase = 1
IF __QB_DATETIME(QB__handle).months = 1 THEN QB__value = "january"
IF __QB_DATETIME(QB__handle).months = 2 THEN QB__value = "february"
IF __QB_DATETIME(QB__handle).months = 3 THEN QB__value = "march"
IF __QB_DATETIME(QB__handle).months = 4 THEN QB__value = "april"
IF __QB_DATETIME(QB__handle).months = 5 THEN QB__value = "may"
IF __QB_DATETIME(QB__handle).months = 6 THEN QB__value = "june"
IF __QB_DATETIME(QB__handle).months = 7 THEN QB__value = "july"
IF __QB_DATETIME(QB__handle).months = 8 THEN QB__value = "august"
IF __QB_DATETIME(QB__handle).months = 9 THEN QB__value = "september"
IF __QB_DATETIME(QB__handle).months = 10 THEN QB__value = "october"
IF __QB_DATETIME(QB__handle).months = 11 THEN QB__value = "november"
IF __QB_DATETIME(QB__handle).months = 12 THEN QB__value = "december"
END IF
QB__n = QB__n + 1: IF QB__i = QB__n THEN QB__s = "YYYY": QB__minDigits = 4: QB__value = QB_STR_long(__QB_DATETIME(QB__handle).years)
QB__n = QB__n + 1: IF QB__i = QB__n THEN QB__s = "YY": QB__minDigits = 2: QB__value = QB_STR_long(__QB_DATETIME(QB__handle).years MOD 100)
QB__n = QB__n + 1: IF QB__i = QB__n THEN QB__s = "MM": QB__minDigits = 2: QB__value = QB_STR_long(__QB_DATETIME(QB__handle).months)
QB__n = QB__n + 1: IF QB__i = QB__n THEN QB__s = "M": QB__minDigits = 1: QB__value = QB_STR_long(__QB_DATETIME(QB__handle).months)
QB__n = QB__n + 1: IF QB__i = QB__n THEN QB__s = "DD": QB__minDigits = 2: QB__value = QB_STR_long(__QB_DATETIME(QB__handle).days)
QB__n = QB__n + 1: IF QB__i = QB__n THEN QB__s = "D": QB__minDigits = 1: QB__value = QB_STR_long(__QB_DATETIME(QB__handle).days)
QB__n = QB__n + 1: IF QB__i = QB__n THEN QB__s = "hh": QB__minDigits = 2: QB__value = QB_STR_long(__QB_DATETIME(QB__handle).hours)
QB__n = QB__n + 1: IF QB__i = QB__n THEN QB__s = "h": QB__minDigits = 1: QB__value = QB_STR_long(__QB_DATETIME(QB__handle).hours)
QB__n = QB__n + 1: IF QB__i = QB__n THEN
QB__s = "HH": QB__minDigits = 2:
QB__x = __QB_DATETIME(QB__handle).hours
IF QB__x > 12 THEN QB__x = QB__x - 12
IF QB__x = 0 THEN QB__x = 12
QB__value = QB_STR_long(QB__x)
END IF
QB__n = QB__n + 1: IF QB__i = QB__n THEN
QB__s = "H": QB__minDigits = 1
QB__x = __QB_DATETIME(QB__handle).hours
IF QB__x > 12 THEN QB__x = QB__x - 12
IF QB__x = 0 THEN QB__x = 12
QB__value = QB_STR_long(QB__x)
END IF
QB__n = QB__n + 1: IF QB__i = QB__n THEN QB__s = "mm": QB__minDigits = 2: QB__value = QB_STR_long(__QB_DATETIME(QB__handle).minutes)
QB__n = QB__n + 1: IF QB__i = QB__n THEN QB__s = "m": QB__minDigits = 1: QB__value = QB_STR_long(__QB_DATETIME(QB__handle).minutes)
QB__n = QB__n + 1: IF QB__i = QB__n THEN QB__s = "ss": QB__minDigits = 2: QB__value = QB_STR_long(__QB_DATETIME(QB__handle).seconds)
QB__n = QB__n + 1: IF QB__i = QB__n THEN QB__s = "s": QB__minDigits = 1: QB__value = QB_STR_long(__QB_DATETIME(QB__handle).seconds)
QB__n = QB__n + 1: IF QB__i = QB__n THEN QB__s = "zzz": QB__minDigits = 3: QB__value = QB_STR_long(__QB_DATETIME(QB__handle).milliseconds)
QB__n = QB__n + 1: IF QB__i = QB__n THEN QB__s = "z": QB__minDigits = 1: QB__value = QB_STR_long(__QB_DATETIME(QB__handle).milliseconds)
IF QB__s <> "" THEN
IF QB__smartCase THEN
QB__rhs = LCASE$(RIGHT$(format, LEN(format) - QB__fi + 1)) + " "
ELSE
QB__rhs = RIGHT$(format, LEN(format) - QB__fi + 1) + " "
END IF
IF LEFT$(QB__rhs, LEN(QB__s)) = QB__s THEN
IF QB__minDigits <> -1 THEN
IF LEN(QB__value) < QB__minDigits THEN
QB__value = STRING$(QB__minDigits - LEN(QB__value), "0") + QB__value
END IF
END IF
IF QB__smartCase THEN
QB__rhs = RIGHT$(format, LEN(format) - QB__fi + 1) + " "
QB__value = __QB_DATETIME_format_smartCase$(QB__rhs, QB__value)
END IF
QB__out = QB__out + QB__value
QB__fi = QB__fi + LEN(QB__s) - 1
EXIT FOR
END IF
END IF
NEXT
IF QB__i = 101 THEN QB__out = QB__out + MID$(format, QB__fi, 1)
NEXT
QB_DATETIME_format$ = QB__out
END FUNCTION
'########################################
'#################### STRING: Methods ####################
FUNCTION QB_STR_empty&
DIM QB__handle AS LONG
QB__handle = QB_HANDLE_new(__QB_STR_handleSet)
IF QB__handle > __QB_STR_stringUbound THEN
__QB_STR_stringUbound = QB__handle * 2
REDIM _PRESERVE __QB_STR_string(__QB_STR_stringUbound) AS STRING
REDIM _PRESERVE __QB_STR_stringValid(__QB_STR_stringUbound) AS LONG
END IF
__QB_STR_stringValid(QB__handle) = 1
IF LEN(__QB_STR_string(QB__handle)) <> 0 THEN __QB_STR_string(QB__handle) = ""
QB_STR_empty& = QB__handle
END FUNCTION
FUNCTION QB_STR_new& (Value AS STRING)
DIM QB__handle AS LONG
QB__handle = QB_HANDLE_new(__QB_STR_handleSet)
IF QB__handle > __QB_STR_stringUbound THEN
__QB_STR_stringUbound = QB__handle * 2
REDIM _PRESERVE __QB_STR_string(__QB_STR_stringUbound) AS STRING
REDIM _PRESERVE __QB_STR_stringValid(__QB_STR_stringUbound) AS LONG
END IF
__QB_STR_stringValid(QB__handle) = 1
__QB_STR_string(QB__handle) = Value
QB_STR_new& = QB__handle
END FUNCTION
FUNCTION QB_STR_get$ (handle AS LONG)
IF handle > __QB_STR_stringUbound OR handle <= 0 THEN
$CHECKING:OFF
ERROR 258 'invalid handle
EXIT SUB
$CHECKING:ON
END IF
IF __QB_STR_stringValid(handle) = 0 THEN
$CHECKING:OFF
ERROR 258 'invalid handle
EXIT SUB
$CHECKING:ON
END IF
QB_STR_get$ = __QB_STR_string(handle)
END FUNCTION
SUB QB_STR_set (handle AS LONG, value AS STRING)
IF handle > __QB_STR_stringUbound OR handle <= 0 THEN ERROR 258: EXIT SUB 'invalid handle
IF __QB_STR_stringValid(handle) = 0 THEN ERROR 258: EXIT SUB
__QB_STR_string(handle) = value
END SUB
SUB QB_STR_free (handle AS LONG)
IF handle > __QB_STR_stringUbound OR handle <= 0 THEN ERROR 258: EXIT SUB 'invalid handle
IF __QB_STR_stringValid(handle) = 0 THEN ERROR 258: EXIT SUB
__QB_STR_stringValid(handle) = 0
QB_HANDLE_free handle, __QB_STR_handleSet
END SUB
FUNCTION QB_STR_long$ (value AS LONG) 'returns a string representation of a long value
QB_STR_long$ = LTRIM$(STR$(value))
END FUNCTION
'##################################################
'#################### HANDLE: Methods ####################
FUNCTION QB_HANDLE_newSet&
DIM QB__context AS LONG
QB__context = QB_HANDLE_new(1)
IF UBOUND(__QB_HANDLE_handler) < QB__context THEN
REDIM _PRESERVE __QB_HANDLE_handler(QB__context * 2) AS __QB_HANDLE_HANDLER
END IF
__QB_HANDLE_handler(QB__context).lastFreedListIndex = 0
__QB_HANDLE_handler(QB__context).lastHandle = 0
__QB_HANDLE_handler(QB__context).count = 0
QB_HANDLE_newSet& = QB__context
END FUNCTION
SUB QB_HANDLE_freeSet (context AS LONG)
QB_HANDLE_free context, 1
END SUB
FUNCTION QB_HANDLE_count& (context AS LONG)
QB_HANDLE_count& = __QB_HANDLE_handler(context).count
END FUNCTION
FUNCTION QB_HANDLE_new& (context AS LONG)
__QB_HANDLE_handler(context).count = __QB_HANDLE_handler(context).count + 1
DIM QB__handle AS LONG
IF __QB_HANDLE_handler(context).lastFreedListIndex = 0 THEN
QB__handle = __QB_HANDLE_handler(context).lastHandle + 1
__QB_HANDLE_handler(context).lastHandle = QB__handle
QB_HANDLE_new& = QB__handle
EXIT FUNCTION
END IF
DIM __QB_HANDLE_lastIndex AS LONG
__QB_HANDLE_lastIndex = __QB_HANDLE_handler(context).lastFreedListIndex
QB__handle = __QB_HANDLE_freedList(__QB_HANDLE_lastIndex).handle
__QB_HANDLE_handler(context).lastFreedListIndex = __QB_HANDLE_freedList(__QB_HANDLE_lastIndex).prevFreedListIndex
'add to freed-freed list so the freed structure can be reused
IF __QB_HANDLE_freedFreedList_Next > __QB_HANDLE_freedFreedList_Last THEN
__QB_HANDLE_freedFreedList_Last = __QB_HANDLE_freedFreedList_Next * 2
REDIM _PRESERVE __QB_HANDLE_freedFreedList(__QB_HANDLE_freedFreedList_Last) AS LONG
END IF
__QB_HANDLE_freedFreedList(__QB_HANDLE_freedFreedList_Next) = __QB_HANDLE_lastIndex
__QB_HANDLE_freedFreedList_Next = __QB_HANDLE_freedFreedList_Next + 1
QB_HANDLE_new& = QB__handle
END FUNCTION
SUB QB_HANDLE_free (handle AS LONG, context AS LONG) 'MUST pass a valid handle
__QB_HANDLE_handler(context).count = __QB_HANDLE_handler(context).count - 1
'add handle to freed list
DIM QB__index AS LONG
IF __QB_HANDLE_freedFreedList_Next > 1 THEN 'recover from freed-freed list?
__QB_HANDLE_freedFreedList_Next = __QB_HANDLE_freedFreedList_Next - 1
QB__index = __QB_HANDLE_freedFreedList(__QB_HANDLE_freedFreedList_Next)
ELSE
IF __QB_HANDLE_freedList_Next > __QB_HANDLE_freedList_Last THEN
__QB_HANDLE_freedList_Last = __QB_HANDLE_freedList_Next * 2
REDIM _PRESERVE __QB_HANDLE_freedList(__QB_HANDLE_freedList_Last) AS __QB_HANDLE_FREEDLIST
END IF
QB__index = __QB_HANDLE_freedList_Next
__QB_HANDLE_freedList_Next = __QB_HANDLE_freedList_Next + 1
END IF
__QB_HANDLE_freedList(QB__index).prevFreedListIndex = __QB_HANDLE_handler(context).lastFreedListIndex
__QB_HANDLE_freedList(QB__index).handle = handle
__QB_HANDLE_handler(context).lastFreedListIndex = QB__index
END SUB
'##################################################
'#################### EACH: Methods ####################
FUNCTION QB_EACH_str_in_str& (value AS STRING, parent AS STRING, separator AS STRING, flags AS LONG, i AS LONG)
'requirements:
' iterator must be a LONG, initially set to 0
'notes:
' refer to constants for available flags (0 is default)
DIM QB__byteValue AS LONG
DIM QB__parentLen AS LONG
DIM QB__sepValue AS LONG
DIM QB__i1 AS LONG
DIM QB__retry AS LONG
QB__sepValue = ASC(separator)
QB__parentLen = LEN(parent)
DO
i = i + 1
IF i > QB__parentLen THEN
value = ""
IF i = QB__parentLen + 1 THEN
IF QB__parentLen <> 0 THEN
IF ASC(parent, i - 1) = QB__sepValue THEN
IF (flags AND (QB_EACH_ALLOW_BLANK OR QB_EACH_ALLOW_ALL_BLANK)) <> 0 THEN QB_EACH_str_in_str& = -1
END IF
ELSE
IF (flags AND QB_EACH_ALLOW_ALL_BLANK) <> 0 THEN QB_EACH_str_in_str& = -1
END IF
END IF
EXIT FUNCTION
END IF
QB__i1 = i
byteValue = ASC(parent, i)
$CHECKING:OFF
DO WHILE byteValue <> QB__sepValue
i = i + 1
IF i > QB__parentLen THEN EXIT DO
byteValue = ASC(parent, i)
LOOP
$CHECKING:ON
value = MID$(parent, QB__i1, i - QB__i1)
IF LEN(value) = 0 AND (flags AND (QB_EACH_ALLOW_BLANK OR QB_EACH_ALLOW_ALL_BLANK)) = 0 THEN
QB__retry = 1
ELSE
QB__retry = 0
END IF
LOOP WHILE QB__retry
QB_EACH_str_in_str& = -1
END FUNCTION
FUNCTION QB_EACH_long_in_str& (value AS LONG, parent AS STRING, separator AS STRING, i AS LONG)
'requirements:
' a comma separated list of valid LONG values
' no whitespace
' no leading, trailing or adjacent commas
' iterator must be a LONG
' value must be a LONG
DIM QB__byteValue AS LONG
DIM QB__parentLen AS LONG
DIM QB__negate AS LONG
DIM QB__sepValue AS LONG
QB__sepValue = ASC(separator)
QB__parentLen = LEN(parent)
value = 0 'reset value (avoids undefined results)
i = i + 1
IF i > QB__parentLen THEN EXIT FUNCTION
QB__byteValue = ASC(parent, i)
IF QB__byteValue = 45 THEN
QB__negate = 1
i = i + 1
QB__byteValue = ASC(parent, i)
END IF
DO WHILE QB__byteValue <> QB__sepValue
value = value * 10 + QB__byteValue - 48
i = i + 1
IF i > QB__parentLen THEN EXIT DO
QB__byteValue = ASC(parent, i)
LOOP
IF QB__negate THEN value = -value
QB_EACH_long_in_str& = -1
END FUNCTION
'##################################################
'#################### Key Value Pair Dictionary Look-Ups: Methods ####################
FUNCTION QB_NODE_newValueWithLabel& (label AS STRING, value AS STRING) 'assume str_str
DIM QB__handle AS LONG
QB__handle = __QB_NODE_new&(QB_NODE_TYPE_VALUE)
__QB_NODE(QB__handle).labelFormat = QB_NODE_FORMAT_STR
__QB_NODE(QB__handle).label = QB_STR_new(label)
__QB_NODE(QB__handle).valueFormat = QB_NODE_FORMAT_STR
__QB_NODE(QB__handle).value = QB_STR_new(value)
QB_NODE_newValueWithLabel& = QB__handle
END FUNCTION
FUNCTION QB_NODE_newValueWithLabel_long& (label AS STRING, value AS LONG) 'assume str_long
DIM QB__handle AS LONG
QB__handle = __QB_NODE_new&(QB_NODE_TYPE_VALUE)
__QB_NODE(QB__handle).labelFormat = QB_NODE_FORMAT_STR
__QB_NODE(QB__handle).label = QB_STR_new(label)
__QB_NODE(QB__handle).valueFormat = QB_NODE_FORMAT_LONG
__QB_NODE(QB__handle).value = value
QB_NODE_newValueWithLabel_long& = QB__handle
END FUNCTION
FUNCTION QB_NODE_newValueWithLabel_bool& (label AS STRING, value AS LONG) 'assume str_bool
DIM QB__handle AS LONG
QB__handle = __QB_NODE_new&(QB_NODE_TYPE_VALUE)
__QB_NODE(QB__handle).labelFormat = QB_NODE_FORMAT_STR
__QB_NODE(QB__handle).label = QB_STR_new(label)
__QB_NODE(QB__handle).valueFormat = QB_NODE_FORMAT_BOOL
IF value = 0 THEN
__QB_NODE(QB__handle).value = QB_FALSE
ELSE
__QB_NODE(QB__handle).value = QB_TRUE
END IF
QB_NODE_newValueWithLabel_bool& = QB__handle
END FUNCTION
FUNCTION QB_NODE_newLabel& (label AS STRING) 'assume str
DIM QB__handle AS LONG
QB__handle = __QB_NODE_new&(QB_NODE_TYPE_VALUE)
__QB_NODE(QB__handle).labelFormat = QB_NODE_FORMAT_STR
__QB_NODE(QB__handle).label = QB_STR_new(label)
QB_NODE_newLabel& = QB__handle
END FUNCTION
FUNCTION QB_NODE_newValue& (value AS STRING) 'assume str
DIM QB__handle AS LONG
QB__handle = __QB_NODE_new&(QB_NODE_TYPE_VALUE)
__QB_NODE(QB__handle).valueFormat = QB_NODE_FORMAT_STR
__QB_NODE(QB__handle).value = QB_STR_new(value)
QB_NODE_newValue& = QB__handle
END FUNCTION
FUNCTION QB_NODE_newLabel_long& (label AS LONG)
DIM QB__handle AS LONG
QB__handle = __QB_NODE_new&(QB_NODE_TYPE_VALUE)
__QB_NODE(QB__handle).labelFormat = QB_NODE_FORMAT_LONG
__QB_NODE(QB__handle).label = label
QB_NODE_newLabel_long& = QB__handle
END FUNCTION
FUNCTION QB_NODE_typeName$ (nodeType AS LONG)
IF nodeType = 1 THEN QB_NODE_typeName$ = "HASHSET"
IF nodeType = 2 THEN QB_NODE_typeName$ = "LIST"
IF nodeType = 4 THEN QB_NODE_typeName$ = "DICTIONARY"
IF nodeType = 8 THEN QB_NODE_typeName$ = "VALUE"
END FUNCTION
FUNCTION QB_NODE_new& (nodeType AS LONG, flags AS LONG)
IF QB_DEBUG_VERBOSE THEN PRINT "QB_NODE_new()"
DIM QB__handle AS LONG
QB__handle = QB_HANDLE_new(__QB_NODE_handleSet)
IF QB__handle > __QB_NODE_ubound THEN
__QB_NODE_ubound = QB__handle * 2
REDIM _PRESERVE __QB_NODE(__QB_NODE_ubound) AS QB_NODE_TYPE
END IF
__QB_NODE(QB__handle) = QB_NODE_TYPE_EMPTY
__QB_NODE(QB__handle).valid = 1
__QB_NODE(QB__handle).type = nodeType
__QB_NODE(QB__handle).flags = flags
IF nodeType AND (QB_NODE_TYPE_HASHSET + QB_NODE_TYPE_DICTIONARY) THEN
__QB_NODE(QB__handle).hashOffset = INT(RND * 16777215)
END IF
IF QB_DEBUG THEN PRINT "Created node type"; nodeType
QB_NODE_new& = QB__handle
END FUNCTION
FUNCTION QB_NODE_newDictionary&
QB_NODE_newDictionary& = QB_NODE_new(QB_NODE_TYPE_DICTIONARY, 0)
END FUNCTION
FUNCTION QB_NODE_newList&
QB_NODE_newList& = QB_NODE_new(QB_NODE_TYPE_LIST, 0)
END FUNCTION
FUNCTION QB_NODE_newHashSet&
QB_NODE_newHashSet& = QB_NODE_new(QB_NODE_TYPE_HASHSET, 0)
END FUNCTION
SUB QB_NODE_setValue_format (QB__handle AS LONG, value AS LONG, format AS LONG)
$CHECKING:OFF
IF __QB_NODE_validateHandle(QB__handle, QB_NODE_TYPE_VALUE) = -1 THEN EXIT FUNCTION
$CHECKING:ON
'format-specific validation here
__QB_NODE(QB__handle).value = value
__QB_NODE(QB__handle).valueFormat = format
END SUB
SUB QB_NODE_setLabel_format (QB__handle AS LONG, label AS LONG, format AS LONG)
$CHECKING:OFF
IF __QB_NODE_validateHandle(QB__handle, QB_NODE_TYPE_VALUE + QB_NODE_TYPE_HASHSET + QB_NODE_TYPE_LIST + QB_NODE_TYPE_DICTIONARY) = -1 THEN EXIT FUNCTION
$CHECKING:ON
'format-specific validation here
__QB_NODE(QB__handle).label = label
__QB_NODE(QB__handle).labelFormat = format
END SUB
SUB QB_NODE_setValue (QB__handle AS LONG, value AS STRING) 'assume str
$CHECKING:OFF
IF __QB_NODE_validateHandle(QB__handle, QB_NODE_TYPE_VALUE) = -1 THEN EXIT FUNCTION
$CHECKING:ON
__QB_NODE(QB__handle).value = QB_STR_new(value)
__QB_NODE(QB__handle).valueFormat = QB_NODE_FORMAT_STR
END SUB
SUB QB_NODE_setLabel (QB__handle AS LONG, label AS STRING) 'assume str
$CHECKING:OFF
IF __QB_NODE_validateHandle(QB__handle, QB_NODE_TYPE_VALUE + QB_NODE_TYPE_HASHSET + QB_NODE_TYPE_LIST + QB_NODE_TYPE_DICTIONARY) = -1 THEN EXIT FUNCTION
$CHECKING:ON
'TODO: If parent is a dictionary/hashset detach then reattach
__QB_NODE(QB__handle).label = QB_STR_new(label)
__QB_NODE(QB__handle).labelFormat = QB_NODE_FORMAT_STR
END SUB
FUNCTION QB_NODE_value$ (QB__handle AS LONG) 'assume str
$CHECKING:OFF
IF __QB_NODE_validateHandle(QB__handle, QB_NODE_TYPE_VALUE) = -1 THEN EXIT FUNCTION
$CHECKING:ON
'format-specific validation here
IF __QB_NODE(QB__handle).valueFormat = QB_NODE_FORMAT_STR THEN
QB_NODE_value$ = QB_STR_get(__QB_NODE(QB__handle).value)
EXIT FUNCTION
END IF
IF __QB_NODE(QB__handle).valueFormat = QB_NODE_FORMAT_LONG THEN
QB_NODE_value$ = QB_STR_long(__QB_NODE(QB__handle).value)
EXIT FUNCTION
END IF
IF __QB_NODE(QB__handle).valueFormat = QB_NODE_FORMAT_NULL THEN
QB_NODE_value$ = "null"
EXIT FUNCTION
END IF
IF __QB_NODE(QB__handle).valueFormat = QB_NODE_FORMAT_BOOL THEN
IF __QB_NODE(QB__handle).value <> 0 THEN
QB_NODE_value$ = "true"
ELSE
QB_NODE_value$ = "false"
END IF
EXIT FUNCTION
END IF
QB_NODE_value$ = "undefined"
END SUB
FUNCTION QB_NODE_label$ (QB__handle AS LONG) 'assume str
$CHECKING:OFF
IF __QB_NODE_validateHandle(QB__handle, QB_NODE_TYPE_VALUE) = -1 THEN EXIT FUNCTION
$CHECKING:ON
'format-specific validation here
IF __QB_NODE(QB__handle).labelFormat = QB_NODE_FORMAT_STR THEN
QB_NODE_label$ = QB_STR_get(__QB_NODE(QB__handle).label)
EXIT FUNCTION
END IF
IF __QB_NODE(QB__handle).labelFormat = QB_NODE_FORMAT_LONG THEN
QB_NODE_label$ = QB_STR_long(__QB_NODE(QB__handle).label)
EXIT FUNCTION
END IF
IF __QB_NODE(QB__handle).labelFormat = QB_NODE_FORMAT_NULL THEN
QB_NODE_label$ = "null"
EXIT FUNCTION
END IF
IF __QB_NODE(QB__handle).labelFormat = QB_NODE_FORMAT_BOOL THEN
IF __QB_NODE(QB__handle).label <> 0 THEN
QB_NODE_label$ = "true"
ELSE
QB_NODE_label$ = "false"
END IF
EXIT FUNCTION
END IF
QB_NODE_label$ = "undefined"
END SUB
FUNCTION QB_NODE_count& (QB__handle AS LONG)
$CHECKING:OFF
IF __QB_NODE_validateHandle(QB__handle, QB_NODE_TYPE_HASHSET + QB_NODE_TYPE_DICTIONARY + QB_NODE_TYPE_LIST) = -1 THEN EXIT FUNCTION
$CHECKING:ON
QB_NODE_count& = __QB_NODE(QB__handle).count
END FUNCTION
FUNCTION QB_NODE_each& (child AS LONG, parent AS LONG, i AS LONG)
$CHECKING:OFF
IF i = 0 THEN
IF __QB_NODE_validateHandle(parent, QB_NODE_TYPE_LIST + QB_NODE_TYPE_HASHSET + QB_NODE_TYPE_DICTIONARY) = -1 THEN EXIT FUNCTION
END IF
$CHECKING:ON
'i is either 0(on first call), -1(end of set reached) or the NEXT node
IF i = -1 THEN
child = 0
EXIT FUNCTION
END IF
IF i = 0 THEN
child = __QB_NODE(parent).firstChild
ELSE
child = i
END IF
IF child = 0 THEN 'node does not exist
i = -1
EXIT FUNCTION
END IF
i = __QB_NODE(child).next
IF i = 0 THEN
i = -1
END IF
QB_NODE_each& = -1
END FUNCTION
FUNCTION QB_NODE_eachWithLabel_format& (child AS LONG, parent AS LONG, label AS LONG, labelFormat AS LONG, i AS LONG)
$CHECKING:OFF
IF i = 0 THEN
IF __QB_NODE_validateHandle(parent, QB_NODE_TYPE_DICTIONARY + QB_NODE_TYPE_HASHSET) = -1 THEN EXIT FUNCTION
END IF
$CHECKING:ON
IF __QB_NODE(parent).type AND (QB_NODE_TYPE_DICTIONARY + QB_NODE_TYPE_HASHSET) THEN
'i is either 0(on first call), -1(end of set reached) or the NEXT node
IF i = -1 THEN
child = 0
EXIT FUNCTION
END IF
DIM QB__label AS STRING
IF i = 0 THEN
DIM QB__hashValue AS LONG
IF labelFormat = QB_NODE_FORMAT_LONG THEN
QB__hashValue = __QB_NODE_hashLong(label, __QB_NODE(parent).hashOffset)
ELSE
IF labelFormat = QB_NODE_FORMAT_STR THEN
QB__label = QB_STR_get(label)
IF (__QB_NODE(parent).flags AND QB_NODE_CASE_SENSITIVE) = 0 THEN
QB__label = LCASE$(QB__label)
END IF
QB__hashValue = __QB_NODE_hashStr(QB__label, __QB_NODE(parent).hashOffset)
END IF
END IF
DIM QB__hashList AS LONG
QB__hashList = __QB_NODE_hashLists(QB__hashValue)
IF QB__hashList = 0 THEN
'no hash list exists
i = -1
EXIT FUNCTION
END IF
i = __QB_NODE(QB__hashList).firstChild
ELSE
IF labelFormat = QB_NODE_FORMAT_STR THEN
QB__label = QB_STR_get(label)
IF (__QB_NODE(parent).flags AND QB_NODE_CASE_SENSITIVE) = 0 THEN
QB__label = LCASE$(QB__label)
END IF
END IF
END IF
DO
IF i = 0 THEN
i = -1
EXIT FUNCTION
END IF
'check if current node matches label
IF __QB_NODE(i).owner = parent THEN 'same owner
IF __QB_NODE(i).labelFormat = labelFormat THEN 'same label format
DIM QB__same AS LONG
QB__same = 0
IF labelFormat = QB_NODE_FORMAT_LONG THEN
IF __QB_NODE(i).label = label THEN QB__same = 1
ELSE
IF QB_STR_get(__QB_NODE(i).label) = QB__label THEN QB__same = 1
END IF
IF QB__same THEN 'same label
child = __QB_NODE(i).value
i = __QB_NODE(i).next
IF i = 0 THEN
i = -1
END IF
QB_NODE_eachWithLabel_format& = -1
EXIT FUNCTION
END IF
END IF
END IF
i = __QB_NODE(i).next
LOOP
END IF 'DICTIONARY
END FUNCTION
FUNCTION QB_NODE_withLabel_format& (parent AS LONG, label AS LONG, labelFormat AS LONG)
$CHECKING:OFF
IF __QB_NODE_validateHandle(parent, QB_NODE_TYPE_DICTIONARY + QB_NODE_TYPE_HASHSET) = -1 THEN EXIT FUNCTION
$CHECKING:ON
IF __QB_NODE(parent).type AND (QB_NODE_TYPE_DICTIONARY + QB_NODE_TYPE_HASHSET) THEN
DIM QB__label AS STRING
DIM QB__hashValue AS LONG
IF labelFormat = QB_NODE_FORMAT_LONG THEN
QB__hashValue = __QB_NODE_hashLong(label, __QB_NODE(parent).hashOffset)
ELSE
IF labelFormat = QB_NODE_FORMAT_STR THEN
QB__label = QB_STR_get(label)
IF (__QB_NODE(parent).flags AND QB_NODE_CASE_SENSITIVE) = 0 THEN
QB__label = LCASE$(QB__label)
END IF
QB__hashValue = __QB_NODE_hashStr(QB__label, __QB_NODE(parent).hashOffset)
END IF
END IF
DIM QB__hashList AS LONG
QB__hashList = __QB_NODE_hashLists(QB__hashValue)
IF QB__hashList = 0 THEN EXIT FUNCTION
DIM QB__i AS LONG
QB__i = __QB_NODE(QB__hashList).firstChild
DO WHILE QB__i <> 0
IF __QB_NODE(QB__i).owner = parent THEN 'same owner
IF __QB_NODE(QB__i).labelFormat = labelFormat THEN 'same label format
DIM QB__same AS LONG
QB__same = 0
IF labelFormat = QB_NODE_FORMAT_LONG THEN
IF __QB_NODE(QB__i).label = label THEN QB__same = 1
ELSE
IF QB_STR_get(__QB_NODE(QB__i).label) = QB__label THEN QB__same = 1
END IF
IF QB__same THEN 'same label
QB_NODE_withLabel_format& = __QB_NODE(QB__i).value
EXIT FUNCTION
END IF
END IF
END IF
QB__i = __QB_NODE(QB__i).next
LOOP
EXIT FUNCTION 'not found
END IF 'DICTIONARY
END FUNCTION
FUNCTION QB_NODE_valueOfLabel$ (parent AS LONG, label AS STRING) 'assume str-label, str-value
DIM QB__i AS LONG
QB__i = QB_NODE_withLabel&(parent, label)
IF QB__i THEN
QB_NODE_valueOfLabel$ = QB_NODE_value(QB__i)
END IF
END FUNCTION
FUNCTION QB_NODE_valueOfLabel_long& (parent AS LONG, label AS STRING) 'assume str-label
DIM QB__i AS LONG
QB__i = QB_NODE_withLabel&(parent, label)
IF QB__i THEN
QB_NODE_valueOfLabel_long& = __QB_NODE(QB__i).value
END IF
END FUNCTION
FUNCTION QB_NODE_valueOfLabel_bool& (parent AS LONG, label AS STRING) 'assume str-label
DIM QB__i AS LONG
QB__i = QB_NODE_withLabel&(parent, label)
IF QB__i THEN
QB_NODE_valueOfLabel_bool& = __QB_NODE(QB__i).value
END IF
END FUNCTION
FUNCTION QB_NODE_withLabel& (parent AS LONG, label AS STRING)
$CHECKING:OFF
IF __QB_NODE_validateHandle(parent, QB_NODE_TYPE_DICTIONARY + QB_NODE_TYPE_HASHSET) = -1 THEN EXIT FUNCTION
$CHECKING:ON
IF __QB_NODE(parent).type AND (QB_NODE_TYPE_DICTIONARY + QB_NODE_TYPE_HASHSET) THEN
DIM QB__label AS STRING
DIM QB__hashValue AS LONG
QB__label = label
IF (__QB_NODE(parent).flags AND QB_NODE_CASE_SENSITIVE) = 0 THEN
QB__label = LCASE$(QB__label)
END IF
QB__hashValue = __QB_NODE_hashStr(QB__label, __QB_NODE(parent).hashOffset)
DIM QB__hashList AS LONG
QB__hashList = __QB_NODE_hashLists(QB__hashValue)
IF QB__hashList = 0 THEN EXIT FUNCTION
DIM QB__i AS LONG
QB__i = __QB_NODE(QB__hashList).firstChild
DO WHILE QB__i <> 0
IF __QB_NODE(QB__i).owner = parent THEN 'same owner
IF __QB_NODE(QB__i).labelFormat = QB_NODE_FORMAT_STR THEN 'same label format
DIM QB__same AS LONG
QB__same = 0
IF QB_STR_get(__QB_NODE(QB__i).label) = QB__label THEN QB__same = 1
IF QB__same THEN 'same label
QB_NODE_withLabel& = __QB_NODE(QB__i).value
EXIT FUNCTION
END IF
END IF
END IF
QB__i = __QB_NODE(QB__i).next
LOOP
EXIT FUNCTION 'not found
END IF 'DICTIONARY
END FUNCTION
SUB QB_NODE_assign (parent AS LONG, child AS LONG)
$CHECKING:OFF
IF __QB_NODE_validateHandle(child, QB_NODE_TYPE_VALUE + QB_NODE_TYPE_HASHSET + QB_NODE_TYPE_LIST + QB_NODE_TYPE_DICTIONARY) = -1 THEN EXIT FUNCTION
IF __QB_NODE_validateHandle(parent, QB_NODE_TYPE_HASHSET + QB_NODE_TYPE_LIST + QB_NODE_TYPE_DICTIONARY) = -1 THEN EXIT FUNCTION
$CHECKING:ON
IF __QB_NODE(parent).type AND (QB_NODE_TYPE_LIST) THEN
QB_NODE_detach child
__QB_NODE_append parent, child
END IF
IF __QB_NODE(parent).type AND (QB_NODE_TYPE_DICTIONARY + QB_NODE_TYPE_HASHSET) THEN
QB_NODE_detach child
DIM QB__label AS STRING
DIM QB__hashValue AS LONG
IF __QB_NODE(child).labelFormat = QB_NODE_FORMAT_LONG THEN
QB__hashValue = __QB_NODE_hashLong(__QB_NODE(child).label, __QB_NODE(parent).hashOffset)
ELSE
IF __QB_NODE(child).labelFormat = QB_NODE_FORMAT_STR THEN
QB__label = QB_STR_get(__QB_NODE(child).label)
IF (__QB_NODE(parent).flags AND QB_NODE_CASE_SENSITIVE) = 0 THEN
QB__label = LCASE$(QB__label)
END IF
QB__hashValue = __QB_NODE_hashStr(QB__label, __QB_NODE(parent).hashOffset)
END IF
END IF
DIM QB__hashList AS LONG
QB__hashList = __QB_NODE_hashLists(QB__hashValue)
DIM QB__canReplace AS LONG
QB__canReplace = 1
IF (__QB_NODE(parent).flags AND QB_NODE_ALLOW_DUPLICATE_KEYS) <> 0 THEN
QB__canReplace = 0
IF (__QB_NODE(parent).flags AND QB_NODE_AVOID_DUPLICATE_VALUES_PER_KEY) <> 0 THEN QB__canReplace = 1
END IF
DIM QB__childValue AS STRING
IF (__QB_NODE(parent).flags AND QB_NODE_AVOID_DUPLICATE_VALUES_PER_KEY) <> 0 AND __QB_NODE(child).labelFormat = QB_NODE_FORMAT_STR THEN
QB__childValue = QB_STR_get(__QB_NODE(child).value)
IF (__QB_NODE(parent).flags AND QB_NODE_DUPLICATE_VALUES_CASE_SENSITIVE) = 0 THEN QB__childValue = LCASE$(QB__childValue)
END IF
IF QB__hashList = 0 OR QB__canReplace = 0 THEN
IF QB__hashList = 0 THEN
QB__hashList = QB_NODE_new(QB_NODE_TYPE_LIST, 0)
__QB_NODE_hashLists(QB__hashValue) = QB__hashList
__QB_NODE(QB__hashList).hashReference = QB__hashValue
END IF
ELSE
DIM QB__this AS LONG
DIM QB__i AS LONG
DO WHILE QB_NODE_each(QB__this, QB__hashList, QB__i)
IF __QB_NODE(QB__this).owner = parent THEN 'same owner
IF __QB_NODE(QB__this).labelFormat = __QB_NODE(child).labelFormat THEN 'same label format
DIM QB__same AS LONG
QB__same = 0
IF __QB_NODE(child).labelFormat = QB_NODE_FORMAT_LONG THEN
IF __QB_NODE(QB__this).label = __QB_NODE(child).label THEN QB__same = 1
ELSE
IF QB_STR_get(__QB_NODE(QB__this).label) = QB__label THEN QB__same = 1
END IF
IF QB__same THEN 'same label
IF (__QB_NODE(parent).flags AND QB_NODE_ALLOW_DUPLICATE_KEYS) <> 0 THEN
IF (__QB_NODE(parent).flags AND QB_NODE_AVOID_DUPLICATE_VALUES_PER_KEY) <> 0 THEN
IF __QB_NODE(QB__this).valueFormat = __QB_NODE(child).valueFormat THEN 'same value format
IF __QB_NODE(child).labelFormat = QB_NODE_FORMAT_LONG THEN
IF __QB_NODE(child).value = __QB_NODE(QB__this).value THEN
'optionally, destroy this child
IF __QB_NODE(parent).flags AND QB_NODE_DESTROY_ORPHANED_CHILDNODES THEN
QB_NODE_destroy child
END IF
EXIT SUB 'entry already exists
END IF
ELSE
IF (__QB_NODE(parent).flags AND QB_NODE_DUPLICATE_VALUES_CASE_SENSITIVE) <> 0 THEN
IF QB_STR_get(__QB_NODE(QB__this).value) = QB__childValue THEN
'optionally, destroy this child
IF __QB_NODE(parent).flags AND QB_NODE_DESTROY_ORPHANED_CHILDNODES THEN
QB_NODE_destroy child
END IF
EXIT SUB 'entry already exists
END IF
ELSE
IF LCASE$(QB_STR_get(__QB_NODE(QB__this).value)) = QB__childValue THEN
'optionally, destroy this child
IF __QB_NODE(parent).flags AND QB_NODE_DESTROY_ORPHANED_CHILDNODES THEN
QB_NODE_destroy child
END IF
EXIT SUB 'entry already exists
END IF
END IF
END IF
END IF
END IF
ELSE
'duplicate keys not allowed
__QB_NODE_append parent, child
'update existing reference to child
DIM QB__oldChild AS LONG
QB__oldChild = __QB_NODE(QB__this).value
__QB_NODE_detach QB__oldChild 'generic detach must be used (reference will be re-used)
'optionally, destroy old child
IF __QB_NODE(parent).flags AND QB_NODE_DESTROY_ORPHANED_CHILDNODES THEN
QB_NODE_destroy QB__oldChild
END IF
__QB_NODE(QB__this).value = child
__QB_NODE(child).hashReference = QB__this
EXIT SUB
END IF
END IF
END IF
END IF
LOOP
END IF
'create new reference to child
__QB_NODE_append parent, child
DIM QB__ref AS LONG
QB__ref = QB_NODE_new(QB_NODE_TYPE_VALUE, 0)
IF __QB_NODE(child).labelFormat = QB_NODE_FORMAT_LONG THEN
QB_NODE_setLabel_format QB__ref, __QB_NODE(child).label, QB_NODE_FORMAT_LONG
ELSE
QB_NODE_setLabel_format QB__ref, QB_STR_new(QB__label), QB_NODE_FORMAT_STR
END IF
QB_NODE_setValue_format QB__ref, child, QB_NODE_FORMAT_LONG
__QB_NODE(QB__ref).owner = parent 'owner allows searching elimination of conflicting hash entries from other sets
'add reference to list
__QB_NODE_append QB__hashList, QB__ref
__QB_NODE(child).hashReference = QB__ref
END IF 'dictionary
END SUB 'assign
SUB QB_NODE_detach (QB__handle AS LONG)
IF QB_DEBUG_VERBOSE THEN
PRINT "QB_NODE_detach: Node"; QB__handle; "of type: " + QB_NODE_typeName(__QB_NODE(QB__handle).type)
END IF
$CHECKING:OFF
IF __QB_NODE_validateHandle(QB__handle, 0) = -1 THEN EXIT FUNCTION
$CHECKING:ON
DIM QB__parent AS LONG
DIM QB__ref AS LONG
DIM QB__ref_parent AS LONG
'dictionaries & hashsets require removal of their hash table reference link
QB__parent = __QB_NODE(QB__handle).parent
IF QB__parent THEN 'has parent
IF __QB_NODE(QB__parent).type AND (QB_NODE_TYPE_HASHSET + QB_NODE_TYPE_DICTIONARY) THEN
QB__ref = __QB_NODE(QB__handle).hashReference
QB__ref_parent = __QB_NODE(QB__ref).parent
IF __QB_NODE(QB__ref_parent).count = 1 THEN
'last reference
IF QB_DEBUG_VERBOSE THEN
PRINT "QB_NODE_detach: Calling destroy on parent hashreference-list node"; QB__ref_parent; "of type: " + QB_NODE_typeName(__QB_NODE(QB__ref_parent).type) + " (no more entries)"
END IF
'clear the hash entry pointing to this list
__QB_NODE_hashLists(__QB_NODE(QB__ref_parent).hashReference) = 0 'step 1 (must happen before step 2)
__QB_NODE_destroy QB__ref_parent 'step 2
ELSE
__QB_NODE_destroy QB__ref
END IF
IF __QB_NODE(QB__parent).flags AND QB_NODE_DESTROY_ORPHANED_CHILDNODES THEN
__QB_NODE_detach QB__handle 'perform generic detach
__QB_NODE_destroy QB__handle
EXIT FUNCTION
END IF
__QB_NODE_detach QB__handle 'perform generic detach
EXIT FUNCTION
END IF
END IF
__QB_NODE_detach QB__handle 'perform generic detach
END SUB
SUB QB_NODE_destroy (QB__handle AS LONG)
$CHECKING:OFF
IF __QB_NODE_validateHandle(QB__handle, 0) = -1 THEN EXIT FUNCTION
$CHECKING:ON
'destroy this node and all its children recursively
__QB_NODE_destroy QB__handle
END SUB
SUB __QB_NODE_destroy (QB__handle AS LONG)
IF QB_DEBUG_VERBOSE THEN
PRINT "__QB_NODE_destroy: Will destroy node"; QB__handle; "of type: " + QB_NODE_typeName(__QB_NODE(QB__handle).type)
END IF
'when a collection is being destroyed, prevent QB_NODE_DESTROY_ORPHANED_CHILDNODES from firing a delete operation twice
IF __QB_NODE(QB__handle).flags AND QB_NODE_DESTROY_ORPHANED_CHILDNODES THEN
__QB_NODE(QB__handle).flags = __QB_NODE(QB__handle).flags - QB_NODE_DESTROY_ORPHANED_CHILDNODES
END IF
'before any node can be destroyed it must be detached
QB_NODE_detach QB__handle
'destroy this node's children (if any)
DIM QB__child AS LONG
DIM QB__next AS LONG
QB__child = __QB_NODE(QB__handle).firstChild
DO WHILE QB__child
QB__next = __QB_NODE(QB__child).next
IF QB_DEBUG_VERBOSE THEN
PRINT "__QB_NODE_destroy: Calling destroy on child node"; QB__child; "of type: " + QB_NODE_typeName(__QB_NODE(QB__child).type)
END IF
__QB_NODE_destroy QB__child
QB__child = QB__next
LOOP
'destroy this object
IF QB_DEBUG_VERBOSE THEN
PRINT "__QB_NODE_destroy: Destroying node"; QB__handle; "of type: " + QB_NODE_typeName(__QB_NODE(QB__handle).type)
END IF
$CHECKING:OFF
IF __QB_NODE_validateHandle(QB__handle, 0) = -1 THEN EXIT FUNCTION
$CHECKING:ON
__QB_NODE(QB__handle).valid = 0
'cleanup string references
IF __QB_NODE(QB__handle).valueFormat = QB_NODE_FORMAT_STR THEN
QB_STR_free __QB_NODE(QB__handle).value
END IF
IF __QB_NODE(QB__handle).labelFormat = QB_NODE_FORMAT_STR THEN
QB_STR_free __QB_NODE(QB__handle).label
END IF
QB_HANDLE_free QB__handle, __QB_NODE_handleSet
END SUB
FUNCTION __QB_NODE_new& (nodeType AS LONG)
IF QB_DEBUG_VERBOSE THEN PRINT "__QB_NODE_new()"
DIM QB__handle AS LONG
QB__handle = QB_HANDLE_new(__QB_NODE_handleSet)
IF QB__handle > __QB_NODE_ubound THEN
__QB_NODE_ubound = QB__handle * 2
REDIM _PRESERVE __QB_NODE(__QB_NODE_ubound) AS QB_NODE_TYPE
END IF
__QB_NODE(QB__handle) = QB_NODE_TYPE_EMPTY
__QB_NODE(QB__handle).valid = 1
__QB_NODE(QB__handle).type = nodeType
IF QB_DEBUG_VERBOSE THEN PRINT "Created node type"; nodeType
__QB_NODE_new& = QB__handle
END FUNCTION
SUB __QB_NODE_append (parent AS LONG, child AS LONG)
'generic append to end of parent list
'assumes child is detached
__QB_NODE(child).parent = parent
IF __QB_NODE(parent).firstChild = 0 THEN
'is first entry in list
__QB_NODE(parent).count = 1
__QB_NODE(parent).firstChild = child
__QB_NODE(parent).lastChild = child
ELSE
'add to existing list
DIM QB__i AS LONG
QB__i = __QB_NODE(parent).lastChild
__QB_NODE(parent).count = __QB_NODE(parent).count + 1
__QB_NODE(parent).lastChild = child
__QB_NODE(QB__i).next = child
__QB_NODE(child).prev = QB__i
END IF
END SUB
FUNCTION __QB_NODE_hashLong (value AS LONG, baseOffset AS LONG)
__QB_NODE_hashLong = (value + baseOffset) AND &HFFFFFF~&
END FUNCTION
FUNCTION __QB_NODE_hashStr (value AS STRING, baseOffset AS LONG)
DIM QB__keyNameLen AS LONG
DIM QB__i AS LONG
DIM QB__hashValue AS LONG
QB__keyNameLen = LEN(value)
QB__i = 1
DO WHILE QB__i <= QB__keyNameLen
QB__hashValue = QB__hashValue + ASC(value, QB__i) * QB__i * 15
QB__i = QB__i + 1
LOOP
__QB_NODE_hashStr = (QB__hashValue + baseOffset) AND &HFFFFFF~&
END FUNCTION
FUNCTION __QB_NODE_validateHandle (handle AS LONG, optionalRequiredType AS LONG)
$CHECKING:OFF
IF handle > __QB_NODE_ubound OR handle <= 0 THEN ERROR 258: EXIT FUNCTION 'invalid handle
IF __QB_NODE(handle).valid = 0 THEN ERROR 258: EXIT FUNCTION
IF optionalRequiredType <> 0 THEN
IF (optionalRequiredType AND __QB_NODE(handle).type) = 0 THEN
ERROR 258
__QB_NODE_validateHandle = -1
EXIT FUNCTION
END IF
END IF
$CHECKING:ON
END FUNCTION
SUB __QB_NODE_detach (handle AS LONG)
IF QB_DEBUG_VERBOSE THEN
PRINT "__QB_NODE_detach: Node"; handle; "of type: " + QB_NODE_typeName(__QB_NODE(handle).type)
END IF
'generic detach method (regardless of parent type)
DIM QB__i
QB__i = __QB_NODE(handle).next
IF QB__i THEN
__QB_NODE(QB__i).prev = __QB_NODE(handle).prev
END IF
QB__i = __QB_NODE(handle).prev
IF QB__i THEN
__QB_NODE(QB__i).next = __QB_NODE(handle).next
END IF
QB__i = __QB_NODE(handle).parent
IF QB__i THEN
IF __QB_NODE(QB__i).firstChild = handle THEN __QB_NODE(QB__i).firstChild = __QB_NODE(handle).next
IF __QB_NODE(QB__i).lastChild = handle THEN __QB_NODE(QB__i).lastChild = __QB_NODE(handle).prev
__QB_NODE(QB__i).count = __QB_NODE(QB__i).count - 1
__QB_NODE(handle).parent = 0
END IF
__QB_NODE(handle).next = 0
__QB_NODE(handle).prev = 0
__QB_NODE(handle).hashReference = 0
END SUB
'##################################################
SUB __QB_NODESET_addChildren (QB__parent AS LONG, QB__selOut AS LONG)
DIM QB__child AS LONG
DIM QB__newSel AS LONG
QB__child = __QB_NODE(QB__parent).firstChild
DO WHILE QB__child
QB__newSel = QB_NODE_newLabel_long(QB__child)
QB_NODE_assign QB__selOut, QB__newSel
IF __QB_NODE(QB__child).firstChild THEN __QB_NODESET_addChildren QB__child, QB__selOut
QB__child = __QB_NODE(QB__child).next
LOOP
END SUB
SUB __QB_NODESET_addChildrenWithDepth (QB__parent AS LONG, QB__selOut AS LONG, currentDepth AS LONG, minDepth AS LONG, maxDepth AS LONG)
DIM QB__child AS LONG
DIM QB__newSel AS LONG
QB__child = __QB_NODE(QB__parent).firstChild
DO WHILE QB__child
IF currentDepth >= minDepth THEN
QB__newSel = QB_NODE_newLabel_long(QB__child)
QB_NODE_assign QB__selOut, QB__newSel
END IF
IF currentDepth < maxDepth THEN
IF __QB_NODE(QB__child).firstChild THEN
__QB_NODESET_addChildrenWithDepth QB__child, QB__selOut, currentDepth + 1, minDepth, maxDepth
END IF
END IF
QB__child = __QB_NODE(QB__child).next
LOOP
END SUB
SUB __QB_NODE_debugInfo (QB__i AS LONG)
PRINT "-------- __QB_NODE_debugInfo:"; QB__i; "--------"
'type
DIM QB__type AS LONG
QB__type = __QB_NODE(QB__i).type
PRINT "TYPE: " + QB_NODE_typeName(QB__type)
'label
IF __QB_NODE(QB__i).labelFormat = QB_NODE_FORMAT_STR THEN
PRINT "LABEL: " + QB_STR_get(__QB_NODE(QB__i).label)
END IF
'value
IF __QB_NODE(QB__i).valueFormat = QB_NODE_FORMAT_STR THEN
PRINT "VALUE: " + QB_STR_get(__QB_NODE(QB__i).value)
END IF
IF __QB_NODE(QB__i).parent THEN
PRINT "Has parent of type " + QB_NODE_typeName(__QB_NODE(__QB_NODE(QB__i).parent).type) + " ["; __QB_NODE(QB__i).parent; "]"
ELSE
PRINT "This is a root element"
END IF
IF __QB_NODE(QB__i).firstChild THEN
PRINT "Has child of type " + QB_NODE_typeName(__QB_NODE(__QB_NODE(QB__i).firstChild).type) + " ["; __QB_NODE(QB__i).firstChild; "]"
END IF
IF __QB_NODE(QB__i).next THEN
PRINT "Has next sibling of type " + QB_NODE_typeName(__QB_NODE(__QB_NODE(QB__i).next).type) + " ["; __QB_NODE(QB__i).next; "]"
END IF
IF __QB_NODE(QB__i).prev THEN
PRINT "Has previous sibling of type " + QB_NODE_typeName(__QB_NODE(__QB_NODE(QB__i).prev).type) + " ["; __QB_NODE(QB__i).prev; "]"
END IF
PRINT "----------------"
END SUB
'##################################################
'#################### JSON: Private Methods ####################
FUNCTION __QB_JSON_unescape$ (QB__in AS STRING, QB__detectedFormat AS LONG, QB__detectedFormatValue AS LONG)
'-unescapes string
'-strips paired single or double quotes
'-detects data type (string, number, bool, null)
'-very permissive
QB__detectedFormat = 0
QB__detectedFormatValue = 0
DIM QB__out AS STRING
DIM QB__i1 AS LONG
DIM QB__i2 AS LONG
DIM QB__i3 AS LONG
DIM QB__in_len AS LONG
DIM QB__a AS LONG
DIM QB__a2 AS LONG
DIM QB__hex AS STRING
DIM QB__hex_len AS LONG
DIM QB__quoted AS LONG
QB__in_len = LEN(QB__in)
QB__out = SPACE$(QB__in_len) 'output is never longer than input
QB__i1 = 1
QB__i2 = 0
QB__i = 0
'trim
QB__in = LTRIM$(RTRIM$(QB__in))
QB__in_len = LEN(QB__in)
'strip quotes
IF ASC(QB__in) = 34 OR ASC(QB__in) = 39 THEN
IF ASC(QB__in, QB__in_len) = ASC(QB__in) AND QB__in_len > 1 THEN
QB__quoted = ASC(QB__in)
QB__detectedFormat = QB_NODE_FORMAT_STR
QB__in_len = QB__in_len - 2
QB__in = MID$(QB__in, 2, QB__in_len)
END IF
END IF
'detect type if not quoted
IF QB__quoted = 0 THEN
IF QB__in_len = 4 THEN
IF LCASE$(QB__in) = "true" THEN QB__in = "true": QB__detectedFormat = QB_NODE_FORMAT_BOOL: QB__detectedFormatValue = QB_TRUE
IF LCASE$(QB__in) = "null" THEN QB__in = "null": QB__detectedFormat = QB_NODE_FORMAT_NULL: QB__detectedFormatValue = QB_NULL
END IF
IF QB__in_len = 5 THEN
IF LCASE$(QB__in) = "false" THEN QB__in = "false": QB__detectedFormat = QB_NODE_FORMAT_BOOL: QB__detectedFormatValue = QB_FALSE
END IF
IF QB__detectedFormat = 0 THEN
QB__a = ASC(QB__in)
IF QB__a >= 48 AND QB__a <= 57 THEN '0-9
IF INSTR(QB__in, ".") = 0 THEN
QB__detectedFormat = QB_NODE_FORMAT_LONG: QB__detectedFormatValue = VAL(QB__in)
END IF
ELSE
IF QB__a = 45 THEN '-
IF INSTR(QB__in, ".") = 0 THEN
QB__detectedFormat = QB_NODE_FORMAT_LONG: QB__detectedFormatValue = VAL(QB__in)
END IF
END IF
IF QB__a = 46 THEN '.
'TODO: decimal support
END IF
END IF
IF QB__detectedFormat = 0 THEN QB__detectedFormat = QB_NODE_FORMAT_STR
END IF
END IF
'if a string, parse to convert escaped content
IF QB__detectedFormat = QB_NODE_FORMAT_STR THEN
DO WHILE QB__i1 <= QB__in_len
QB__a = ASC(QB__in, QB__i1)
IF QB__a <> 92 OR QB__i1 = QB__in_len THEN 'not \ or at end
QB__i2 = QB__i2 + 1: ASC(QB__out, QB__i2) = QB__a
ELSE
QB__i1 = QB__i1 + 1: QB__a = ASC(QB__in, QB__i1)
QB__a2 = __QB_JSON_escape_lookup_reversed(QB__a)
IF QB__a2 THEN
QB__i2 = QB__i2 + 1: ASC(QB__out, QB__i2) = QB__a2
ELSE
IF QB__a = 117 OR QB__a = 85 AND QB__i1 + 4 <= QB__in_len THEN 'u or U
QB__a2 = VAL("&H" + MID$(QB__in, QB__i1 + 1, 4) + "~&") 'unicode code point
QB__i1 = QB__i1 + 4
QB__a = 0
IF QB__a2 = 0 THEN
QB__i2 = QB__i2 + 1: ASC(QB__out, QB__i2) = 0
ELSE
'todo: replace with dictionary lookup
FOR QB__i3 = 1 TO 255
IF QB__a2 = _MAPUNICODE(QB__i3) THEN
QB__a = QB__i3
QB__i2 = QB__i2 + 1: ASC(QB__out, QB__i2) = QB__a
EXIT FOR
END IF
NEXT
IF QB__i3 = 256 THEN 'could not locate a match for the character, show a question mark
QB__i2 = QB__i2 + 1: ASC(QB__out, QB__i2) = 63 '?
END IF
END IF
ELSE
'unknown \??? combination (add as is)
QB__i2 = QB__i2 + 1: ASC(QB__out, QB__i2) = 92 '\
QB__i2 = QB__i2 + 1: ASC(QB__out, QB__i2) = QB__a '2nd character
END IF
END IF
END IF
QB__i1 = QB__i1 + 1
LOOP
__QB_JSON_unescape$ = LEFT$(QB__out, QB__i2)
ELSE
__QB_JSON_unescape$ = QB__in
END IF
END FUNCTION
FUNCTION __QB_JSON_escape$ (QB__in AS STRING)
DIM QB__out AS STRING
DIM QB__i1 AS LONG
DIM QB__i2 AS LONG
DIM QB__in_len AS LONG
DIM QB__a AS LONG
DIM QB__a2 AS LONG
DIM QB__hex AS STRING
DIM QB__hex_len AS LONG
QB__in_len = LEN(QB__in)
QB__out = SPACE$(QB__in_len * 6) 'worst possible case is double size (\uXXXX)
QB__i1 = 1
QB__i2 = 0
QB__i = 0
DO WHILE QB__i1 <= QB__in_len
QB__a = ASC(QB__in, QB__i1)
IF QB__a <> 92 AND QB__a <> 34 AND (QB__a >= 32 AND QB__a <= 126) THEN 'not \ or " and valid standard ASCII
QB__i2 = QB__i2 + 1: ASC(QB__out, QB__i2) = QB__a
ELSE
QB__i2 = QB__i2 + 1: ASC(QB__out, QB__i2) = 92 '\
QB__a2 = __QB_JSON_escape_lookup(QB__a)
IF QB__a2 THEN
QB__i2 = QB__i2 + 1: ASC(QB__out, QB__i2) = QB__a2
ELSE
QB__i2 = QB__i2 + 1: ASC(QB__out, QB__i2) = 117 'u
IF QB__a = 0 THEN
QB__hex = "0"
ELSE
QB__hex = HEX$(_MAPUNICODE(QB__a))
END IF
QB__hex_len = LEN(QB__hex)
QB__a2 = 48
FOR QB__i = 1 TO 4
IF 5 - QB__i <= QB__hex_len THEN
QB__a2 = ASC(QB__hex, QB__i - (4 - QB__hex_len))
END IF
QB__i2 = QB__i2 + 1: ASC(QB__out, QB__i2) = QB__a2
NEXT
END IF
END IF
QB__i1 = QB__i1 + 1
LOOP
__QB_JSON_escape$ = LEFT$(QB__out, QB__i2)
END FUNCTION
FUNCTION __QB_JSON_output_string$ (QB__in AS STRING)
__QB_JSON_output_string$ = QB_STR_QUOTE + __QB_JSON_escape$(QB__in) + QB_STR_QUOTE
END FUNCTION
SUB __QB_JSON_serialize (json AS STRING, first AS LONG, addSiblings AS LONG)
DIM QB__i AS LONG
QB__i = first
DO WHILE QB__i
IF QB__i <> first THEN
json = json + ","
END IF
IF __QB_NODE(QB__i).type = QB_NODE_TYPE_DICTIONARY THEN
IF __QB_NODE(QB__i).labelFormat = QB_NODE_FORMAT_STR THEN
json = json + __QB_JSON_output_string(QB_STR_get(__QB_NODE(QB__i).label)) + ":"
END IF
json = json + "{"
__QB_JSON_serialize json, __QB_NODE(QB__i).firstChild, 1
json = json + "}"
END IF
IF __QB_NODE(QB__i).type = QB_NODE_TYPE_LIST THEN
IF __QB_NODE(QB__i).labelFormat = QB_NODE_FORMAT_STR THEN
json = json + __QB_JSON_output_string(QB_STR_get(__QB_NODE(QB__i).label)) + ":"
END IF
json = json + "["
__QB_JSON_serialize json, __QB_NODE(QB__i).firstChild, 1
json = json + "]"
END IF
IF __QB_NODE(QB__i).type = QB_NODE_TYPE_VALUE THEN
IF __QB_NODE(QB__i).labelFormat = QB_NODE_FORMAT_STR THEN
json = json + __QB_JSON_output_string(QB_STR_get(__QB_NODE(QB__i).label)) + ":"
END IF
IF __QB_NODE(QB__i).valueFormat <> QB_NODE_FORMAT_STR THEN
json = json + QB_NODE_value(QB__i)
ELSE
json = json + __QB_JSON_output_string(QB_NODE_value(QB__i))
END IF
END IF
IF addSiblings THEN
QB__i = __QB_NODE(QB__i).next
ELSE
QB__i = 0
END IF
LOOP
END SUB
FUNCTION __QB_JSON_deserialize (QB__json AS STRING, QB__index AS LONG, QB__parent AS LONG)
'returns the first node created
DIM QB__firstNodeCreated AS LONG
DIM QB__ignore AS LONG
DIM QB__index1 AS LONG
QB__index1 = QB__index
DIM QB__asc AS LONG
DIM QB__labelIndex AS LONG
DIM QB__label AS STRING
DIM QB__value AS STRING
DIM QB__obj AS LONG
DIM QB__objAdded AS LONG
DIM QB__final AS LONG
DIM QB__detectedFormat AS LONG
DIM QB__detectedFormatValue AS LONG
DIM QB__contentExists AS LONG
DO WHILE QB__index <= LEN(QB__json) + 1
IF QB__index = LEN(QB__json) + 1 THEN
QB__final = 1
QB__asc = 32 'whitespace
ELSE
QB__asc = ASC(QB__json, QB__index)
END IF
IF QB__asc = 44 OR QB__asc = 125 OR QB__asc = 93 OR QB__final <> 0 THEN ', } ] final
IF QB__objAdded = 0 AND QB__contentExists <> 0 THEN
QB__value = MID$(QB__json, QB__index1, (QB__index - QB__index1))
'TODO: derive value format here
QB__obj = QB_NODE_new(QB_NODE_TYPE_VALUE, 0)
IF QB__firstNodeCreated = 0 THEN QB__firstNodeCreated = QB__obj
IF QB__label <> "" THEN
QB_NODE_setLabel QB__obj, __QB_JSON_unescape$(QB__label, 0, 0)
QB__label = ""
END IF
QB__value = __QB_JSON_unescape$(QB__value, QB__detectedFormat, QB__detectedFormatValue)
IF QB__detectedFormat = QB_NODE_FORMAT_STR THEN
QB_NODE_setValue_format QB__obj, QB_STR_new(QB__value), QB__detectedFormat
ELSE
QB_NODE_setValue_format QB__obj, QB__detectedFormatValue, QB__detectedFormat
END IF
IF QB__parent <> 0 THEN QB_NODE_assign QB__parent, QB__obj
END IF
'end of block encountered?
IF QB__asc = 125 OR QB__asc = 93 OR QB__final <> 0 THEN '} ] final
__QB_JSON_deserialize = QB__firstNodeCreated
EXIT FUNCTION
END IF
QB__index1 = QB__index + 1
QB__objAdded = 0
QB__contentExists = 0
END IF
IF QB__asc <> 44 AND QB__asc <> 32 AND QB__asc <> 9 THEN QB__contentExists = 1
IF QB__asc = 58 THEN ':
IF LEN(QB__label) THEN
'already has label
PRINT "Invalid label separator encountered ':'"
END
END IF
QB__label = MID$(QB__json, QB__index1, (QB__index - QB__index1))
QB__index1 = QB__index + 1 'move start location
QB__contentExists = 0
END IF
IF QB__asc = 123 THEN '{
IF QB__objAdded <> 0 THEN
PRINT "Expected ,"
END
END IF
QB__obj = QB_NODE_newDictionary
IF QB__firstNodeCreated = 0 THEN QB__firstNodeCreated = QB__obj
IF QB__label <> "" THEN
QB_NODE_setLabel QB__obj, __QB_JSON_unescape$(QB__label, 0, 0)
QB__label = ""
END IF
QB__index = QB__index + 1
QB__ignore = __QB_JSON_deserialize(QB__json, QB__index, QB__obj)
IF ASC(QB__json, QB__index) <> 125 THEN '}
PRINT "Expected }"
END
END IF
IF QB__parent <> 0 THEN QB_NODE_assign QB__parent, QB__obj
QB__objAdded = 1
QB__contentExists = 0
END IF
IF QB__asc = 91 THEN '[
IF QB__objAdded <> 0 THEN
PRINT "Expected ,"
END IF
QB__obj = QB_NODE_newList
IF QB__firstNodeCreated = 0 THEN QB__firstNodeCreated = QB__obj
IF QB__label <> "" THEN
QB_NODE_setLabel QB__obj, __QB_JSON_unescape$(QB__label, 0, 0)
QB__label = ""
END IF
QB__index = QB__index + 1
QB__ignore = __QB_JSON_deserialize(QB__json, QB__index, QB__obj)
IF ASC(QB__json, QB__index) <> 93 THEN ']
PRINT "Expected ]"
END
END IF
IF QB__parent <> 0 THEN QB_NODE_assign QB__parent, QB__obj
QB__objAdded = 1
QB__contentExists = 0
END IF
QB__index = QB__index + 1
LOOP
PRINT "Unexpected end of loop encountered"
END
END FUNCTION
'##################################################
'#################### DATETIME: Private Methods ####################
FUNCTION __QB_DATETIME_format_smartCase$ (format AS STRING, value AS STRING)
DIM QB__type AS LONG
DIM QB__a AS LONG
DIM QB__a2 AS LONG
QB__a = ASC(format, 2)
QB__a2 = ASC(format, 3)
IF QB__a >= 65 AND QB__a <= 90 THEN
IF QB__a2 >= 65 AND QB__a2 <= 90 THEN
value = UCASE$(value)
ELSE
value = UCASE$(LEFT$(value, 1)) + LCASE$(MID$(value, 2))
END IF
ELSE
value = LCASE$(value)
END IF
__QB_DATETIME_format_smartCase$ = value
END FUNCTION
'##################################################