Skip to content

6. XML e JAVA

Neste capítulo, apresentamos o uso de documentos XML com Java. Faremos isso no contexto do aplicativo de impostos estudado no capítulo anterior.

6.1. Arquivos XML e folhas de estilo XSL

Consideremos o seguinte arquivo XML simulations.xml, que poderia representar o resultado de simulações de cálculos de impostos:

<?xml version="1.0" encoding="ISO-8859-1"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

Se visualizarmos com o IE 6, obtemos o seguinte resultado:

Image

O IE6 reconhece que se trata de um arquivo XML (graças à extensão .xml do arquivo) e o formata de maneira própria. Com o Netscape, obtém-se uma página em branco. No entanto, se observarmos o código-fonte (Exibir/Fonte), vemos que está presente o arquivo original XML:

Image

Por que o Netscape não exibe nada? Porque ele precisa de uma folha de estilo que indique como transformar o arquivo XML no arquivo HTML, que ele poderá então exibir. Acontece que o IE 6 possui uma folha de estilo padrão, enquanto o arquivo XML não oferece nenhuma, o que era o caso aqui.

Existe uma linguagem chamada XSL (eXtended StyleSheet Language) que permite descrever as transformações a serem realizadas para converter um arquivo XML em qualquer arquivo de texto. O XSL permite o uso de diversas instruções e se assemelha bastante às linguagens de programação. Não vamos detalhá-lo aqui, pois isso exigiria várias dezenas de páginas. Vamos simplesmente descrever dois exemplos de folhas de estilo XSL. A primeira é aquela que transformará o arquivo XML simulations.xml em código HTML. Modificamos este último para que ele indique a folha de estilo que os navegadores poderão usar para transformá-lo no documento HTML, documento que eles poderão exibir:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

O pedido XML

<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>

designa o arquivo simulations.xsl como uma folha de estilo (xml-stylesheet) do tipo text/xsl c.a.d. Um arquivo de texto contendo o código XSL. Essa folha de estilo será utilizada pelos navegadores para transformar o texto XML em um documento HTML. Aqui está o resultado obtido com o Netscape 7 ao carregar o arquivo XML simulations.xml:

Image

Ao verificarmos o código-fonte do documento (Exibir/Fonte), encontramos o documento XML original e não o documento HTML exibido:

Image

O Netscape utilizou a folha de estilo simulations.xsl para transformar o documento XML acima no documento HTML exibível. Agora é hora de examinar o conteúdo dessa folha de estilo:

<?xml version="1.0" encoding="ISO-8859-1"?>
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="html" indent="yes"/>

    <xsl:template match="/">
        <html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
            <hr/>
            <table border="1">
                    <th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
                          <xsl:apply-templates select="/simulations/simulation"/>
                        </table>
         </center>
       </body>
     </html>
        </xsl:template>


    <xsl:template match="simulation">
          <tr>
            <td><xsl:value-of select="@marie"/></td>
            <td><xsl:value-of select="@enfants"/></td>
            <td><xsl:value-of select="@salaire"/></td>
            <td><xsl:value-of select="@impot"/></td>
        </tr>
       </xsl:template>
  </xsl:stylesheet>
  • Uma folha de estilo XSL é um arquivo XML e, portanto, segue as mesmas regras. Entre outras coisas, ela deve estar “bem formada”, ou seja, toda tag aberta deve ser fechada.
  • O arquivo começa com dois comandos XML que podem ser mantidos em qualquer folha de estilo XSL:
<?xml version="1.0" encoding="ISO-8859-1"?>
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

O atributo encoding="ISO-8859-1" permite o uso de caracteres acentuados na folha de estilo.

  • A tag <xsl:output method="html" indent="yes"/> indica ao interpretador XSL que se deseja gerar HTML “indentado”.
  • A tag <xsl:template match="elemento"> serve para definir o elemento do documento XML ao qual serão aplicadas as instruções contidas entre <xsl:template ...> e </xsl:template>.
    <xsl:template match="/">
................
        </xsl:template>

No exemplo acima, o elemento “/” designa a raiz do documento. Isso significa que, assim que o início do documento XML for encontrado, os comandos XSL localizados entre as duas tags serão executados.

  • Tudo o que não for uma tag XSL é inserido tal como está no fluxo de saída. As tags XSL, por sua vez, são executadas. Algumas delas produzem um resultado que é inserido no fluxo de saída. Vejamos o exemplo a seguir:
    <xsl:template match="/">
        <html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
            <hr/>
            <table border="1">
                    <th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
                          <xsl:apply-templates select="/simulations/simulation"/>
                        </table>
          </center>
        </body>
      </html>
        </xsl:template>

Vale lembrar que o documento XML analisado é o seguinte:

<?xml version="1.0" encoding="ISO-8859-1"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

Desde o início do documento XML analisado (match="/"), o interpretador XSL produzirá como saída o texto

<html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
            <hr>
            <table border="1">
                    <th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>

Observe-se que, no texto inicial, havia <hr/> e não <hr>. No texto inicial, não era possível escrever <hr>, pois, embora seja uma tag HTML válida, é uma tag XML inválida. No entanto, estamos lidando aqui com um texto XML que deve ser “bem formado”, ou seja, c.a.d, o que significa que toda tag deve ser fechada. Portanto, escrevemos <hr/> e, como escrevemos <xsl:output text="html ...">, o interpretador XSL transformará o texto <hr/> em <hr>. Após esse texto, virá o texto produzido pelo comando XSL:

      <xsl:apply-templates select="/simulations/simulation"/>

Veremos mais adiante qual é esse texto. Por fim, o interpretador adicionará o texto:

                    </table>
          </center>
        </body>
      </html>

O comando <xsl:apply-templates select="/simulations/simulation"/> solicita que seja executado o “template” (modelo) do elemento /simulations/simulation. Ela será executada sempre que o interpretador XSL encontrar, no texto XML analisado, uma tag <simulation>..</simulations> ou <simulation/> dentro de uma tag <simulations>..</simulations>. Ao encontrar a tag <simulation>, o interpretador executará as instruções do modelo a seguir:

    <xsl:template match="simulation">
          <tr>
            <td><xsl:value-of select="@marie"/></td>
            <td><xsl:value-of select="@enfants"/></td>
            <td><xsl:value-of select="@salaire"/></td>
            <td><xsl:value-of select="@impot"/></td>
        </tr>
       </xsl:template>

Consideremos as seguintes linhas XML:

<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>

A linha <simulation ..> corresponde ao modelo da instrução XSL <xsl:apply-templates select="/simulations/simulation>". O interpretador XSL irá, portanto, procurar aplicar a ela as instruções que correspondem a esse modelo. Ele encontrará o modelo <xsl:template match="simulation"> e o executará. Vale lembrar que o que não for um comando XSL é reproduzido tal como está pelo interpretador XSL, e que os comandos XSL são substituídos pelo resultado de sua execução. A instrução XSL <xsl:value-of select="@champ"/> é, assim, substituída pelo valor do atributo “champ” do nó analisado (neste caso, um nó <simulation>). A análise da linha XML anterior produzirá o seguinte resultado:

XSL
saída
<tr><td>
<tr><td>
<xsl:value-of select="@marie"/>
sim
</td><td>
</td><td>
<xsl:value-of select="@enfants"/>
2
</td><td>
</td><td>
<xsl:value-of select="@salário"/>
200000
</td><td>
</td><td>
<xsl:value-of select="@impot"/>
22.504
</td></tr>
</td></tr>

No total, a linha XML

<simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>

será transformada na linha HTML:

<tr><td>oui</td><td>2</td><td>200000</td><td>22504</td></tr>

Todas essas explicações são um pouco rudimentares, mas agora deve ficar claro para o leitor que o texto XML a seguir:

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

acompanhado pela seguinte folha de estilo XSL simulations.xsl:

<?xml version="1.0" encoding="ISO-8859-1"?>
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="html" indent="yes"/>

    <xsl:template match="/">
        <html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
            <hr/>
            <table border="1">
                    <th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
                          <xsl:apply-templates select="/simulations/simulation"/>
                        </table>
         </center>
       </body>
     </html>
        </xsl:template>


    <xsl:template match="simulation">
          <tr>
            <td><xsl:value-of select="@marie"/></td>
            <td><xsl:value-of select="@enfants"/></td>
            <td><xsl:value-of select="@salaire"/></td>
            <td><xsl:value-of select="@impot"/></td>
        </tr>
       </xsl:template>
  </xsl:stylesheet>

gera o seguinte texto: HTML:

<html>
<head>
<title>Simulations de calculs d'impôts</title>
</head>
<body>
<center>
<h3>Simulations de calculs d'impots</h3>
<hr>
<table border="1">
<th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
<tr>
<td>oui</td><td>2</td><td>200000</td><td>22504</td>
</tr>
<tr>
<td>non</td><td>2</td><td>200000</td><td>33388</td>
</tr>
</table>
</center>
</body>
</html>

O arquivo XML simulations.xml, acompanhado da folha de estilo simulations.xsl, quando aberto em um navegador recente (neste caso, o Netscape 7), é exibido da seguinte forma:

Image

6.2. Aplicativo de impostos: versão 6

6.2.1. Os arquivos XML e as folhas de estilo XSL do aplicativo de impostos

Voltemos ao aplicativo web de impostos e modifiquemo-lo para que a resposta enviada aos clientes seja no formato XML, em vez de HTML. Essa resposta XML será acompanhada por uma folha de estilo XSL para que os navegadores possam exibi-la. No parágrafo anterior, apresentamos:

  • o arquivo simulations.xml, que é o protótipo de uma resposta XML contendo simulações de cálculos de impostos
  • o arquivo simulations.xsl, que será a folha de estilo XSL que acompanhará essa resposta XML

Também precisamos prever o caso de uma resposta com erros. O protótipo da resposta XML, nesse caso, será o seguinte arquivo erreurs.xml:

<?xml version="1.0" encoding="windows-1252"?>
<?xml-stylesheet type="text/xsl" href="erreurs.xsl"?>
<erreurs>
    <erreur>erreur 1</erreur>
  <erreur>erreur 2</erreur>
</erreurs>

A folha de estilo erreurs.xsl, que permite exibir este documento XML em um navegador, será a seguinte:

<?xml version="1.0" encoding="windows-1252"?>
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="html" indent="yes"/>

    <xsl:template match="/">
        <html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
          </center>            
          <hr/>
          Les erreurs suivantes se sont produites :
          <ul>
                  <xsl:apply-templates select="/erreurs/erreur"/>
              </ul>            
        </body>
      </html>
        </xsl:template>

    <xsl:template match="erreur">
          <li><xsl:value-of select="."/></li>
        </xsl:template>
</xsl:stylesheet>    

Esta folha de estilo introduz um comando XSL ainda não encontrado: <xsl:value-of select="."/>. Esse comando gera como saída o valor do nó analisado, neste caso, um nó <erreur>texte</erreur>. O valor desse nó é o texto contido entre as duas tags de abertura e fechamento, neste caso, texte.

O código erreurs.xml é transformado pela folha de estilo erreurs.xsl no seguinte documento HTML:

<html>
<head>
<title>Simulations de calculs d'impots</title>
</head>
<body>
<center>
<h3>Simulations de calculs d'impots</h3>
</center>
<hr>
          Les erreurs suivantes se sont produites :
<ul>
<li>erreur 1</li>
<li>erreur 2</li>
</ul>
</body>
</html>

O arquivo erreurs.xml, acompanhado de sua folha de estilo, é exibido por um navegador da seguinte maneira:

Image

6.2.2. O servlet xmlsimulations

Criamos um arquivo index.html, que colocamos no diretório do aplicativo impots. A página exibida é a seguinte:

Image

Este documento HTML é um documento estático. Seu código é o seguinte:

<html>
    <head>
      <title>impots</title>
    <script language="JavaScript" type="text/javascript">
        function effacer(){
           // Limpar o formulário
        with(document.frmImpots){
            optMarie[0].checked=false;
          optMarie[1].checked=true;
          txtEnfants.value="";
          txtSalaire.value="";
          txtImpots.value="";
        }//com
      }//apagar

      function calculer(){
           // verificação dos parâmetros antes de enviá-los ao servidor
        with(document.frmImpots){
          //número de filhos
          champs=/^\s*(\d+)\s*$/.exec(txtEnfants.value);
          if(champs==null){
            // o modelo não foi verificado
            alert("Le nombre d'enfants n'a pas été donné ou est incorrect");
            nbEnfants.focus();
            return;
          }//se
          //salário
          champs=/^\s*(\d+)\s*$/.exec(txtSalaire.value);
          if(champs==null){
            // o modelo não foi verificado
            alert("Le salaire n'a pas été donné ou est incorrect");
            salaire.focus();
            return;
          }//se
          // tudo certo — vamos enviar
          submit();
        }//com
      }//calcular        
        </script>
  </head>

  <body background="/impots/images/standard.jpg">
      <center>
        Calcul d'impôts
        <hr>
      <form name="frmImpots" action="/impots/xmlsimulations" method="POST">
          <table>
            <tr>
              <td>Etes-vous marié(e)</td>
            <td>
                <input type="radio" name="optMarie" value="oui">oui
              <input type="radio" name="optMarie" value="non" checked>non
            </td>
          </tr>
          <tr>
              <td>Nombre d'enfants</td>
            <td><input type="text" size="3" name="txtEnfants" value=""></td>
          </tr>
          <tr>
              <td>Salaire annuel</td>
            <td><input type="text" size="10" name="txtSalaire" value=""></td>
          </tr>
          <tr></tr>
          <tr>
              <td><input type="button" value="Calculer" onclick="calculer()"></td>
            <td><input type="button" value="Effacer" onclick="effacer()"></td>
          </tr>
        </table>
      </form>
    </center>
 </body>
</html>

Observe que os dados do formulário são enviados para o URL /impostos/xmlsimulações. Essa aplicação é um servlet Java configurado da seguinte maneira no arquivo web.xml da aplicação impots:

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
...........
    <servlet>
      <servlet-name>xmlsimulations</servlet-name>
    <servlet-class>xmlsimulations</servlet-class>
    <init-param>
          <param-name>xslSimulations</param-name>
        <param-value>simulations.xsl</param-value>
    </init-param>
    <init-param>
          <param-name>xslErreurs</param-name>
        <param-value>erreurs.xsl</param-value>
    </init-param>

    <init-param>
          <param-name>DSNimpots</param-name>
        <param-value>mysql-dbimpots</param-value>
    </init-param>
    <init-param>
          <param-name>admimpots</param-name>
        <param-value>admimpots</param-value>
    </init-param>
    <init-param>
          <param-name>mdpimpots</param-name>
        <param-value>mdpimpots</param-value>
    </init-param>    
  </servlet>
........
  <servlet-mapping>
      <servlet-name>xmlsimulations</servlet-name>
    <url-pattern>/xmlsimulations</url-pattern>
  </servlet-mapping>
</web-app>
  • A servlet se chama xmlsimulations e se baseia na classe xmlsimulations.class.
  • Ela possui como parâmetros os parâmetros DSNimpots, admimpots e mdpimpots, necessários para acessar o banco de dados de impostos. Além disso, ela aceita dois outros parâmetros:
    • xslSimulations, que é o nome do arquivo de estilo que deve acompanhar a resposta XML contendo as simulações
    • xslErreurs, que é o nome do arquivo de estilo que deve acompanhar a resposta XML, contendo eventuais erros
  • ela possui um alias xmlsimulations que a torna acessível por meio do URL http://localhost:8080/impots/xmlsimulations.

A estrutura da servlet xmlsimulations é semelhante à da servlet simulations já estudada. A principal diferença reside no fato de que ela deve gerar XML em vez de HTML. Isso implicará na remoção dos arquivos JSP utilizados nas aplicações anteriores. Sua principal função era melhorar a legibilidade do código HTML gerado, evitando que ele ficasse perdido no código Java da servlet. Essa função agora não tem mais razão de ser. A servlet tem dois tipos de código XML a serem gerados:

  • aquele para as simulações
  • aquele para erros

Anteriormente, apresentamos e estudamos os dois tipos de resposta XML a serem fornecidas nesses dois casos, bem como as folhas de estilo que devem acompanhá-las. O código da servlet é o seguinte:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.regex.*;
import java.util.*;

public class xmlsimulations extends HttpServlet{

    // variáveis de instância
    String msgErreur=null;
    String xslSimulations=null;
    String xslErreurs=null;
    String DSNimpots=null;
    String admimpots=null;
    String mdpimpots=null;
    impotsJDBC impots=null;

    //-------- GET
    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException{

        // recuperamos o fluxo de gravação para o cliente
        PrintWriter out=response.getWriter();

        // especifica-se o tipo da resposta
        response.setContentType("text/xml");

         // a lista de erros
        ArrayList erreurs=new ArrayList();

         // a inicialização ocorreu corretamente?
        if(msgErreur!=null){
             // Concluído — enviamos a resposta com erros ao servidor
            erreurs.add(msgErreur);
            sendErreurs(out,xslErreurs,erreurs);
             // concluído
            return;
        }

         // recuperando as simulações anteriores da sessão
        HttpSession session=request.getSession();
        ArrayList simulations=(ArrayList)session.getAttribute("simulations");
        if(simulations==null) simulations=new ArrayList();

        // recuperando os parâmetros da consulta atual
        String optMarie=request.getParameter("optMarie");              // estado civil
        String txtEnfants=request.getParameter("txtEnfants");       // número de filhos
        String txtSalaire=request.getParameter("txtSalaire");       // salário anual

         // estão todos os parâmetros esperados?
        if(optMarie==null || txtEnfants==null || txtSalaire==null){
             // faltam parâmetros
             // enviamos a resposta com erros
            erreurs.add("Demande incomplète. Il manque des paramètres");
            sendErreurs(out,xslErreurs,erreurs);
             // concluído
            return;
        }

         // temos todos os parâmetros — estamos verificando-os
         // estado civil
        if( ! optMarie.equals("oui") && ! optMarie.equals("non")){
             // erro
            erreurs.add("Etat marital incorrect");
        }
         // número de filhos
        txtEnfants=txtEnfants.trim();
        if(! Pattern.matches("^\\d+$",txtEnfants)){
            // erro
            erreurs.add("Nombre d'enfants incorrect");
        }
         // salário
        txtSalaire=txtSalaire.trim();
        if(! Pattern.matches("^\\d+$",txtSalaire)){
            // erro
            erreurs.add("Salaire incorrect");
        }

        if(erreurs.size()!=0){
            // se houver erros, eles serão sinalizados
            sendErreurs(out,xslErreurs,erreurs);
        }else{
             // sem erros
            try{
                 // é possível calcular o imposto a pagar
                int nbEnfants=Integer.parseInt(txtEnfants);
                int salaire=Integer.parseInt(txtSalaire);
                String txtImpots=""+impots.calculer(optMarie.equals("oui"),nbEnfants,salaire);
                 // adiciona-se o resultado atual às simulações anteriores
                String[] simulation={optMarie.equals("oui") ? "oui" : "non",txtEnfants, txtSalaire, txtImpots};
                simulations.add(simulation);
                 // envia-se a resposta com as simulações
                sendSimulations(out,xslSimulations,simulations);
            }catch(Exception ex){}
        }//if-else
         // reinsere-se a lista de simulações na sessão
        session.setAttribute("simulations",simulations);
    }//GET

     //-------- POST
    public void doPost(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException{
        doGet(request,response);
    }//POST

     //-------- INIT
    public void init(){
         // recuperando os parâmetros de inicialização
        ServletConfig config=getServletConfig();
        xslSimulations=config.getInitParameter("xslSimulations");
        xslErreurs=config.getInitParameter("xslErreurs");
        DSNimpots=config.getInitParameter("DSNimpots");
        admimpots=config.getInitParameter("admimpots");
        mdpimpots=config.getInitParameter("mdpimpots");

         // parâmetros corretos?
        if(xslSimulations==null || DSNimpots==null || admimpots==null || mdpimpots==null){
            msgErreur="Configuration incorrecte";
            return;
        }

         // criando uma instância de impotsJDBC
        try{
            impots=new impotsJDBC(DSNimpots,admimpots,mdpimpots);
        }catch(Exception ex){
            msgErreur=ex.getMessage();
        }
    }//inicialização

     //-------- sendErreurs
    private void sendErreurs(PrintWriter out,String xslErreurs,ArrayList erreurs){
        String réponse="<?xml version=\"1.0\" encoding=\"windows-1252\"?>"
                    + "<?xml-stylesheet type=\"text/xsl\" href=\""+xslErreurs+"\"?>\n"
              +"<erreurs>\n";
        for(int i=0;i<erreurs.size();i++){
            réponse+="<erreur>"+(String)erreurs.get(i)+"</erreur>\n";
        }//for
        réponse+="</erreurs>\n";
         // envia-se a resposta
        out.println(réponse);
    }

     //-------- sendSimulations
    private void sendSimulations(PrintWriter out, String xslSimulations, ArrayList simulations){
        String réponse="<?xml version=\"1.0\" encoding=\"windows-1252\"?>"
                    + "<?xml-stylesheet type=\"text/xsl\" href=\""+xslSimulations+"\"?>\n"
              + "<simulations>\n";
        String[] simulation=null;
        for(int i=0;i<simulations.size();i++){
            // simulação n.º i
            simulation=(String[])simulations.get(i);
            réponse+="<simulation "
                     +"marie=\""+(String)simulation[0]+"\" "
                     +"enfants=\""+(String)simulation[1]+"\" "
                     +"salaire=\""+(String)simulation[2]+"\" "
                 +"impot=\""+(String)simulation[3]+"\" />\n";
        }//para
        réponse+="</simulations>\n";
         // envia-se a resposta
        out.println(réponse);
    }
}

Vamos detalhar as principais novidades desse código em relação ao que já conhecíamos:

  • o procedimento `init` recupera novos parâmetros do arquivo de configuração web.xml: os nomes das duas folhas de estilo XSL que devem acompanhar a resposta são armazenados nas variáveis xslSimulations e xslErreurs. Essas duas folhas de estilo são os arquivos simulations.xsl e erreurs.xsl analisados anteriormente. Elas estão localizadas no diretório do aplicativo impots:
dos>dir E:\data\serge\Servlets\impots\*.xsl
27/08/2002  08:15                1 030 simulations.xsl
27/08/2002  09:23                  795 erreurs.xsl
  • O procedimento GET começa verificando se houve algum erro durante a inicialização. Se houver, ele chama o procedimento sendErreurs, que gera a resposta XML adequada a esse caso e, em seguida, encerra-se. Nessa resposta, é inserida a instrução que indica a folha de estilo a ser utilizada.
  • Se não houver erros, o procedimento GET analisa os parâmetros da solicitação do cliente. Se encontrar algum erro, ele o sinaliza utilizando, também nesse caso, o procedimento sendErreurs. Caso contrário, ela calcula a nova simulação, adiciona-a às anteriores armazenadas na sessão atual e conclui enviando sua resposta XML por meio do procedimento sendSimulations. Este último procede de maneira análoga ao procedimento sendErreurs.
  • Observe-se que o servlet anuncia sua resposta como sendo do tipo text/xml:
         // especifica-se o tipo da resposta
        response.setContentType("text/xml");

Aqui estão alguns exemplos de execução. O formulário inicial é preenchido da seguinte maneira:

Image

O banco de dados MySQL não foi iniciado, impossibilitando a construção do objeto impots no procedimento init do servlet. A resposta deste é, então, a seguinte:

Image

O código recebido pelo navegador (Exibir/Fonte) é o seguinte:

Image

Se agora repetirmos duas simulações após ter iniciado o banco de dados MySQL, obtemos o seguinte resultado:

Image

Desta vez, o navegador recebeu o seguinte código:

Image

Observe-se que nosso novo aplicativo está mais simples do que antes, devido à remoção dos arquivos JSP. Parte do trabalho realizado por essas páginas foi transferida para as folhas de estilo XSL. A vantagem dessa nova divisão de tarefas é que, uma vez definido o formato XML das respostas do servlet, o desenvolvimento das folhas de estilo passa a ser independente do desenvolvimento do servlet.

6.3. Análise de um documento XML em Java

As versões 7 e 8 do nosso aplicativo de impostos serão clientes programados do servlet anterior xmlsimulations. Esses clientes receberão o código XML, que deverão analisar para extrair as informações de seu interesse. Vamos fazer aqui uma pausa em nossas diferentes versões e aprender como se pode analisar um documento XML em Java. Faremos isso a partir de um exemplo fornecido com o JBuilder 7, chamado MySaxParser. O programa tem o seguinte nome:

dos>java MySaxParser
Usage: java MySaxParser [URI]

A aplicação MySaxParser aceita um parâmetro: o URI (Uniform Resource Identifier) do documento XML a ser analisado. No nosso exemplo, esse URI será simplesmente o nome de um arquivo XML localizado no diretório do aplicativo MySaxParser. Vejamos dois exemplos de execução. No primeiro exemplo, o arquivo XML analisado é o arquivo erreurs.xml:

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="erreurs.xsl"?>
<erreurs>
    <erreur>erreur 1</erreur>
  <erreur>erreur 2</erreur>
</erreurs>

A análise apresenta os seguintes resultados:

dos> java MySaxParser erreurs.xml
Début du document
Début élément <erreurs>
Début élément <erreur>
[erreur 1]
Fin élément <erreur>
Début élément <erreur>
[erreur 2]
Fin élément <erreur>
Fin élément <erreurs>
Fin du document

Ainda não havíamos explicado o que o aplicativo MySaxParser fazia, mas vemos aqui que ele exibe a estrutura do documento XML analisado. O segundo exemplo analisa o arquivo XML simulations.xml:

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

A análise apresenta os seguintes resultados:

dos>java MySaxParser simulations.xml
Début du document
Début élément <simulations>
Début élément <simulation>
marie = oui
enfants = 2
salaire = 200000
impot = 22504
Fin élément <simulation>
Début élément <simulation>
marie = non
enfants = 2
salaire = 200000
impot = 33388
Fin élément <simulation>
Fin élément <simulations>
Fin du document

A classe MySaxParser contém tudo o que precisamos em nosso aplicativo de impostos, pois ela foi capaz de identificar tanto os erros quanto as simulações que o servidor web poderia enviar. Vamos examinar seu código:

import java.io.IOException;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import org.apache.xerces.parsers.SAXParser;
import java.util.regex.*;

// a classe
public class MySaxParser extends DefaultHandler {

     // valor de um elemento da árvore XML
    private StringBuffer valeur=new StringBuffer();
    // uma expressão regular do valor de um elemento quando se deseja ignorar
     // os “espaços” que o precedem ou seguem
    private static Pattern ptnValeur=null;
    private static Matcher résultats=null;

    // -------- principal
    public static void main(String[] argv) {
        // verificação do número de parâmetros
        if (argv.length != 1) {
            System.out.println("Usage: java MySaxParser [URI]");
            System.exit(0);
        }
         // recupera-se o URI do arquivo XML a ser analisado
        String uri = argv[0];
        try {
            // criação de um analisador XML (parser)
            XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
             // indica-se ao analisador o objeto que implementará os métodos
             // startDocument, endDocument, startElement, endElement, caracteres
            MySaxParser MySaxParserInstance = new MySaxParser();
            parser.setContentHandler(MySaxParserInstance);
             // inicializa-se o modelo de valor de um elemento
            ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
             // indica-se ao analisador o documento XML a ser analisado
            parser.parse(uri);
        }
        catch(Exception ex) {
             // erro
            System.err.println("Erreur : " + ex);
             // log
            ex.printStackTrace();
        }
    }//main


     // -------- startDocument
    public void startDocument() throws SAXException {
         // procedimento chamado quando o analisador encontra o início do documento
        System.out.println("Début du document");
    }//startDocument

     // -------- endDocument
    public void endDocument() throws SAXException {
         // procedimento chamado quando o analisador encontra o fim do documento

        System.out.println("Fin du document");
    }//endDocument

     // -------- startElement
    public void startElement(String uri, String localName, String qName,
        Attributes attributes) throws SAXException {
         // procedimento chamado pelo analisador quando ele encontra o início de uma tag
         // URI: URI do documento analisado?
         // localName: nome do elemento que está sendo analisado
         // qName: o mesmo, mas “qualificado” por um namespace, caso exista
         // atributos: lista dos atributos do elemento

         // rastreamento
        System.out.println("Début élément <"+localName+">");
         // o elemento possui atributos?
        for (int i = 0; i < attributes.getLength(); i++) {
            System.out.println(attributes.getLocalName(i) + " = " + attributes.getValue(i));
        }//para
    }//startElement

    // -------- caracteres
    public void characters(char[] ch, int start, int length) throws SAXException {
        // procedimento chamado repetidamente pelo analisador quando ele encontra texto
         // entre duas tags <tag>texto</tag>
         // o texto está em ch a partir do caractere start por length caracteres

         // o texto é adicionado ao buffer valor
        valeur.append(ch, start, length);
    }//caracteres

        // -------- endElement
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
         // procedimento chamado pelo analisador quando ele encontra o fim de uma tag
         // URI: URI do documento analisado?
         // localName: nome do elemento que está sendo analisado
         // qName: o mesmo, mas “qualificado” por um namespace, caso exista

         // exibe-se o valor do elemento
        String strValeur=valeur.toString();
        if (ptnValeur==null) System.out.println("null");
        résultats=ptnValeur.matcher(strValeur);
        if (résultats.find() && ! résultats.group(1).equals("")){
            System.out.println("["+résultats.group(1)+"]");
        }//se
         // define-se o valor do elemento como vazio
        valeur.setLength(0);

         // seguido de
        System.out.println("Fin élément <"+localName+">");
    }//endElement

}//classe

Vamos, antes de mais nada, definir uma sigla que aparece com frequência na análise de documentos XML: SAX, que significa Simple API for XML. Trata-se de um conjunto de classes Java que facilitam o trabalho com documentos XML. Existem duas versões do API: SAX1 e SAX2. O aplicativo acima utiliza o API SAX2.

O aplicativo importa uma série de pacotes:

import org.xml.sax.*;
import org.xml.sax.helpers.*;
import org.apache.xerces.parsers.SAXParser;

Os dois primeiros vêm com o JDK 1.4, o terceiro não. O pacote xerces.jar está disponível no site do servidor Web Apache. Ele vem com o JBuilder 7, mas também com o Tomcat 4.x:

Image

Portanto, se quisermos compilar a aplicação anterior fora do JBuilder 7 e tivermos o JDK 1.4 e o Tomcat 4.x, poderemos escrever:

dos>javac -classpath ".;E:\Program Files\Apache Tomcat 4.0\common\lib\xerces.jar" MySaxParser.java

Na execução, faremos o mesmo:

dos>java -classpath ".;E:\Program Files\Apache Tomcat 4.0\common\lib\xerces.jar" MySaxParser simulations.xml

A classe MySaxParser deriva da classe DefaultHandler. Voltaremos a esse assunto. Vamos analisar o código da procedimento main:

         // recupera-se o URI do arquivo XML a ser analisado
        String uri = argv[0];
        try {
            // criação de um analisador XML (parser)
            XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
             // indica-se ao analisador o objeto que implementará os métodos
             // startDocument, endDocument, startElement, endElement, caracteres
            MySaxParser MySaxParserInstance = new MySaxParser();
            parser.setContentHandler(MySaxParserInstance);
             // inicializa-se o modelo de valor de um elemento
            ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
             // indica-se ao analisador o documento XML a ser analisado
            parser.parse(uri);
        }
        catch(Exception ex) {
             // erro
            System.err.println("Erreur : " + ex);
             // log
            ex.printStackTrace();
        }

Para analisar um documento XML, nosso aplicativo precisa de um analisador de código XML chamado “parser”.

            XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");

O analisador XML utilizado é aquele fornecido pelo pacote xerces.jar. O objeto recuperado é do tipo XMLReader. XMLReader é uma interface da qual utilizamos aqui dois métodos:

void setContentHandler(ContentHandler handler)
indica ao analisador o objeto do tipo ContentHandler que irá processar os eventos que ele irá gerar durante a análise do documento XML
void parse(InputSource input)
inicia a análise do documento XML passado como parâmetro

Quando o analisador analisar o documento XML, ele emitirá eventos como: “encontrei o início do documento, o início de uma tag, um atributo de tag, o conteúdo de uma tag, o fim de uma tag, o fim do documento, ...”. Ele transmite esses eventos ao objeto ContentHandler que lhe foi fornecido. ContentHandler é uma interface que define os métodos a serem implementados para processar todos os eventos que o analisador XML pode gerar. DefaultHandler é uma classe que fornece uma implementação padrão desses métodos. Os métodos implementados em DefaultHandler não realizam nenhuma ação, mas existem. Quando é necessário indicar ao analisador qual objeto irá processar os eventos que ele irá gerar por meio da instrução

void setContentHandler(ContentHandler handler)

, é prático passar como parâmetro um objeto do tipo DefaultHandler. Se ficássemos por aí, nenhum evento do analisador seria tratado, mas nosso programa seria sintaticamente correto. Na prática, passa-se como parâmetro ao analisador um objeto derivado da classe DefaultHandler, no qual são redefinidos os métodos que tratam apenas dos eventos que nos interessam. É isso que é feito aqui:

             // indica-se ao analisador o objeto que implementará os métodos
             // startDocument, endDocument, startElement, endElement, caracteres
            MySaxParser MySaxParserInstance = new MySaxParser();
            parser.setContentHandler(MySaxParserInstance);
             // indica-se ao analisador o documento XML a ser analisado
            parser.parse(uri);

Passamos ao analisador uma instância da classe mySaxParser, que é a nossa classe e que foi definida anteriormente pela declaração

public class MySaxParser extends DefaultHandler {

e iniciamos a análise do documento, passando o URI como parâmetro. A partir daí, começa a análise do documento XML. O analisador emite eventos e, para cada um deles, chama um método específico do objeto responsável por processar esses eventos, neste caso, nosso objeto MySaxParser. Este processa cinco eventos específicos, sendo os demais ignorados:

evento emitido pelo analisador
método de processamento
début du document    
void startDocument()
fin du document
void endDocument()
début d'un élément :<balise>
public void startElement(String uri, String localName, String qName, Attributes attributes)
uri: ?
localName: nome do elemento analisado. Se o elemento encontrado for <simulations>, teremos localName="simulações".
qName: nome qualificado por um espaço de nomes do elemento analisado. Um documento XML pode definir um espaço de nomes, como XX, por exemplo. O nome qualificado da tag anterior seria, então, XX:simulations.
attributes: lista de atributos da tag
valeur d'un élément :
<balise>valeur</balise>
public void characters(char[] ch, int start, int length)
ch: matriz de caracteres
start: índice do primeiro caractere a ser utilizado na matriz ch
length: número de caracteres a serem retirados da matriz ch
O método characters pode ser chamado repetidamente. Para construir o valor de um elemento, utiliza-se então um buffer que:
  • esvazia no início do elemento
  • preenche a cada nova chamada do método characters
  • finaliza ao término do elemento
fin d'un élément : </balise> ou <balise .../>
void endElement(String uri, String localName, String qName)
os parâmetros são os mesmos do método startElement.

O método startElement permite recuperar os atributos do elemento por meio do parâmetro attributes, do tipo Attributes:

  • o número de atributos está disponível em attributes.getLength()
  • o nome do atributo i está disponível em attributes.getLocalName(i)
  • o valor do atributo i está disponível em attributes.getValue(i)
  • o valor do atributo de nome localName em attributes.getValue(localName)

Uma vez explicado isso, o programa anterior, acompanhado dos exemplos de execução, torna-se autoexplicativo. Foi utilizada uma expressão regular para recuperar os valores dos elementos, de modo que um texto XML como:

<erreur>
    erreur 1
</erreur>

resulte no valor associado à tag <erreur>, que é o texto “erro 1”, sem os espaços e quebras de linha que possam precedê-lo e/ou segui-lo.

6.4. Aplicativo de impostos: versão 7

Agora temos todos os elementos para escrever clientes programados para nosso serviço de impostos, que gera o XML. Retomamos a versão 4 de nosso aplicativo para criar o cliente e mantemos a versão 6 para o servidor. Nesta aplicação cliente-servidor:

  • o serviço de simulações do cálculo de impostos é realizado pelo servlet xmlsimulations. A resposta do servidor está, portanto, no formato XML, como vimos na versão 6.
  • o cliente não é mais um navegador, mas um cliente Java autônomo. Sua interface gráfica é a da versão 4.

Aqui estão alguns exemplos de execução. Primeiro, um caso de erro: o cliente consulta o servlet xmlsimulations, embora este não tenha conseguido se inicializar corretamente, pois o SGBD MySQL não havia sido iniciado:

Image

Iniciamos o MySQL e realizamos algumas simulações:

Image

O cliente desta nova versão difere do cliente da versão 4 apenas na forma como processa a resposta do servidor. Nada mais muda. Na versão 4, o cliente recebia o código HTML, no qual buscava as informações de seu interesse usando expressões regulares. Aqui, o cliente recebe o código XML, no qual ele recupera as informações de seu interesse com a ajuda de um analisador XML.

Vamos relembrar as principais etapas do procedimento relacionado ao menu “Calcular” da versão 4 do nosso cliente, já que é principalmente ali que as alterações são feitas:

  void mnuCalculer_actionPerformed(ActionEvent e) {
....
    try{
             // calcula-se o imposto
            calculerImpots(urlImpots,rdOui.isSelected(),nbEnfants.intValue(),salaire);
    }catch (Exception ex){
             // exibe-se o erro
            JOptionPane.showMessageDialog(this,"L'erreur suivante s'est produite : " + ex.getMessage(),"Erreur",JOptionPane.ERROR_MESSAGE);
        }
....
    }//mnuCalculer_actionPerformed

    public void calculerImpots(URL urlImpots,boolean marié, int nbEnfants, int salaire)
          throws Exception{
         // cálculo do imposto
         // urlImpots: URL da Receita Federal
         // casado: true se for casado, false caso contrário
         // nbEnfants: número de filhos
         // salário: salário anual

         // retira-se de urlImpots as informações necessárias para a conexão com o servidor da Receita Federal
....
        try{
             // conecta-se ao servidor
....

             // criam-se os fluxos de entrada e saída do cliente TCP
....

             // solicita-se o URL - envio dos cabeçalhos HTTP
....

             // -se a primeira linha da resposta
....
             // -se a resposta até o final dos cabeçalhos, procurando por um eventual cookie
            while((réponse=IN.readLine())!=null){
....            }//enquanto

            // os cabeçalhos terminaram HTTP  passa-se para o código HTML
             // para recuperar as simulações
            ArrayList listeSimulations=getSimulations(IN,OUT,simulations);
            simulations.clear();
            for (int i=0;i<listeSimulations.size();i++){
                simulations.addElement(listeSimulations.get(i));
            }

            // acabou
....
    }//calculerImpots

    private ArrayList getSimulations(BufferedReader IN, PrintWriter OUT, DefaultListModel simulations) throws Exception{
....
    }

Todo esse código permanece válido na nova versão. Apenas o processamento da resposta HTML do servidor (parte destacada acima) e sua exibição devem ser substituídos pelo processamento da resposta XML do servidor e sua exibição:

             // concluído para os cabeçalhos HTTP  passamos para o código XML
             // para recuperar as simulações ou os erros
            ImpotsSaxParser parseur=new ImpotsSaxParser(IN);
            ArrayList listeErreurs=parseur.getErreurs();
            ArrayList listeSimulations=parseur.getSimulations();
             // encerramento da conexão com o servidor
            client.close();

             // limpeza da lista de exibição
            simulations.clear();
             // erros
            if(listeErreurs.size()!=0){
                 // concatenamos todos os erros
                String msgErreur="Le serveur a signalé les erreurs suivantes :\n";
                for(int i=0;i<listeErreurs.size();i++){
                    msgErreur+=" - "+(String)listeErreurs.get(i);
                }
                 // exibição de erros
                throw new Exception(msgErreur);
            }//if

            // simulações
            for (int i=0;i<listeSimulations.size();i++){
                simulations.addElement(listeSimulations.get(i));
            }
            return;

O que faz o trecho de código acima?

  • Ela cria um analisador XML e passa a ele o fluxo IN, que contém o código XML enviado pelo servidor. Esse fluxo também continha os cabeçalhos HTTP, mas estes já foram lidos e processados. Portanto, resta apenas a parte XML da resposta. O analisador gera duas listas de cadeias de caracteres: a lista de erros, caso haja algum, ou, caso contrário, a lista de simulações. Essas duas listas são mutuamente exclusivas.
  • Se a lista de erros não estiver vazia, as mensagens contidas na lista são concatenadas em uma única mensagem de erro e uma exceção é lançada com essa mensagem como parâmetro. Essa exceção é exibida no procedimento mnuCalculer_actionPeformed, que chamou o calculerImpots.
  • Se a lista de simulações não estiver vazia, ela é exibida no componente jList da interface gráfica.

Vamos agora conhecer o analisador da resposta XML do servidor, analisador que decorre diretamente do estudo que realizamos anteriormente sobre como analisar um documento XML em Java:

import java.io.IOException;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import org.apache.xerces.parsers.SAXParser;
import java.util.regex.*;
import java.io.*;
import java.util.*;
import javax.swing.*;

// a classe
public class ImpotsSaxParser extends DefaultHandler {

    // valor de um elemento da árvore XML
    private StringBuffer valeur=new StringBuffer();
    // uma expressão regular do valor de um elemento quando se deseja ignorar
     // os espaços que o precedem ou o seguem
    private Pattern ptnValeur=null;
    private Matcher résultats=null;
     // as listas de elementos XML
    private ArrayList listeSimulations=new ArrayList();
    private ArrayList listeErreurs=new ArrayList();
     // elementos XML
    private ArrayList éléments=new ArrayList();
    String élément="";

     // -------- fabricante
    public ImpotsSaxParser(BufferedReader IN) throws Exception{
        // criação de um analisador XML (parser)
        XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
         // indica-se ao analisador o objeto que implementará os métodos
         // startDocument, endDocument, startElement, endElement, caracteres
        parser.setContentHandler(this);
        // inicializa-se o modelo de valor de um elemento
        ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
         // inicialmente, não  nenhum elemento XML ativo
        éléments.add("");
         // analisa-se o documento
        parser.parse(new InputSource(IN));
    }//construtor

     // -------- startElement
    public void startElement(String uri, String localName, String qName,
        Attributes attributes) throws SAXException {
         // procedimento chamado pelo analisador quando ele encontra o início de uma tag
         // URI: URI do documento analisado?
         // localName: nome do elemento que está sendo analisado
         // qName: o mesmo, mas qualificado por um namespace, caso exista
         // attributes: lista dos atributos do elemento

         // observa-se o nome do elemento
        élément=localName.toLowerCase();
        éléments.add(élément);
         // o elemento possui atributos?
        if(élément.equals("simulation") && attributes.getLength()==4){
            // trata-se de uma simulação  recuperam-se os atributos
            String simulation=attributes.getValue("marie")+","+
                                         attributes.getValue("enfants")+","+
                                         attributes.getValue("salaire")+","+
                                         attributes.getValue("impot");
             // adiciona-se a simulação à lista de simulações
            listeSimulations.add(simulation);
        }//if
    }//startElement

    // -------- caracteres
    public void characters(char[] ch, int start, int length) throws SAXException {
        // procedimento chamado repetidamente pelo analisador quando ele encontra texto
         // entre duas tags <tag>texto</tag>
         // o texto está em ch a partir do caractere start por length caracteres

         // o texto é adicionado ao buffer valor se for o elemento erro
        if (élément.equals("erreur"))
           valeur.append(ch, start, length);
    }//caracteres

        // -------- endElement
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
         // procedimento chamado pelo analisador quando ele encontra o fim de uma tag
         // URI: URI do documento analisado?
         // localName: nome do elemento que está sendo analisado
         // qName: o mesmo, mas qualificado por um namespace, caso exista

         // caso de erro
        if(élément.equals("erreur")){
             // recuperamos o valor do elemento de erro
            String strValeur=valeur.toString();
             // removemos os espaços desnecessários e o registramos na lista de
             // erros, caso ela não esteja vazia
            résultats=ptnValeur.matcher(strValeur);
            if (résultats.find() && ! résultats.group(1).equals("")){
                listeErreurs.add(résultats.group(1));
            }//if
        }
         // coloca-se o valor do elemento como vazio
        valeur.setLength(0);
         // reinicializa-se o nome do elemento
        éléments.remove(éléments.size()-1);
        élément=(String)éléments.get(éléments.size()-1);
    }//endElement

     // --------- getErreurs
    public ArrayList getErreurs(){
        return listeErreurs;
    }

     // --------- getSimulations
    public ArrayList getSimulations(){
        return listeSimulations;
    }

}//classe
  • O construtor recebe o fluxo XML IN para análise e realiza essa análise imediatamente. Concluída a análise, o objeto foi criado, assim como as listas (ArrayList) de erros (listeErreurs) e de simulações (listeSimulations). Resta agora ao procedimento que criou o objeto recuperar as duas listas por meio dos métodos getErreurs e getSimulations.
  • Apenas três eventos gerados pelo analisador XML nos interessam aqui:
    • início de um elemento XML, evento que será processado pela rotina startElement. Este procedimento deverá processar as tags <simulation marie=".." enfants=".." salaire=".." impot=".."> e <erreur>...</erreur>.
    • valor de um elemento XML, evento que será processado pela rotina characters.
    • fim de um elemento XML, evento que será processado pelo procedimento endElement.
  • no procedimento startElement, se estivermos lidando com o elemento <simulation marie=".." enfants=".." salaire=".." impot="..">, recuperamos os quatro atributos por meio de attributes.getValue("nome do atributo"). Em todos os casos, armazena-se o nome do elemento em uma variável `elemento` e adiciona-se a uma lista (ArrayList) de elementos: elem1, elem2, ..., elemn. Essa lista é gerenciada como uma pilha, cujo último elemento é o elemento XML que está sendo analisado. Quando ocorre o evento “fim de elemento”, o último elemento da lista é removido e o novo elemento atual é modificado. Isso é feito no procedimento endElement.
  • O procedimento characters é idêntico ao que foi analisado em um exemplo anterior. Basta apenas verificar se o elemento atual é realmente o elemento <erreur>, precaução que normalmente seria desnecessária neste caso. Esse tipo de precaução também havia sido tomado no procedimento startElement para verificar se se tratava de um elemento <simulation>.

6.5. Conclusão

Graças à sua resposta XML, o aplicativo impots tornou-se mais fácil de gerenciar tanto para seu desenvolvedor quanto para os desenvolvedores dos aplicativos clientes.

  • A concepção do aplicativo servidor agora pode ser confiada a dois tipos de profissionais: o desenvolvedor Java do servlet e o designer gráfico, que irá gerenciar a aparência da resposta do servidor nos navegadores. Basta que este último conheça a estrutura da resposta XML do servidor para criar as folhas de estilo que a acompanharão. Vale lembrar que essas folhas de estilo são arquivos XSL separados e independentes do servlet Java. O designer gráfico pode, portanto, trabalhar independentemente do desenvolvedor Java.
  • Os desenvolvedores de aplicativos clientes também precisam apenas conhecer a estrutura da resposta XML do servidor. As alterações que o designer gráfico possa fazer nas folhas de estilo não têm qualquer repercussão nessa resposta XML, que permanece sempre a mesma. Essa é uma enorme vantagem.
  • Como o desenvolvedor pode atualizar seu servlet Java sem causar problemas? Em primeiro lugar, desde que sua resposta XML não mude, ele pode organizar seu servlet como quiser. Ele também pode atualizar a resposta XML, desde que mantenha os elementos <erro> e <simulação> esperados por seus clientes. Assim, ele pode adicionar novas tags a essa resposta. O web designer levará isso em consideração em suas folhas de estilo, e os navegadores poderão receber as novas versões da resposta. Já os clientes programados continuarão funcionando com o modelo antigo, sendo que as novas tags serão simplesmente ignoradas. Para que isso seja possível, é necessário que, na análise XML da resposta do servidor, as tags procuradas sejam devidamente identificadas. Foi isso que foi feito em nosso cliente XML do aplicativo de impostos, onde, nos procedimentos, especificava-se que se tratavam as tags <erreur> e <simulation>. Assim, as demais tags são ignoradas.