Модуль:WDBackend
Для документации этого модуля может быть создана страница Модуль:WDBackend/doc
require('strict')
local p = {}
local base = require('Модуль:WDBase')
local Backend = {}
function Backend:new(lang)
local obj = { lang=lang }
setmetatable(obj, self)
self.__index = self
return obj
end
function Backend:safeField(source, fieldName, parentField)
if not parentField then
parentField = source
end
local info = parentField[fieldName]
if not info then
info = {}
end
return info
end
function Backend:trySetField(source, fieldName, info, parentField)
if info.value or info.entity then
if not parentField then
parentField = source
end
parentField[fieldName] = info
end
end
local function inArray(value, array)
for _, currValue in ipairs(array) do
if currValue == value then
return true
end
end
return false
end
function Backend:fetchFieldsByQualifiers(source, fieldMap, qualifiers, parentField)
for _, map in ipairs(fieldMap) do
local qualifier = qualifiers[map.property]
if qualifier then
if map.filter then
qualifier = map.filter(qualifier, self.lang)
end
local currParentField = parentField
if map.isLocal then
currParentField = parentField.components or {}
end
self:fetchFieldByMap(source, map, qualifier, base.dataBySnak, currParentField)
if map.isLocal and next(currParentField) ~= nil then
parentField.components = currParentField
end
end
end
end
function Backend:fetchFieldItem(source, map, statementOrSnak, getData)
local item = getData(statementOrSnak, self.lang)
if item then
item.retrieved = true
else
item = {}
end
if map.mapEntity then
local entity = map.mapEntity[item.entity]
if not entity then
return nil
end
item = base.dataByEntity(entity)
if item then
item.retrieved = true
end
end
if map.getValue then
item.value = map.getValue(item.entity, self.lang)
end
if map.allowedEntities and item.entity and not inArray(item.entity, map.allowedEntities) then
return nil
end
if map.defaultUnit and not item.unitEntity then
item.unitEntity = map.defaultUnit
end
if map.allowedUnits then
if not item.unitEntity or not inArray(item.unitEntity, map.allowedUnits) then
return nil
end
end
if not item.value and not item.entity then
return nil
end
if map.exact then
item.exact = true
end
return item
end
local function skipGetIf(item, cond)
if not cond then
return false
end
for key, value in pairs(cond) do
if item[key] == value then
return true
end
end
return false
end
function Backend:fetchFieldByMap(source, map, statementsOrSnaks, getData, parentField)
local fieldName = map.name
local currParentField = source
if map.isLocal or not fieldName then
currParentField = parentField
end
if fieldName then
local fieldTable = self:safeField(source, fieldName, currParentField)
if map.match and getData == base.dataByStatement and fieldTable.value then
local statement = base.searchStatementByValue(statementsOrSnaks, fieldTable.value)
if not statement then
return
end
local item = self:fetchFieldItem(source, map, statement, getData)
if item then
currParentField[fieldName] = item
if map.qualifiers and statement.qualifiers then
self:fetchFieldsByQualifiers(source, map.qualifiers, statement.qualifiers, item)
end
if map.get and not skipGetIf(item, map.skipGetIf) then
self:fetchFieldsByMap(source, item.entity, map.get, currParentField)
end
else
if map.elseGet then
self:fetchFieldsByMap(source, parentField.entity, map.elseGet, parentField)
end
end
return
end
if fieldTable.value and not map.overwrite then
return
end
end
local count = table.getn(statementsOrSnaks)
if map.max then
if count > map.max then
count = map.max
end
end
local items = {}
local indices = {}
for i=1, count do
local statementOrSnak = statementsOrSnaks[i]
if statementOrSnak ~= nil then
local item = self:fetchFieldItem(source, map, statementOrSnak, getData)
if item then
table.insert(items, item)
indices[i] = table.getn(items)
else
indices[i] = 0
end
end
end
local triggerElseGet = false
if fieldName then
if table.getn(items) == 1 and not map.isArray then
currParentField[fieldName] = items[1]
elseif next(items) ~= nil then
currParentField[fieldName] = items
else
triggerElseGet = true
end
elseif next(items) ~= nil then
if map.overwriteValue then
currParentField.value = items[1].value
currParentField.exact = map.exact
end
if map.overwriteEntity then
currParentField.entity = items[1].entity
end
else
triggerElseGet = true
end
for i=1, count do
local statementOrSnak = statementsOrSnaks[i]
local item = items[indices[i]]
if map.qualifiers and statementOrSnak.qualifiers then
self:fetchFieldsByQualifiers(source, map.qualifiers, statementOrSnak.qualifiers, item)
end
end
if fieldName then
for j, item in ipairs(items) do
if map.get and not skipGetIf(item, map.skipGetIf) then
local components = {}
self:fetchFieldsByMap(source, item.entity, map.get, components)
if next(components) ~= nil then
item.components = components
end
end
end
else
for j, item in ipairs(items) do
if map.get and not skipGetIf(item, map.skipGetIf) then
self:fetchFieldsByMap(source, item.entity, map.get, currParentField)
end
end
end
if triggerElseGet and map.elseGet then
self:fetchFieldsByMap(source, parentField.entity, map.elseGet, parentField)
end
end
function Backend:fetchFieldByCustomFunc(source, entity, map, parentField)
local currParentField = source
if map.isLocal then
currParentField = parentField
end
local fieldTable = self:safeField(source, map.name, currParentField)
if fieldTable.value and not map.overwrite then
return
end
fieldTable.value = map.getValue(entity, self.lang)
self:trySetField(source, map.name, fieldTable, currParentField)
end
local function getPropertyByPath(source, propertyPath)
local currField = source
for _, pathEntry in ipairs(propertyPath) do
currField = currField[pathEntry]
if not currField then
return nil
end
end
return currField
end
local function filterStatementsByPropertiesAndValues(statements, properties)
local filtered = {}
local propsCount = table.getn(properties)
for _, statement in ipairs(statements) do
local matched = 0
local qualifiers = statement.qualifiers
if qualifiers then
for _, propInfo in ipairs(properties) do
local propQualifiers = qualifiers[propInfo.property]
if propQualifiers then
for _, propQualifier in ipairs(propQualifiers) do
if propInfo.value then
local value = base.valueBySnak(propQualifier)
if value == propInfo.value then
matched = matched + 1
else
break
end
else
matched = matched + 1
end
end
end
end
end
if matched == propsCount then
table.insert(filtered, statement)
end
end
if not next(filtered) then
return nil
end
return filtered
end
local function statementsByCache(entity, property, cache)
if cache then
local entityCache = cache[entity]
if entityCache then
local statements = entityCache[property]
if statements then
return statements
end
end
end
return base.statements(entity, property)
end
function Backend:fetchFieldsByMap(source, entity, fieldMap, parentField)
for _, map in ipairs(fieldMap) do
local currEntity = entity
if map.entity then
currEntity = map.entity
end
if currEntity then
local propertySpecified = false
local statements
if map.property then
statements = statementsByCache(currEntity, map.property, map.cache)
propertySpecified = true
elseif map.properties then
statements = base.statementsByProperties(currEntity, map.properties)
propertySpecified = true
elseif map.propertyPath then
local property = getPropertyByPath(source, map.propertyPath)
if property then
statements = statementsByCache(currEntity, property, map.cache)
end
propertySpecified = true
end
if propertySpecified then
if statements then
if map.filter then
statements = map.filter(statements, self.lang)
end
if map.has then
statements = filterStatementsByPropertiesAndValues(statements, map.has)
end
self:fetchFieldByMap(source, map, statements, base.dataByStatement, parentField)
else
if map.elseGet then
self:fetchFieldsByMap(source, currEntity, map.elseGet, parentField)
end
end
else
if not propertySpecified and map.getValue then
self:fetchFieldByCustomFunc(source, currEntity, map, parentField)
end
end
end
end
end
function Backend:fetch(source, fieldsMap)
for _, map in ipairs(fieldsMap) do
local fieldTable = self:safeField(source, map.name)
if fieldTable then
self:fetchFieldsByMap(source, fieldTable.entity, map.get, source)
end
end
end
function Backend:fetchEntity(source, entity, fieldMap)
self:fetchFieldsByMap(source, entity, fieldMap)
end
function Backend:ensureLang()
if self.lang then
return
end
local defaultLangObj = mw.getContentLanguage()
local defaultLang = defaultLangObj:getCode()
self.lang = defaultLang
end
function Backend:assertLang()
assert(self.lang, 'No language selected.')
end
function p.new(lang)
return Backend:new(lang)
end
return p