/usr/share/lua/5.1/lgi/component.lua is in lua-lgi 0.9.0.20151101.git.885af4-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 | ------------------------------------------------------------------------------
--
-- LGI Basic repo type component implementation
--
-- Copyright (c) 2010, 2011, 2012, 2013 Pavel Holejsovsky
-- Licensed under the MIT license:
-- http://www.opensource.org/licenses/mit-license.php
--
------------------------------------------------------------------------------
local assert, pcall, setmetatable, getmetatable, pairs, next, rawget, rawset,
type, select, error
= assert, pcall, setmetatable, getmetatable, pairs, next, rawget, rawset,
type, select, error
local table = require 'table'
local string = require 'string'
local core = require 'lgi.core'
-- Generic component metatable. Component is any entity in the repo,
-- e.g. record, object, enum, etc.
local component = { mt = {} }
-- Creates new component table by cloning all contents and setting
-- Gets table for category of compound (i.e. _field of struct or _property
-- for class etc). Installs metatable which performs on-demand lookup of
-- symbols.
function component.get_category(children, xform_value,
xform_name, xform_name_reverse)
-- Either none or both transform methods must be provided.
assert(not xform_name or xform_name_reverse)
-- Early shortcircuit; no elements, no table needed at all.
if #children == 0 then return nil end
-- Index contains array of indices which were still not retrieved
-- from 'children' table, and table part contains name->index
-- mapping.
local index, mt = {}, {}
for i = 1, #children do index[i] = i end
-- Fully resolves the category (i.e. loads everything remaining to
-- be loaded in given category) and disconnects on-demand loading
-- metatable.
local function resolve(category)
-- Load al values from unknown indices.
local ei, en, val
local function xvalue(arg)
if not xform_value then return arg end
if arg then
local ok, res = pcall(xform_value, arg)
return ok and res
end
end
while #index > 0 do
ei = children[table.remove(index)]
val = xvalue(ei)
if val then
en = ei.name
en = not xform_name_reverse and en or xform_name_reverse(en)
if en then category[en] = val end
end
end
-- Load all known indices.
for en, idx in pairs(index) do
val = xvalue(children[idx])
en = not xform_name_reverse and en or xform_name_reverse(en)
if en then category[en] = val end
end
-- Metatable is no longer needed, disconnect it.
return setmetatable(category, nil)
end
function mt:__index(requested_name)
-- Check if closure for fully resolving the category is needed.
if requested_name == '_resolve' then return resolve end
-- Transform name by transform function.
local name = not xform_name and requested_name
or xform_name(requested_name)
if not name then return end
-- Check, whether we already know its index.
local idx, val = index[name]
if idx then
-- We know at least the index, so get info directly.
val = children[idx]
index[name] = nil
else
-- Not yet, go through unknown indices and try to find the
-- name.
while #index > 0 do
idx = table.remove(index)
val = children[idx]
local en = val.name
if en == name then break end
val = nil
index[en] = idx
end
end
-- If there is nothing in the index, we can disconnect
-- metatable, because everything is already loaded.
if not next(index) then
setmetatable(self, nil)
end
-- Transform found value and store it into the category (self)
-- table.
if not val then return nil end
if xform_value then val = xform_value(val) end
if not val then return nil end
self[requested_name] = val
return val
end
return setmetatable({}, mt)
end
-- Creates new component table by cloning all contents and setting
-- categories table.
function component.mt:clone(type, categories)
local new_component = {}
for key, value in pairs(self) do new_component[key] = value end
new_component._type = type
if categories then
table.insert(categories, 1, '_attribute')
new_component._categories = categories
end
return new_component
end
-- __call implementation, uses _new method to create new instance of
-- component type.
function component.mt:__call(...)
return self:_new(...)
end
-- Fully resolves the whole typetable, i.e. load all symbols normally
-- loaded on-demand at once. Returns self, so that resolve can be
-- easily chained for the caller.
function component.mt:_resolve()
local categories = self._categories or {}
for i = 1, #categories do
-- Invoke '_resolve' function for all category tables, if they have it.
local category = rawget(self, categories[i])
local resolve = type(category) == 'table' and category._resolve
if resolve then resolve(category) end
end
return self
end
-- Implementation of _access method, which is called by _core when
-- repo instance is accessed for reading or writing.
function component.mt:_access(instance, symbol, ...)
-- Invoke _element, which converts symbol to element and category.
local element, category = self:_element(instance, symbol)
if not element then
error(("%s: no `%s'"):format(self._name, tostring(symbol)), 3)
end
-- Get category handler to be used, and invoke it.
if category then
local handler = self['_access' .. category]
if handler then return handler(self, instance, element, ...) end
end
-- If specific accessor does not exist, consider the element to be
-- 'static const' attribute of the class. This works well for
-- methods, constants and assorted other elements added manually
-- into the class by overrides.
if select('#', ...) > 0 then
error(("%s: `%s' is not writable"):format(self._name, symbol), 4)
end
return element
end
-- Keyword translation dictionary. Used for translating Lua keywords
-- which might appear as symbols in typelibs into Lua-neutral identifiers.
local keyword_dictionary = {
_end = 'end', _do = 'do', _then = 'then', _elseif = 'elseif', _in = 'in',
_local = 'local', _function = 'function', _nil = 'nil', _false = 'false',
_true = 'true', _and = 'and', _or = 'or', _not = 'not',
}
-- Retrieves (element, category) pair from given componenttable and
-- instance for given symbol.
function component.mt:_element(instance, symbol, origin)
-- This generic version can work only with strings. Refuse
-- everything other, hoping that some more specialized _element
-- implementation will handle it.
if type(symbol) ~= 'string' then return end
-- Check keyword translation dictionary. If the symbol can be
-- found there, try to lookup translated symbol.
symbol = keyword_dictionary[symbol] or symbol
-- Check whether symbol is directly accessible in the component.
local element = rawget(self, symbol)
if element then return element end
-- Check whether symbol is accessible in cached directory of the
-- component, packed as element value and category
local cached = rawget(self, '_cached')
if cached then
element = cached[symbol]
if element then return element[1], element[2] end
end
-- Decompose symbol name, in case that it contains category prefix
-- (e.g. '_field_name' when requesting explicitely field called
-- name).
local category, name = string.match(symbol, '^(_.-)_(.*)$')
if category and name and category ~= '_access' then
-- Check requested category.
local cat = rawget(self, category)
element = cat and cat[name]
elseif string.sub(symbol, 1, 1) ~= '_' then
-- Check all available categories.
local categories = self._categories or {}
for i = 1, #categories do
category = categories[i]
local cat = rawget(self, category)
element = cat and cat[symbol]
if element then break end
end
end
if element then
-- Make sure that table-based attributes have symbol name, so
-- that potential errors contain the name of referenced
-- attribute.
if type(element) == 'table' and category == '_attribute' then
element._name = element._name or symbol
end
-- If possible, cache the element in root table.
if not category or not (origin or self)['_access' .. category] then
-- No category or no special category handler is present,
-- store it directly, which results in fastest access. This
-- is most typical for methods.
self[symbol] = element
else
-- Store into _cached table, because we have to preserve the
-- category.
if not cached then
cached = {}
self._cached = cached
end
cached[symbol] = { element, category }
end
return element, category
end
end
-- __index implementation, uses _element method to perform lookup.
function component.mt:__index(key)
-- First try to invoke our own _element method.
local _element, mt = rawget(self, '_element')
if not _element then
mt = getmetatable(self)
_element = rawget(mt, '_element')
end
local value, category = _element(self, nil, key)
if value then
if category then
-- Mangle the result by type-specific '_index_<category>'
-- method, if present.
local index_name = '_index' .. category
local index = rawget(self, index_name)
if not index then
if not mt then mt = getmetatable(self) end
if mt then index = rawget(mt, index_name) end
end
if index then value = index(self, value) end
end
return value
end
-- If not found as object element, examine the metatable itself.
return rawget(mt or getmetatable(self), key)
end
-- Implementation of attribute accessor. Attribute is either function
-- to be directly invoked, or table containing set and get functions.
function component.mt:_access_attribute(instance, element, ...)
-- If element is a table, assume that this table contains 'get' and
-- 'set' methods. Dispatch to them, and error out if they are
-- missing.
if type(element) == 'table' then
local mode = select('#', ...) == 0 and 'get' or 'set'
if not element[mode] then
error(("%s: cannot %s `%s'"):format(
self._name, mode == 'get' and 'read' or 'write',
element._name or '<unknown>'), 5)
end
element = element[mode]
end
-- Invoke attribute access function.
return element(instance, ...)
end
-- Pretty prints type name
function component.mt:__tostring()
return self._name
end
-- Creates new component and sets up common parts according to given
-- info.
function component.create(info, mt, name)
local gtype
if core.gi.isinfo(info) then
gtype = info.gtype
name = info.fullname
else
gtype = info and core.gtype(info)
end
-- Fill in meta of the compound.
local component = { _name = name }
if gtype then
-- Bind component in repo, make the relation using GType.
component._gtype = gtype
core.index[gtype] = component
end
return setmetatable(component, mt)
end
return component
|