Universidade Federal do Rio Grande do Sul Instituto de Informática Departamento de Informática Aplicada INF01120 TÉCNICAS DE CONSTRUÇÃO DE PROGRAMAS RELATÓRIO DO TRABALHO FINAL DA DISCIPLINA 1 PROFESSOR OTACÍLIO JOSÉ CAROLLO DE SOUZA SEMESTRE 2007/2 TURMA C PROJETO FINAL CodeGear: The JavaCode Editor 2 Realizado por: Pietro Facchini Biasuz Cartão: 151362 Soraya Sybele Hossain Cartão: 124917 26 de novembro de 2007. ÍNDICE 1. Introdução ............................................... 2. Projeto Inicial ............................................... 2.1 Escolhendo o projeto ............................................... 2.2 A idéia ............................................... 2.3 Desenvolvendo a idéia ............................................... 2.4 O projeto ............................................... 3 2.5 Escolha da linguagem ............................................... 3. Pesquisa ............................................... 3.1 Editores de texto X Editores de código ............................................... 3.2 Aplicando conhecimentos da disciplina ............................................... 4. Planejamento 4.1 Diagrama de estrututras ............................................... 4.2 Divisão das tarefas e prioridades ............................................... 4.3 Comunicação entre equipe ............................................... 5. Implementação ............................................... 5.1 Recursos disponíveis ............................................... 5.2 Detalhes da implementação ............................................... 5.3 Testes realizados em trechos fundamentais ............................................... 5.4 Dificuldades encontradas ............................................... 5.5 Comunicação ............................................... 6. Experiência Adquirida ............................................... 6.1 Se reiniciado, como seria ............................................... 6.2 O que poderia ser melhorado ............................................... 7. Considerações finais ............................................... 7.1 Sobre o CodeGear ............................................... 7.2 Sobre nosso aprendizado ............................................... 8. Anexos .............................................................. 8.1 Anexo 1 - O código-fonte do projeto ............................................... 8.2 Anexo 2 - Screenshots do programa ............................................... 9. Referências ............................................................................. 1. INTRODUÇÃO 4 A criação de um software para fins acadêmicos (geralmente de maneira individual) para atender a demanda de determinada disciplina em um curso de graduação é tarefa à qual todo estudante está habituado. Porém o desenvolvimento em grupo de um projeto extenso, com muitos passos de implementação, atendendo a diversos pré-requisitos não é trivial. Com o objetivo de familiarização com as etapas e problemas envolvidos no ciclo de vida de um programa, foi desenvolvido pelo grupo, ao longo do semestre, um programa para edição de textos, especialmente para a escrita de código. Um editor de textos é um programa complexo, com diversas funcionalidades básicas ou avançadas a serem implementadas. Ele exige uma análise detalhada de requisitos, um processo de decisão de projeto (tanto para aspectos genéricos quanto para detalhes de implementação), filosofia de trabalho, mecanismos de desenvolvimento paralelo por mais de um implementador simultâneamente e de modularização e integração de software. É necessário também que se leve em conta, antes de começar uma etapa de codificação, o modelo utilizado na abordagem de um problema. Quando se está desenvolvendo em um grupo onde não se tem a visão geral do software, não basta resolver o problema. É necessário resolver o problema de maneira que outra pessoa possa facilmente compreender qual foi a solução abordada. Para isso, é necessário criar esquemas e modelos para que as peculiaridades pessoais de um desenvolvedor no grupo não acabem prejudicando o projeto e tomando mais tempo (pois no "mundo real", tempo é sinônimo de custo). Neste documento, nossa intenção é descrever as diversas situações em que estivemos envolvidos no decorrer desse projeto. Iremos detalhar as etapas, requisitos, modelos e problemas descritos acima para que se possa ter uma melhor compreensão do projeto como um todo (compreensão essa que não pode ser obtida apenas com a avaliação do resultado final, isto é, do editor de texto pronto que foi "entregue ao cliente"). Utilizaremos diversas vezes a analogia de que o programa foi desenvolvido para um mercado consumidor ou para um cliente, uma vez que o desenvolvimento ocorreu com o intuito de criar um software 5 competitivo e inovador, tal qual o processo de desenvolvimento que se dá no "mundo real". 2. PROJETO INICIAL 2.1 ESCOLHENDO O PROJETO Ao longo de semestres anteriores da disciplina Técnicas de Construção de Programas, os projetos finais eram alternados entre editores de texto e planilhas eletrônicas. Esses projetos ilustram bem todo o conhecimento teórico adquirido na disciplina, pois são projetos grandes, para serem realizados em equipe e que utilizam todas as técnicas de desenvolvimento de um software que tivemos oportunidade de aprender. No semestre corrente, deveria ser proposta a implementação de um editor de texto, mas foi decidido que a escolha do tema dos projetos seriam liberados. É complicado encontrar um tema de projeto em que possamos por em prática tudo o que aprendemos na disciplina, ainda mais que a escolha do tema teria que ser feita no início do semestre, quando ainda não tínhamos estudado todo o conteúdo da disciplina ainda. Como nos foi dito pelo professor, em aula, que implementar um editor de texto seria uma ótima forma de avaliar todo o conhecimento da disciplina, optamos por implementar uma versão melhorada de um editor de texto comum: queríamos realizar um projeto de um editor de texto para a escrita de código de programação. 2.2 A IDÉIA Muitas vezes, precisamos editar pequenos trechos de código ou implementar pequenos programas. Quando isso ocorre, recorremos a editores de código bastante simples. Há uma grande variedade de editores de código disponíveis na Web, alguns grátis (Freeware) e outros pagos (que geralmente são disponibilizados em uma versão Shareware ou Trial, para que os usuários testem o software antes de realizar a compra, caso fiquem satisfeitos com o produto). Apesar da aparente simplicidade de alguns dos editores de código mais populares, eles, muitas vezes, apresentam um comportamento 6 inesperado, surpreendendo o usuário, prejudicando o andamento das tarefas e, por conseqüência, tornando o trabalho simples mais demorado que o necessário, e assim perdendo a vantagem principal da utilização de um editor de código, a velocidade de edição. Além disso, a maior parte dos editores não apresentam algumas funcionalidades simples que facilitariam muito a codificação. Pensando no problema descrito, decidimos que seria de grande valia a construção de um editor de código que conseguisse incorporar uma utilização mais intuitiva e que possuísse funcionalidades que ajudassem o programador tanto na sintaxe da linguagem quanto na compilação. Além disso, a escolha do projeto nos permitiria experimentar como é o desenvolvimento de um projeto grande, comparado com o que tínhamos feito até agora. 2.3 DESENVOLVENDO A IDÉIA Ao início do projeto, imaginamos muitas funcionalidades inovadoras e que facilitariam a vida dos mais variados tipos de usuários de editores de código. Chegamos a imaginar funcionalidades inovadoras até hoje nunca implementadas nesse tipo de software. Pensamos também em implementar muitas funcionalidades que já existem nos editores usados atualmente, sendo essas as nossas preferidas de forma a adaptar o editor ao nosso uso diário. A criação de uma ferramenta que pudesse auxiliar nas tarefas básicas do dia-a-dia foi, com certeza, prioridade. Por esse motivo, essas ferramentas foram estudadas com mais ênfase na etapa inicial do projeto. Já as ferramentas revolucionárias foram idealizadas e sua aplicabilidade e possibilidade de implementação foi pesquisada com mais receio, uma vez que estávamos explorando território desconhecido. A primeira etapa do projeto constituiu-se de uma pesquisa de mercado, em que realizamos uma coleta de informações sobre produtos similares ao que foi projetado. Essa coleta e posterior síntese de dados em um relatório e uma apresentação nos rendeu muitas idéias sobre o trabalho que tínhamos pela frente. A etapa seguinte foi projetar o programa, criando um modelo de modularização e um cronograma aproximado de eventos para o desenvolvimento. Este projeto nos serviu de base para diversas etapas seguintes, mas também se mostrou falho em alguns aspectos depois de algum tempo. Por esse motivo, ele foi, por diversas vezes adaptado e modificado ao longo do tempo. 7 Pagamos caro pela nossa ousadia. Pensar grande demais nos fez perder muito tempo com pequenos detalhes irrelevantes ao trabalho. Apesar disso, conseguimos, depois de perceber o tamanho do tempo perdido, implementar o que nos era pedido de forma clara e simples desde o início deste semestre. 2.4 O PROJETO Aqui citamos as features que gostaríamos de implementar em nosso editor de código. Algumas não chegaram a ser implementadas pela falta de planejamento, como citamos acima, já que não estávamos habituados a trabalhar com grandes projetos. Mas elas ficam como sugestão para uma futura melhora do programa. O nosso editor de código, que aqui já teria até um nome: CodeGear, herda muitas funcionalidades de um editor de texto comum, como segue: Criar novo arquivo; Ações de abrir, editar e salvar arquivos existentes; Ações de copiar, colar e recortar; Possibilidade de desfazer e refazer as últimas alterações realizadas; Busca de um sub-texto dentro do texto corrente. Abdicamos das funcionalidades de escolha de fonte, tamanho e cor, pois isso seria desnecessário na escrita de um código. Como uma funcionalidade extra, pensamos em implementar a utilização de abas para possibilitar a edição de diversos códigos na mesma janela do programa. Além disso, existem as funcionalidades extras específicas que caracterizam um editor de código e auxiliam na codificação de um programa. Entre elas: Realçar os tokens (palavras reservadas) da linguagem específica em que o usuário está programando; Salvar o trabalho automaticamente a um período de tempo pré-determinado, em arquivos separados, caso o usuário desejar voltar para um estado anterior da codificação; Localizador de identificadores que indiquem abertura e fechamento de um 8 bloco de programa, por exemplo, as {} (chaves) das linguagens C e Java. Quando o usuário selecionasse um dos identificadores, o programa se encarregaria de achar o identificador correspondente, seja de abertura ou fechamento do bloco de código. Também pensamos em algumas opções diferenciais que não são normalmente em encontradas nos editores de código, em geral: Disponibilizar, no momento em que o usuário digita o código, ajuda sobre o uso de cada Token, por exemplo: no caso do usuário estar programando em Java, ao digitar 'for ', o programa reconheceria o Token e preencheria for com (;;) e apareceria uma caixa de texto informando o que cada parâmetro a ser digitado significa; Ter a possibilidade de compilar o código ativo diretamente do editor, utilizando um compilador de linha de comando DOS, como por exemplo, o JDK, no caso de Java. 2.5 ESCOLHA DA LINGUAGEM Para a implementação do editor de código, optamos por utilizar a linguagem de programação Java[1], mais especificamente, a versão J2SE, por ser uma linguagem orientada a objetos, com a qual poderíamos utilizar esse recurso para o reaproveitamento de determinadas partes do código, como nos foi ensinado em aula, além de ser uma linguagem multi-plataforma, tendo em vista que, em geral, programadores utilizam variados sistemas operacionais e não se restringem a um único, como no caso de um usuário comum. Outro fator decisivo na escolha da linguagem, foi o aprendizado de uma nova linguagem que teríamos ao desenvolver o programa, visto que não tínhamos trabalhado com essa linguagem ainda e seria uma boa oportunidade de conhecê-la. Sobre as questões técnicas da linguagem, utilizaremos a biblioteca Strings que facilitará o trabalho em relação a manipulação de strings, para podermos identificar os Tokens (palavras reservadas), localizar as variáveis e os identificadores de abertura e término de um bloco de programa. 9 Para criação da interface, foi escolhida a biblioteca Swing, pois é uma biblioteca bastante completa para trabalhar com interfaces e ela ajudará na construção dos botões e menus, e também na própria área de digitação do código. Para compilar o código diretamente do editor, será necessária a construção de um arquivo em lotes que deverá ser executado no momento em que se deseja realizar a compilação. Será necessária a instalação separadamente do JDK (Java Development Kit) no computador do usuário para realizar essa tarefa. A parte de ajuda ao usuário, a parte de localização de variáveis e também a parte da compilação diretamente do editor são especificas para cada linguagem. Já as demais partes são universais, ou seja, funcionariam independentemente da linguagem, sem necessidade de adaptações especiais para cada linguagem. Inicialmente, o editor de código será voltado para a linguagem Java. 3. PESQUISA 3.1 EDITORES DE TEXTO x EDITORES DE CÓDIGO Primeiramente, antes de começarmos a implementação do projeto, tínhamos que pesquisar sobre ferramentas já existentes, para assim poder realizar o planejamento de nosso trabalho. Começamos pesquisando sobre editores de texto básicos, afinal, essa seria a base para o nosso software. Ao buscar a definição de editores de texto, encontramos o seguinte: “Um editor de texto é um tipo de programa utilizado para a edição de arquivos de texto plenos. Editores de texto são geralmente disponibilizados juntamente com Sistemas Operacionais, ou pacotes de desenvolvimento, e podem ser usados para modificar documentos, arquivos de configuração e código-fonte de programação.”[2] 10 Figura 1: exemplo de um famoso editor de código: o Vim. Conhecido pela suas inúmeras funcionalidades, mas também pela sua grande dificuldade de utilização. Existem importantes diferenças entre arquivos de texto plenos criados por um editor de texto, e documentos criados por processadores de texto, tais como Microsoft Word, WordPerfect ou OpenOffice. Basicamente: Um arquivo de texto pleno é representado e editado ao mostrar todos os caracteres que estão presentes no arquivo. Os únicos caracteres utilizados para marcação são os caracteres de controle no conjunto de caracteres utilizados. Em prática, isso inclui “nova linha” (CR+LF) e “tab”. O conjunto de caracteres mais comumente 11 utilizado é o ASCII (American Standard Code for Information Interchange). Arquivos de texto plano são mais utilizados para programação e configuração, e menos freqüentemente utilizados para documentação, como eram antigamente. Documentos criados por um processador de texto geralmente contém caracteres de controle específicos do seu tipo de arquivo, diferente do que é definido no conjunto de caracteres. Isso permite funções tais como negrito, itálico, fontes, colunas, tabelas, etc. Processadores de texto podem normalmente editar arquivos de texto plenos e salvá-los nesse mesmo formato. Mas precisamos ter cuidado em dizer para o programa que é isso que queremos. Isso é especialmente importante em casos como código-fonte, HTML e arquivos de configuração e controle. Senão o arquivo conterá esses “caracteres especiais” únicos de tipos de arquivo de processadores de texto e não serão tratados corretamente na utilidade para a qual os arquivos foram destinados. Pelos motivos citados anteriormente, vimos que a melhor maneira de implementar um editor de código, seria tendo um editor de texto pleno como base. Assim, consideramos o que foi descrito da sessão 2.4, e decidimos não utilizar opções de formatação de texto, pois isso poderia prejudicar o usuário na hora de escrever o código no editor, e trazer conseqüências indesejáveis. 3.2 APLICANDO CONHECIMENTOS DA DISCIPLINA Como o objetivo do trabalho final era exatamente mostrar o que foi aprendido na disciplina, e a sua aplicação no desenvolvimento de um aplicativo de grande porte, também pesquisamos exaustivamente a bibliografia da disciplina, assim como o conteúdo dado em aula. Depois dessa pesquisa, onde já tínhamos conseguido reunir um bom conhecimento do que íriamos utilizar, resolvemos começar o planejamento do projeto. 4. PLANEJAMENTO 4.1 DIAGRAMA DE ESTRUTURAS 12 Figura 2: Diagrama de estruturas do CodeGear 4.2 DIVISÃO DAS TAREFAS E PRIORIDADES Antes de começarmos a implementar o editor, teríamos que dividir algumas tarefas, pois não tínhamos muita disponibilidade de encontros presenciais, e a nossa comunicação durante as etapas do desenvolvimento teria que ser feita de forma virtual. Estabelecemos prioridades durante a divisão das tarefas, pois primeiramente teríamos que construir uma base para o nosso editor de código, que seria o editor de texto pleno, para depois nos preocuparmos com as funcionalidades de um editor de código em si. Segue o esquemático do que ficou definido: 13 1ª apresentação Prioridades Implementação do editor de texto pleno. Implementação da interface básica do Pietro editor; Ações de copiar, colar e recortar um determinado trecho de texto. 2ª apresentação Implementação do editor de código, com base no editor de texto pleno. Realçar os tokens (palavras reservadas) no texto corrente; Criar mecanismo de salvamento automático do arquivo dentro de um tempo estipulado pelo usuário. Implementação da inferface básica do Implementação da interface completa do Soraya editor; editor; Manipulações sobre arquivos: criar novo Criar um mecanismo de abas para cada novo arquivo, abrir arquivo, salvar arquivo. arquivo; Extras Pietro Possibilidade de desfazer e refazer as últimas alterações realizadas no arquivo Ajuda sobre o uso de cada token; Maneira de integrar o editor de código com um compilador. Localizador de identificadores de abertura e Soraya Criar um mecanismo de busca de um fechamento de um bloco de código; sub-texto dentro do texto corrente Maneira de integrar o editor de código com um compilador. Tabela 1: divisão das tarefas do projeto 4.3 COMUNICAÇÃO ENTRE EQUIPE A comunicação entre os membros da equipe de desenvolvimento foi realizada, na maioria das vezes, de forma virtual. Em sessões posteriores serão mostrados exemplos de conversa entre a equipe. 5. IMPLEMENTAÇÃO 5.1 RECURSOS DISPONÍVEIS Ao escolhermos uma linguagem orientada a objetos na implementação do nosso 14 editor, pensamos em quão útil seria a característica de reuso de código, típica das linguagens OO. O ambiente escolhido para o desenvolvimento foi o NetBeans[4], que se mostrou uma ferramenta bastante poderosa na implementação do programa, e também de fácil uso. O NetBeans é um IDE Java desenvolvido pela empresa Sun Microsystems, a mesma que desenvolve a linguagem Java. Ele é um ambiente de desenvolvimento integrado gratuito e de código aberto para desenvolvedores de software. O IDE é executado em muitas plataformas, como Windows, Linux, Solaris e MacOS. É fácil de instalar e usar. O NetBeans IDE oferece aos desenvolvedores todas as ferramentas necessárias para criar aplicativos profissionais de desktop, empresariais, Web e móveis multiplataformas. Nós utilizamos a versão 5.5.1 em nosso projeto. 5.2 DETALHES DA IMPLEMENTAÇÃO Em um trabalho como o nosso, a descrição de detalhes de implementação pode ser bastante maçante. Por esse motivo, daremos aqui uma breve noção sobre o código do editor. Mais detalhes podem ser encontrados nos anexos ou então nos comentários em código. A interface gráfica foi construída utilizando a API Swing, que é amplamente utilizada por quase todas as aplicações Java que requerem um layout de interação com o usuário. Foram criados botões e seus comportamentos; um painel de Scrool para permitir a rolagem do texto e uma área de texto, propriamente dita, onde se digita o código. Para o reconhecimento das palavras reservadas da linguagem, foi construído um vetor com algumas palavras e complementos. A partir daí, o programa, em tempo real, reconhece as palavras reservadas, sugere seus finais e as destaca do restante do código. O destaque das palavras é feito com a utilização de Highlights. Ao reconhecer uma palavra da linguagem, o programa pinta o fundo ao redor da palavra. Foram utilizados Highlights diferentes para cada função: azul para achar as chaves, vermelho para palavras procuradas e cinza para palavras reservadas. As funções de achar a outra chave correspondente e a função de Salvar automático foram feitas utilizando Timers. A um dado intervalo de tempo, as tarefas programadas são executadas e seus resultados (arquivo gerado e Highlights) são gerados. Para essa tarefa não interromper a execução do programa principal foram 15 utilizadas Threads. 5.3 TESTES REALIZADOS EM TRECHOS FUNDAMENTAIS Houve uma grande quantidade de testes realizados, desde o início a partir da implementação da interface até a parte da lógica utilizada pelo programa para o reconhecimento das palavras. Um recurso de teste bastante utilizado foi o breakpoint. Além desse, o programa apresentar seus resultados passo-a-passo também foi muito utilizado principalmente na parte da programação lógica. Sem dúvida, a parte de implementação que mais necessitou testes, tanto para verificar se a lógica empregada estava correta e, também para entender o que cada função utilizada realizava de fato, foi a parte da implementação do reconhecimento e procura das palavras reservadas. 5.4 DIFICULDADES ENCONTRADAS Houve muitas dificuldades para duas pessoas, que ainda não se conheciam, trabalhar em uma mesma implementação. A comunicação e a definição de como as partes de cada um deviam interagir foi essencial para o sucesso do trabalho. A maior dificuldade encontrada foi a disponibilidade de tempo e organização do projeto. No início da implementação, tudo foi feito calmamente e de forma "experimental", pois tínhamos a idéia de que havia ainda muito tempo disponível para fazer a programação. Conforme o prazo final se aproximou, o ritmo de trabalho teve que ser revisado, com o aumento da produtividade e com menos tempo para projetar e repensar maneiras mais eficientes de implementação. Outra grande dificuldade foi a divisão de tarefas entre os membros do grupo, pois até o momento tínhamos o hábito de programar sozinhos e ter uma noção global do programa. 16 5.5 COMUNICAÇÃO Desde o início do trabalho, armazenamos nossas conversas por e-mail com a intenção de não perdermos boas idéias durante a implementação de nosso editor. Houve além da troca de e-mails, longas conversas por I.M. que fizeram muita diferença nas decisões tomadas diante de como fazer o editor. Abaixo seguem algumas conversas por e-mail, e no fim delas uma das conversas por IM citadas acima: <completar, ou retirar – não esquecer do índice> 6. EXPERIÊNCIA ADQUIRIDA 6.1 SE REINICIADO, COMO SERIA Se reiniciássemos o programa hoje, a principal diferença seria na organização inicial com um melhor planejamento das tarefas a serem realizadas. Também organizaríamos melhor o tempo, e a divisão de tarefas seria mais bem distribuída, de maneira mais "encapsulada", onde cada integrante do grupo teria tarefas bem definidas e pensadas antes da implementação o que diminuiria dependência de dados. Havendo tempo, poderíamos implementar várias coisas que tínhamos em mente, mas que não foram possíveis. 6.1 O QUE PODERIA SER MELHORADO Se desde o início planejássemos nosso tempo de forma a compreender o tempo necessário para o correto funcionamento do que teríamos de implementar de mais básico, talvez pudéssemos ter ido mais longe na implementação dos extras que planejávamos. 17 7. CONSIDERAÇÕES FINAIS 7.1 SOBRE O CODEGEAR Atingimos nosso objetivo na criação de um editor genérico e fácil de usar. Foram implementadas várias funcionalidades que havíamos visado no projeto inicial do editor de código. Além de ter contribuído para o enriquecimento do aprendizado na disciplina, pois pudemos por em prática os conhecimentos adquiridos. No Anexo 2 – Screenshots do programa é possível ter uma idéia do resultado, através das capturas de tela exibidas. Elas mostram como a interface ficou simples, porém amigável, exatamente o pretendido. Também mostramos as funcionalidades implementadas e o que foi alcançado do que foi pretendido. 7.2 SOBRE NOSSO APRENDIZADO A disciplina de Técnicas de Construção de Programas, por fim, se mostrou uma das disciplinas mais importantes do curso, pois nela é possível colocar (ou ao menos tentar) em prática tudo o que aprendemos até o momento e ainda ter vontade de aprender mais para poder aperfeiçoar a nossa futura produção de software. 8. ANEXOS 8.1 ANEXO 1 – O CÓDIGO-FONTE DO PROJETO import java.awt.Event; import java.awt.FileDialog; import java.awt.Insets; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; 18 import java.text.SimpleDateFormat; import javax.swing.*; import java.util.*; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import java.awt.event.ActionEvent; import javax.swing.event.UndoableEditEvent; import javax.swing.event.UndoableEditListener; import javax.swing.text.AbstractDocument; import javax.swing.text.BadLocationException; import javax.swing.GroupLayout.*; import javax.swing.text.DefaultEditorKit; import javax.swing.text.DefaultHighlighter; import javax.swing.text.Document; import javax.swing.text.Highlighter; import javax.swing.text.JTextComponent; import javax.swing.text.StyledDocument; import javax.swing.undo.*; import java.awt.Color; import java.io.*; public class CodeGear extends JFrame implements DocumentListener { private JLabel jLabel1; private JScrollPane jScrollPane1; private JTextArea textArea; JTextPane textPane; AbstractDocument doc; private JMenuBar menuBar; private JMenu buttonMenu, buttonEdit, buttonOpcoes, buttonSearch; private JMenuItem btnOpen, btnClose, btnSave, btnSaveAs, btnAuto, btnCopy, btnPaste, btnCut, btnSelAll, btnFind, btnUnfind; private JTextField textFinder; private JSpinner winSave; private JFrame frame; private JRadioButtonMenuItem tempo3, tempo5, tempo10, tempo15, tempoOff; private JTextField entry; private static final String COMMIT_ACTION = "commit"; private static final String SPACE_ACTION = "space"; private static enum Mode { INSERT, COMPLETION }; private final List<String> words; private final List<String> plus; private final int cursor []; private Mode mode = Mode.INSERT; private int posVet, posIni=0; private String saveStr = null; private int lastSpc = 0; private String filename; private boolean firstSave = true; private String strCopy = ""; 19 public java.util.Timer tSave, tChaves; private Object[] options = {"Salvar","Descartar","Cancelar"}; public String buf = ""; public String palavra=""; public Highlighter.HighlightPainter PainterGray = new MyHighlightPainter(Color.LIGHT_GRAY); public Highlighter.HighlightPainter PainterRed = new MyHighlightPainter(Color.RED); public Highlighter.HighlightPainter PainterBlue = new MyHighlightPainter(Color.BLUE); protected UndoAction undoAction; protected RedoAction redoAction; protected UndoManager undo = new UndoManager(); public CodeGear() { super("CodeGear - The JavaCodeEditor"); initComponents(); textArea.getDocument().addDocumentListener(this); textArea.getDocument().addUndoableEditListener(new MyUndoableEditListener()); InputMap im = textArea.getInputMap(); ActionMap am = textArea.getActionMap(); im.put(KeyStroke.getKeyStroke("ENTER"), COMMIT_ACTION); am.put(COMMIT_ACTION, new CommitAction()); im.put(KeyStroke.getKeyStroke("SPACE"), SPACE_ACTION); am.put(SPACE_ACTION, new SpaceAction()); //cria um banco de dados com algumas palavras reservadas da linguagem Java (Tokens) words = new ArrayList<String>(); plus = new ArrayList<String>(); cursor = new int[30]; words.add("ArrayList"); plus.add("ArrayList<TYPE>(SIZE) "); cursor[0]=10; words.add("Boolean"); plus.add("Boolean "); plus.add("class "); plus.add("enum "); plus.add("extends "); plus.add("false "); plus.add("final "); cursor[1]=8; words.add("class"); cursor[2]=13; words.add("enum"); cursor[3]=5; words.add("extends"); cursor[4]=8; words.add("false"); cursor[5]=6; words.add("final"); cursor[6]=6; words.add("for"); plus.add("for (IND=0;IND >0;IND--)"); cursor[7]=5; words.add("if"); plus.add("if () {\n \n} else {\n 20 \n} "); cursor[8]=4; words.add("import"); plus.add("import "); plus.add("int "); plus.add("new "); plus.add("null "); plus.add("package "); plus.add("private "); cursor[9]=7; words.add("int"); cursor[10]=4; words.add("new"); cursor[11]=4; words.add("null"); cursor[12]=4; words.add("package"); cursor[13]=8; words.add("private"); cursor[14]=8; words.add("psvm"); {\n \n} "); plus.add("public static void main(String args[]) cursor[15]=52; words.add("public"); plus.add("public "); cursor[16]=7; words.add("sout"); plus.add("System.out.println(\"\"); "); cursor[17]=20; words.add("string"); plus.add("String "); plus.add("super "); cursor[18]=7; words.add("super"); cursor[19]=6; words.add("switch"); plus.add("switch () "); plus.add("this "); cursor[20]=9; words.add("this"); cursor[21]=5; words.add("try"); plus.add("try {\n \n} catch (EXCEPTION e) {\n e.printStackTrace(); \n} ");cursor[22]=13; words.add("void"); plus.add("void "); cursor[23]=5; words.add("while"); plus.add("while () "); cursor[24]=7; words.add("else"); plus.add("else "); } //cria a interface gráfica do programa private void initComponents() { textArea = new JTextArea(); textPane = new JTextPane(); textPane.setCaretPosition(0); textPane.setMargin(new Insets(5,5,5,5)); StyledDocument styledDoc = textPane.getStyledDocument(); setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); jScrollPane1 = new JScrollPane(textArea); tSave = new java.util.Timer(); 21 tSave.schedule(new autoSave(),300000,300000); tChaves = new java.util.Timer(); tChaves.schedule(new acharOutro(),500,500); addButtons(); addListenersButtons(); GroupLayout layout = new GroupLayout(getContentPane()); getContentPane().setLayout(layout); ParallelGroup hGroup = layout.createParallelGroup(GroupLayout.Alignment.LEADING); SequentialGroup h1 = layout.createSequentialGroup(); ParallelGroup h2 = layout.createParallelGroup(GroupLayout.Alignment.TRAILING); h2.addComponent(jScrollPane1, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 212, Short.MAX_VALUE); h1.addContainerGap(); h1.addGroup(h2); h1.addContainerGap(); hGroup.addGroup(Alignment.TRAILING,h1); layout.setHorizontalGroup(hGroup); ParallelGroup vGroup = layout.createParallelGroup(GroupLayout.Alignment.LEADING); SequentialGroup v1 = layout.createSequentialGroup(); v1.addContainerGap(); v1.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED); v1.addComponent(jScrollPane1, GroupLayout.DEFAULT_SIZE, 100, Short.MAX_VALUE); v1.addContainerGap(); vGroup.addGroup(v1); layout.setVerticalGroup(vGroup); pack(); } //cria e adiciona os botões à interface gráfica private void addButtons() { 22 menuBar = new javax.swing.JMenuBar(); buttonMenu = new javax.swing.JMenu(); buttonEdit = new javax.swing.JMenu(); buttonOpcoes = new javax.swing.JMenu(); buttonSearch = new javax.swing.JMenu(); btnOpen = new javax.swing.JMenuItem(); btnClose = new javax.swing.JMenuItem(); btnSave = new javax.swing.JMenuItem(); btnSaveAs = new javax.swing.JMenuItem(); btnCopy = new javax.swing.JMenuItem(); btnPaste = new javax.swing.JMenuItem(); btnCut = new javax.swing.JMenuItem(); btnSelAll = new javax.swing.JMenuItem(); btnFind = new javax.swing.JMenuItem(); btnUnfind = new javax.swing.JMenuItem(); undoAction = new UndoAction(); buttonEdit.add(undoAction); redoAction = new RedoAction(); buttonEdit.add(redoAction); buttonMenu.setText("Arquivo"); buttonEdit.setText("Editar"); buttonOpcoes.setText("AutoSave"); buttonSearch.setText("Buscar"); btnOpen.setText("Abrir"); btnClose.setText("Fechar"); btnSave.setText("Salvar"); btnSaveAs.setText("Salvar Como"); btnCopy.setText("Copiar"); btnPaste.setText("Colar"); btnCut.setText("Recortar"); btnSelAll.setText("Selecionar Tudo"); btnFind.setText("Marcar"); btnUnfind.setText("Desmarcar"); menuBar.add(buttonMenu); menuBar.add(buttonEdit); menuBar.add(buttonOpcoes); menuBar.add(buttonSearch); buttonMenu.add(btnOpen); buttonMenu.add(btnClose); buttonMenu.add(btnSave); 23 buttonMenu.add(btnSaveAs); buttonEdit.add(btnSelAll); buttonEdit.add(btnCut); buttonEdit.add(btnCopy); buttonEdit.add(btnPaste); buttonSearch.add(btnFind); buttonSearch.add(btnUnfind); ButtonGroup group = new ButtonGroup(); tempoOff = new JRadioButtonMenuItem("OFF"); tempo3 = new JRadioButtonMenuItem("3 Minutos"); tempo5 = new JRadioButtonMenuItem("5 Minutos"); tempo10 = new JRadioButtonMenuItem("10 Minutos"); tempo15 = new JRadioButtonMenuItem("15 Minutos"); group.add(tempoOff); group.add(tempo3); group.add(tempo5); tempo5.setSelected(true); group.add(tempo10); group.add(tempo15); buttonOpcoes.add(tempoOff); buttonOpcoes.add(tempo3); buttonOpcoes.add(tempo5); buttonOpcoes.add(tempo10); buttonOpcoes.add(tempo15); setJMenuBar(menuBar); } //*********************FUNÇÕES DE MENU************************************** //implementação da função salvar; salva o texto digitado na janela public void saveFunction() { if (firstSave) saveAsFunction(); else { BufferedWriter out; String text = textArea.getText(); try { String filenameDatado = putDate(filename); out = new BufferedWriter(new FileWriter(filenameDatado)); out.write(text); out.close(); } 24 catch (IOException ex) { ex.printStackTrace(); } } } //implementação da função salvar como public void saveAsFunction(){ JFileChooser fc = new JFileChooser(); int answ = fc.showSaveDialog(null); if (answ == JFileChooser.APPROVE_OPTION) { if (fc.getSelectedFile().exists()) { //arquivo jah existe, abre caixa int n = JOptionPane.showConfirmDialog( frame, "Arquivo já existe! Subescrever esse arquivo?", "Confirmação", JOptionPane.YES_NO_OPTION); if (n == JOptionPane.YES_OPTION) { if (fc.getSelectedFile().toString().endsWith(".java")) filename = fc.getSelectedFile().toString(); else filename = fc.getSelectedFile().toString()+ ".java"; } else filename ="default.java"; } else filename = fc.getSelectedFile().toString()+ ".java"; BufferedWriter out; String text = textArea.getText(); try { out = new BufferedWriter(new FileWriter(filename)); out.write(text); out.close(); } catch (IOException ex) { ex.printStackTrace(); } firstSave = false; } else {}; } //insere no final do nome do arquivo a data formata para a função de salvar autmomático public String putDate(String str) { String formato = " (MMM dd, HH mm ss)"; 25 SimpleDateFormat formatter = new SimpleDateFormat(formato); Date agora = new Date(); String data = formatter.format(agora); data += ".java"; int corte = str.length()-5; String strIni = str.substring(0,corte); str = strIni + data; return str; } //cria um timer para realizar o salvar automático class autoSave extends TimerTask { public void run() { saveFunction(); } }; //função para achar as palavras no código; funciona como um localizar public void find() { String pattern=(String)JOptionPane.showInputDialog(frame, "", "Localizar", JOptionPane.PLAIN_MESSAGE, null, null, ""); if (pattern!=null) highlightFinder(textArea, pattern); } //*********************LISTENERS************************************** //adiciona os comportamentos aos botões private void addListenersButtons() { btnOpen.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JFileChooser fc = new JFileChooser(); int answ = fc.showOpenDialog(null); if (answ == JFileChooser.APPROVE_OPTION) { File arch = fc.getSelectedFile(); textArea.setText(""); fc.removeAll(); if (fc.getSelectedFile().toString().endsWith(".java")) filename = fc.getSelectedFile().toString(); else filename = fc.getSelectedFile().toString()+ ".java"; 26 try { BufferedReader in = new BufferedReader(new FileReader(arch)); String str, text = ""; while ((str = in.readLine())!= null) { text += str; text += "\n"; } textArea.setText(text); vasculharTexto(); in.close(); firstSave = false; } catch (IOException ioe){ System.out.println("fuck"); } } } }); btnSaveAs.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { saveAsFunction(); } }); btnClose.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { int n = JOptionPane.showOptionDialog (frame, "O que você deseja fazer com o código atual?", "Aviso", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[2]); if (n == JOptionPane.YES_OPTION) { saveFunction(); textArea.setText(""); filename=null; firstSave = true; tSave.cancel(); } else if (n == JOptionPane.NO_OPTION) { textArea.setText(""); filename=null; firstSave = true; 27 tSave.cancel(); } else if (n == JOptionPane.CANCEL_OPTION) { } else { System.out.println("t decide"); } } }); btnSave.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { saveFunction(); } }); tempoOff.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { tSave.cancel(); } }); tempo3.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { tSave.cancel(); tSave = new java.util.Timer(); tSave.schedule(new autoSave(),180000,180000); } }); tempo5.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { tSave.cancel(); tSave = new java.util.Timer(); tSave.schedule(new autoSave(),300000,300000); } }); tempo10.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { tSave.cancel(); tSave = new java.util.Timer(); tSave.schedule(new autoSave(),600000,600000); } }); tempo15.addActionListener(new ActionListener() { 28 public void actionPerformed(ActionEvent e) { tSave.cancel(); tSave = new java.util.Timer(); tSave.schedule(new autoSave(),900000,900000); } }); btnCopy.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { textArea.copy(); } }); btnPaste.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { textArea.paste(); vasculharTexto(); } }); btnCut.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { textArea.cut(); } }); btnSelAll.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { textArea.selectAll(); } }); btnFind.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { find(); } }); btnUnfind.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { removeHighlights(textArea,PainterRed); } }); } //adiciona os comportamentos ao documento public void changedUpdate(DocumentEvent ev) { } 29 public void removeUpdate(DocumentEvent ev) { vasculharTexto(); retiraChaves(); } public void insertUpdate(DocumentEvent ev) { vasculharTexto(); retiraChaves(); if (ev.getLength() != 1) { return; } int pos = ev.getOffset(); String content = null; try { content = textArea.getText(0, pos+1); } catch (BadLocationException e) { e.printStackTrace(); } // encontra onde a palavra começa int w; for (w = pos; w >= 0; w--) { if (! Character.isLetter(content.charAt(w))) { break; } } if (pos - w < 2) { //só começa a reconhecer a partir de 2 caracteres return; } String prefix = content.substring(w + 1).toLowerCase(); int n = Collections.binarySearch(words, prefix); if ((pos - w) == 2) posVet = -n; if (n < 0 && -n <= words.size()) { String match = words.get(-n - 1); if (match.startsWith(prefix)) { String completion = match.substring(pos - w); //encontrou complemento //complementar a tarefa depois SwingUtilities.invokeLater( new CompletionTask(completion, pos + 1)); 30 } } else { // não achou nada mode = Mode.INSERT; } } private class CompletionTask implements Runnable { String completion; int position; CompletionTask(String completion, int position) { this.completion = completion; this.position = position; } public void run() { textArea.insert(completion, position); textArea.setCaretPosition(position + completion.length()); textArea.moveCaretPosition(position); mode = Mode.COMPLETION; } } private class CommitAction extends AbstractAction { public void actionPerformed(ActionEvent ev) { if (mode == Mode.COMPLETION) { int endPos = textArea.getSelectionEnd(); String buf = words.get(posVet-1); String plusBuf = plus.get(posVet-1); int sizeWord = buf.length() - 1; int iniPos = endPos-sizeWord-1; textArea.select(iniPos,endPos); textArea.cut(); textArea.insert(plusBuf, iniPos); textArea.setCaretPosition(cursor[posVet-1]+iniPos); 31 lastSpc = textArea.getCaretPosition(); mode = Mode.INSERT; } else { int lugar = textArea.getCaretPosition(); textArea.insert(" ",lugar); textArea.replaceSelection("\n"); } } } private class SpaceAction extends AbstractAction { public void actionPerformed(ActionEvent ev) { } } //**********************FUNÇÕES DE REDO E UNDO********************************** //funções de desfazer e refazer class RedoAction extends AbstractAction { public RedoAction() { super("Redo"); setEnabled(false); } public void actionPerformed(ActionEvent e) { try { undo.redo(); } catch (CannotRedoException ex) { System.out.println("Unable to redo: " + ex); ex.printStackTrace(); } updateRedoState(); undoAction.updateUndoState(); } protected void updateRedoState() { if (undo.canRedo()) { setEnabled(true); putValue(Action.NAME, undo.getRedoPresentationName()); } else { setEnabled(false); putValue(Action.NAME, "Redo"); } } } protected class MyUndoableEditListener implements UndoableEditListener { public void undoableEditHappened(UndoableEditEvent e) { 32 undo.addEdit(e.getEdit()); undoAction.updateUndoState(); redoAction.updateRedoState(); } } class UndoAction extends AbstractAction { public UndoAction() { super("Undo"); setEnabled(false); } public void actionPerformed(ActionEvent e) { try { undo.undo(); } catch (CannotUndoException ex) { System.out.println("Unable to undo: " + ex); ex.printStackTrace(); } updateUndoState(); redoAction.updateRedoState(); } protected void updateUndoState() { if (undo.canUndo()) { setEnabled(true); putValue(Action.NAME, undo.getUndoPresentationName()); } else { setEnabled(false); putValue(Action.NAME, "Undo"); } } } //***********************FUNÇÕES DE HIGHLIGHTS********************************* //funções de destaque (highlight) no texto class MyHighlightPainter extends DefaultHighlighter.DefaultHighlightPainter { public MyHighlightPainter(Color color) { super(color); } } //se o cursor estiver posicionado após uma chave, encontra a respectiva chave do mesmo, destacando-a class acharOutro extends TimerTask { public void run() { Highlighter hilite = textArea.getHighlighter(); 33 retiraChaves(); int posCar = textArea.getCaretPosition(); int posAchou = 0; String s=""; int ne=2; int chaves=0; int nd=1; if (posCar != 0) { try { s = textArea.getText(posCar-1, 1); } catch (BadLocationException ex) { ex.printStackTrace(); } } if (s.equals("}")) { chaves++; String text = textArea.getText(); while ((posCar-ne>=0) && (chaves>0)) { String w = text.substring(posCar-ne,posCar-ne+1); if (w.equals("}")) chaves++; if (w.equals("{")){ chaves--; } ne++; } if (chaves==0) { posAchou = posCar-ne+1; try { hilite.addHighlight(posAchou, posAchou+1, PainterBlue); } catch (BadLocationException ex) { ex.printStackTrace(); } } } if (s.equals("{")) { chaves++; String text = textArea.getText(); while (((nd+posCar)<text.length()) && (chaves>0)) { String w = text.substring(posCar+nd,posCar+nd+1); 34 if (w.equals("{")) chaves++; if (w.equals("}")){ chaves--; } nd++; } if (chaves==0) { posAchou = posCar+nd; try { hilite.addHighlight(posAchou-1, posAchou, PainterBlue); } catch (BadLocationException ex) { ex.printStackTrace(); } } } } } //retira o destaque das chaves public void retiraChaves () { removeHighlights(textArea, PainterBlue); } //vasculha o texto em busca de palavras reservadas public void vasculharTexto() { try { Highlighter hilite = textArea.getHighlighter(); String text = textArea.getText(); int n; String s=" "; for (n=0; n < text.length();n++) { if (text.charAt(n) != s.charAt(0)) // != espaço { buf += text.charAt(n); } else { buf = buf.trim(); //realiza uma busca no vetor words pela palavra buf int marc = Collections.binarySearch(words, buf); if (marc>=0) {//achou int posF = n; int posI = posF - buf.length(); hilite.addHighlight(posI,posF,PainterGray); } 35 buf=""; } } } catch (BadLocationException e) { } } //procura a palavra pattern no texto textArea public void highlightFinder(JTextComponent textArea, String pattern) { removeHighlights(textArea,PainterRed); Boolean achou = false; try { Highlighter hilite = textArea.getHighlighter(); Document doc = textArea.getDocument(); String text = doc.getText(0, doc.getLength()); int pos = 0; while ((pos = text.indexOf(pattern, pos)) >= 0) { hilite.addHighlight(pos, pos+pattern.length(), PainterRed); pos += pattern.length(); achou = true; } } catch (BadLocationException e) { } if (!(achou)) JOptionPane.showMessageDialog(frame,"Palavra não encontrada!"); } //remove os destaque do tipo Painter no texto textArea public void removeHighlights(JTextComponent textArea, Highlighter.HighlightPainter Painter) { Highlighter hilite = textArea.getHighlighter(); Highlighter.Highlight[] hilites = hilite.getHighlights(); for (int i=0; i<hilites.length; i++) { if (hilites[i].getPainter()== Painter) { hilite.removeHighlight(hilites[i]); } } } //****************************************************************************** public static void main(String args[]) { SwingUtilities.invokeLater(new Runnable() { public void run() { UIManager.put("swing.boldMetal", Boolean.FALSE); 36 new CodeGear().setVisible(true); } }); } } 8.2 ANEXO 2 – SCREENSHOTS DO PROGRAMA Aqui serão mostradas diversas screenshots do CodeGear, indicando as suas funcionalidades. 9. REFERÊNCIAS [1] http://java.sun.com/ [2] http://en.wikipedia.org/wiki/Text_editor [3] http://www.javafree.org/wiki/Java [4] http://www.netbeans.org/ [5] http://www.netbeans.org/index_pt_BR.html [6] Harvey M. Deitel, Paul J. Deitel; Java: Como Programar; Editora Bookman. [7] Charles E. Leiserson, Clifford Stein, Ronald L. Rivest, Thomas H. Cormen; Algoritmos: Teoria e Prática, Editora Campus. [8] <completar> 37 38