--[[ Copyright (c) 2012-2013 Kaarle Ritvanen See LICENSE file for license details --]] module(..., package.seeall) local raise = require('acf.error').raise 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 name = path[1] local next = self[name] if next == nil then raise(getmetatable(self).path, 'Subordinate does not exist: '..name) end table.remove(path, 1) return TreeNode.search(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 raise(path, 'Collection cannot be empty') 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 raise(path, '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')