1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
--[[
Copyright (c) 2012-2013 Kaarle Ritvanen
See LICENSE file for license details
--]]
module(..., package.seeall)
local object = require('acf.object')
local class = object.class
local super = object.super
local pth = require('acf.path')
BoundField = class()
function BoundField:init(parent, field)
local pmt = getmetatable(parent)
local mt = {}
function mt.__index(t, k)
local member = field[k]
if type(member) ~= 'function' then return member end
return function(self, ...)
local name
if field.name then name = field.name
else
name = arg[1]
table.remove(arg, 1)
end
return member(field,
pmt.txn,
pth.join(pmt.path, name),
pmt.addr and pth.join(pmt.addr, name),
unpack(arg))
end
end
setmetatable(self, mt)
end
TreeNode = class()
function TreeNode:init(txn, path, addr)
local mt = getmetatable(self)
mt.txn = txn
mt.path = path
mt.addr = addr
end
function TreeNode:search(path)
if #path == 0 then return self end
local next = path[1]
table.remove(path, 1)
return TreeNode.search(self[next], path)
end
Collection = class(TreeNode)
function Collection:init(txn, path, addr, field, required)
super(self, Collection):init(txn, path, addr)
if required then
txn.validate[path] = function()
if #txn:get(addr) == 0 then
error('Collection cannot be empty: '..path)
end
end
end
self.init = nil
self.search = nil
local mt = getmetatable(self)
mt.field = BoundField(self, field)
mt.meta = {type='collection', members=mt.field:meta('$')}
function mt.mmeta(name) return mt.meta.members end
function mt.members() return txn:get(addr) or {} end
function mt.__index(t, k) return mt.field:load(k) end
function mt.__newindex(t, k, v) mt.field:save(k, v) end
end
PrimitiveList = class(Collection)
function PrimitiveList:init(txn, path, addr, field, required)
super(self, PrimitiveList):init(txn, path, addr, field, required)
local mt = getmetatable(self)
local index = mt.__index
function mt.__index(t, k)
if type(k) == 'number' then return index(t, k) end
for i, j in ipairs(txn:get(addr) or {}) do
assert(i == tonumber(j))
if mt.field:load(i) == k then return k end
end
error('Value does not exist: '..k)
end
end
-- experimental
Mixed = class(Collection)
function Mixed:init(txn, path, addr, field, required)
super(self, Mixed):init(txn, path, addr, field, required)
-- TODO dynamic meta: list non-leaf children
getmetatable(self).meta = {type='mixed'}
end
local function meta_func(attr)
return function(node, ...)
local res = getmetatable(node)[attr]
if type(res) == 'function' then return res(unpack(arg)) end
return res
end
end
members = meta_func('members')
meta = meta_func('meta')
mmeta = meta_func('mmeta')
path = meta_func('path')
|