Módulo:ISBN: mudanças entre as edições

De Wikincat
Ir para navegação Ir para pesquisar
Sem resumo de edição
 
Sem resumo de edição
 
(Uma revisão intermediária pelo mesmo usuário não está sendo mostrada)
Linha 16: Linha 16:
-- _publisher_code, sting, varying lengths
-- _publisher_code, sting, varying lengths
-- _article_code - string, varying lengths
-- _article_code - string, varying lengths

-- From: https://geoffrichards.co.uk/lua/isbn/
local M = { _NAME = "isbn" }
local M = { _NAME = "isbn" }

Edição atual tal como às 10h10min de 13 de outubro de 2022

A documentação para este módulo pode ser criada em Módulo:ISBN/doc

-- This Lua code was originally based on the Perl modules ISBN::Business and
-- ISBN::Business::Data by brian d foy <bdfoy@cpan.org>.  The Lua version is
-- a complete rewrite with a simpler structure and much less code.
--
-- This software and documentation is Copyright (c) 2007 Geoff Richards
-- <geoff@geoffrichards.co.uk>.  It is free software; you can redistribute it
-- and/or modify it under the terms of the S<Lua 5.0> license.  The full terms
-- are given in the file I<COPYRIGHT> supplied with the source code package,
-- and are also available here: http://www.lua.org/license.html
-- More in: http://www.geoffrichards.co.uk/lua/isbn/
--
-- Objects of this class have the following private fields in (all of
-- which are required):
--    _isbn - string, 10 or 13 chars
--    _group_code, string, varying lengths
--    _publisher_code, sting, varying lengths
--    _article_code - string, varying lengths

-- From: https://geoffrichards.co.uk/lua/isbn/
 
local M = { _NAME = "isbn" }
M.__index = M
M.VERSION = "1.2"
 
local isbndata = require( 'Module:Isbn._data' )
 
local _max_publisher_code_length, _prefix_length,
      _calc_checksum, _parse_isbn,
      _parse_group_code, _parse_publisher_code, _extract_article_code
 
function _max_publisher_code_length (self)
    local isbn = self._isbn
    return isbn:len()
         - _prefix_length(isbn)         -- prefix
         - self._group_code:len()       -- group
         - 1                            -- article
         - 1                            -- checksum
end
 
local function _new (class, input_data, correction_mode)
    if not correction_mode then correction_mode = "normal" end
 
    input_data = input_data:upper()             -- we want uppercase X's
    if correction_mode == "strict" then
        input_data = input_data:gsub("[-%s]", "")
        if input_data:find("[^0-9X]") then
            return nil, "character not allowed"
        end
    else
        input_data = input_data:gsub("[^0-9X]", "")
    end
 
    local len = input_data:len()
    if correction_mode == "fix-checksum" and (len == 9 or len == 12) then
        -- allow checksum to be missing if we're going to correct it anyway.
        input_data = input_data .. "0"
    elseif len ~= 10 and len ~= 13 then
        return nil, "wrong number of digits"
    end
    if not input_data:find("^%d+[%dX]$") then
        return nil, "digit 'X' not allowed in middle"
    end
 
    local o = { _isbn = input_data }
    setmetatable(o, class)
 
    local err = _parse_isbn(o, correction_mode)
    if err then return nil, err end
 
    return o
end
 
-- Allow access to old 'new' class function for backwards compatibility.
M.new = _new
 
function M:isbn () return self._isbn end
function M:prefix ()
    local data = self._isbn
    return data:len() == 13 and data:sub(1, 3) or ""
end
function _prefix_length (isbn) return isbn:len() == 13 and 3 or 0 end
 
function M:group_code () return self._group_code end
function M:group_name ()
    return isbndata.country_name[self:group_code()]
end
 
function M:publisher_code () return self._publisher_code end
function M:article_code () return self._article_code end
function M:checksum () return self._isbn:sub(-1) end
 
function M:as_isbn10 ()
    local data = self._isbn
    if data:len() == 10 then return self end
    if self:prefix() ~= "978" then return nil end
    return _new(M, data:sub(4), "fix-checksum")
end
 
function M:as_isbn13 ()
    local data = self._isbn
    if data:len() == 13 then return self end
    return _new(M, "978" .. data, "fix-checksum")
end
 
function _calc_checksum (data)
    local sum = 0
 
    if data:len() == 10 then
        for i = 1, 9 do
            sum = sum + (10 - i + 1) * data:sub(i, i)
        end
 
        local checksum = (11 - (sum % 11)) % 11
        return checksum == 10 and "X" or tostring(checksum)
    else
        for i = 0, 10, 2 do
            sum = sum + data:sub(i + 1, i + 1)
                      + data:sub(i + 2, i + 2) * 3
        end
 
        -- take the next higher multiple of 10 and subtract the sum.
        -- if $sum is 37, the next highest multiple of ten is 40. the
        -- check digit would be 40 - 37 => 3.
        local tenth = sum / 10
        return tostring((10 * (tenth - tenth % 1 + 1) - sum) % 10)
    end
end
 
function M:__tostring ()
    local isbn = self._isbn
    local prefix = isbn:len() == 13 and isbn:sub(1, 3) .. "-" or ""
    return prefix .. self._group_code .. "-" .. self._publisher_code .. "-" ..
           self._article_code .. "-" .. isbn:sub(-1)
end
 
function M:eq (other)
    if type(other) == "string" then
        local tmp = _new(M, other)
        if not tmp then error("invalid ISBN value '" .. other .. "'", 2) end
        other = tmp
    end
 
    local selfstr, otherstr = self._isbn, other._isbn
    if selfstr:len() ~= otherstr:len() then
        if selfstr:len() == 10 then
            selfstr = self:as_isbn13()._isbn
        else
            otherstr = other:as_isbn13()._isbn
        end
    end
 
    return selfstr == otherstr
end
M.__eq = M.eq
 
function _parse_isbn (self, correction_mode)
    local isbn = self._isbn
    local result
 
    if isbn:len() == 13 and not isbn:find("^97[89]") then
        return "invalid prefix"
    end
 
    result = _parse_group_code(self)
    if not result then return "invalid group code" end
    self._group_code = result
 
    result = _parse_publisher_code(self)
    if not result then return "invalid publisher code" end
    self._publisher_code = result
 
    _extract_article_code(self)
 
    local expected_checksum = _calc_checksum(isbn)
    if isbn:sub(-1) ~= expected_checksum then
        if correction_mode == "fix-checksum" then
            self._isbn = isbn:sub(1, -2) .. expected_checksum
        else
            return "wrong checksum digit"
        end
    end
end
 
function _parse_group_code (self)
    local isbn = self._isbn
    local start = _prefix_length(isbn) + 1      -- first char of group code
    local count = 1
 
    while true do
        local code = isbn:sub(start, start + count - 1)
        if isbndata.country_ranges[code] then return code end
 
        count = count + 1
        if count > isbndata.max_country_code_length then return end
    end
end
 
function _parse_publisher_code  (self)
    -- get the longest possible publisher code
    -- I'll try substrs of this to get the real one
    local isbn = self._isbn
    local strt = _prefix_length(isbn) + self._group_code:len() + 1
    local longest = isbn:sub(strt, strt + _max_publisher_code_length(self) - 1)
 
    local ranges = isbndata.country_ranges[self:group_code()]
    for i = 1, #ranges, 2 do
        local lower = ranges[i]
        local upper = ranges[i + 1]
        local code  = longest:sub(1, lower:len())
        if code >= lower and code <= upper then return code end
    end
 
    return      -- failed if I got this far
end
 
function _extract_article_code (self)
    local isbn = self._isbn
    local start = _prefix_length(isbn) +
                  self._group_code:len() +
                  self._publisher_code:len()
    local length = isbn:len() - start - 1
    self._article_code = isbn:sub(start + 1, start + length)
end
 
return setmetatable(M, { _NAME = "isbn metatable", __call = _new })
-- vi:ts=4 sw=4 expandtab