Module:User:Theknightwho/families
Jump to navigation
Jump to search
- This module lacks a documentation subpage. You may create it.
- Useful links: root page • root page’s subpages • links • transclusions • testcases • user page • user talk page • userspace
This is a private module sandbox of Theknightwho, for their own experimentation. Items in this module may be added and removed at Theknightwho's discretion; do not rely on this module's stability.
local export = {}
function export.makeObject(code, data, useRequire)
local Family = {}
function Family:getCode()
return self._code
end
function Family:getCanonicalName()
return self._rawData[1]
end
function Family:getDisplayForm()
return self:getCategoryName("nocap")
end
function Family:getOtherNames(onlyOtherNames)
return require("Module:language-like").getOtherNames(self, onlyOtherNames)
end
function Family:getAliases()
return self._rawData.aliases or {}
end
function Family:getVarieties(flatten)
return require("Module:language-like").getVarieties(self, flatten)
end
--function Family:getAllNames()
-- return self._rawData.names
--end
--[==[Given a list of types as strings, returns true if the family has all of them. Possible types are explained in [[Module:families/data]].]==]
function Family:hasType(...)
if not self._type then
self._type = {family = true}
if self._rawData.type then
for _, type in ipairs(mw.text.split(self._rawData.type, "%s*,%s*")) do
self._type[type] = true
end
end
end
for _, type in ipairs{...} do
if not self._type[type] then
return false
end
end
return true
end
--[==[Returns a <code>Family</code> object for the superfamily that the family belongs to.]==]
function Family:getFamily()
if self._familyObject == nil then
local familyCode = self:getFamilyCode()
if familyCode then
self._familyObject = export.getByCode(familyCode, useRequire)
else
self._familyObject = false
end
end
return self._familyObject or nil
end
--[==[Returns the code of the family's superfamily.]==]
function Family:getFamilyCode()
if not self._familyCode then
self._familyCode = self._rawData[3]
end
return self._familyCode
end
--[==[Returns the canonical name of the family's superfamily.]==]
function Family:getFamilyName()
if self._familyName == nil then
local family = self:getFamily()
if family then
self._familyName = family:getCanonicalName()
else
self._familyName = false
end
end
return self._familyName or nil
end
--[==[Check whether the family belongs to `superfamily` (which can be a family code or object). A list of codes/objects can be given in place of `family`; in that case, return true if the family belongs to any of the specified families. A family is '''not''' considered to belong to itself.]==]
function Family:inFamily(...)
for _, superfamily in ipairs{...} do
if type(superfamily) == "table" then
superfamily = superfamily:getCode()
end
local family, code = self:getFamily()
while true do
if not family then
return false
end
code = family:getCode()
family = family:getFamily()
-- If family is parent to itself, return false.
if family and family:getCode() == code then
return false
elseif code == superfamily then
return true
end
end
end
end
function Family:getFull()
if not self._nonEtymologicalObject then
self._nonEtymologicalObject = self
end
return self._nonEtymologicalObject
end
function Family:getFullCode()
return self:getCode()
end
function Family:getFullName()
if self._nonEtymologicalName == nil then
self._nonEtymologicalName = self:getFull():getCanonicalName()
end
return self._nonEtymologicalName or nil
end
function Family:getProtoLanguage()
if self._protoLanguageObject == nil then
self._protoLanguageObject = require("Module:User:Theknightwho/languages").getByCode(self._rawData.protoLanguage or self:getCode() .. "-pro", nil, true, nil, useRequire) or false
end
return self._protoLanguageObject or nil
end
function Family:getProtoLanguageCode()
if self._protoLanguageCode == nil then
local protoLanguage = self:getProtoLanguage()
self._protoLanguageCode = protoLanguage and protoLanguage:getCode() or false
end
return self._protoLanguageCode or nil
end
function Family:getProtoLanguageName()
if not self._protoLanguageName then
self._protoLanguageName = self:getProtoLanguage():getCanonicalName()
end
return self._protoLanguageName
end
function Family:hasAncestor(...)
-- Go up the family tree until a protolanguage is found.
local family = self
local protolang = family:getProtoLanguage()
while not protolang do
family = family:getFamily()
protolang = family:getProtoLanguage()
-- Return false if the family is its own family, to avoid an infinite loop.
if family:getFamilyCode() == family:getCode() then
return false
end
end
-- If the protolanguage is not in the family, it must therefore be ancestral to it. Check if it is a match.
for _, otherlang in ipairs{...} do
if (
type(otherlang) == "string" and protolang:getCode() == otherlang or
type(otherlang) == "table" and protolang:getCode() == otherlang:getCode()
) and not protolang:inFamily(self) then
return true
end
end
-- If not, check the protolanguage's ancestry.
return protolang:hasAncestor(...)
end
local function fetch_descendants(self, format)
local languages = require("Module:languages/code to canonical name")
local etymology_languages = require("Module:etymology languages/code to canonical name")
local families = require("Module:families/code to canonical name")
local descendants = {}
local family = self:getFamily()
-- Iterate over all three datasets.
for _, data in ipairs{languages, etymology_languages, families} do
for code in pairs(data) do
local lang = require("Module:User:Theknightwho/languages").getByCode(code, nil, true, true, useRequire)
if lang:inFamily(self) then
if format == "object" then
table.insert(descendants, lang)
elseif format == "code" then
table.insert(descendants, code)
elseif format == "name" then
table.insert(descendants, lang:getCanonicalName())
end
end
end
end
return descendants
end
function Family:getDescendants()
if not self._descendantObjects then
self._descendantObjects = fetch_descendants(self, "object")
end
return self._descendantObjects
end
function Family:getDescendantCodes()
if not self._descendantCodes then
self._descendantCodes = fetch_descendants(self, "code")
end
return self._descendantCodes
end
function Family:getDescendantNames()
if not self._descendantNames then
self._descendantNames = fetch_descendants(self, "name")
end
return self._descendantNames
end
function Family:hasDescendant(...)
for _, lang in ipairs{...} do
if type(lang) == "string" then
lang = require("Module:User:Theknightwho/languages").getByCode(lang, nil, true, nil, useRequire)
end
if lang:inFamily(self) then
return true
end
end
return false
end
function Family:getCategoryName(nocap)
local name = self._rawData[1]
-- If the name already has "languages" in it, don't add it.
if not name:find("[Ll]anguages$") then
name = name .. " languages"
end
if not nocap then
name = mw.getContentLanguage():ucfirst(name)
end
return name
end
function Family:makeCategoryLink()
return "[[:Category:" .. self:getCategoryName() .. "|" .. self:getDisplayForm() .. "]]"
end
function Family:getWikidataItem()
local item = self._rawData[2] or self._rawData.wikidata_item
if not item then
return nil
end
if type(item) ~= "number" then
error("The method getWikidataItem expects the item to be stored as a number, but it is currently a " .. type(code) .. ".")
end
return "Q" .. item
end
function Family:getWikipediaArticle()
return (self:getWikidataItem() and mw.wikibase and mw.wikibase.sitelink(self:getWikidataItem(), 'enwiki')) or
self:getCategoryName()
end
function Family:makeWikipediaLink()
return "[[w:" .. self:getWikipediaArticle() .. "|" .. self:getCanonicalName() .. "]]"
end
function Family:toJSON()
if not self._type then
self:hasType()
end
local types = {}
for type in pairs(self._type) do
table.insert(types, type)
end
local ret = {
canonicalName = self:getCanonicalName(),
categoryName = self:getCategoryName("nocap"),
code = self:getCode(),
family = self._rawData[3],
protoLanguage = self._rawData.protoLanguage,
otherNames = self:getOtherNames(true),
aliases = self:getAliases(),
varieties = self:getVarieties(),
type = types,
wikidataItem = self:getWikidataItem(),
}
return require("Module:JSON").toJSON(ret)
end
function Family:getRawData()
return self._rawData
end
Family.__index = Family
return data and setmetatable({ _rawData = data, _code = code }, Family) or nil
end
function export.getByCode(code, useRequire)
local function conditionalRequire(modulename)
if useRequire then
return require(modulename)
else
return mw.loadData(modulename)
end
end
local data = conditionalRequire("Module:User:Theknightwho/families/data")[code]
if data then
return export.makeObject(code, data, useRequire)
end
data = conditionalRequire("Module:User:Theknightwho/families/data/etymology")[code]
if data then
return require("Module:User:Theknightwho/languages").makeObject(code, data, true, true, useRequire)
end
return nil
end
function export.getByCanonicalName(name, useRequire)
local function conditionalRequire(modulename)
if useRequire then
return require(modulename)
else
return mw.loadData(modulename)
end
end
local byName = conditionalRequire("Module:families/canonical names")
local code = byName and byName[name] or
byName[name:match("^(.*) languages$")]
return export.getByCode(code, useRequire)
end
return export