Jump to content

Module:Japanese calendar

Permanently protected module
From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Mr. Stradivarius (talk | contribs) at 17:26, 22 August 2013 (add a comment at the top along with a fixme notice). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

-- This module defines an "era" class for processing eras in the Japanese calendar.
-- It also contains functions to export the class properties to #invoke.

-- @todo FIXME: era:getNextEra() and era:getPreviousEra() are generally broken
-- when used with nonexistent data entries.

local eras = mw.loadData( 'Module:Japanese calendar/data' )

--------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------

local function yearToEraIndex( year )
    year = tonumber( year )
    if type( year ) ~= 'number' then return end
    for i, t in ipairs( eras ) do
        if year >= t.year then
            return i
        end
    end
end

local function textToEraIndex( s )
    for i, t in ipairs( eras ) do
        if s == t.article or s == t.kanji then
            return i
        end
    end
end

--------------------------------------------------------------------
-- Era class definition
--------------------------------------------------------------------

local era = {}
era.__index = era

function era:new( init )
    init = type( init ) == 'table' and init or {}
    local obj = {}
    
    -- Grab the data from the init table.
    obj.gregorianYear = tonumber( init.year )
    local eraText = type( init.era ) == 'string' and init.era or nil
    
    -- Calculate the era data from the input. Find the era from the era name or the kanji if possible, as this allows 
    -- us to specify the last year of one era rather than the first year of the next one, if that is the desired behaviour.
    local eraIndex
    if eraText then
        eraIndex = textToEraIndex( eraText )
    elseif obj.gregorianYear then
        eraIndex = yearToEraIndex( obj.gregorianYear )
    end
    
    -- If the data entry was found for the era, process it and add it to the object.
    if eraIndex then
        local eraData = eras[ eraIndex ]
        obj.startYear = eraData.year
        obj.article = eraData.article
        obj.kanji = eraData.kanji
        obj.label = eraData.label
        
        -- Create a link to the era article if possible.
        if obj.label and obj.article then
            obj.link = mw.ustring.format( '[[%s|%s]]', obj.article, obj.label )
        elseif obj.article then
            obj.link = mw.ustring.format( '[[%s]]', obj.article )
        end
        
        -- Allow matching years to different eras, but only for the first year of the next era. For example, Taisho 15 is also Showa 1, but there is no such thing as Taisho 16.
        local nextEraData = eras[ eraIndex - 1 ]
        local nextStartYear = nextEraData and nextEraData.year
        if obj.gregorianYear
            and (
                not nextStartYear
                or ( nextStartYear and obj.gregorianYear <= nextStartYear )
            )
            and obj.gregorianYear >= obj.startYear -- Don't allow negative years.
            and math.floor( obj.gregorianYear ) ~= 687 -- 686 is a special case as the Shuchō era lasted for less than one year. Disallowing 687 is a quick fix for this.
            then
            obj.eraYear = obj.gregorianYear - obj.startYear + 1
            obj.eraYearKanji = tostring( obj.eraYear ) -- Although most of these will be numbers, one is a kanji character, so convert them all to strings.
            if obj.eraYearKanji == '1' then
                obj.eraYearKanji = '元'
            end
        end
    end

    -- Make sure obj.label is available even if it is the same as the article name.
    obj.label = obj.label or obj.article
    
    -- Add methods to get the next and previous eras.
    function obj:getNextEra()
        if not eraIndex then return end       
        local eraData = eras[ eraIndex - 1 ]
        if eraData then
            local initText = eraData.article
            local initYear = eraData.year
            if initText then
                return era:new{ era = initText, year = obj.gregorianYear }
            elseif initYear then
                return era:new{ year = initYear }
            end
        end
    end
    
    function obj:getPreviousEra()
        if not eraIndex then return end            
        local eraData = eras[ eraIndex + 1 ]
        if eraData then
            local initText = eraData.article
            local initYear = eraData.year
            if initText then
                return era:new{ era = initText, year = obj.gregorianYear }
            elseif initYear then
                return era:new{ year = initYear }
            end
        end
    end
    
    return setmetatable( obj, {
        __index = self
    })
end

--------------------------------------------------------------------
-- Interface for old Japanese calendar templates
--------------------------------------------------------------------

local function getStartYear( obj )
    return obj.startYear
end

local function getEraYear( obj )
    return obj.eraYear
end

local function getEraYearKanji( obj )
    return obj.eraYearKanji
end

local function getArticle( obj )
    return obj.article
end

local function getLabel( obj )
    return obj.label
end

local function getLink( obj )
    return obj.link
end

local function getKanji( obj )
    return obj.kanji
end

local function getLabelAndEraYear( obj, kanji )
    local eraYear = kanji and obj.eraYearKanji or obj.eraYear
    if obj.label and eraYear then
        return mw.ustring.format( '%s %s', obj.label, tostring( eraYear ) )
    end
end

local function getLinkAndEraYear( obj, kanji )
    local eraYear = kanji and obj.eraYearKanji or obj.eraYear
    if obj.link and eraYear then
        return mw.ustring.format( '%s %s', obj.link, tostring( eraYear ) )
    end
end

local function getLabelAndEraYearKanji( obj )
    return getLabelAndEraYear( obj, true )
end

local function getLinkAndEraYearKanji( obj )
    return getLinkAndEraYear( obj, true )
end

-- Process the arguments from #invoke.
local function makeWrapper( func )
    return function( frame )
        -- If called via #invoke, use the args passed into the invoking
        -- template, or the args passed to #invoke if any exist. Otherwise
        -- assume args are being passed directly in from the debug console
        -- or from another Lua module.
        local origArgs
        if frame == mw.getCurrentFrame() then
            origArgs = frame:getParent().args
            for k, v in pairs( frame.args ) do
                origArgs = frame.args
                break
            end
        else
            origArgs = frame
        end
        -- Trim whitespace and remove blank arguments.
        local args = {}
        for k, v in pairs( origArgs ) do
            if type( v ) == 'string' then
                v = mw.text.trim( v )
            end
            if v ~= '' then
                args[k] = v
            end
        end
        
        local myEra
        if args.next and args.previous then
            return '<strong class="error">[[Module:Japanese calendar]] error: you cannot specify both the "next" parameter and the "previous" parameter.</strong>'
        elseif args.next then
            myEra = era:new( args ):getNextEra()
        elseif args.previous then
            myEra = era:new( args ):getPreviousEra()
        else
            myEra = era:new( args )
        end
        return myEra and func( myEra ) or ''
    end
end

--------------------------------------------------------------------
-- Return the era class and the template interface
--------------------------------------------------------------------
 
return {
    era = era,
    baseyear = makeWrapper( getStartYear ),
    year = makeWrapper( getEraYear ),
    kanjiyear = makeWrapper( getEraYearKanji ),
    article = makeWrapper( getArticle ),
    label = makeWrapper( getLabel ),
    link = makeWrapper( getLink ),
    kanji = makeWrapper( getKanji ),
    label_year = makeWrapper( getLabelAndEraYear ),
    link_year = makeWrapper( getLinkAndEraYear ),
    label_kanjiyear = makeWrapper( getLabelAndEraYearKanji ),
    link_kanjiyear = makeWrapper( getLinkAndEraYearKanji )
}