C&L Lua

De Grupo de Engenharia de Requisitos PUC-Rio

Tabela de conteúdo

O que é?

O C&L é um software para edição de símbolos do léxico e cenários. Este software disponibiliza um ambiente em que usuários podem interagir para construir, manter, evoluir e gerenciar projetos contendo cenários e símbolos do léxico. Acesse o software em: C&L.

O primeiro protótipo do C&L foi desenvolvido em PHP durante a disciplina Princípios de Engenharia de Software, oferecida pela PUC-Rio ao curso de Engenharia de Computação, no primeiro semestre de 2002. O software atual é o resultado da evolução deste protótipo por estudantes da graduação, mestrado e doutorado do Departamento de Informática da PUC-Rio ao longo de seis anos. O C&L foi desenvolvido desde o início com a filosofia de software livre e seu código fonte está disponível para quem quiser baixá-lo.

No ano de 2007 o C&L passou por um processo de manutenção e atualização, realizado pelos integrantes do grupo de engenharia de requisitos da PUC-Rio . Este processo corrigiu algumas de suas funcionalidades e adicionou uma nova ferramenta para geração de grafos, desenvolvida por um aluno da PUC-Rio em seu projeto final de graduação.

Em 2008 o C&L passou por um processo de re-engenharia. Este processo foi guiado por princípios de transparência e migrou o software da plataforma PHP-Apache para Lua-Kepler.


Arquitetura e tecnologias utilizadas

Escolhemos a linguagem Lua [Ierusalimschy03] para implementação da ferramenta. Esta escolha foi motivada por três motivos principais. O primeiro deles é que Lua provê um excelente suporte a programação funcional e acreditamos que o uso de funções se adequa mais a descrição por cenários proposta pelo nosso método. O Segundo motivo é que Lua é uma linguagem cada vez mais utilizada, e acreditamos que suas principais características (livre, rápida, extensível e pequena, dentre outras) sejam bastante úteis quando aplicadas ao desenvolvimento de sistemas Web. E por último, o C&L faz muitas comparações baseadas em expressões regulares para determinar o relacionamento entre elementos. Lua oferece um excelente suporte ao uso de expressões regulares e comparações entre strings.

Lua não foi projetada para desenvolvimento de sistema Web, por isso utilizamos a plataforma Kepler [Kepler09]. Esta plataforma disponibiliza uma série de módulos que facilitam o desenvolvimento de código Lua para Web. Dentre os módulos podemos destacar o CGILua que permite a criação de páginas dinâmicas para Web e a manipulação de dados provenientes de formulário, o LuaSQL que fornece uma interface simples para interação de Lua com alguns dos principais sistemas gerenciadores de banco de dados e, por último, o Xavante, que é um poderoso servidor Web HTTP 1.1, que usa uma arquitetura modular baseada em tratadores URI mapeados.

Para a construção da nova arquitetura adotamos o framework MVC, pois acreditamos que a divisão em camadas proporcionada por ele favorece a organização e entendimento do código fonte do software, proporcionando uma maior transparência a nível de código. Em nossa arquitura existe a divisão física do sistema em camadas e a divisão lógica em módulos. Cada um dos módulos está distribuído pelas camadas de visão, controle e modelo, como mostra a Figura abaixo.



Algoritmo que implementa a rastreabilidade automática

Apresentação do Problema

Um projeto do C&L pode possuir vários elementos cadastrados, que podem ser símbolos do léxico ou cenários. Estes elementos possuem seus identificadores únicos dentro do projeto. No caso de símbolos do léxico, este identificador é o seu nome e no caso de cenários, este identificador é seu título. O desejado é que o C&L identifique automaticamente a ocorrência destes identificadores no texto dos elementos que estão sendo exibidos e que, caso encontre algum identificador, o substituía por um elo para o elemento correspondente. Estes elos representam os relacionamentos que podem ocorrer entre dois cenários, entre dois símbolos do léxico ou entre um cenário e um símbolo do léxico. Os identificadores dos elementos podem ser compostos de várias palavras. Neste caso o C&L deve dar preferência aos identificadores compostos de mais palavras.

Para exemplificar, suponhamos que exista um projeto com apenas um símbolo do léxico cadastrado, cujo nome seja “software”, e que estejamos visualizando o texto da Figura abaixo.



Neste caso, o termo “software” deveria ser substituído por um elo para o símbolo software, como na figura baixo.



Suponhamos que mais adiante, o usuário cadastre um novo símbolo de nome “engenharia de software” no projeto, e que novamente estejamos visualizando o texto da Figura 51. Agora, o C&L deve substituir o termo “engenharia de software” por um elo para o símbolo “engenharia de software” e não teríamos mais uma referência ao termo “software”, como na figura a seguir.



Funcionamento do algoritmo

O primeiro passo do é montar um vetor com todos os identificadores dos elementos cadastrados no projeto e ordená-los de acordo com seu número de palavras. Também devemos criar um vetor auxiliar inicialmente vazio. Este vetor será utilizado no decorrer do algoritmo. Após a criação do vetor auxiliar, iremos percorrer o vetor ordenado a partir do identificador com maior número de palavras até os com menor número de palavras, procurando a ocorrência deles no texto. Caso seja encontrada uma ocorrência, o identificador do símbolo do léxico será colocado no vetor auxiliar e sua ocorrência no texto será substituída pelo código xzzxk*kxy, onde o * deverá ser substituído pela posição que o identificador ocupará no vetor auxiliar. Caso o identificador seja o título de um cenário, o procedimento será o mesmo só mudará o código utilizado, que deverá ser wzczxk*kxyyc, onde o * deve ser substituído pela posição que o identificador ocupará no vetor auxiliar. Os passos da aplicação deste algoritmo podem ser vistos na figura a seguir.



Após a execução destes cinco passos teremos um texto com códigos e o vetor auxiliar com os identificadores correspondentes a estes códigos. Para finalizar basta substituir os códigos por elos para os elementos correspondentes, como mostrado abaixo.



Implementação do algoritmo

A seguir mostraremos a operacionalização do cenário “Colocar atalhos no texto”, responsável pela descrição do algoritmo apresentado na subseção anterior. Este cenário faz uso do relacionamento de subcenários para destacar algumas funcionalidades e recuperar dados do banco de dados. Os subcenários “selecionar todos os léxicos do banco de dados”, “selecionar sinônimos do banco de dados” e “selecionar cenários do banco de dados” recuperam, respectivamente, todos os símbolos do léxico, sinônimos de símbolos e cenários de um determinado projeto. O subcenário “organizar tabela” organiza uma tabela de acordo com o número de palavras de seus elementos. E o subcenário “contar palavras” para determina o número de palavras um elemento específico. O relacionamento entre o cenário e seus subcenários pode ser visto na figura a seguir.


--[[
@Titulo: Colocar elos no texto
@Objetivo: Substituir a ocorrência de nomes de termos do léxico e seus sinônimos ou de títulos de cenários, já cadastrado no projeto, por um atalho que
leve para definição do termo ou para descrição do cenário. Para tanto, cada elemento cadastrado no projeto (termos do léxico e cenários) é procurado no texto, caso alguma ocorrência seja encontrada é criado um atalho no texto para definição deste elemento.
@Contexto: O usuário deve possuir um projeto com pelo menos um elemento cadastrado.
@Atores: selecionar_todos_lexicos_bd, selecionar_sinonimos_projeto_bd, selecionar_todos_cenarios_bd, ordenar_tabela, contar_palavras.
@Recursos: Identificador do projeto (parâmetro id_projeto), texto em que os elementos serão procurados (parâmetro texto), tipo de elemento que foi selecionado pelo usuário: termo do léxico ou cenário (parâmetro tipo) e banco de dados.   
]]—
function colocar_elos (id_projeto, texto, tipo)

--@Episódio 1: SELECIONAR TODOS OS LEXICOS DO BANCO DE DADOS
	local lexicos = selecionar_todos_lexicos_bd(id_projeto);
	
--@Episódio 2: SELECIONAR TODOS OS SINÔNIMOS DO BANCO DE DADOS
	local sinonimos = selecionar_sinonimos_projeto_bd(id_projeto);

--@Episódio 3: SELECIONAR TODOS OS CENÁRIOS DO BANCO DE DADOS
	local cenarios = selecionar_todos_cenarios_bd(id_projeto);
	
--@Episódio 4: Criar uma tabela única, contendo nome dos termos do léxico e seus siônimos.
	for index, sinonimo in pairs(sinonimos) do
		table.insert(lexicos, sinonimo);
	end 
	local lexicos_e_sinonimos = lexicos;
	
--@Episódio 5: ORDENAR TABELA sinonimos.
	sinonimos = ordenar_tabela(sinonimos, 1, table.maxn(sinonimos) ,"lexico" )
	
--@Episódio 6: ORDENAR TABELA cenários.
	cenarios =  ordenar_tabela(cenarios, 1, table.maxn(cenarios) ,"cenario" )
	
--@Episódio 7: ORDENAR TABELA lexicos_e_sinonimos.	
lexicos_e_sinonimos = ordenar_tabela(lexicos_e_sinonimos, 1, table.maxn(lexicos_e_sinonimos),"lexico" )
	
--@Episódio 8: Obter o tamanho da tabela lexicos_e_sinonimos, da tabela cenários e o total, que corresponde a soma dos tamanhos das duas tabelas.
	local tam_lexicos_e_sinonimos = #lexicos_e_sinonimos;
	local tam_cenarios = #cenarios;
	local tam_total = tam_lexicos_e_sinonimos + tam_cenarios ;
	
--@Episódio 9: Criar tabelas que irão armazenar os links criados para termos do léxico e para cenários.	
	local tabela_links_lexico = {};
	local tabela_links_cenario = {};
	
	if ((tipo == "lexico") or (tam_cenarios == 0)) then
--@Episódio 10: Se não há cenários cadastrados no projeto então apenas a lista de léxicos e sinônimos será percorrida.
		for index, lexico in pairs(lexicos_e_sinonimos) do

--@Episódio 11: A variável nome_lexico recebe o nome do termo do léxico ou sinônimo que está sendo verificado no momento.
			local nome_lexico = lexico["NOME"];
			
--@Episódio 12: Uma expressão regular é montada com o nome do termo do léxico ou sinônimo. Esta expressão regular evita erros devido a pontuação e espaços em branco na hora da busca.			
local expressao_regular =       "([%p%s])"..nome_lexico.."([%p%s])";

			if (string.find(texto, expressao_regular) ~= nil) then
			
				local nome_lexico_link = "";
--@Episódio 13: Se alguma ocorrência da expressão regular for encontrada no texto, então esta expressão é substituída pelo código (wzzxk*kxy), onde o * será substituído por um número que identificará a posição do termo do léxico ou sinônimo na tabela léxicos e sinônimos.
texto = string.gsub(texto, expressao_regular, "%1".."wzzxk"..index.."kxy".."%2");

--@Episódio 14: Verifica se a expressão encontrada é realmente um termo do léxico ou é um sinônimo de um termo do léxico. 
				if (lexico["LEXICO"] ~= nil) then
				
--@Episódio 15: Se a expressão encontrada for um sinônimo então o nome_lexico_link deve armazenar o nome do termo do léxico a que pertence o sinônimo. Isto é feito para que possamos criar um atalho para o termo do léxico a que o sinônimo corresponde.
					nome_lexico_link = lexico["LEXICO"]
				else
				
--@Episódio 16: Se a expressão encontrada for um termo do léxico então o nome_lexico_link deve armazenar o nome do termo do léxico.
					nome_lexico_link = nome_lexico;
				end	
--@Episódio 17: Criar o atalho para a expressão encontrada no texto.
link = "<a title=\"Léxico\"   href=\"../visao/exibe_lexico.lp?id_projeto="..id_projeto.."&nome="..nome_lexico_link.."\">"..nome_lexico.."</a>";

--@Episódio 18: Inserir o atalho criado na tabela_links_lexico na mesma posição que o elemento encontrado ocupa na tabela lexicos_e_sinonimos. Isto permitirá que identifiquemos a que código (wzzxk*kxy) do texto este atalho corresponde.
				table.insert(tabela_links_lexico, index, link);
			end --if
				
	    end --for
	
	else	
	
		if (tam_lexicos_e_sinonimos == 0 and tam_cenarios > 0) then
		
--@Episódio 19: Se não há léxicos no projeto então apenas a lista de cenários será percorrida.
			for index, cenario in pairs(cenarios) do
			
--@Episódio 20: A variável nome_cenario recebe o título do cenário que está sendo verificado no momento.
		        nome_cenario = cenario["TITULO"];
				
--@Episódio 21: Uma expressão regular é montada com o título do cenário. Esta expressão regular evita erros devido à pontuação e espaços em branco na hora da busca.
				expressao_regular = "([%p%s])"..nome_cenario.."([%p%s])";
							
				if (string.find(texto, expressao_regular) ~= nil) then
				
--Episódio 22: Se alguma ocorrência de expressão regular for encontrada no texto, então um atalho é montado com o nome do cenário.
link = "<a title=\"Cenário\" href=\"../visao/exibe_cenario.lp?id_projeto="..id_projeto.."&titulo="..titulo_cenario.."&comando=exibir\">"..titulo_cenario.."</a>";
					
--Episódio 23: O atalho montado montado é inserido na tabela_links_cenario na mesma posição que o título do cenário encontrado ocupa na tabela cenarios.
					table.insert(tabela_links_cenario, index, link);
					
--@Episódio 24: A expressão é substituída pelo código (wzczxk*kxyyc), onde o * será substituído por um número que identificará a posição do título do cenário na tabela cenários.
texto = string.gsub(texto, expressao_regular, "%1".."wzczxk"..i.."kxyyc".."%2");
				end --if
				
		    end	-- for
		
		elseif (tam_total > 0) then
		
--@Episódio 25: Se há léxicos e cenários no projeto então tanto a tabela cenarios quanto a lexicos_e_sinonimos serão percorridas.	
			i = 1;
			j = 1;
			contador = 1;
			while (contador <= tam_total) do
				
--@Episódio 26: Verifica se a tabela cenarios e a tabela lexicos_e_sinonimos já foram percorridas.
if ( ( i <= tam_lexicos_e_sinonimos ) and (j <= tam_cenarios) ) then
					
--@Episódio 27:Se a tabela cenarios ainda não foi completamente percorrida e a tabela lexicos_e_sinonimos também não foi completamente percorrida então CONTAR PALAVRAS do título do cenário e CONTAR PALAVRAS do nome do léxico ou sinônimo.
if ( contar_palavras(cenarios[j]["TITULO"]) <= contar_palavras(lexicos_e_sinonimos[i]["NOME"]) ) then
					    
--@Episódio 28: Se o título do cenário atual possui um número menor ou igual de palavras que o nome do termo do léxico, então procuraremos a ocorrência deste léxico no texto.
nome_lexico = lexicos_e_sinonimos[i]["NOME"];
						
--@Episódio 29: Uma expressão regular é criada com o nome do termo do léxico atual. Esta expressão regular impede que erros sejam causados por causa da pontuação e espaços em branco.					
expressao_regular = "([%p%s])"..nome_lexico.."([%p%s])";
						
if( string.gfind(texto, expressao_regular) ~= nil ) then
						
--@Episódio 30: Se uma ocorrência da expressão regular é encontrada no texto então a expressão é substituída pelo código (wzzxk*kxy), onde o * será substituído por um número que identificará a posição do nome do termo do léxico ou sinônimo na tabela lexicos_e_sinonimos.
texto = string.gsub(texto, expressao_regular, "%1".."wzzxk"..i.."kxy".."%2");
							
--@Episódio 31: É feita um verificação para saber se o termo encontrado é um termo do léxico ou um sinônimo de um termo do léxico. 
if (lexicos_e_sinonimos[i]["LEXICO"] ~= nil) then

--@Episódio 32: Se o elemento encontrado for um sinônimo então o termo do léxico a que este sinônimo corresponde é armazenado na variável nome_lexico_link. Caso contrário, se o elemento encontrado for um termo do léxico, então ele é armazenado na variável nome_léxico.
nome_lexico_link = lexicos_e_sinonimos[i]["LEXICO"];
							else
nome_lexico_link = nome_lexico;
							end	
							
--@Episódio 33: Um atalho para o termo encontrado é criado e colocado na tabela links_lexico, na mesma posição que o léxico atual ocupa na tabela lexicos e sinônimos. Isto permitirá que identifiquemos a que código (wzzxk*kxy) do texto este atalho corresponde.							
link = "<a title=\"Léxico\" href=\"../visao/exibe_lexico.lp?id_projeto="..id_projeto.."&nome="..nome_lexico_link.."\">"..nome_lexico.."</a>";
table.insert(tabela_links_lexico, i, link);
						end --if
						i = i + 1;
						
					else --if
--@Episódio 34: Se o título do cenário atual possui um maior de palavras que o nome do léxico atual, então procuraremos a ocorrência do título deste cenário no texto.
						titulo_cenario = cenarios[j]["TITULO"];

--@Episódio 35: Uma expressão regular é criada com o título do cenário atual. Esta expressão regular impede que erros sejam causados por causa da pontuação e espaços em branco. 								
expressao_regular = "([%p%s])"..titulo_cenario.."([%p%s])";
												
if( string.gfind(texto, expressao_regular) ~= nil ) then
						
--@Episódio 36: Caso encontremos uma ocorrência do cenário atual no texto, um atalho será criado e armazenado no tabela tabela_links_cenario, na mesma posição que o cenário atual ocupa na tabela cenários.
link = "<a title=\"Cenário\" href=\"../visao/exibe_cenario.lp?id_projeto="..id_projeto.."&titulo="..titulo_cenario.."\">"..titulo_cenario.."</a>";
table.insert(tabela_links_cenario, j, link);
							
--@Episódio 37: A expressão é substituída pelo código (wzczxk*kxyyc), onde o * será substituído por um número que identificará a posição do título do cenário na tabela cenarios.							
texto = string.gsub(texto, expressao_regular, "%1".."wzczxk"..j.."kxyyc".."%2");
										
						end --if
						j = j + 1;
					
					end --if
			
				elseif( tam_lexicos_e_sinonimos == i-1 ) then
				
--@Episódio 38: Se a tabela de termos do léxico chegou ao fim e a tabela de cenários ainda possui cenários que ainda não foram procurados, então devemos continuar percorrendo apenas a tabela cenários.			 
					titulo_cenario = cenarios[j]["TITULO"];
			
--@Episódio 39: Uma expressão regular é criada com o título do cenário atual. Esta expressão regular impede que erros sejam causados por causa da pontuação e espaços em branco. 								
expressao_regular = "([%p%s])"..titulo_cenario.."([%p%s])";
					
if( string.gfind(texto, expressao_regular) ~= nil ) then
					
--@Episódio 40: Caso seja encontrada uma ocorrência do cenário atual no texto, um atalho será criado e armazenado no tabela tabela_links_cenario, na mesma posição que o cenário atual ocupa na tabela cenários. 	
link = "<a title=\"Cenário\" href=\"../visao/exibe_cenario.lp?id_projeto="..id_projeto.."&titulo="..titulo_cenario.."\">"..titulo_cenario.."</a>";
						table.insert(tabela_links_cenario, j, link);
						
--@Episódio 41: A expressão é substituída pelo código (wzczxk*kxyyc), onde o * será substituído por um número que identificará a posição do título do cenário na tabela cenarios. 						
texto = string.gsub(texto, expressao_regular, "%1".."wzczxk"..j.."kxyyc".."%2"); 						
							
					end --if
					
				elseif( tam_cenarios == j-1 )	then

--@Episódio 42: Se a tabela de cenários chegou ao fim e a tabela lexicos_e_sinonimos ainda possui elementos que ainda não foram procurados, então devemos continuar percorrendo apenas a tabela lexicos_e _sinonimos.						
					nome_lexico = lexicos_e_sinonimos[i]["NOME"];

--@Episódio 43: Uma expressão regular é criada com o termo do léxico atual. Esta expressão regular impede que erros sejam causados por causa da pontuação e espaços em branco.							
expressao_regular = "([%p%s])"..nome_lexico.."([%p%s])";
					
if( string.gfind(texto, expressao_regular) ~= nil ) then
					
--@Episódio 44: Caso seja encontrada uma ocorrência do termo do léxico atual no texto, A expressão é substituída pelo código (wzzxk*kxy), onde o * será substituído por um número que identificará a posição do nome do termo do léxico na tabela léxicos.	
texto = string.gsub(texto, expressao_regular, "%1".."wzzxk"..i.."kxy".."%2");
												
if (lexicos_e_sinonimos[i]["LEXICO"] ~= nil) then
						
--@Episódio 45: Se o elemento encontrado for um sinônimo então o termo do léxico a que este sinônimo corresponde é armazenado na variável nome_lexico_link. Caso contrário, se o elemento encontrado for um termo do léxico, então ele é armazenado na variável nome_léxico.						
nome_lexico_link = lexicos_e_sinonimos[i]["LEXICO"];
						else
							nome_lexico_link = nome_lexico;
						end	
						
--@Episódio 46: Um atalho para o termo do léxico encontrado é criado e armazenado no tabela tabela_links_lexico, na mesma posição que o léxico atual.
link = "<a title=\"Léxico\" href=\"../visao/exibe_lexico.lp?id_projeto="..id_projeto.."&nome="..nome_lexico_link.."\">"..nome_lexico.."</a>";
						table.insert(tabela_links_lexico, i, link); 
							
					end --if
					
					i = i + 1;
				
				end --if
				contador = contador + 1;
			
			end --while
			
		end --if
	end--if
	
	contador = 1;

--@Episódio 47: A tabela links lexico é percorrida e os códigos inseridos anteriormente nos texto são substituídos pelos links armazenados na tabela. O número que se encontra no meio do código corresponde a posição na tabela de links que o atalho que será inserido se encontra.
	for i, link in pairs(tabela_links_lexico) do
		expressao_regular = ("([%p%s])".."wzzxk"..i.."kxy".."([%p%s])");
		texto = string.gsub(texto, expressao_regular, "%1"..link.."%2");
	end
--@Episódio 48: A tabela links cenários é percorrida e os códigos inseridos anteriormente nos texto são substituídos pelos links armazenados na tabela. O número que está no meio do código corresponde a posição na tabela de links que o atalho que será inserido se encontra.
	for i, link in pairs(tabela_links_cenario) do
		expressao_regular = ("([%p%s])".."wzczxk"..i.."kxyyc".."([%p%s])");
		texto = string.gsub(texto, expressao_regular, "%1"..link.."%2");
	end
--@Episódio 49: O texto com os links é retornado para o usuário.
	cgilua.put(texto);	
	
end
Ferramentas pessoais
Reuniões do grupo
Languages