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

--===========================================================================--
-- --
-- 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)