Módulo:Format ISBN

Fonte: Enciclopédia de conhecimento da Igreja de Deus
Saltar para a navegação Saltar para a pesquisa

A documentação para este módulo pode ser criada na página Módulo:Format ISBN/doc

require ('strict');
local data = mw.loadData ('Módulo:Format ISBN/data');       -- buscar dados de posicionamento do separador
	local hyphen_pos_t = data.hyphen_pos_t;          -- a tabela k/v de dados de posicionamento de hífen
	local index_t = data.index_t;            -- uma sequência de índice na tabela de dados de posicionamento de hífen; usado por binary_search()
	local idx_count = data.count;            -- de count = #index_t; em ~/data; usado por binary_search()
--[[--------------------------< B I N A R Y _ S E A R C H >----------------------------------------------------
Faz uma pesquisa binária para os dados de posicionamento do hífen para <target_isbn> em <hyphen_pos_t> usando
sua sequência de índice <índice_t>.
Aceita uma entrada <target_isbn> (uma string) que converte em um número.
Retorna o índice em <hyphen_pos_t> como um número quando a formatação adequada é encontrada; nada mais
]]
local function binary_search (target_isbn)
	target_isbn = tonumber (target_isbn);          -- converte para número porque os valores de index_t[x] são números
	if (index_t[1] >= target_isbn) or (index_t[idx_count] < target_isbn) then -- inválido; fora do intervalo; 9780000000000 para qual é que seja o último valor
		return;                 
	end
	
	local idx_bot = 1;               -- inicializa no índice 1 (primeiro elemento em <index_t>)
	local idx_top = idx_count;             -- inicializa para o índice do último elemento em <index_t>
	
	while idx_bot ~= idx_top do
		local idx_mid = math.ceil ((idx_bot + idx_top) / 2);     -- obtem o ponto médio na sequência do índice
		if index_t[idx_mid] >= target_isbn then         -- quando o valor do índice do ponto médio é maior ou igual ao isbn alvo
			if index_t[idx_mid-1] < target_isbn then       -- e quando o valor <index_t> anterior for menor que o isbn alvo
				return index_t[idx_mid];          -- encontramos o mapeamento correto para <target> isbn; retornar índice em <hyphen_pos_t>
			end
			idx_top = idx_mid - 1;            -- ajusta <idx_top>
		else
			idx_bot = idx_mid;             -- ajusta <idx_bot>
		end
	end
	mw.logObject ('didn\'t find formatting for isbn: ' .. target_isbn);   -- apenas para se acontecer
end
--[[--------------------------< C O N V E R T _ T O _ I S B N 1 0 >--------------------------------------------
Converter isbn de 13 dígitos em isbn de 10 dígitos; remove o prefixo 978 GS1 e recalcula o dígito de
verificação.
Recebe uma única entrada; o isbn de 13 dígitos como uma string sem separadores.
Assume que o prefixo GS1 é 978; não há mapeamento entre isbn10 e isbn13 com prefixo 979. funções de chamada são
necessárias para garantir que <isbn13> seja uma sequência formada corretamente de 13 dígitos (sem separadores)
que comece com 978.
]]
local function convert_to_isbn10 (isbn13)
	local isbn9 = isbn13:sub (4, 12);           -- obtem os 9 dígitos de <isbn13> que seguem o prefixo '978' GS1 (elimina o dígito de verificação)
	local check = 0;               -- inicializa o cálculo do dígito de verificação
	local i = 1;                -- índice
	for j=10, 2, -1 do               -- <j> é a ponderação de cada um dos 9 dígitos; contagem regressiva, da esquerda para a direita
		check = check + tonumber (isbn9:sub (i, i)) * j;      -- acumula a soma dos produtos ponderados dos dígitos
		i = i + 1;                -- próximo índice
	end
	check = check % 11;               -- o resto dos produtos ponderados dos dígitos dividido por 11
	if 0 == check then
		return isbn9 .. '0';             -- caso especial
	else
		check = 11 - check;              -- calcula o dígito de verificação
		return isbn9 ..  ((10 == check) and 'X' or check);      -- quando <check> for dez, use 'X'; <check> nos outros casos
	end
end
--[[--------------------------< C O N V E R T _ T O _ I S B N 1 3 >--------------------------------------------
Converte isbn de 10 dígitos em isbn de 13 dígitos; adiciona o prefixo 978 GS1 e recalcula o dígito de
verificação
Recebe uma única entrada; o isbn de 10 dígitos como uma string (sem separadores)
]]
local function convert_to_isbn13 (isbn10)
	local isbn12 = '978'.. isbn10:sub(1, 9);         -- concatena '978' com os primeiros 9 dígitos de <isbn10> (elimina o dígito de verificação)
	local check = 0;               -- inicia o cálculo do dígito de verificação
	for i=1, 12 do                -- para os primeiros 12 dígitos ('978' e 9 outros)
		check = check + tonumber (isbn12:sub (i, i)) * (3 - (i % 2) * 2);  -- acumular soma de verificação
	end
	return isbn12 .. ((10 - (check % 10)) %10);         -- extrai o dígito de verificação da soma de verificação; anexa e está pronto
end
--[[--------------------------< _ F O R M A T _ I S B N >------------------------------------------------------
Ponto de entrada do módulo quando require() é colocado em outro módulo
leva cinco entradas:
	<isbn_str> – isbn como uma string
	<show_err_msg>: boolean: quando verdadeiro, mostra mensagem de erro retornada de check_isbn(); nenhuma
	outra mensagem
	<separador>: booleano: quando verdadeiro, usa caractere de espaço como separador; outro hífen
	<template_name>: fornecido pelo modelo para uso em mensagens de erro
	<output_format>: um valor de 10 ou 13 determina o formato da saída; outros valores ignorados
Retorna sbn, isbn10 ou isbn13 formatado (qualquer que tenha sido a entrada ou por |out=) em caso de sucesso;
senão retorna <isbn_str> inicial
]]
local function _format_isbn (isbn_str, show_err_msg, separator, output_format, template_name)
	if (not isbn_str) or ('' == isbn_str) then
		return '';                -- entrada vazia ou nula retorna vazio
	end
	local isbn_str_raw = isbn_str;            -- este será o valor de retorno se não for possível formatar
	isbn_str = isbn_str:gsub ('[^%dX]', '');         -- retira toda a formatação (espaços e hífens) do isbn/sbn
	local flags = {};               -- um lugar conveniente para flags
	if '13' == output_format then            -- define uma flag para o formato de saída; ignorado quando <isbn_str> é um sbn
		flags.out13 = true;
	elseif  '10' == output_format then
		flags.out10 = true;
	end
	if 9 == #isbn_str then              -- parece uma sbn?
		isbn_str = '0' .. isbn_str;            -- converte para isbn10
		flags.sbn = true;              -- define uma flag
	end
	
	local err_msg = require ("Módulo:Check isxn").check_isbn ({args={isbn_str, template_name=template_name}}); -- <isbn_str> 'parece' um isbn válido? não verifica a faixa
	if '' ~= err_msg then              -- quando há uma mensagem de erro
		if show_err_msg then             -- e estamos mostrando mensagens de erro
			return isbn_str_raw,  err_msg;          -- retornar nossa entrada e a mensagem
		else
			return isbn_str_raw;            -- não mostra mensagens de erro; retorna nossa entrada sem a mensagem
		end
	end
	if 13 == #isbn_str and flags.out10 then          -- se é isbn13 mas queremos uma saída isbn10
		flags.isbn10_check_digit = (convert_to_isbn10 (isbn_str)):sub (-1);  -- calcula e extrai o dígito de verificação isbn10 para mais tarde
	end
	
	if 10 == #isbn_str then              -- se é isbn10 ou sbn
		flags.isbn10_check_digit = isbn_str:sub (-1);       -- extrai o dígito de verificação para mais tarde
		isbn_str = convert_to_isbn13 (isbn_str);        -- converte isbn10 em isbn13 para formatação
	end
	
	local index = binary_search (isbn_str);          -- procura a formatação que se aplica a <isbn_str>
	if index then                -- se achar
		local format_t = hyphen_pos_t[index];         -- obtem a sequência de formatação
		local result_t = {isbn_str:sub (1, 3)};         -- inicia <result_t> com prefixo; o elemento de prefixo GS1 ('978' ou '979')
		local digit_ptr = 4;             -- inicia para apontar para o elemento do grupo de registro
		
		for _, n in ipairs (format_t) do          -- fica em loop na sequência de formatação para construir uma sequência de elementos isbn13
			table.insert (result_t, isbn_str:sub (digit_ptr, digit_ptr+n-1)); -- adiciona os dígitos de <isbn_str>[<digit_ptr>] a <isbn_str>[<digit_ptr+n-1>] à sequência <result_t>
			digit_ptr = digit_ptr + n;           -- avança o ponteiro do dígito
		end
		table.insert (result_t, isbn_str:sub (13));        -- e adiciona o elemento do dígito de verificação a <result_t>
		isbn_str = table.concat (result_t, separator and ' ' or '-');   -- monta <isbn_str> formatado com separadores de espaço ou hífen (padrão)
		if flags.isbn10_check_digit then          -- se salvamos o dígito de verificação de um sbn ou isbn10
			if flags.sbn then             -- quando a entrada é um sbn
				isbn_str = isbn_str:gsub ('^978%-0%-', ''):gsub ('%d$', flags.isbn10_check_digit); -- remove o prefixo GS1 e o grupo de registro; restaura dígito de verificação
			else                -- quando a entrada é um isbn10
				if not flags.out13 then
					isbn_str = isbn_str:gsub ('^978%-', ''):gsub ('%d$', flags.isbn10_check_digit); -- remove o elemento de prefixo GS1; restaurar dígito de verificação
				end
			end
		end
		return isbn_str;              -- retorna formatado <isbn_str>
	end
	return isbn_str_raw;              -- nunca deveria ser realmente alcançado; mas, se acontecer, retorna a string de entrada original
end
--[[--------------------------< F O R M A T _ P L A I N >------------------------------------------------------
Saída de texto sem formatação: 
	sem ligação para Especial:Fontes_de_livros
	nenhuma saída de mensagem de erro – em caso de erro, retorna a entrada; para uso nas predefinições cs1|2 nos parâmetros |isbn=, não faz sentido causar confusão devido a múltiplas mensagens de erro
	|separator=space – renderizar ISBN formatado com espaços em vez de hífens
	|out= – leva 10 ou 13 para especificar o formato de saída se for diferente do padrão
	
{{#invoke:format isbn|format_plain}}
]]
local function format_plain (frame)
	local args_t = require ('Módulo:Arguments').getArgs (frame);    -- obtem a predefinição e invocar parâmetros
	local isbn_str = args_t[1];
	local separator = 'space' == args_t.separator;        -- booleano: quando verdadeiro usa separador de espaço; senão hífen
	local output_format = args_t.out;           -- 10 ou 13 para converter o formato de entrada para outro para saída
	return _format_isbn (isbn_str, nil, separator, output_format);    -- sem mensagens de erro
end
--[[--------------------------< F O R M A T _ L I N K >--------------------------------------------------------
Saída de texto com ligações: 
	sem ligação para Especial:Fontes_de_livros
	
	|suppress-errors=yes – suprime mensagens de erro
	|separator=space – renderiza ISBN formatado com espaços em vez de hífens
	|out= – leva 10 ou 13 para especificar o formato de saída se for diferente do padrão
	
{{#invoke:format isbn|format_linked|template=Format ISBN link}}
]]
local function format_linked (frame)
	local args_t = require ('Módulo:Arguments').getArgs (frame);    -- obtem a predefinição e invocar parâmetros
	local isbn_str = args_t[1];
	local show_err_msg = 'yes' ~= args_t['suppress-errors'];     -- sempre mostra os erros, a não ser que |suppress-errors=yes
	local separator = 'space' == args_t.separator;        -- booleano: quando verdadeiro usa separador de espaço; senão hífen
	local output_format = args_t.out;           -- 10 ou 13 para converter o formato de entrada para outro para saída
	local formatted_isbn_str, err_msg = _format_isbn (isbn_str, show_err_msg, separator, output_format, args_t.template_name); -- exibe mensagens de erro a não ser que seja suprimido
	if err_msg then
		return formatted_isbn_str .. ' ' .. err_msg;       -- retornar isbn não formatado, sem ligações e mensagem de erro
	else
		return '[[Special:BookSources/' ..isbn_str .. '|' .. formatted_isbn_str ..']]'; -- retornar isbn formatado e com ligações
	end
end
--[[--------------------------< E X P O R T S >----------------------------------------------------------------
]]
return {
	format_plain = format_plain,            -- pontos de entrada da predefinição
	format_linked = format_linked,
	
	_format_isbn = _format_isbn,            -- ponto de entrada quando este módulo requer () em outro módulo
	}