6. XML ve JAVA
Bu bölümde, Java ile XML belgelerinin kullanımını tanıtacağız. Bunu, bir önceki bölümde incelenen vergi uygulaması bağlamında yapacağız.
6.1. XML dosyaları ve XSL stil sayfaları
Vergi hesaplamaları simülasyonlarının sonucunu temsil edebilecek aşağıdaki XML simulations.xml dosyasını ele alalım:
<?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>
Bunu IE 6 ile görüntülediğimizde şu sonuç elde edilir:

IE6, (dosyanın .xml uzantısı sayesinde) bir XML dosyasıyla karşı karşıya olduğunu algılar ve dosyayı kendine özgü bir şekilde sayfa düzenine yerleştirir. Netscape ile boş bir sayfa görüntülenir. Ancak kaynak koduna (Görünüm/Kaynak) bakıldığında, orijinal XML dosyası olduğu görülür:

Netscape neden hiçbir şey göstermiyor? Çünkü XML dosyasını, görüntüleyebileceği HTML dosyasına nasıl dönüştüreceğini belirten bir stil sayfasına ihtiyacı var. Aslında IE 6'nın varsayılan bir stil sayfası varken, XML dosyasında böyle bir stil sayfası bulunmuyor; bu durum burada da geçerliydi.
XSL (eXtended StyleSheet Dili) adlı bir dil mevcuttur; bu dil, bir XML dosyasını herhangi bir metin dosyasına dönüştürmek için gerçekleştirilmesi gereken dönüşümleri tanımlamaya olanak tanır. XSL, çok sayıda komutun kullanımına izin verir ve programlama dillerine oldukça benzer. Burada ayrıntılı olarak ele almayacağız, çünkü bu onlarca sayfa gerektirecektir. Sadece iki XSL stil sayfası örneğini açıklayacağız. İlki, XML dosyasını simulations.xml koduna dönüştürecek olan stil sayfasıdır. Bu dosyayı, tarayıcıların onu HTML belgesine dönüştürmek için kullanabilecekleri stil sayfasını belirtecek şekilde değiştiriyoruz; tarayıcılar bu belgeyi görüntüleyebileceklerdir:
<?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>
XML komutu
komutu, simulations.xsl dosyasını text/xsl türünde bir stil sayfası (xml-stylesheet) olarak tanımlar; c.a.d ise XSL kodunu içeren bir metin dosyasıdır. Bu stil sayfası, tarayıcılar tarafından XML metnini HTML belgesine dönüştürmek için kullanılacaktır. XML simulations.xml dosyasını yüklediğimizde Netscape 7 ile elde edilen sonuç şöyledir:

Belgenin kaynak koduna baktığımızda (Görünüm/Kaynak), görüntülenen HTML belgesi yerine, orijinal XML belgesini görüyoruz:

Netscape, yukarıdaki XML belgesini görüntülenebilir HTML belgesine dönüştürmek için simulations.xsl stil sayfasını kullanmıştır. Şimdi bu stil sayfasının içeriğine bir göz atalım:
<?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>
- XSL stil sayfası, bir XML dosyasıdır ve bu nedenle onun kurallarına uyar. Diğer şeylerin yanı sıra "düzgün biçimlendirilmiş" olmalıdır, yani açılan tüm etiketler kapatılmalıdır.
- Dosya, her XSL stil sayfasında korunabilecek iki komutla başlar:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
encoding="ISO-8859-1" özniteliği, stil sayfasında aksanlı karakterlerin kullanılmasını sağlar.
- <xsl:output method="html" indent="yes"/> etiketi, XSL yorumlayıcısına "girintili" HTML çıktısı üretilmesini istediğimizi belirtir.
- <xsl:template match="eleman"> etiketi, <xsl:template ...> ile </xsl:template> arasında yer alan talimatların uygulanacağı XML belgesindeki elemanı tanımlamak için kullanılır.
Yukarıdaki örnekte "/" öğesi, belgenin kökünü belirtir. Bu, XML belgesinin başlangıcına ulaşılır ulaşılmaz, iki etiket arasında yer alan XSL komutlarının yürütüleceği anlamına gelir.
- XSL etiketleri dışında kalan her şey, olduğu gibi çıktı akışına eklenir. XSL etiketleri ise yürütülür. Bunlardan bazıları, çıktı akışına eklenen bir sonuç üretir. Aşağıdaki örneği inceleyelim:
<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>
Analiz edilen XML belgesinin içeriği şöyledir:
<?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>
Analiz edilen XML belgesinin başından itibaren (match="/"), XSL yorumlayıcısı şu metni çıktı olarak üretecektir
<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>
İlk metinde <hr/> olduğu, <hr> olmadığı dikkatinizi çekecektir. İlk metinde <hr> yazılmamıştı; bu, geçerli bir HTML etiketi olsa da, geçersiz bir XML etiketidir. Oysa burada "düzgün biçimlendirilmiş" olması gereken bir metinle karşı karşıyayız; yani her etiket kapatılmalıdır. Bu nedenle <hr/> yazılır ve <xsl:output text="html ..."> yazdığımız için XSL yorumlayıcısı <hr/> metnini <hr> şeklinde dönüştürecektir. Bu metnin ardından, XSL komutu tarafından üretilen metin gelecektir:
Bu metnin ne olduğunu daha sonra göreceğiz. Son olarak yorumlayıcı şu metni ekleyecektir:
<xsl:apply-templates select="/simulations/simulation"/> komutu, /simulations/simulation öğesinin "şablonunun" (template) çalıştırılmasını ister. Bu komut, XSL yorumlayıcısı, analiz edilen XML metninde bir <simulations>..</simulations> etiketi içinde bir <simulation>..</simulations> veya <simulation/> etiketi ile her karşılaştığında çalıştırılacaktır. <simulation> etiketiyle karşılaştığında, yorumlayıcı aşağıdaki şablondaki talimatları uygulayacaktır:
<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>
Aşağıdaki XML satırlarını ele alalım:
<simulation ..> satırı, XSL <xsl:apply-templates select="/simulations/simulation>" komutunun şablonuna karşılık gelir. Dolayısıyla XSL yorumlayıcısı, bu şablona karşılık gelen komutları ona uygulamaya çalışacaktır. <xsl:template match="simulation"> şablonunu bulacak ve bunu çalıştıracaktır. Hatırlanacağı üzere, XSL komutu olmayan kısımlar XSL yorumlayıcısı tarafından olduğu gibi alınır ve XSL komutları ise yürütülmelerinin sonucu ile değiştirilir. Böylece XSL <xsl:value-of select="@champ"/> komutu, analiz edilen düğümün (burada bir <simulation> düğümü) "champ" özniteliğinin değeriyle değiştirilir. Önceki XML satırının analizi, aşağıdaki çıktıyı verecektir:
XSL | çıktı |
<tr><td> | <tr><td> |
<xsl:value-of select="@marie"/> | evet |
</td><td> | </td><td> |
<xsl:value-of select="@enfants"/> | 2 |
</td><td> | </td><td> |
<xsl:value-of select="@salaire"/> | 200000 |
</td><td> | </td><td> |
<xsl:value-of select="@impot"/> | 22504 |
</td></tr> | </td></tr> |
Toplamda, XML satırı
satırı, HTML satırına dönüştürülecektir:
Tüm bu açıklamalar biraz basit olsa da, okuyucuya şu anda aşağıdaki XML metninin:
<?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şağıdaki XSL simulations.xsl stil sayfası ile birlikte:
<?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>
aşağıdaki HTML metnini üretir:
<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>
XML ve simulations.xml dosyaları, simulations.xsl stil sayfasıyla birlikte yeni bir tarayıcıda (burada Netscape 7) açıldığında şu şekilde görüntülenir:

6.2. Vergi uygulaması: sürüm 6
6.2.1. Vergi uygulamasına ait XML dosyaları ve XSL stil sayfaları
Vergi web uygulamasına geri dönelim ve müşterilere verilen yanıtın HTML formatında değil, XML formatında olmasını sağlayacak şekilde uygulamayı değiştirelim. Bu XML yanıtına, tarayıcıların onu görüntüleyebilmesi için bir XSL stil sayfası eşlik edecektir. Bir önceki paragrafta şunları sunmuştuk:
- simulations.xml dosyası; bu, vergi hesaplama simülasyonları içeren bir XML yanıtının prototipidir
- simulations.xsl dosyası; bu, XML yanıtına eşlik edecek olan XSL stil sayfası olacaktır
Ayrıca, hatalı yanıt durumunu da göz önünde bulundurmamız gerekiyor. Bu durumda XML yanıtının prototipi, aşağıdaki erreurs.xml dosyası olacaktır:
<?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>
Bu XML belgesini bir tarayıcıda görüntülemek için kullanılan erreurs.xsl stil sayfası aşağıdaki gibi olacaktır:
<?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>
Bu stil sayfası, henüz karşılaşılmamış bir XSL komutunu tanıtmaktadır: <xsl:value-of select="."/>. Bu komut, analiz edilen düğümün değerini çıktı olarak verir; burada bu, bir <erreur>texte</erreur> düğümüdür. Bu düğümün değeri, açılış ve kapanış etiketleri arasında yer alan metindir; bu örnekte texte.
erreurs.xml kodu, erreurs.xsl stil sayfası tarafından aşağıdaki HTML belgesine dönüştürülür:
<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>
erreurs.xml dosyası ve stil sayfası, bir tarayıcıda şu şekilde görüntülenir:

6.2.2. xmlsimulations servleti
index.html adlı bir dosya oluşturup bunu impots uygulamasının dizinine yerleştiriyoruz. Görüntülenen sayfa şu şekildedir:

Bu HTML belgesi statik bir belgedir. Kodu şu şekildedir:
<html>
<head>
<title>impots</title>
<script language="JavaScript" type="text/javascript">
function effacer(){
// formu sıfırla
with(document.frmImpots){
optMarie[0].checked=false;
optMarie[1].checked=true;
txtEnfants.value="";
txtSalaire.value="";
txtImpots.value="";
}//with
}//sil
function calculer(){
// parametreleri sunucuya göndermeden önce kontrol et
with(document.frmImpots){
//çocuk sayısı
champs=/^\s*(\d+)\s*$/.exec(txtEnfants.value);
if(champs==null){
// şablon doğrulanmadı
alert("Le nombre d'enfants n'a pas été donné ou est incorrect");
nbEnfants.focus();
return;
}//eğer
//maaş
champs=/^\s*(\d+)\s*$/.exec(txtSalaire.value);
if(champs==null){
// şablon doğrulanmadı
alert("Le salaire n'a pas été donné ou est incorrect");
salaire.focus();
return;
}//eğer
// tamam - gönderiliyor
submit();
}//ile
}//hesapla
</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>
Form verilerinin URL /impots/xmlsimulations adresine gönderildiğine dikkat edilmelidir. Bu uygulama, impots uygulamasının web.xml dosyasında aşağıdaki şekilde yapılandırılmış bir Java servletidir:
<?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>
- Servlet'in adı xmlsimulations'tır ve xmlsimulations.class sınıfını temel alır.
- Bu servletin parametreleri arasında, vergi veritabanına erişim için gerekli olan DSNimpots, admimpots ve mdpimpots parametreleri bulunmaktadır. Ayrıca, iki parametre daha kabul eder:
- xslSimulations, simülasyonları içeren XML yanıtına eşlik etmesi gereken stil dosyasının adıdır
- xslErreurs, olası hataları içeren XML yanıtına eşlik etmesi gereken stil dosyasının adıdır
- Bu servletin xmlsimulations takma adı vardır; bu sayede URL ve http://localhost:8080/impots/xmlsimulations aracılığıyla erişilebilir.
xmlsimulations servletinin iskeleti, daha önce incelediğimiz simulations servletininkine benzerdir. Temel fark, HTML yerine XML üretmesi gerektiğidir. Bu durum, önceki uygulamalarda kullanılan JSP dosyalarının kaldırılmasına yol açacaktır. Bu dosyaların temel işlevi, oluşturulan HTML kodunun, servletin Java kodunun içinde kaybolmasını önleyerek okunabilirliğini artırmaktı. Artık bu işlevin bir gereği kalmamıştır. Servlet, iki tür XML kodu oluşturmalıdır:
- simülasyonlar için olan
- hatalar için olan
Daha önce, bu iki durumda sağlanması gereken iki tür XML yanıtını ve bunlara eşlik etmesi gereken stil sayfalarını tanıtmış ve incelemiştik. Servlet kodu şöyledir:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.regex.*;
import java.util.*;
public class xmlsimulations extends HttpServlet{
// örnek değişkenleri
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{
// müşteriye yazma akışını alıyoruz
PrintWriter out=response.getWriter();
// yanıt türünü belirtme
response.setContentType("text/xml");
// hata listesi
ArrayList erreurs=new ArrayList();
// başlatma işlemi başarılı oldu mu?
if(msgErreur!=null){
// işlem tamamlandı - hataları içeren yanıt sunucuya gönderiliyor
erreurs.add(msgErreur);
sendErreurs(out,xslErreurs,erreurs);
// işlem tamamlandı
return;
}
// oturumun önceki simülasyonları alınıyor
HttpSession session=request.getSession();
ArrayList simulations=(ArrayList)session.getAttribute("simulations");
if(simulations==null) simulations=new ArrayList();
// geçerli isteğin parametreleri alınıyor
String optMarie=request.getParameter("optMarie"); // medeni durum
String txtEnfants=request.getParameter("txtEnfants"); // çocuk sayısı
String txtSalaire=request.getParameter("txtSalaire"); // yıllık maaş
// beklenen tüm parametreler mevcut mu
if(optMarie==null || txtEnfants==null || txtSalaire==null){
// parametreler eksik
// yanıt hatalı olarak gönderiliyor
erreurs.add("Demande incomplète. Il manque des paramètres");
sendErreurs(out,xslErreurs,erreurs);
// işlem tamamlandı
return;
}
// tüm parametreler mevcut - kontrol ediliyor
// medeni durum
if( ! optMarie.equals("oui") && ! optMarie.equals("non")){
// hata
erreurs.add("Etat marital incorrect");
}
// çocuk sayısı
txtEnfants=txtEnfants.trim();
if(! Pattern.matches("^\\d+$",txtEnfants)){
// hata
erreurs.add("Nombre d'enfants incorrect");
}
// maaş
txtSalaire=txtSalaire.trim();
if(! Pattern.matches("^\\d+$",txtSalaire)){
// hata
erreurs.add("Salaire incorrect");
}
if(erreurs.size()!=0){
// hatalar varsa bildirilir
sendErreurs(out,xslErreurs,erreurs);
}else{
// hata yok
try{
// ödenecek vergi hesaplanabilir
int nbEnfants=Integer.parseInt(txtEnfants);
int salaire=Integer.parseInt(txtSalaire);
String txtImpots=""+impots.calculer(optMarie.equals("oui"),nbEnfants,salaire);
// mevcut sonucu önceki simülasyonlara eklenir
String[] simulation={optMarie.equals("oui") ? "oui" : "non",txtEnfants, txtSalaire, txtImpots};
simulations.add(simulation);
// simülasyonlarla birlikte yanıt gönderilir
sendSimulations(out,xslSimulations,simulations);
}catch(Exception ex){}
}//if-else
// simülasyon listesini oturuma geri aktarılır
session.setAttribute("simulations",simulations);
}//GET
//-------- POST
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException{
doGet(request,response);
}//POST
//-------- INIT
public void init(){
// başlatma parametreleri alınır
ServletConfig config=getServletConfig();
xslSimulations=config.getInitParameter("xslSimulations");
xslErreurs=config.getInitParameter("xslErreurs");
DSNimpots=config.getInitParameter("DSNimpots");
admimpots=config.getInitParameter("admimpots");
mdpimpots=config.getInitParameter("mdpimpots");
// parametreler uygun mu?
if(xslSimulations==null || DSNimpots==null || admimpots==null || mdpimpots==null){
msgErreur="Configuration incorrecte";
return;
}
// impotsJDBC örneği oluşturuluyor
try{
impots=new impotsJDBC(DSNimpots,admimpots,mdpimpots);
}catch(Exception ex){
msgErreur=ex.getMessage();
}
}//başlatma
//-------- 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";
// yanıt gönderiliyor
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++){
// simülasyon no. 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";
}//için
réponse+="</simulations>\n";
// yanıt gönderiliyor
out.println(réponse);
}
}
Bu kodun, daha önce bildiğimiz versiyona kıyasla getirdiği başlıca yenilikleri ayrıntılı olarak inceleyelim:
- init prosedürü, web.xml yapılandırma dosyasından yeni parametreler alır: yanıtla birlikte gönderilmesi gereken iki stil sayfasının (XSL) adları, xslSimulations ve xslErreurs değişkenlerine yerleştirilir. Bu iki stil sayfası, daha önce incelediğimiz simulations.xsl ve erreurs.xsl dosyalarıdır. Bunlar, impots uygulamasının dizinine yerleştirilir:
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
- GET prosedürü, öncelikle başlatma sırasında bir hata olup olmadığını kontrol eder. Eğer varsa, bu duruma uygun XML yanıtını oluşturan sendErreurs prosedürünü çağırır ve ardından sonlanır. Bu XML yanıtına, kullanılacak stil sayfasını belirten talimat eklenir.
- Hata olmaması durumunda, GET prosedürü istemcinin isteğinin parametrelerini analiz eder. Herhangi bir hata bulursa, yine sendErreurs prosedürünü kullanarak bunu bildirir. Aksi takdirde, yeni simülasyonu hesaplar, bunu mevcut oturumda saklanan eski simülasyonlara ekler ve sendSimulations prosedürü aracılığıyla XML yanıtını göndererek işlemi sonlandırır. Bu son prosedür, sendErreurs prosedürüne benzer şekilde çalışır.
- Servlet'in yanıtını text/xml türü olarak bildirdiği dikkat çekmektedir:
İşte bazı yürütme örnekleri. Başlangıç formu şu şekilde doldurulur:

MySQL veritabanı başlatılmadığından, servletin init prosedüründe impots nesnesinin oluşturulması mümkün olmamıştır. Servletin yanıtı şu şekildedir:

Tarayıcı tarafından alınan kod (Görünüm/Kaynak) şöyledir:

Şimdi, MySQL veritabanını başlattıktan sonra iki simülasyonu tekrar yaparsak, şu sonucu elde ederiz:

Tarayıcı bu sefer şu kodu aldı:

JSP dosyalarının kaldırılması nedeniyle yeni uygulamamızın eskisinden daha basit olduğu dikkatinizi çekecektir. Bu sayfaların üstlendiği işlerin bir kısmı, XSL stil sayfalarına aktarılmıştır. Yeni görev dağılımımızın avantajı, servletin yanıtlarının XML formatı bir kez belirlendikten sonra, stil sayfalarının geliştirilmesinin servletin geliştirilmesinden bağımsız hale gelmesidir.
6.3. Java'da bir XML belgesinin analizi
Vergi uygulamamızın 7 ve 8 sürümleri, önceki xmlsimulations servletinin programlanmış istemcileri olacaktır. Bu istemciler, XML kodunu alacak ve ilgilendikleri bilgileri çıkarmak için bu kodu analiz etmek zorunda kalacaklardır. Burada farklı sürümlerimize bir ara verip, Java'da bir XML belgesinin nasıl analiz edilebileceğini öğreneceğiz. Bunu, JBuilder 7 ile birlikte gelen ve MySaxParser adlı bir örnek üzerinden yapacağız. Programın adı şu şekildedir:
MySaxParser uygulaması tek bir parametre kabul eder: analiz edilecek XML belgesinin URI (Tekdüzen Kaynak Tanımlayıcı) değeri. Örneğimizde, bu URI, MySaxParser uygulamasının dizinine yerleştirilmiş bir XML dosyasının adı olacaktır. İki çalışma örneğini ele alalım. İlk örnekte, analiz edilen XML dosyası, erreurs.xml dosyasıdır:
<?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>
Analiz şu sonuçları vermektedir:
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
MySaxParser uygulamasının ne işe yaradığını henüz belirtmemiştik, ancak burada, analiz edilen XML belgesinin yapısını görüntülediğini görüyoruz. İkinci örnekte XML ve simulations.xml dosyaları analiz ediliyor:
<?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>
Analiz şu sonuçları vermektedir:
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
MySaxParser sınıfı, web sunucusunun gönderebileceği hataları veya simülasyonları tespit edebildiğinden, vergi uygulamamızda ihtiyacımız olan her şeyi içeriyor. Koduna bir göz atalım:
import java.io.IOException;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import org.apache.xerces.parsers.SAXParser;
import java.util.regex.*;
// sınıf
public class MySaxParser extends DefaultHandler {
// ağacın bir elemanının değeri XML
private StringBuffer valeur=new StringBuffer();
// bir öğenin değerini göz ardı etmek istendiğinde kullanılan düzenli ifade
// önündeki veya arkasındaki "boşlukları" göz ardı etmek istendiğinde
private static Pattern ptnValeur=null;
private static Matcher résultats=null;
// -------- ana
public static void main(String[] argv) {
// parametre sayısının doğrulanması
if (argv.length != 1) {
System.out.println("Usage: java MySaxParser [URI]");
System.exit(0);
}
// analiz edilecek XML dosyasından URI alınır
String uri = argv[0];
try {
// XML ayrıştırıcısı (parser) oluşturuluyor
XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
// parser'a, yöntemleri uygulayacak nesneyi belirtir
// startDocument, endDocument, startElement, endElement karakterlerini içeren nesneyi aktarmalıyız
MySaxParser MySaxParserInstance = new MySaxParser();
parser.setContentHandler(MySaxParserInstance);
// bir öğenin değer şablonu başlatılır
ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
// ayıklayıcıya analiz edilecek XML belgesini belirtilir
parser.parse(uri);
}
catch(Exception ex) {
// hata
System.err.println("Erreur : " + ex);
// izleme
ex.printStackTrace();
}
}//ana işlev
// -------- startDocument
public void startDocument() throws SAXException {
// ayrıştırıcı belgenin başına geldiğinde çağrılan yordam
System.out.println("Début du document");
}//startDocument
// -------- endDocument
public void endDocument() throws SAXException {
// parser belgenin sonuna ulaştığında çağrılan prosedür
System.out.println("Fin du document");
}//endDocument
// -------- startElement
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
// Ayrıştırıcı, bir etiket başlangıcına ulaştığında çağrılan prosedür
// uri: Analiz edilen belgenin URI'i mi?
// localName: analiz edilen öğenin adı
// qName: aynı, ancak varsa bir ad alanı ile "nitelendirilmiş" hali
// attributes: öğenin özniteliklerinin listesi
// izleme
System.out.println("Début élément <"+localName+">");
// Öğenin öznitelikleri var mı?
for (int i = 0; i < attributes.getLength(); i++) {
System.out.println(attributes.getLocalName(i) + " = " + attributes.getValue(i));
}//for
}//startElement
// -------- karakterler
public void characters(char[] ch, int start, int length) throws SAXException {
// parser, metinle karşılaştığında tekrar tekrar çağırdığı yordam
// <etiket>metin</etiket> etiketleri arasında
// metin, start karakterinden başlayarak length karakter uzunluğunda ch içinde yer alır
// metin, değer tamponuna eklenir
valeur.append(ch, start, length);
}//karakter
// -------- endElement
public void endElement(String uri, String localName, String qName)
throws SAXException {
// parser, bir etiket sonu ile karşılaştığında bu prosedürü çağırır
// URI: Analiz edilen belgenin URI'i mi?
// localName: analiz edilen öğenin adı
// qName: aynı, ancak varsa bir ad alanı ile "nitelendirilmiş"
// öğenin değeri görüntülenir
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)+"]");
}//if
// öğenin değeri boş bırakılır
valeur.setLength(0);
// ardından
System.out.println("Fin élément <"+localName+">");
}//endElement
}//sınıf
Öncelikle, XML belgelerinin analizinde sıkça karşımıza çıkan bir kısaltmayı tanımlayalım: SAX, yani Simple API for XML. Bu, XML belgeleriyle çalışmayı kolaylaştıran bir Java sınıfları kümesidir. API'in iki sürümü vardır: SAX1 ve SAX2. Yukarıdaki uygulama, API ve SAX2'i kullanmaktadır.
Uygulama bir dizi paketi içe aktarıyor:
İlk ikisi JDK 1.4 sürümüyle birlikte gelir, üçüncüsü ise gelmez. xerces.jar paketi, Apache web sunucusunun web sitesinde mevcuttur. Bu paket, JBuilder 7 sürümüyle birlikte gelir, ancak aynı zamanda Tomcat 4.x sürümüyle de uyumludur:

Dolayısıyla, önceki uygulamayı JBuilder 7 dışında derlemek istiyorsak ve JDK 1.4 ile Tomcat 4.x'e sahipsek, şu komutu yazabiliriz:
Çalıştırma sırasında da aynı şekilde yapacağız:
dos>java -classpath ".;E:\Program Files\Apache Tomcat 4.0\common\lib\xerces.jar" MySaxParser simulations.xml
MySaxParser sınıfı, DefaultHandler sınıfından türetilmiştir. Buna daha sonra tekrar döneceğiz. Şimdi main prosedürünün kodunu inceleyelim:
// analiz edilecek XML dosyasından URI alınır
String uri = argv[0];
try {
// XML analizcisi (parser) oluşturulur
XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
// parser'a, yöntemleri uygulayacak nesneyi belirtir
// startDocument, endDocument, startElement, endElement karakterlerini içeren nesneyi aktarmalıyız
MySaxParser MySaxParserInstance = new MySaxParser();
parser.setContentHandler(MySaxParserInstance);
// bir öğenin değer şablonu başlatılır
ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
// ayıklayıcıya analiz edilecek XML belgesini belirtilir
parser.parse(uri);
}
catch(Exception ex) {
// hata
System.err.println("Erreur : " + ex);
// izleme
ex.printStackTrace();
}
Bir XML belgesini analiz etmek için, uygulamamızın "parser" adı verilen bir XML kod analizcisine ihtiyacı vardır.
Kullanılan XML ayrıştırıcısı, xerces.jar paketi tarafından sağlanan ayrıştırıcıdır. Alınan nesne, XMLReader türündedir. XMLReader bir arayüzdür ve burada bu arayüzün iki yöntemini kullanıyoruz:
, ayrıştırıcıya, ContentHandler türündeki nesneyi belirtir; bu nesne, XML belgesinin ayrıştırılması sırasında ayrıştırıcının oluşturacağı olayları işleyecektir | |
parametre olarak geçirilen XML belgesinin analizini başlatır |
Ayrıştırıcı, XML belgesini ayrıştırırken şu tür olaylar üretecektir: "belgenin başlangıcına rastladım, bir etiket başlangıcına rastladım, bir etiket özniteliğine rastladım, bir etiket içeriğine rastladım, bir etiket sonuna rastladım, belgenin sonuna rastladım, ...". Bu olayları kendisine verilen ContentHandler nesnesine iletir. ContentHandler, XML ayrıştırıcısının üretebileceği tüm olayları işlemek için uygulanması gereken yöntemleri tanımlayan bir arayüzdür. DefaultHandler, bu yöntemlerin varsayılan uygulamasını yapan bir sınıftır. DefaultHandler'te uygulanan yöntemler hiçbir işlem yapmaz, ancak mevcuttur. Aşağıdaki komut aracılığıyla ayrıştırıcıya, üreteceği olayları hangi nesnenin işleyeceğini belirtmemiz gerektiğinde
komutu aracılığıyla oluşturacağı olayları hangi nesnenin işleyeceğini belirtmemiz gerektiğinde, DefaultHandler türünde bir nesneyi parametre olarak geçirmek pratiktir. Eğer burada durursak, ayrıştırıcının hiçbir olayı işlenmez, ancak programımız sözdizimsel olarak doğru olur. Uygulamada ise, ayrıştırıcıya DefaultHandler sınıfından türetilmiş bir nesne parametre olarak verilir; bu nesnede, yalnızca ilgilendiğimiz olayları işleyen yöntemler yeniden tanımlanmıştır. Burada yapılan da budur:
// ayıklayıcıya, yöntemleri uygulayacak nesneyi belirtilir
// startDocument, endDocument, startElement, endElement karakterlerini içeren nesneyi belirtin
MySaxParser MySaxParserInstance = new MySaxParser();
parser.setContentHandler(MySaxParserInstance);
// parser'a analiz edilecek XML belgesini belirtir
parser.parse(uri);
Parsere, bizim sınıfımız olan ve yukarıda şu bildirimle tanımlanan mySaxParser sınıfının bir örneğini aktarıyoruz
ile tanımlanmış olan sınıfımızın bir örneğini parser'a aktarıyoruz ve URI'i parametre olarak aktardığımız belgenin analizini başlatıyoruz. Bu noktadan itibaren, XML belgesinin analizi başlar. Ayrıştırıcı olaylar üretir ve her bir olay için bu olayları işlemekle görevli nesnenin (burada MySaxParser nesnesi) belirli bir yöntemini çağırır. Bu nesne beş özel olayı işler; diğerleri ise göz ardı edilir:
ayrıştırıcı tarafından gönderilen olay | işleme yöntemi |
void startDocument() | |
void endDocument() | |
public void startElement(String uri, String localName, String qName, Attributes attributes) uri : ? localName: analiz edilen öğenin adı. Karşılaşılan öğe <simulations> ise, localName="simulations" olur. qName: Analiz edilen öğenin ad alanı ile nitelendirilmiş adı. Bir XML belgesi, örneğin XX gibi bir ad alanı tanımlayabilir. Bu durumda, önceki etiketin nitelikli adı XX:simulations olur. attributes: etiketin öznitelik listesi | |
public void characters(char[] ch, int start, int length) ch: karakter dizisi start: ch dizisinde kullanılacak ilk karakterin indeksi length: ch dizisinden alınacak karakter sayısı characters yöntemi tekrar tekrar çağrılabilir. Bir elemanın değerini oluşturmak için bir tampon kullanılır; bu tampon:
| |
void endElement(String uri, String localName, String qName) parametreler, startElement yöntemindeki parametrelerle aynıdır. |
startElement yöntemi, Attributes türündeki attributes parametresi sayesinde öğenin özniteliklerini almayı sağlar:
- Özelliklerin sayısı attributes.getLength() içinde mevcuttur
- i özniteliğinin adı attributes.getLocalName(i)'de mevcuttur
- i özniteliğinin değeri attributes.getValue(i) içinde mevcuttur
- localName adlı özniteliğin değeri, attributes.getValue(localName) içinde bulunur
Bu açıklama yapıldıktan sonra, önceki program ve yürütme örnekleri kendiliğinden anlaşılır hale gelir. Öğelerin değerlerini almak için bir düzenli ifade kullanılmıştır; böylece XML gibi bir metin:
<erreur> etiketine atanan değer, öncesinde ve/veya sonrasında bulunabilecek boşluklar ve satır sonlarından arındırılmış "hata 1" metni olacaktır.
6.4. Vergi uygulaması: sürüm 7
Artık, XML çıktısı veren vergi hizmetimiz için programlanmış istemciler yazmak üzere tüm unsurlara sahibiz. İstemciyi oluşturmak için uygulamamızın 4. sürümünü kullanıyoruz ve sunucu tarafında ise 6. sürümü koruyoruz. Bu istemci-sunucu uygulamasında:
- vergi hesaplama simülasyon hizmeti, xmlsimulations servlet'i tarafından gerçekleştirilir. Dolayısıyla, sunucunun yanıtı, sürüm 6'da gördüğümüz gibi XML biçimindedir.
- İstemci artık bir tarayıcı değil, bağımsız bir Java istemcisidir. Grafik arayüzü, sürüm 4'tekiyle aynıdır.
İşte birkaç çalışma örneği. Öncelikle bir hata durumu: istemci, xmlsimulations servlet’ine sorgu gönderir, ancak SGBD ve MySQL başlatılmadığı için servlet düzgün bir şekilde başlatılamamıştır:

MySQL'i başlatıp birkaç simülasyon yapıyoruz:

Bu yeni sürümün istemcisi, sunucunun yanıtını işleme biçiminden başka, sürüm 4'ün istemcisinden hiçbir fark göstermiyor. Başka hiçbir şey değişmiyor. Sürüm 4'te istemci, HTML kodunu alıyordu ve bu koddan düzenli ifadeler kullanarak ilgilendiği bilgileri çıkarıyordu. Burada istemci, XML kodunu alır ve bu koddan, bir XML ayrıştırıcısı yardımıyla ilgilendiği bilgileri çıkarır.
Değişikliklerin esas olarak burada yapıldığı için, istemcimizin 4. sürümündeki Hesapla menüsüyle ilgili prosedürün ana hatlarını hatırlayalım:
void mnuCalculer_actionPerformed(ActionEvent e) {
....
try{
// vergi hesaplanır
calculerImpots(urlImpots,rdOui.isSelected(),nbEnfants.intValue(),salaire);
}catch (Exception ex){
// hata görüntülenir
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{
// vergi hesaplaması
// urlImpots: Vergi dairesinden gelen URL
// evli: evliyse true, değilse false
// nbEnfants: çocuk sayısı
// maaş: yıllık maaş
// urlImpots'ten vergi dairesi sunucusuna bağlanmak için gerekli bilgiler alınır
....
try{
// sunucuya bağlanılır
....
// müşterinin giriş-çıkış akışları oluşturulur TCP
....
// URL talep ediliyor - başlıkların gönderilmesi HTTP
....
// yanıtın ilk satırı okunur
....
// Yanıt, başlıkların sonuna kadar okunur ve olası bir çerez aranır
while((réponse=IN.readLine())!=null){
.... }//while
// HTTP başlıkları bittiğinde - HTML koduna geçilir
// simülasyonları almak için
ArrayList listeSimulations=getSimulations(IN,OUT,simulations);
simulations.clear();
for (int i=0;i<listeSimulations.size();i++){
simulations.addElement(listeSimulations.get(i));
}
// bitti
....
}//calculerImpots
private ArrayList getSimulations(BufferedReader IN, PrintWriter OUT, DefaultListModel simulations) throws Exception{
....
}
Bu kodun tamamı yeni sürümde de geçerlidir. Yalnızca sunucudan gelen HTML yanıtının işlenmesi (yukarıda çerçeveli kısım) ve görüntülenmesi, sunucudan gelen XML yanıtının işlenmesi ve görüntülenmesi ile değiştirilmelidir:
// başlıklar için işlem tamamlandı HTTP - XML koduna geçiyoruz
// simülasyonları veya hataları almak için
ImpotsSaxParser parseur=new ImpotsSaxParser(IN);
ArrayList listeErreurs=parseur.getErreurs();
ArrayList listeSimulations=parseur.getSimulations();
// sunucu bağlantısını kapatma
client.close();
// görüntüleme listesini temizleme
simulations.clear();
// hatalar
if(listeErreurs.size()!=0){
// tüm hataları birleştirme
String msgErreur="Le serveur a signalé les erreurs suivantes :\n";
for(int i=0;i<listeErreurs.size();i++){
msgErreur+=" - "+(String)listeErreurs.get(i);
}
// hataların görüntülenmesi
throw new Exception(msgErreur);
}//if
// simülasyonlar
for (int i=0;i<listeSimulations.size();i++){
simulations.addElement(listeSimulations.get(i));
}
return;
Yukarıdaki kod parçası ne işe yarar?
- Bir XML ayrıştırıcısı oluşturur ve ona, sunucu tarafından gönderilen XML kodunu içeren IN akışını iletir. Bu akışta HTTP başlıkları da bulunuyordu, ancak bunlar daha önce okunup işlenmişti. Dolayısıyla yanıtın geriye kalan kısmı sadece XML kısmıdır. Ayrıştırıcı iki karakter dizisi listesi üretir: hata varsa hata listesi, yoksa simülasyon listesi. Bu iki liste birbirini dışlar.
- Hata listesi boş değilse, listede yer alan mesajlar tek bir hata mesajı olarak birleştirilir ve bu mesaj parametre olarak kullanılarak bir istisna atılır. Bu istisna, calculerImpots'i çağıran mnuCalculer_actionPeformed prosedüründe görüntülenir.
- Simülasyon listesi boş değilse, bu liste grafik arayüzün jList bileşeninde görüntülenir.
Şimdi, sunucunun XML yanıtını ayrıştıran ayrıştırıcıyı inceleyelim; bu ayrıştırıcı, daha önce Java'da bir XML belgesinin nasıl analiz edileceğine dair yaptığımız çalışmadan doğrudan türetilmiştir:
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.*;
// sınıf
public class ImpotsSaxParser extends DefaultHandler {
// ağacın bir elemanının değeri XML
private StringBuffer valeur=new StringBuffer();
// bir öğenin değerini göz ardı etmek istediğimizde kullanılacak düzenli ifade
// önündeki veya arkasındaki "boşlukları" göz ardı etmek istendiğinde
private Pattern ptnValeur=null;
private Matcher résultats=null;
// XML öğe listeleri
private ArrayList listeSimulations=new ArrayList();
private ArrayList listeErreurs=new ArrayList();
// XML öğeleri
private ArrayList éléments=new ArrayList();
String élément="";
// -------- üretici
public ImpotsSaxParser(BufferedReader IN) throws Exception{
// XML analizörünün (parser) oluşturulması
XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
// parser'a yöntemleri uygulayacak nesneyi belirtir
// startDocument, endDocument, startElement, endElement karakterlerini içeren nesneyi aktarmalıyız
parser.setContentHandler(this);
// bir öğenin değer şablonu başlatılır
ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
// başlangıçta geçerli bir XML öğesi yok
éléments.add("");
// belge analiz ediliyor
parser.parse(new InputSource(IN));
}//oluşturucu
// -------- startElement
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
// parser, bir etiket başlangıcıyla karşılaştığında çağırdığı prosedür
// URI: Analiz edilen belgenin URI adresi mi?
// localName: analiz edilen öğenin adı
// qName: aynı, ancak varsa bir ad alanı ile "nitelendirilmiş" hali
// attributes: öğenin özniteliklerinin listesi
// öğenin adı belirtilir
élément=localName.toLowerCase();
éléments.add(élément);
// Öğenin öznitelikleri var mı?
if(élément.equals("simulation") && attributes.getLength()==4){
// bu bir simülasyondur - öznitelikler alınır
String simulation=attributes.getValue("marie")+","+
attributes.getValue("enfants")+","+
attributes.getValue("salaire")+","+
attributes.getValue("impot");
// simülasyonu simülasyonlar listesine eklenir
listeSimulations.add(simulation);
}//if
}//startElement
// -------- karakterler
public void characters(char[] ch, int start, int length) throws SAXException {
// parser, metinle karşılaştığında tekrar tekrar çağırdığı yordam
// iki <etiket>metin</etiket> etiketi arasında
// metin, start karakterinden başlayarak length karakter uzunluğunda ch içinde yer alır
// metin, hata öğesi ise s değerindeki tampona eklenir
if (élément.equals("erreur"))
valeur.append(ch, start, length);
}//karakter
// -------- endElement
public void endElement(String uri, String localName, String qName)
throws SAXException {
// parser, bir etiket sonu ile karşılaştığında çağırdığı prosedür
// uri: Analiz edilen belgenin URI'i mi?
// localName: analiz edilen öğenin adı
// qName: aynı, ancak varsa bir ad alanı ile "nitelendirilmiş"
// hata durumu
if(élément.equals("erreur")){
// hata öğesinin değeri alınır
String strValeur=valeur.toString();
// gereksiz "boşluklar" temizlenir ve
// boş değilse hata listesine kaydedilir
résultats=ptnValeur.matcher(strValeur);
if (résultats.find() && ! résultats.group(1).equals("")){
listeErreurs.add(résultats.group(1));
}//if
}
// elemanın değerini boş bırakılır
valeur.setLength(0);
// öğenin adı sıfırlanır
é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;
}
}//sınıf
- Üretici, analiz edilmesi gereken XML IN akışını alır ve bu analizi derhal gerçekleştirir. Analiz tamamlandıktan sonra, nesne oluşturulur ve hata listesi (ArrayList), hata listesi (listeErreurs) ile simülasyon listesi (listeSimulations) oluşturulur. Nesneyi oluşturan prosedürün geriye kalan tek işi, getErreurs ve getSimulations yöntemlerini kullanarak bu iki listeyi almaktır.
- Burada, XML ayrıştırıcısı tarafından üretilen yalnızca üç olay bizi ilgilendiriyor:
- XML öğesinin başlangıcı; bu olay, startElement prosedürü tarafından işlenecektir. Bu yordam, <simulation marie=".." enfants=".." salaire=".." impot=".."> ve <erreur>...</erreur> etiketlerini işleyecektir.
- XML öğesinin değeri; bu olay, characters yordamı tarafından işlenecektir.
- XML öğesinin sonu; bu olay, endElement prosedürü tarafından işlenecektir.
- startElement yordamında, <simulation marie=".." enfants=".." salaire=".." impot=".."> öğesiyle karşılaşıldığında, dört öznitelik attributes.getValue("öznitelik adı") ile alınır. Her durumda, öğenin adı "élément" adlı bir değişkene kaydedilir ve öğe listesine (ArrayList) eklenir: élé1, élé2, ..., élén. Bu liste, son elemanı analiz edilen XML elemanı olan bir yığın olarak yönetilir. "Eleman sonu" olayı meydana geldiğinde, listenin son elemanı kaldırılır ve yeni geçerli eleman güncellenir. Bu işlem, endElement prosedüründe gerçekleştirilir.
- characters prosedürü, önceki bir örnekte incelenen prosedürle aynıdır. Sadece geçerli öğenin gerçekten <erreur> öğesi olup olmadığını kontrol etmeye özen gösteriyoruz; bu önlem burada normalde gereksizdir. Bu tür bir önlem, startElement prosedüründe de, söz konusu öğenin <simulation> öğesi olup olmadığını doğrulamak için alınmıştı.
6.5. Sonuç
XML yanıtı sayesinde, impots uygulaması hem tasarımcısı hem de istemci uygulamalarının tasarımcıları için daha kolay yönetilebilir hale gelmiştir.
- Artık sunucu uygulamasının tasarımı iki tür kişiye emanet edilebilir: servletin Java geliştiricisi ve tarayıcılarda sunucu yanıtının görünümünü yönetecek grafik tasarımcı. Grafik tasarımcının, buna eşlik edecek stil sayfalarını oluşturmak için sunucunun XML yanıtının yapısını bilmesi yeterlidir. Bu stil sayfalarının, Java servletinden ayrı ve bağımsız XSL dosyaları olarak düzenlendiğini hatırlatalım. Dolayısıyla grafik tasarımcı, Java geliştiricisinden bağımsız olarak çalışabilir.
- Müşteri uygulamalarının tasarımcıları da sadece sunucunun XML yanıtının yapısını bilmek zorundadır. Grafik tasarımcının stil sayfalarında yapabileceği değişiklikler, her zaman aynı kalan bu XML yanıtı üzerinde hiçbir etki yaratmaz. Bu çok büyük bir avantajdır.
- Geliştirici, her şeyi bozmadan Java servletini nasıl geliştirebilir? Öncelikle, XML yanıtı değişmediği sürece, servletini istediği gibi düzenleyebilir. Ayrıca, istemcilerin beklediği <hata> ve <simülasyon> öğelerini koruduğu sürece XML yanıtını da geliştirebilir. Böylece bu yanıta yeni etiketler ekleyebilir. Grafik tasarımcısı bunları stil sayfalarında dikkate alacak ve tarayıcılar yanıtın yeni sürümlerini alabilecektir. Programlanmış istemciler ise eski modelle çalışmaya devam edecek; yeni etiketler basitçe göz ardı edilecektir. Bunun mümkün olabilmesi için, sunucunun yanıtının XML analizinde aranan etiketlerin doğru bir şekilde tanımlanmış olması gerekir. Vergi uygulamasındaki XML istemcimizde de bu şekilde yapılmıştır; burada prosedürlerde, özellikle <erreur> ve <simulation> etiketlerinin işleneceği belirtilmiştir. Dolayısıyla diğer etiketler göz ardı edilir.