Документация
require('strict')
local p = {}
local NS_MODULE = 828 --: https://www.mediawiki.org/wiki/Extension_default_namespaces
local moduleNamespace = mw.site.namespaces[NS_MODULE].name
local base = require(moduleNamespace .. ':Песочница/D6194c-1cc/WDBaseV2')
local Backend = {}
function Backend:new(lang)
local defaultLangObj = mw.getContentLanguage()
local defaultLang = defaultLangObj:getCode()
local obj = {
lang = lang,
defaultLang = defaultLang
}
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:parseFieldPath(source, map, parentField)
local fieldName = map.name
local currParentComponents = source
local currParentField = nil
if map.isLocal or not fieldName then
currParentField = parentField
if parentField then
currParentComponents = currParentField.components or {}
end
end
if type(fieldName) == 'table' then
local lastParentField = currParentField
local lastParentComponents = currParentComponents
local lastNotFound = false
for i, name in ipairs(fieldName) do
if i ~= 1 then
-- It's much easier to break at last iteration, but it's a problem
-- to get the count of parts if the map will be loaded by mw.loadData()
if lastNotFound then
return nil
end
currParentField = lastParentField
currParentComponents = lastParentComponents
end
if lastParentComponents then
lastParentField = lastParentComponents[name]
lastParentComponents = lastParentField and lastParentField.components
else
lastNotFound = true
end
fieldName = name
end
currParentComponents = currParentComponents or {}
end
return currParentField, currParentComponents, fieldName
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:getLang(map)
return (map.useDefaultLang and self.defaultLang) or self.lang
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:getLang(map))
end
self:fetchFieldByMap(source, map, qualifier, base.dataBySnak, parentField)
end
end
end
function Backend:fetchFieldItem(source, map, statementOrSnak, getData)
local lang = self:getLang(map)
local item = getData(statementOrSnak, lang, map.cache)
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, lang, map.cache)
if item then
item.retrieved = true
end
end
if item.entity then
if map.getData then
item = map.getData(item.entity, lang)
item.retrieved = true
elseif map.getValue then
item.value, item.lang = map.getValue(item.entity, lang)
end
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:tryGet(source, map, getTable, items, currParentField)
if not getTable then
return
end
local fieldName = map.name
if fieldName then
for j, item in ipairs(items) do
if not skipGetIf(item, map.skipGetIf) then
self:fetchFieldsByMap(source, item.entity, getTable, item)
end
end
else
for j, item in ipairs(items) do
if not skipGetIf(item, map.skipGetIf) then
self:fetchFieldsByMap(source, item.entity, getTable, currParentField)
end
end
end
end
function Backend:fetchFieldByMap(source, map, statementsOrSnaks, getData, parentField)
local currParentField, currParentComponents, fieldName
= self:parseFieldPath(source, map, parentField)
if not currParentComponents then
return
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
currParentComponents[fieldName] = item
if map.qualifiers and statement.qualifiers then
self:fetchFieldsByQualifiers(source, map.qualifiers, statement.qualifiers, item)
end
local getTable = map.forceGet or map.get
if getTable and not skipGetIf(item, map.skipGetIf) then
self:fetchFieldsByMap(source, item.entity, getTable, currParentField)
end
else
if map.elseGet then
self:fetchFieldsByMap(source, parentField.entity, map.elseGet, currParentField)
end
end
return
end
if fieldTable.value and not map.overwrite then
if map.substInto and map.substInto.force then
self:substFieldInto(source, map, parentField)
end
local items = fieldTable
if table.getn(items) == 0 then
items = { items }
end
self:tryGet(source, map, map.forceGet, items, currParentField)
return
end
end
local maxCount = map.max
if not maxCount then
maxCount = math.huge
end
local items = {}
local indices = {}
for i, statementOrSnak in ipairs(statementsOrSnaks) do
if i > maxCount then
break
end
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
currParentComponents[fieldName] = items[1]
elseif next(items) ~= nil then
currParentComponents[fieldName] = items
else
triggerElseGet = true
end
elseif next(items) ~= nil then
if map.overwriteValue then
parentField.value = items[1].value
parentField.exact = map.exact
end
if map.overwriteEntity then
parentField.entity = items[1].entity
end
else
triggerElseGet = true
end
if currParentField and next(currParentComponents) ~= nil then
currParentField.components = currParentComponents
end
if map.substInto then
self:substFieldInto(source, map, parentField)
end
for i, statementOrSnak in ipairs(statementsOrSnaks) do
if i > maxCount then
break
end
local item = items[indices[i]]
if map.qualifiers and statementOrSnak.qualifiers then
self:fetchFieldsByQualifiers(source, map.qualifiers, statementOrSnak.qualifiers, item)
end
end
self:tryGet(source, map, map.forceGet or map.get, items, currParentField)
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, currParentComponents, fieldName
= self:parseFieldPath(source, map, parentField)
if not currParentComponents then
return
end
local fieldTable = currParentComponents[fieldName]
if fieldTable and fieldTable.value and not map.overwrite then
return
end
local lang = self:getLang(map)
if map.getData then
fieldTable = map.getData(entity, lang)
else
fieldTable = fieldTable or {}
fieldTable.value = map.getValue(entity, lang)
end
fieldTable.retrieved = true
self:trySetField(source, fieldName, fieldTable, currParentComponents)
if currParentField and next(currParentComponents) ~= nil then
currParentField.components = currParentComponents
end
end
function Backend:fetchFieldLabel(source, map, parentField)
local currParentField, currParentComponents, fieldName
= self:parseFieldPath(source, map, parentField)
if not currParentComponents then
return
end
local fieldTable = currParentComponents[fieldName]
local entity
if fieldTable then
if fieldTable.value then
return
end
entity = fieldTable.entity
end
entity = map.entity or fieldTable.entity
if not entity then
return
end
local components = fieldTable and fieldTable.components
fieldTable = base.dataByEntity(entity, self:getLang(map), map.cache)
fieldTable.components = components
currParentComponents[fieldName] = fieldTable
if currParentField and next(currParentComponents) ~= nil then
currParentField.components = currParentComponents
end
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
function Backend:tryForceGetByMap(source, map, parentField)
local currParentField, currParentComponents, fieldName
= self:parseFieldPath(source, map, parentField)
if not currParentComponents then
return
end
local fieldTable = self:safeField(source, fieldName, currParentComponents)
local items = fieldTable
if table.getn(items) == 0 then
items = { fieldTable }
end
self:tryGet(source, map, map.forceGet, items, currParentField)
end
function Backend:substFieldInto(source, map, parentField)
local currParentField, currParentComponents, fieldName
= self:parseFieldPath(source, map, parentField)
if not currParentComponents then
return
end
local fieldTable = currParentComponents[fieldName]
if not fieldTable then
return
end
local targetCurrParentField, targetCurrParentComponents, targetFieldName
= self:parseFieldPath(source, map.substInto, parentField)
if not targetCurrParentComponents then
return
end
local targetFieldTable = self:safeField(source, targetFieldName, targetCurrParentComponents)
local templateCurrParentField, templateCurrParentComponents, templateFieldName
= self:parseFieldPath(source, map.substInto.template, parentField)
if not templateCurrParentComponents then
return
end
local templateFieldTable = templateCurrParentComponents[templateFieldName]
if not templateFieldTable then
return
end
targetFieldTable.value = templateFieldTable.value:gsub('%$1', fieldTable.value)
self:trySetField(source, targetFieldName, targetFieldTable, targetCurrParentComponents)
if targetCurrParentField and next(targetCurrParentComponents) ~= nil then
targetCurrParentField.components = targetCurrParentComponents
end
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 = base.statements(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 = base.statements(currEntity, property, map.cache)
end
propertySpecified = true
end
if propertySpecified then
if statements then
if map.filter then
statements = map.filter(statements, self:getLang(map))
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 map.getData or map.getValue then
self:fetchFieldByCustomFunc(source, currEntity, map, parentField)
end
if map.getLabel then
self:fetchFieldLabel(source, map, parentField)
end
end
if not propertySpecified or not statements then
self:tryForceGetByMap(source, map, parentField)
end
else
self:tryForceGetByMap(source, map, parentField)
end
end
end
function Backend:fetch(source, fieldsMap)
for _, map in ipairs(fieldsMap) do
local fieldTable = self:safeField(source, map.name)
if next(fieldTable) ~= nil then
if map.get then
self:fetchFieldsByMap(source, fieldTable.entity, map.get, fieldTable)
end
if map.substInto then
self:substFieldInto(source, map)
end
end
end
end
function Backend:fetchEntity(source, entity, fieldMap)
self:fetchFieldsByMap(source, entity, fieldMap)
end
function Backend:ensureLang()
if self.lang then
return
end
self.lang = self.defaultLang
end
function Backend:assertLang()
assert(self.lang, 'No language selected.')
end
function p.new(lang)
return Backend:new(lang)
end
return p