You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
566 lines
21 KiB
566 lines
21 KiB
--===========================================================================--
|
|
-- --
|
|
-- System.Collections.Dictionary --
|
|
-- --
|
|
--===========================================================================--
|
|
|
|
--===========================================================================--
|
|
-- Author : kurapica125@outlook.com --
|
|
-- URL : http://github.com/kurapica/PLoop --
|
|
-- Create Date : 2016/02/28 --
|
|
-- Update Date : 2020/07/06 --
|
|
-- Version : 1.1.6 --
|
|
--===========================================================================--
|
|
|
|
PLoop(function(_ENV)
|
|
namespace "System.Collections"
|
|
|
|
import "System.Serialization"
|
|
|
|
-- Helpers
|
|
export { yield = coroutine.yield, ipairs = ipairs }
|
|
|
|
__Iterator__() iterforpair = function (lstKey, lstValue)
|
|
local yield = yield
|
|
local iter, o, idx, value = (lstValue.GetIterator or ipairs)(lstValue)
|
|
for _, key in (lstValue.GetIterator or ipairs)(lstKey) do
|
|
idx, value = iter(o, idx)
|
|
if idx then yield(key, value) else break end
|
|
end
|
|
end
|
|
|
|
--- Represents the key-value pairs collections
|
|
interface "IDictionary" { Iterable }
|
|
|
|
--- The un-safe dictionary, it'll use the table as the object directly to gain
|
|
-- the best performance, it's safe when no method name, property name will be
|
|
-- used as keys.
|
|
__Sealed__() __Serializable__() __Arguments__{ AnyType, AnyType }( Any, Any )
|
|
__NoNilValue__(false):AsInheritable() __NoRawSet__(false):AsInheritable()
|
|
class "Dictionary" (function (_ENV, keytype, valtype)
|
|
extend "IDictionary" "ISerializable"
|
|
|
|
export {
|
|
ipairs = ipairs,
|
|
pairs = pairs,
|
|
GetErrorMessage = Struct.GetErrorMessage,
|
|
tostring = tostring,
|
|
|
|
List[keytype], List[valtype]
|
|
}
|
|
|
|
if keytype ~= Any then
|
|
export { kvalid = getmetatable(keytype).ValidateValue, rawset = rawset }
|
|
end
|
|
|
|
if valtype ~= Any then
|
|
export { vvalid = getmetatable(valtype).ValidateValue, rawset = rawset }
|
|
end
|
|
|
|
-----------------------------------------------------------
|
|
-- serialization --
|
|
-----------------------------------------------------------
|
|
function Serialize(self, info)
|
|
local key = {}
|
|
local val = {}
|
|
local idx = 1
|
|
for k, v in self:GetIterator() do
|
|
key[idx]= k
|
|
val[idx]= v
|
|
idx = idx + 1
|
|
end
|
|
|
|
info:SetValue(1, List[keytype](key))
|
|
info:SetValue(2, List[valtype](val))
|
|
end
|
|
|
|
__Arguments__{ SerializationInfo }
|
|
function __new(_, info)
|
|
local key = info:GetValue(1, List[keytype])
|
|
local val = info:GetValue(2, List[valtype])
|
|
|
|
return this(_, key, val)
|
|
end
|
|
|
|
-----------------------------------------------------------
|
|
-- method --
|
|
-----------------------------------------------------------
|
|
GetIterator = pairs
|
|
|
|
if keytype == Any and valtype == Any then
|
|
--- Update the dictionary
|
|
__Arguments__{ RawTable }
|
|
function Update(self, dict)
|
|
for k, v in pairs(dict) do self[k] = v end
|
|
return self
|
|
end
|
|
|
|
__Arguments__{ IDictionary }
|
|
function Update(self, dict)
|
|
for k, v in dict:GetIterator() do self[k] = v end
|
|
return self
|
|
end
|
|
|
|
__Arguments__{ Callable, System.Any/nil, System.Any/nil }
|
|
function Update(self, iter, obj, idx)
|
|
for k, v in iter, obj, idx do self[k] = v end
|
|
return self
|
|
end
|
|
elseif keytype == Any and valtype ~= Any then
|
|
__Arguments__{ RawTable }
|
|
function Update(self, dict)
|
|
for k, v in pairs(dict) do
|
|
local ret, msg = vvalid(valtype, v, true)
|
|
if not msg then
|
|
self[k] = v
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
|
|
__Arguments__{ IDictionary }
|
|
function Update(self, dict)
|
|
for k, v in dict:GetIterator() do
|
|
local ret, msg = vvalid(valtype, v, true)
|
|
if not msg then
|
|
self[k] = v
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
|
|
__Arguments__{ Callable, System.Any/nil, System.Any/nil }
|
|
function Update(self, iter, obj, idx)
|
|
for k, v in iter, obj, idx do
|
|
local ret, msg = vvalid(valtype, v, true)
|
|
if not msg then
|
|
self[k] = v
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
elseif keytype ~= Any and valtype == Any then
|
|
__Arguments__{ RawTable }
|
|
function Update(self, dict)
|
|
for k, v in pairs(dict) do
|
|
local ret, msg = kvalid(keytype, k, true)
|
|
if not msg then
|
|
self[k] = v
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
|
|
__Arguments__{ IDictionary }
|
|
function Update(self, dict)
|
|
for k, v in dict:GetIterator() do
|
|
local ret, msg = kvalid(keytype, k, true)
|
|
if not msg then
|
|
self[k] = v
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
|
|
__Arguments__{ Callable, System.Any/nil, System.Any/nil }
|
|
function Update(self, iter, obj, idx)
|
|
for k, v in iter, obj, idx do
|
|
local ret, msg = kvalid(keytype, k, true)
|
|
if not msg then
|
|
self[k] = v
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
else
|
|
__Arguments__{ RawTable }
|
|
function Update(self, dict)
|
|
for k, v in pairs(dict) do
|
|
local ret, msg = kvalid(keytype, k, true)
|
|
if not msg then
|
|
ret, msg = vvalid(valtype, v, true)
|
|
if not msg then
|
|
self[k] = v
|
|
end
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
|
|
__Arguments__{ IDictionary }
|
|
function Update(self, dict)
|
|
for k, v in dict:GetIterator() do
|
|
local ret, msg = kvalid(keytype, k, true)
|
|
if not msg then
|
|
ret, msg = vvalid(valtype, v, true)
|
|
if not msg then
|
|
self[k] = v
|
|
end
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
|
|
__Arguments__{ Callable, System.Any/nil, System.Any/nil }
|
|
function Update(self, iter, obj, idx)
|
|
for k, v in iter, obj, idx do
|
|
local ret, msg = kvalid(keytype, k, true)
|
|
if not msg then
|
|
ret, msg = vvalid(valtype, v, true)
|
|
if not msg then
|
|
self[k] = v
|
|
end
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
end
|
|
|
|
-----------------------------------------------------------
|
|
-- constructor --
|
|
-----------------------------------------------------------
|
|
__Arguments__{ }
|
|
function __new() return {} end
|
|
|
|
__Arguments__{ RawTable }
|
|
function __new(_, dict) return dict, true end
|
|
|
|
__Arguments__{ RawTable + IList, RawTable + IList }
|
|
function __new(_, lstKey, lstValue)
|
|
local dict = {}
|
|
local iter, o, idx, value = (lstValue.GetIterator or ipairs)(lstValue)
|
|
for _, key in (lstKey.GetIterator or ipairs)(lstKey) do
|
|
idx, value = iter(o, idx)
|
|
if idx then
|
|
dict[key] = value
|
|
else
|
|
break
|
|
end
|
|
end
|
|
return dict, true
|
|
end
|
|
|
|
__Arguments__{ RawTable + IList, Any }
|
|
function __new(_, lstKey, value)
|
|
local dict = {}
|
|
for _, key in (lstKey.GetIterator or ipairs)(lstKey) do
|
|
dict[key] = value
|
|
end
|
|
return dict, true
|
|
end
|
|
|
|
__Arguments__{ IDictionary }
|
|
function __new(_, obj)
|
|
local dict = {}
|
|
for key, value in obj:GetIterator() do
|
|
dict[key] = value
|
|
end
|
|
return dict, true
|
|
end
|
|
|
|
__Arguments__{ Callable, Any/nil, Any/nil }
|
|
function __new(_, iter, obj, idx)
|
|
local dict = {}
|
|
for key, value in iter, obj, idx do
|
|
dict[key] = value
|
|
end
|
|
return dict, true
|
|
end
|
|
|
|
if keytype ~= Any and valtype ~= Any then
|
|
function __ctor(self)
|
|
local msg
|
|
for k, v in self:GetIterator() do
|
|
k, msg = kvalid(keytype, k)
|
|
if msg then throw(GetErrorMessage(msg, "field")) end
|
|
|
|
v, msg = vvalid(valtype, v)
|
|
if msg then throw(GetErrorMessage(msg, "value")) end
|
|
self[k]= v
|
|
end
|
|
end
|
|
elseif keytype ~= Any then
|
|
function __ctor(self)
|
|
local msg
|
|
for k, v in self:GetIterator() do
|
|
k, msg = kvalid(keytype, k)
|
|
if msg then throw(GetErrorMessage(msg, "field")) end
|
|
end
|
|
end
|
|
elseif valtype ~= Any then
|
|
function __ctor(self)
|
|
local msg
|
|
for k, v in self:GetIterator() do
|
|
v, msg = vvalid(valtype, v)
|
|
if msg then throw(GetErrorMessage(msg, "value")) end
|
|
self[k]= v
|
|
end
|
|
end
|
|
end
|
|
|
|
-----------------------------------------------------------
|
|
-- meta-method --
|
|
-----------------------------------------------------------
|
|
if keytype ~= Any or valtype ~= Any then
|
|
__Arguments__{ keytype, valtype }
|
|
__newindex = rawset
|
|
end
|
|
end)
|
|
|
|
--- The dynamic dictionary
|
|
__Sealed__() __NoRawSet__(true)
|
|
class "XDictionary" (function(_ENV)
|
|
extend "IDictionary"
|
|
export { iterforpair = iterforpair, pairs = pairs }
|
|
|
|
XDICT_TYPE_ITER = 1
|
|
XDICT_TYPE_DICT = 2
|
|
XDICT_TYPE_PAIR = 3
|
|
|
|
-----------------------------------------------------------
|
|
-- method --
|
|
-----------------------------------------------------------
|
|
function GetIterator(self)
|
|
local type = self[1]
|
|
|
|
if type == XDICT_TYPE_ITER then
|
|
return self[2], self[3], self[4]
|
|
elseif type == XDICT_TYPE_DICT then
|
|
return self[2]:GetIterator()
|
|
elseif type == XDICT_TYPE_PAIR then
|
|
return iterforpair(self[2], self[3])
|
|
end
|
|
end
|
|
|
|
-----------------------------------------------------------
|
|
-- constructor --
|
|
-----------------------------------------------------------
|
|
__Arguments__{ RawTable }
|
|
function __new(_, dict) return { XDICT_TYPE_ITER, pairs(dict) }, true end
|
|
|
|
__Arguments__{ RawTable + IList, RawTable + IList }
|
|
function __new(_, lstKey, lstValue) return { XDICT_TYPE_PAIR, lstKey, lstValue }, true end
|
|
|
|
__Arguments__{ IDictionary }
|
|
function __new(_, dict) return { XDICT_TYPE_DICT, dict }, true end
|
|
|
|
__Arguments__{ Callable, Any/nil, Any/nil }
|
|
function __new(_, iter, obj, idx) return { XDICT_TYPE_ITER, iter, obj, idx }, true end
|
|
end)
|
|
|
|
--- the dictionary stream worker, used to provide stream filter, map and
|
|
-- etc operations on a dictionary without creating any temp caches
|
|
__Final__() __Sealed__() __SuperObject__(false)
|
|
__NoRawSet__(false) __NoNilValue__(false)
|
|
class "DictionaryStreamWorker" (function (_ENV)
|
|
extend "IDictionary"
|
|
|
|
export {
|
|
DictionaryStreamWorker,
|
|
|
|
type = type,
|
|
yield = coroutine.yield,
|
|
tinsert = table.insert,
|
|
tremove = table.remove,
|
|
}
|
|
|
|
-----------------------------------------------------------
|
|
-- helpers --
|
|
-----------------------------------------------------------
|
|
local getIdleworkers
|
|
local rycIdleworkers
|
|
|
|
if Platform.MULTI_OS_THREAD then
|
|
getIdleworkers = Toolset.fakefunc
|
|
rycIdleworkers = Toolset.fakefunc
|
|
else
|
|
-- Keep idle workers for re-usage
|
|
local idleworkers = {}
|
|
getIdleworkers = function() return tremove(idleworkers) end
|
|
rycIdleworkers = function(worker) tinsert(idleworkers, worker) end
|
|
end
|
|
|
|
-----------------------------------------------------------
|
|
-- constant --
|
|
-----------------------------------------------------------
|
|
export {
|
|
FLD_TARGETDICT = 0,
|
|
FLD_MAPACTITON = 1,
|
|
FLD_FILTERACTN = 2,
|
|
}
|
|
-----------------------------------------------------------
|
|
-- method --
|
|
-----------------------------------------------------------
|
|
__Iterator__()
|
|
function GetIterator(self)
|
|
local targetDict = self[FLD_TARGETDICT]
|
|
local map = self[FLD_MAPACTITON]
|
|
local filter = self[FLD_FILTERACTN]
|
|
|
|
-- Clear self and put self into recycle
|
|
self[FLD_TARGETDICT] = nil
|
|
self[FLD_MAPACTITON] = nil
|
|
self[FLD_FILTERACTN] = nil
|
|
|
|
rycIdleworkers(self)
|
|
|
|
-- Generate the iterator
|
|
local dowork
|
|
|
|
-- Generate the do-work
|
|
if filter then
|
|
-- Check Function
|
|
if map then
|
|
for key, value in targetDict:GetIterator() do
|
|
if filter(key, value) then
|
|
value = map(key, value)
|
|
if value ~= nil then
|
|
yield(key, value)
|
|
end
|
|
end
|
|
end
|
|
else
|
|
for key, value in targetDict:GetIterator() do
|
|
if filter(key, value) then yield(key, value) end
|
|
end
|
|
end
|
|
else
|
|
-- No filter
|
|
if map then
|
|
for key, value in targetDict:GetIterator() do
|
|
value = map(key, value)
|
|
if value ~= nil then
|
|
yield(key, value)
|
|
end
|
|
end
|
|
else
|
|
for key, value in targetDict:GetIterator() do
|
|
yield(key, value)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-----------------------------------------------------------
|
|
-- Queue method --
|
|
-----------------------------------------------------------
|
|
--- Map the items to other type datas
|
|
__Arguments__{ Callable }
|
|
function Map(self, func)
|
|
if self[FLD_MAPACTITON] then return DictionaryStreamWorker(self):Map(func) end
|
|
self[FLD_MAPACTITON] = func
|
|
return self
|
|
end
|
|
|
|
--- Used to filter the items with a check function
|
|
__Arguments__{ Callable }
|
|
function Filter(self, func)
|
|
if self[FLD_FILTERACTN] or self[FLD_MAPACTITON] then return DictionaryStreamWorker(self):Filter(func) end
|
|
self[FLD_FILTERACTN] = func
|
|
return self
|
|
end
|
|
|
|
-----------------------------------------------------------
|
|
-- constructor --
|
|
-----------------------------------------------------------
|
|
__Arguments__{ IDictionary }
|
|
function __ctor(self, dict)
|
|
self[FLD_TARGETDICT] = dict
|
|
end
|
|
|
|
-----------------------------------------------------------
|
|
-- meta-method --
|
|
-----------------------------------------------------------
|
|
__Arguments__{ IDictionary }
|
|
function __exist(_, dict)
|
|
local worker = getIdleworkers()
|
|
if worker then worker[FLD_TARGETDICT] = dict end
|
|
return worker
|
|
end
|
|
end)
|
|
|
|
__Sealed__()
|
|
interface "IDictionary" (function (_ENV)
|
|
export {
|
|
yield = coroutine.yield,
|
|
}
|
|
|
|
export { DictionaryStreamWorker, ListStreamWorker, XDictionary }
|
|
|
|
-----------------------------------------------------------
|
|
-- Queue method --
|
|
-----------------------------------------------------------
|
|
--- Map the items to other type datas
|
|
__Arguments__{ Callable }
|
|
function Map(self, func) return DictionaryStreamWorker(self):Map(func) end
|
|
|
|
--- Used to filter the items with a check function
|
|
__Arguments__{ Callable }
|
|
function Filter(self, func) return DictionaryStreamWorker(self):Filter(func) end
|
|
|
|
-----------------------------------------------------------
|
|
-- Final method --
|
|
-----------------------------------------------------------
|
|
--- Convert the selected items to a raw hash table
|
|
function ToTable(self)
|
|
local result = {}
|
|
for key, value in self:GetIterator() do result[key] = value end
|
|
return result
|
|
end
|
|
|
|
--- Convert the selected items to a dictionary
|
|
__Arguments__{ -IDictionary/Dictionary }
|
|
function ToDict(self, cls) return cls(self) end
|
|
|
|
--- Save the link operations into a xdictionary so we can use it as a new start for link operations
|
|
function ToXDict(self) return XDictionary(self) end
|
|
|
|
--- Combine the key-value pairs to get a result
|
|
__Arguments__{ Callable, Any/nil }
|
|
function Reduce(self, func, init)
|
|
for key, value in self:GetIterator() do init = func(key, value, init) end
|
|
return init
|
|
end
|
|
|
|
--- Call the function for each element or set property's value for each element
|
|
__Arguments__{ Callable, Any * 0 }
|
|
function Each(self, func, ...) for key, value in self:GetIterator() do func(key, value, ...) end end
|
|
|
|
--- get the keys
|
|
__Iterator__()
|
|
function GetKeys(self)
|
|
local index = 0
|
|
for key in self:GetIterator() do
|
|
index = index + 1
|
|
yield(index, key)
|
|
end
|
|
end
|
|
|
|
-- get the values
|
|
__Iterator__()
|
|
function GetValues(self)
|
|
local index = 0
|
|
for _, value in self:GetIterator() do
|
|
index = index + 1
|
|
yield(index, value)
|
|
end
|
|
end
|
|
|
|
-----------------------------------------------------------
|
|
-- list property --
|
|
-----------------------------------------------------------
|
|
--- Get a list stream worker of the dictionary's keys
|
|
property "Keys" {
|
|
Get = function (self)
|
|
return ListStreamWorker( self:GetKeys() )
|
|
end
|
|
}
|
|
|
|
--- Get a list stream worker of the dictionary's values
|
|
property "Values" {
|
|
Get = function (self)
|
|
return ListStreamWorker( self:GetValues() )
|
|
end
|
|
}
|
|
end)
|
|
end)
|
|
|