「モジュール:Wd」の版間の差分

en:Module:Wd 2017年7月18日 (火) 06:28(UTC)より
template>K-iczn
(en:Module:Wd 2017年5月28日 (日) 18:42(UTC)より)
template>K-iczn
(en:Module:Wd 2017年7月18日 (火) 06:28(UTC)より)
25行目: 25行目:
importedFrom    = "P143",
importedFrom    = "P143",
statedIn        = "P248",
statedIn        = "P248",
pages          = "P304",
publicationDate = "P577",
publicationDate = "P577",
startTime      = "P580",
startTime      = "P580",
endTime        = "P582",
endTime        = "P582",
chapter        = "P792",
retrieved      = "P813",
retrieved      = "P813",
referenceURL    = "P854",
referenceURL    = "P854",
96行目: 98行目:
cfg.propertyValue = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
cfg.bestRank = true
107行目: 110行目:
cfg.mdyDate = false
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.pageTitle = false
cfg.pageTitle = false
117行目: 122行目:
cfg.states.qualifiersCount = 0
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
return cfg
143行目: 150行目:
return stt
return stt
end
function replaceAlias(ID)
if aliasesP[ID] then
ID = aliasesP[ID]
end
return ID
end
end


177行目: 192行目:
end
end


-- used for cleaner output when subst:ituting this module
function replaceSpecialChar(chr)
function replaceHTMLSpaces(str)
if chr == '_' then
return mw.ustring.gsub(str, " ", " ")
-- replace underscores with spaces
return ' '
else
return chr
end
end
 
function replaceSpecialChars(str)
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
end


245行目: 287行目:
end
end
end
end
 
return outString
return outString
end
end
366行目: 408行目:
if hookNames[param] then
if hookNames[param] then
return hookNames[param][index]
return hookNames[param][index]
elseif string.len(param) > 2 then
elseif param:len() > 2 then
return hookNames[string.sub(param, 1, 2).."\\d"][index]
return hookNames[param:sub(1, 2).."\\d"][index]
else
else
return nil
return nil
494行目: 536行目:
param = param - 1
param = param - 1
elseif param == 1 then
elseif param == 1 then
if not string.match(chr, '%d') then
if not chr:match('%d') then
endParam()
endParam()
end
end
end
end
cur.str = cur.str .. chr
cur.str = cur.str .. replaceSpecialChar(chr)
end
end
else
else
603行目: 645行目:
end
end


function Config:getValue(snak, raw, link, short, anyLang)
function Config:getValue(snak, raw, link, short, anyLang, noSpecial)
raw = raw or false
raw = raw or false
link = link or false
link = link or false
short = short or false
short = short or false
anyLang = anyLang or false
anyLang = anyLang or false
noSpecial = noSpecial or false
if snak.snaktype == 'value' then
if snak.snaktype == 'value' then
950行目: 993行目:
-- use string.format() to strip decimal point followed by a zero (.0) for whole numbers
-- use string.format() to strip decimal point followed by a zero (.0) for whole numbers
latSeconds = tonumber(string.format(strFormat, math.floor(latitude * 3600 * 10^numDigits + 0.5) / 10^numDigits))
latSeconds = tonumber(strFormat:format(math.floor(latitude * 3600 * 10^numDigits + 0.5) / 10^numDigits))
lonSeconds = tonumber(string.format(strFormat, math.floor(longitude * 3600 * 10^numDigits + 0.5) / 10^numDigits))
lonSeconds = tonumber(strFormat:format(math.floor(longitude * 3600 * 10^numDigits + 0.5) / 10^numDigits))
latMinutes = math.floor(latSeconds / 60)
latMinutes = math.floor(latSeconds / 60)
974行目: 1,017行目:
if precision < (1 / 60) then
if precision < (1 / 60) then
latSeconds = string.format(strFormat, latSeconds)
latSeconds = strFormat:format(latSeconds)
lonSeconds = string.format(strFormat, lonSeconds)
lonSeconds = strFormat:format(lonSeconds)
if not raw then
if not raw then
1,050行目: 1,093行目:
return '<strong class="error">' .. unknownDataTypeError(snak.datavalue.type) .. '</strong>'
return '<strong class="error">' .. unknownDataTypeError(snak.datavalue.type) .. '</strong>'
end
end
elseif snak.snaktype == 'somevalue' then
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
if raw then
return " "  -- single space represents 'somevalue'
return " "  -- single space represents 'somevalue'
1,056行目: 1,099行目:
return i18n['values']['unknown']
return i18n['values']['unknown']
end
end
elseif snak.snaktype == 'novalue' then
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
if raw then
return ""  -- empty string represents 'novalue'
return ""  -- empty string represents 'novalue'
1,172行目: 1,215行目:
elseif flag == "mdy" then
elseif flag == "mdy" then
self.mdyDate = true
self.mdyDate = true
return true
elseif flag == "single" then
self.singleClaim = true
return true
elseif flag == "sourced" then
self.sourcedOnly = true
return true
return true
elseif flag == "best" or flag:match('^preferred[+-]?$') or flag:match('^normal[+-]?$') or flag:match('^deprecated[+-]?$') then
elseif flag == "best" or flag:match('^preferred[+-]?$') or flag:match('^normal[+-]?$') or flag:match('^deprecated[+-]?$') then
1,212行目: 1,261行目:
end
end
-- create a new State for each command
-- create a new state for each command
self.states[param] = State.new(self)
self.states[param] = State.new(self)
1,221行目: 1,270行目:
self.states[param].separator = self.separators["sep"..param]  -- will be nil for param=="%p", which will be set separately
self.states[param].separator = self.separators["sep"..param]  -- will be nil for param=="%p", which will be set separately
if string.sub(flag, -1) ~= 's' then
if flag:sub(-1) ~= 's' then
self.states[param].singleValue = true
self.states[param].singleValue = true
end
end
1,230行目: 1,279行目:
end
end


function Config:rankMatches(rankPos)
function Config:qualifierMatches(claim, ID, value)
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[ID] end
if qualifiers then
for i, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
return true
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
 
function Config:rankMatches(rankPos)
if self.bestRank then
if self.bestRank then
return (self.ranks[rankPos] and self.foundRank >= rankPos)
return (self.ranks[rankPos] and self.foundRank >= rankPos)
1,301行目: 1,368行目:
return false
return false
end
-- determines if a claim has references by prefetching them from the claim using getReferences,
-- which applies some filtering that determines if a reference is actually returned,
-- and caches the references for later use
function State:isSourced(claim)
self.conf.prefetchedRefs = self:getReferences(claim)
return (#self.conf.prefetchedRefs > 0)
end
function State:resetCaches()
-- any prefetched references of the previous claim must not be used
self.conf.prefetchedRefs = nil
end
end


function State:claimMatches(claim)
function State:claimMatches(claim)
local matches, rankPos
local matches, rankPos
-- first of all, reset any cached values used for the previous claim
self:resetCaches()
-- if a property value was given, check if it matches the claim's property value
-- if a property value was given, check if it matches the claim's property value
1,311行目: 1,394行目:
else
else
matches = true
matches = true
end
-- if any qualifier values were given, check if each matches one of the claim's qualifier values
for i, v in pairs(self.conf.qualifierIDsAndValues) do
matches = (matches and self.conf:qualifierMatches(claim, i, v))
end
end
1,316行目: 1,404行目:
rankPos = convertRank(claim.rank)
rankPos = convertRank(claim.rank)
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
-- if only claims with references must be returned, check if this one has any
if self.conf.sourcedOnly then
matches = (matches and self:isSourced(claim))  -- prefetches and caches references
end
return matches, rankPos
return matches, rankPos
1,401行目: 1,494行目:
-- iterate through claim's qualifier statements to collect their values;
-- iterate through claim's qualifier statements to collect their values;
-- return array with multiple value objects
-- return array with multiple value objects
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1})  -- pass qualifier State with level 2 hook
return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1})  -- pass qualifier state with level 2 hook
else
else
return {}  -- return empty array
return {}  -- return empty array
1,448行目: 1,541行目:
-- level 1 hook
-- level 1 hook
function State:getReferences(claim)
function State:getReferences(claim)
if self.conf.prefetchedRefs then
-- return references that have been prefetched by isSourced
return self.conf.prefetchedRefs
end
if claim.references then
if claim.references then
-- iterate through claim's reference statements to collect their values;
-- iterate through claim's reference statements to collect their values;
-- return array with multiple value objects
-- return array with multiple value objects
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1})  -- pass reference State with level 2 hook
return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1})  -- pass reference state with level 2 hook
else
else
return {}  -- return empty array
return {}  -- return empty array
1,460行目: 1,558行目:
-- logic determined based on https://www.wikidata.org/wiki/Help:Sources
-- logic determined based on https://www.wikidata.org/wiki/Help:Sources
function State:getReference(statement)
function State:getReference(statement)
local snakValue, lang, property, url, title
local language, referenceURL, title, statedIn, statedInRaw
local authors = {}
local params = {}
local citeParams = {}
local value = ""
local value = ""
local ref = {}
local ref = {}
local snaks = {}
local params = {}
-- number of parameters that do not go along with "stated in"-sources other than web pages as per https://www.wikidata.org/wiki/Help:Sources;
local leadParams = {}
-- these are parameters of properties other than "pages(s)" and "chapter" (for books) and "title" and "publication date" (for databases) and also "stated in"
local hasExtraParams = false
if statement.snaks then
if statement.snaks then
for i, v in pairs(statement.snaks) do
-- don't include "imported from", which has been added by a bot
if v[1] then
if statement.snaks[aliasesP.importedFrom] then
snaks[i] = v[1]
statement.snaks[aliasesP.importedFrom] = nil
end
end
end
-- don't include "imported from" that has been added by a bot
-- not linked yet because we need the plain value for comparison first
if snaks[aliasesP.importedFrom] then
language = self:getReferenceDetail(statement.snaks, aliasesP.language, false, false, false, false, true)  -- (noUnset = true)
snaks[aliasesP.importedFrom] = nil
if language then
end
-- not part of a "stated in"-source
hasExtraParams = true
-- use the general template for citing web references if both URL and title are present
if snaks[aliasesP.referenceURL] and snaks[aliasesP.title] and i18n['cite']['cite-web'] and i18n['cite']['cite-web'] ~= "" then
params[i18n['cite']['url']]  = self.conf:getValue(snaks[aliasesP.referenceURL])
params[i18n['cite']['title']] = self.conf:getValue(snaks[aliasesP.title], false, false, false, true)  -- anyLang = true
if snaks[aliasesP.publicationDate] then params[i18n['cite']['date']]        = self.conf:getValue(snaks[aliasesP.publicationDate])                  end
-- only add language to the reference if it differs from the local one
if snaks[aliasesP.retrieved]      then params[i18n['cite']['access-date']]  = self.conf:getValue(snaks[aliasesP.retrieved])                        end
if self.conf.langName ~= language then
if snaks[aliasesP.archiveURL]      then params[i18n['cite']['archive-url']]  = self.conf:getValue(snaks[aliasesP.archiveURL])                      end
if self.linked then
if snaks[aliasesP.archiveDate]    then params[i18n['cite']['archive-date']] = self.conf:getValue(snaks[aliasesP.archiveDate])                      end
-- retrieve language again, but this time with link
if snaks[aliasesP.author]          then params[i18n['cite']['author']]      = self.conf:getValue(snaks[aliasesP.author], false, self.linked)      end  -- link = true/false
params[aliasesP.language] = self:getReferenceDetail(statement.snaks, aliasesP.language, false, true)  -- link = true
if snaks[aliasesP.publisher]      then params[i18n['cite']['publisher']]    = self.conf:getValue(snaks[aliasesP.publisher], false, self.linked)    end  -- link = true/false
else
if snaks[aliasesP.quote]          then params[i18n['cite']['quote']]        = self.conf:getValue(snaks[aliasesP.quote], false, false, false, true) end  -- anyLang = true
params[aliasesP.language] = language
if snaks[aliasesP.language] then
snakValue = self.conf:getValue(snaks[aliasesP.language], false, self.linked)  -- link = true/false
if self.conf.langName ~= snakValue then
params[i18n['cite']['language']] = snakValue
end
end
end
-- we have to manually unset, since the first call to getReferenceDetail was with noUnset and the second call might not have happened
statement.snaks[aliasesP.language] = nil
end
authors = self:getReferenceDetails(statement.snaks, aliasesP.author, false, self.linked)  -- link = true/false
if #authors > 0 then
-- not part of a "stated in"-source
hasExtraParams = true
end
referenceURL = self:getReferenceDetail(statement.snaks, aliasesP.referenceURL)
if referenceURL then
-- not part of a "stated in"-source
hasExtraParams = true
end
-- the next two may be part of a "stated in"-source, so retrieve them already so that they won't count as hasExtraParams
title    = self:getReferenceDetail(statement.snaks, aliasesP.title, false, false, false, true)  -- anyLang = true
statedIn = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, false, true, false, false, true)  -- link = true, (noUnset = true)
-- (1) if both "reference URL" and "title" are present, then use the general template for citing web references
if referenceURL and title and i18n['cite']['cite-web'] and i18n['cite']['cite-web'] ~= "" then
citeParams[i18n['cite']['url']]    = referenceURL
citeParams[i18n['cite']['title']]  = title
citeParams[i18n['cite']['website']] = statedIn
citeParams[i18n['cite']['language']] = params[aliasesP.language]
citeParams[i18n['cite']['date']]        = self:getReferenceDetail(statement.snaks, aliasesP.publicationDate)
citeParams[i18n['cite']['access-date']]  = self:getReferenceDetail(statement.snaks, aliasesP.retrieved)
citeParams[i18n['cite']['archive-url']]  = self:getReferenceDetail(statement.snaks, aliasesP.archiveURL)
citeParams[i18n['cite']['archive-date']] = self:getReferenceDetail(statement.snaks, aliasesP.archiveDate)
citeParams[i18n['cite']['publisher']]    = self:getReferenceDetail(statement.snaks, aliasesP.publisher, false, self.linked)  -- link = true/false
citeParams[i18n['cite']['quote']]        = self:getReferenceDetail(statement.snaks, aliasesP.quote, false, false, false, true)  -- anyLang = true
for i, v in ipairs(authors) do
citeParams[i18n['cite']['author']..i] = v
end
end
-- if this module is being substituted then build a regular template call, otherwise expand the template
-- if this module is being substituted then build a regular template call, otherwise expand the template
if mw.isSubsting() then
if mw.isSubsting() then
for i, v in pairs(params) do
for i, v in pairs(citeParams) do
value = value .. "|" .. i .. "=" .. v
value = value .. "|" .. i .. "=" .. v
end
end
1,508行目: 1,638行目:
value = "{{" .. i18n['cite']['cite-web'] .. value .. "}}"
value = "{{" .. i18n['cite']['cite-web'] .. value .. "}}"
else
else
value = mw.getCurrentFrame():expandTemplate{title=i18n['cite']['cite-web'], args=params}
value = mw.getCurrentFrame():expandTemplate{title=i18n['cite']['cite-web'], args=citeParams}
end
end
else
else
-- if no general template for citing web references was defined but URL and title are present, add these together
-- we need the raw Q-identifier for the next template
if snaks[aliasesP.referenceURL] and snaks[aliasesP.title] then
statedInRaw = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true)  -- raw = true
url  = self.conf:getValue(snaks[aliasesP.referenceURL])
title = self.conf:getValue(snaks[aliasesP.title], false, false, false, true)  -- anyLang = true
-- the next three may be part of a "stated in"-source, so retrieve them already so that they won't count as hasExtraParams
params[aliasesP.pages]           = self:getReferenceDetail(statement.snaks, aliasesP.pages)
leadParams[#leadParams + 1] = "[" .. url .. " " .. title .. "]"
params[aliasesP.chapter]         = self:getReferenceDetail(statement.snaks, aliasesP.chapter)
params[aliasesP.publicationDate] = self:getReferenceDetail(statement.snaks, aliasesP.publicationDate)
-- set to nil so that they won't be added a second time
snaks[aliasesP.referenceURL] = nil
-- retrieve the rest of the parameters and make them count as hasExtraParams
snaks[aliasesP.title] = nil
for i in pairs(statement.snaks) do
params[i] = self:getReferenceDetail(statement.snaks, i, false, self.linked, false, true)  -- link = true/false, anyLang = true
hasExtraParams = true
end
end
for i, v in pairs(snaks) do
-- (2) if "stated in" is present without any parameters not belonging to a "stated in"-source, then use this template which expands the stated-in item
property = getLabel(i)
if statedInRaw and not hasExtraParams and i18n['cite']['cite-q'] and i18n['cite']['cite-q'] ~= "" then
citeParams[i18n['cite']['pages']]  = params[aliasesP.pages]
citeParams[i18n['cite']['chapter']] = params[aliasesP.chapter]
citeParams[i18n['cite']['date']]    = params[aliasesP.publicationDate]
if property ~= "" then
if mw.isSubsting() then
snakValue, lang = self.conf:getValue(v, false, self.linked or (i == aliasesP.statedIn) or (i == aliasesP.referenceURL), false, true)  -- link = true/false, anyLang = true
for i, v in pairs(citeParams) do
value = value .. "|" .. i .. "=" .. v
if lang and lang ~= self.conf.langCode then
snakValue = "''" .. snakValue .. "'' (" .. mw.language.fetchLanguageName(lang, self.conf.langCode) .. ")"
end
end
value = "{{" .. i18n['cite']['cite-q'] .. "|" .. statedInRaw .. value .. "}}"
else
citeParams[1] = statedInRaw
value = mw.getCurrentFrame():expandTemplate{title=i18n['cite']['cite-q'], args=citeParams}
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in" or "reference URL" is present
elseif statedIn or referenceURL then
-- authors were already retrieved; start by adding them up front
if #authors > 0 then
citeParams[#citeParams + 1] = table.concat(authors, " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if referenceURL and title then
citeParams[#citeParams + 1] = "[" .. referenceURL .. " " .. title .. "]"
elseif referenceURL then
citeParams[#citeParams + 1] = referenceURL
elseif title then
citeParams[#citeParams + 1] = title
end
if statedIn then
citeParams[#citeParams + 1] = statedIn
end
for i, v in pairs(params) do
i = getLabel(i)
if i == aliasesP.referenceURL or i == aliasesP.statedIn then
if i ~= "" then
leadParams[#leadParams + 1] = snakValue
citeParams[#citeParams + 1] = i .. ": " .. v
elseif i ~= aliasesP.language or self.conf.langName ~= snakValue then
params[#params + 1] = property .. ": " .. snakValue
end
end
end
end
end
value = table.concat(citeParams, "; ")
value = table.concat(leadParams, "; ")
params = table.concat(params, "; ")
if params ~= "" then
if value ~= "" then
if value ~= "" then
value = value .. "; "
value = value .. "."
end
end
value = value .. params
end
if value ~= "" then
value = value .. "."
end
end
end
end
if value ~= "" then
if value ~= "" then
ref = {value}  -- create one value object
value = {value}  -- create one value object
if not self.rawValue then
if not self.rawValue then
-- this should become a <ref> tag, so safe the reference's hash for later
-- this should become a <ref> tag, so safe the reference's hash for later
ref.refHash = statement.hash
value.refHash = statement.hash
end
end
ref = {ref}  -- wrap the value object in an array
ref = {value}  -- wrap the value object in an array
end
end
end
end
1,571行目: 1,722行目:
end
end


function State:callHook(param, hooks, statement, result)
-- gets a detail of one particular type for a reference
local valuesArray, refHash
function State:getReferenceDetail(snaks, dType, raw, link, short, anyLang, noUnset)
raw = raw or false
link = link or false
short = short or false
anyLang = anyLang or false
noUnset = noUnset or false
local switchLang = anyLang
local value = nil
-- call a parameter's hook if it has been defined and if it has not been called before
if not snaks[dType] then
if not result[param] and hooks[param] then
return nil
valuesArray = self[hooks[param]](self, statement, param, result, hooks)  -- array with value objects
-- add to the result
if #valuesArray > 0 then
result[param] = valuesArray
result.count = result.count + 1
else
result[param] = {}  -- an empty array to indicate that we've tried this hook already
return true  -- miss == true
end
end
end
return false
-- if anyLang, first try the local language and otherwise any language
repeat
for i, v in ipairs(snaks[dType]) do
value = self.conf:getValue(v, raw, link, short, anyLang and not switchLang, true)  -- noSpecial = true
if value then
break
end
end
if value or not anyLang then
break
end
switchLang = not switchLang
until anyLang and switchLang
if not noUnset then
-- remove detail(s) to make it possible to get the rest of the details in one loop
snaks[dType] = nil
end
return value
end
end


-- iterate through claims, claim's qualifiers or claim's references to collect values
-- gets the details of one particular type for a reference
function State:iterate(statements, hooks, matchHook)
function State:getReferenceDetails(snaks, dType, raw, link, short, anyLang, noUnset)
matchHook = matchHook or alwaysTrue
raw = raw or false
link = link or false
short = short or false
anyLang = anyLang or false
noUnset = noUnset or false
local values = {}
if not snaks[dType] then
return {}
end
for i, v in ipairs(snaks[dType]) do
-- if nil is returned then it will not be added to the table
values[#values + 1] = self.conf:getValue(v, raw, link, short, anyLang, true)  -- noSpecial = true
end
local matches = false
if not noUnset then
local rankPos = nil
-- remove detail(s) to make it possible to get the rest of the details in one loop
local result, gotRequired
snaks[dType] = nil
end
for i, v in ipairs(statements) do
return values
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
end
matches, rankPos = matchHook(self, v)
 
function State:callHook(param, hooks, statement, result)
if matches then
local valuesArray, refHash
result = {count = 0}  -- collection of arrays with value objects
-- call a parameter's hook if it has been defined and if it has not been called before
local function walk(formatTable)
if not result[param] and hooks[param] then
local miss
valuesArray = self[hooks[param]](self, statement, param, result, hooks)  -- array with value objects
for i2, v2 in pairs(formatTable.req) do
-- add to the result
-- call a hook, adding its return value to the result
if #valuesArray > 0 then
miss = self:callHook(i2, hooks, v, result)
result[param] = valuesArray
result.count = result.count + 1
if miss then
else
-- we miss a required value for this level, so return false
result[param] = {}  -- an empty array to indicate that we've tried this hook already
return false
return true  -- miss == true
end
end
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
return false
-- returning at this point breaks the loop
end
return true
 
end
-- iterate through claims, claim's qualifiers or claim's references to collect values
end
function State:iterate(statements, hooks, matchHook)
matchHook = matchHook or alwaysTrue
local matches = false
local rankPos = nil
local result, gotRequired
for i, v in ipairs(statements) do
-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
matches, rankPos = matchHook(self, v)
if matches then
result = {count = 0}  -- collection of arrays with value objects
local function walk(formatTable)
local miss
for i2, v2 in ipairs(formatTable) do
for i2, v2 in pairs(formatTable.req) do
if result.count == hooks.count then
-- call a hook, adding its return value to the result
miss = self:callHook(i2, hooks, v, result)
if miss then
-- we miss a required value for this level, so return false
return false
end
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- returning at this point breaks the loop
return true
end
end
for i2, v2 in ipairs(formatTable) do
if result.count == hooks.count then
-- we're done if all hooks have been called;
-- we're done if all hooks have been called;
-- returning at this point prevents further childs from being processed
-- returning at this point prevents further childs from being processed
1,750行目: 1,968行目:
nextIndex = nextIndex + 1
nextIndex = nextIndex + 1
elseif nextArg:sub(1,9):lower() == "property:" then
elseif nextArg:sub(1,9):lower() == "property:" then
nextArg = mw.text.trim(nextArg:sub(10))
nextArg = replaceAlias(mw.text.trim(nextArg:sub(10)))
if aliasesP[nextArg] then
nextArg = aliasesP[nextArg]
end
_.entity = mw.wikibase.getEntity(nextArg)  -- entity ID of a property given
_.entity = mw.wikibase.getEntity(nextArg)  -- entity ID of a property given
_.propertyID = mw.text.trim(args[nextIndex] or "")  -- property ID
_.propertyID = mw.text.trim(args[nextIndex] or "")  -- property ID
1,765行目: 1,978行目:
-- check if given property ID is an alias
-- check if given property ID is an alias
if aliasesP[_.propertyID] then
_.propertyID = replaceAlias(_.propertyID):upper()
_.propertyID = aliasesP[_.propertyID]
end
_.propertyID = _.propertyID:upper()
if _.states.qualifiersCount > 0 then
if _.states.qualifiersCount > 0 then
1,777行目: 1,986行目:
-- claim ID or literal value has been given
-- claim ID or literal value has been given
nextArg = args[nextIndex] -- don't trim because might be single space representing 'somevalue'
_.propertyValue = mw.text.trim(args[nextIndex])
nextIndex = nextIndex + 1
nextIndex = nextIndex + 1
_.propertyValue = nextArg
end
end
1,787行目: 1,994行目:
nextIndex = nextIndex + 1
nextIndex = nextIndex + 1
-- check if given qualifier ID is an alias
-- check if given qualifier ID is an alias and add it
if aliasesP[nextArg] then
_.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg):upper()
nextArg = aliasesP[nextArg]
end
_.qualifierIDs[parameters.qualifier..i] = nextArg:upper()
end
end
elseif _.states[parameters.reference] then
elseif _.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
-- do further processing if "reference(s)" command was given
nextArg = args[nextIndex]
nextArg = args[nextIndex] -- claim ID or literal value (possibly nil)
nextIndex = nextIndex + 1
nextIndex = nextIndex + 1
_.propertyValue = nextArg -- claim ID or literal value (possibly nil)
if nextArg then
_.propertyValue = mw.text.trim(nextArg)
end
end
end
-- check for special property value 'somevalue' or 'novalue'
-- check for special property value 'somevalue' or 'novalue'
if _.propertyValue then
if _.propertyValue then
_.propertyValue = replaceSpecialChars(_.propertyValue)
if _.propertyValue ~= "" and mw.text.trim(_.propertyValue) == "" then
if _.propertyValue ~= "" and mw.text.trim(_.propertyValue) == "" then
_.propertyValue = " "  -- single space represents 'somevalue', whereas empty string represents 'novalue'
_.propertyValue = " "  -- single space represents 'somevalue', whereas empty string represents 'novalue'
1,814行目: 2,021行目:
-- parse the desired format, or choose an appropriate format
-- parse the desired format, or choose an appropriate format
if args["format"] then
if args["format"] then
parsedFormat, formatParams = parseFormat(replaceHTMLSpaces(mw.text.trim(args["format"])))
parsedFormat, formatParams = parseFormat(args["format"])
elseif _.states.qualifiersCount > 0 then  -- "qualifier(s)" command given
elseif _.states.qualifiersCount > 0 then  -- "qualifier(s)" command given
if _.states[parameters.property] then  -- "propert(y|ies)" command given
if _.states[parameters.property] then  -- "propert(y|ies)" command given
1,833行目: 2,040行目:
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
-- if only "reference(s)" has been given, set the default separator to none (except when raw)
if _.states[parameters.reference] and not _.states[parameters.property] and _.states.qualifiersCount == 0
if _.states[parameters.reference] and not _.states[parameters.property] and _.states.qualifiersCount == 0
  and not _.states[parameters.reference].rawValue then
  and not _.states[parameters.reference].rawValue then
_.separators["sep"][1] = nil
_.separators["sep"][1] = nil
end
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent;
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent;
-- must come BEFORE overriding the separator values
-- must come BEFORE overriding the separator values
if _.states.qualifiersCount == 1 then
if _.states.qualifiersCount == 1 then
_.separators["sep"..parameters.qualifier] = _.separators["sep"..parameters.qualifier.."1"]
_.separators["sep"..parameters.qualifier] = _.separators["sep"..parameters.qualifier.."1"]
end
end
-- process overridden separator values;
-- process overridden separator values;
-- must come AFTER parsing the formats
-- must come AFTER parsing the formats
for i, v in pairs(_.separators) do
for i, v in pairs(_.separators) do
if args[i] then
if args[i] then
sep = replaceHTMLSpaces(mw.text.trim(args[i]))
sep = replaceSpecialChars(args[i])
if sep ~= "" then
_.separators[i][1] = {sep}
else
_.separators[i][1] = nil
end
end
end
-- make sure that at least one required parameter has been defined
if not next(parsedFormat.req) then
error(missingRequiredParameterError())
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if parsedFormat.req[parameters.separator] then
error(extraRequiredParameterError(parameters.separator))
end
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(_.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[i:sub(1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and _.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not _.states[parameters.property] then
_.states[parameters.property] = State.new(_)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if _.singleClaim then
_.states[parameters.property].singleValue = true
end
end
-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if _.sourcedOnly and not _.states[parameters.reference] then
_:processFlagOrCommand("reference")  -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark)
_.states[parameters.property].parsedFormat = parsedFormat
_.states[parameters.property].separator = _.separators["sep"]
_.states[parameters.property].movSeparator = _.separators["sep"..parameters.separator]
_.states[parameters.property].puncMark = _.separators["punc"]
-- process qualifier matching values, analogous to _.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or aliasesP[i] then
v = replaceSpecialChars(v)
-- check for special qualifier value 'somevalue'
if v ~= "" and mw.text.trim(v) == "" then
v = " "  -- single space represents 'somevalue'
end
if sep ~= "" then
_.qualifierIDsAndValues[replaceAlias(i):upper()] = v
_.separators[i][1] = {sep}
else
_.separators[i][1] = nil
end
end
end
-- make sure that at least one required parameter has been defined
if not next(parsedFormat.req) then
error(missingRequiredParameterError())
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if parsedFormat.req[parameters.separator] then
error(extraRequiredParameterError(parameters.separator))
end
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given
for i, v in pairs(_.states) do
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q"
if formatParams[i] or formatParams[string.sub(i, 1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
end
end
end
end
-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...);
-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given
if formatParams[parameters.qualifier] and _.states.qualifiersCount > 0 then
hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
hooks.count = hooks.count + 1
end
-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
-- must come AFTER defining the hooks
if not _.states[parameters.property] then
_.states[parameters.property] = State.new(_)
end
-- set the parsed format and the separators (and optional punctuation mark)
_.states[parameters.property].parsedFormat = parsedFormat
_.states[parameters.property].separator = _.separators["sep"]
_.states[parameters.property].movSeparator = _.separators["sep"..parameters.separator]
_.states[parameters.property].puncMark = _.separators["punc"]
if _.entity and _.entity.claims then claims = _.entity.claims[_.propertyID] end
if _.entity and _.entity.claims then claims = _.entity.claims[_.propertyID] end
1,903行目: 2,138行目:
-- then iterate through the claims to collect values
-- then iterate through the claims to collect values
return concatValues(_.states[parameters.property]:iterate(claims, hooks, State.claimMatches))  -- pass property State with level 1 hooks and matchHook
return concatValues(_.states[parameters.property]:iterate(claims, hooks, State.claimMatches))  -- pass property state with level 1 hooks and matchHook
else
else
return ""
return ""
1,945行目: 2,180行目:
if ID then
if ID then
if aliasesP[ID] then
ID = replaceAlias(ID):upper()
ID = aliasesP[ID]
end
ID = ID:upper()
-- check if this is a valid ID, and if the number is not larger than max int (to prevent error)
-- check if this is a valid ID, and if the number is not larger than max int (to prevent error)
if not string.match(ID, '^[QP]%d+$') or tonumber(string.match(ID, '%d+')) > 2147483647 then
if not ID:match('^[QP]%d+$') or tonumber(ID:match('%d+')) > 2147483647 then
return ""
return ""
end
end
2,005行目: 2,236行目:
if not ID then
if not ID then
label = mw.wikibase.getEntityIdForCurrentPage()
label = mw.wikibase.getEntityIdForCurrentPage()
elseif mw.wikibase.getEntity(ID) or mw.wikibase.resolvePropertyId(ID) then
elseif mw.wikibase.getEntity(ID) then
label = ID
label = ID
匿名利用者