Skip to content

6. XML و JAVA

در این فصل، استفاده از اسناد XML با جاوا را معرفی می‌کنیم. این کار را در چارچوب برنامه مالیاتی مورد بحث در فصل قبلی انجام خواهیم داد.

6.1. فایل‌های XML و صفحات سبک XSL

فایل زیر XML simulations.xml را در نظر بگیرید که می‌تواند نتایج شبیه‌سازی‌های محاسبه مالیات را نشان دهد:

<?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>

اگر آن را با استفاده از IE 6 مشاهده کنید، نتیجه زیر را دریافت می‌کنید:

Image

IE6 تشخیص می‌دهد که با یک فایل XML (به لطف پسوند .xml فایل) سر و کار دارد و آن را به شیوهٔ خود قالب‌بندی می‌کند. در نت‌اسکیپ، یک صفحه خالی نمایش داده می‌شود. با این حال، اگر کد منبع (View/Source) را مشاهده کنید، می‌توانید فایل اصلی XML را ببینید:

Image

چرا نت‌اسکیپ هیچ چیزی نمایش نمی‌دهد؟ زیرا به یک شیوه‌نامه نیاز دارد تا به آن بگوید چگونه فایل XML را به فایل HTML تبدیل کند، فایلی که سپس می‌تواند آن را نمایش دهد. مشخص شد که IE 6 دارای یک صفحه‌سبک پیش‌فرض است، در حالی که فایل XML هیچ صفحه‌سبکی ارائه نمی‌دهد، که در اینجا نیز همین‌طور بود.

زبان XSL (زبان eXtended StyleSheet) وجود دارد که به کاربر امکان می‌دهد تبدیلات لازم برای تبدیل یک فایل XML به هر فایل متنی را توصیف کند. XSL از دستورالعمل‌های متعددی پشتیبانی می‌کند و شباهت زیادی به زبان‌های برنامه‌نویسی دارد. در اینجا وارد جزئیات نمی‌شویم، زیرا این موضوع چندین ده صفحه طول خواهد کشید. ما صرفاً دو مثال از صفحات سبک XSL را شرح می‌دهیم. اولین مثال، صفحه‌ای است که فایل XML را به کد simulations.xml تبدیل می‌کند. ما مورد دوم را طوری تغییر می‌دهیم که شیوه‌نامه‌ای را مشخص کند که مرورگرها می‌توانند از آن برای تبدیل به سند HTML استفاده کنند، سندی که سپس می‌توانند آن را نمایش دهند:

<?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

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

نشان‌دهندهٔ فایل simulations.xsl به‌عنوان یک xml-stylesheet از نوع text/xsl c.a.d است. یک فایل متنی حاوی کد XSL. این شیوه‌نامه توسط مرورگرها برای تبدیل متن XML به سند HTML استفاده خواهد شد. در اینجا نتیجهٔ به‌دست‌آمده با Netscape 7 هنگام بارگذاری فایل XML simulations.xml آمده است:

Image

وقتی کد منبع سند را مشاهده می‌کنیم (View/Source)، به جای سند نمایش داده شده HTML، سند اصلی XML را می‌بینیم:

Image

نت‌اسکیپ از صفحه‌آرایی simulations.xsl برای تبدیل سند XML بالا به سند قابل نمایش HTML استفاده کرد. اکنون زمان آن است که به محتویات این صفحه‌آرایی نگاهی بیندازیم:

<?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 یک فایل XML است و بنابراین از همان قوانین پیروی می‌کند. از جمله موارد دیگر، باید «خوب‌ساخت» باشد، به این معنی که هر تگ آغازین باید بسته شود.
  • این فایل با دو دستور XML آغاز می‌شود که می‌توان آن‌ها را در هر شیوه‌نامه‌ای XSL حفظ کرد:
<?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" اجازه می‌دهد تا از کاراکترهای دارای نشانه در شیوه‌نامه استفاده شود.

  • تگ <xsl:output method="html" indent="yes"/> به تفسیرگر XSL دستور می‌دهد که خروجی «با حاشیه‌گذاری» HTML تولید کند.
  • برچسب <xsl:template match="element"> برای تعریف عنصر در سند XML استفاده می‌شود که دستورالعمل‌های موجود بین <xsl:template ...> و </xsl:template> بر روی آن اعمال خواهد شد.
    <xsl:template match="/">
................
        </xsl:template>

در مثال بالا، عنصر "/" نشان‌دهنده ریشه سند است. این بدان معناست که به محض برخورد با شروع سند XML، دستورات XSL که بین این دو تگ قرار دارند، اجرا خواهند شد.

  • هر چیزی که یک تگ XSL نباشد، به همان شکل در جریان خروجی گنجانده می‌شود. تگ‌های XSL اجرا می‌شوند. برخی از آن‌ها نتیجه‌ای تولید می‌کنند که در جریان خروجی گنجانده می‌شود. بیایید به مثال زیر نگاه کنیم:
    <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>

شایان ذکر است که سند مورد تحلیل، XML، به شرح زیر است:

<?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>

از ابتدای سند تحلیل‌شده XML (match="/"), تفسیرگر XSL متن را خروجی خواهد داد

<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>

توجه داشته باشید که در متن اصلی ما <hr/> داشتیم و نه <hr>. در متن اصلی، امکان نوشتن <hr> وجود نداشت، که اگرچه یک تگ معتبر HTML است، اما یک تگ نامعتبر XML است. با این حال، ما در اینجا با متنی از نوع XML سروکار داریم که باید «خوب‌فرمت» باشد، به این معنی که هر تگ باید بسته شود. بنابراین ما <hr/> را می‌نویسیم و از آنجا که <xsl:output text="html ..."> را نوشته‌ایم، تفسیرگر XSL متن <hr/> را به <hr> تبدیل خواهد کرد. پس از این متن، متنی که توسط دستور XSL تولید شده است، قرار خواهد گرفت:

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

بعداً خواهیم دید که این متن چیست. در نهایت، مفسر متن زیر را اضافه خواهد کرد:

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

دستور <xsl:apply-templates select="/simulations/simulation"/> به سیستم دستور می‌دهد تا «قالب» مربوط به عنصر /simulations/simulation را اجرا کند. این دستور هر بار که مفسر XSL در متن تجزیه‌شده XML با تگ <simulation>..</simulations> یا <simulation/> درون تگ <simulations>..</simulations> مواجه می‌شود، اجرا خواهد شد. با مواجهه با تگ <simulation>، مفسر دستورالعمل‌های قالب زیر را اجرا خواهد کرد:

    <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>

بیایید خطوط زیر از XML را در نظر بگیریم:

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

خط <simulation ..> معادل قالب دستور XSL <xsl:apply-templates select="/simulations/simulation"> است. بنابراین مفسر XSL در پی اجرای دستورات متناظر با این قالب بر روی آن خواهد بود. این دستورالعمل قالب <xsl:template match="simulation"> را پیدا کرده و آن را اجرا خواهد کرد. به یاد داشته باشید که هر چیزی که یک فرمان XSL نباشد، توسط مفسر XSL بدون تغییر عبور داده می‌شود، در حالی که فرمان‌های XSL با نتیجه اجرای آن‌ها جایگزین می‌شوند. دستور XSL <xsl:value-of select="@champ"/> بدین ترتیب با مقدار ویژگی «champ» گره تجزیه‌شده (در این مورد، یک گره <simulation>) جایگزین می‌شود. پارس کردن خط قبلی XML خروجی زیر را تولید خواهد کرد:

XSL
خروجی
 
<xsl:value-of select="@marie"/>
<xsl:value-of select="@marie"/>
بله
  
<xsl:value-of select="@children"/>
۲
  
<xsl:value-of select="@salary"/>
۲۰۰۰۰۰
  
<xsl:value-of select="@tax"/>
۲۲۵۰۴
 
<xsl:value-of select="@impot"/>22504

در مجموع، خط XML

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

به خط HTML تبدیل خواهد شد:

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

تمام این توضیحات تا حدی ابتدایی هستند، اما اکنون باید برای خواننده روشن باشد که متن زیر، 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>

همراه با برگهٔ سبک زیر: 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>

متن زیر را تولید می‌کند: 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>

فایل XML simulations.xml، همراه با صفحهٔ سبک simulations.xsl، هنگامی که در یک مرورگر جدید (در این مورد Netscape 7) مشاهده می‌شود، به صورت زیر نمایش داده می‌شود:

Image

6.2. درخواست مالیات: نسخه ۶

6.2.1. فایل‌های XML و صفحات سبک XSL برای برنامه مالیات

بیایید به اپلیکیشن وب مالیاتی بازگردیم و آن را طوری اصلاح کنیم که پاسخ ارسال‌شده به کلاینت‌ها به جای HTML، در قالب XML باشد. این پاسخ XML با یک صفحه‌شیوه‌نامه XSL همراه خواهد بود تا مرورگرها بتوانند آن را نمایش دهند. در پاراگراف قبلی، ما ارائه کردیم:

  • فایل simulations.xml، که نمونه اولیه یک پاسخ XML حاوی محاسبات مالیاتی شبیه‌سازی‌شده است
  • فایل simulations.xsl، که به‌عنوان صفحه‌شیوه XSL همراه این پاسخ XML عمل خواهد کرد

ما همچنین باید برای حالتی که پاسخ حاوی خطا باشد، پیش‌بینی کنیم. در این حالت، نمونه اولیه پاسخ XML فایل زیر خواهد بود: 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>

فایل سبک erreurs.xsl که برای نمایش این سند XML در یک مرورگر استفاده می‌شود، به شرح زیر خواهد بود:

<?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>    

این شیوه‌نامه یک دستور XSL را معرفی می‌کند که هنوز با آن مواجه نشده است: <xsl:value-of select="."/>. این دستور مقدار گره تجزیه‌شده را خروجی می‌دهد، که در این مورد یک گره <erreur>texte</erreur> است. مقدار این گره، متن بین تگ‌های آغاز و پایان است، در این مورد texte.

کد erreurs.xml توسط شیوه‌نامه erreurs.xsl به سند زیر 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>

فایل erreurs.xml، همراه با صفحه‌شیوه‌نامه‌اش، توسط یک مرورگر وب به شرح زیر نمایش داده می‌شود:

Image

6.2.2. سرولِت xmlsimulations

ما فایلی به نام index.html ایجاد می‌کنیم و آن را در دایرکتوری برنامهٔ impots قرار می‌دهیم. صفحهٔ نمایش‌داده‌شده به شرح زیر است:

Image

این سند، HTML، یک سند ایستا است. کد آن به شرح زیر است:

<html>
    <head>
      <title>impots</title>
    <script language="JavaScript" type="text/javascript">
        function effacer(){
           // پاک کردن فرم
        with(document.frmImpots){
            optMarie[0].checked=false;
          optMarie[1].checked=true;
          txtEnfants.value="";
          txtSalaire.value="";
          txtImpots.value="";
        }//با
      }//پاک کردن

      function calculer(){
           // پارامترها را قبل از ارسال به سرور بررسی کنید
        with(document.frmImpots){
          //تعداد فرزندان
          champs=/^\s*(\d+)\s*$/.exec(txtEnfants.value);
          if(champs==null){
            //فرم اعتبارسنجی نشده است
            alert("Le nombre d'enfants n'a pas été donné ou est incorrect");
            nbEnfants.focus();
            return;
          }//اگر
          //حقوق
          champs=/^\s*(\d+)\s*$/.exec(txtSalaire.value);
          if(champs==null){
            //قالب تأیید نشده است
            alert("Le salaire n'a pas été donné ou est incorrect");
            salaire.focus();
            return;
          }//اگر
          //OK – ارسال
          submit();
        }//با
      }//محاسبه        
        </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>

توجه داشته باشید که داده‌های فرم به URL /impots/xmlsimulations ارسال می‌شود. این برنامه یک سروِلِت جاوا است که در فایل web.xml از برنامه 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>
  • سرولت xmlsimulations نام دارد و بر پایه کلاس xmlsimulations.class ساخته شده است.
  • پارامترهای آن DSNimpots، admimpots و mdpimpots هستند که برای دسترسی به پایگاه داده مالیات لازم می‌باشند. همچنین دو پارامتر دیگر را می‌پذیرد:
    • xslSimulations، که نام فایل استایل است و باید همراه پاسخ XML که شامل شبیه‌سازی‌ها است، ارائه شود.
    • xslErreurs، که نام فایل استایل است و باید همراه پاسخ XML که شامل هرگونه خطا است، ارائه شود
  • این servlet دارای نام مستعار xmlsimulations است که دسترسی به آن را از طریق URL و http://localhost:8080/impots/xmlsimulations امکان‌پذیر می‌سازد.

ساختار سرولت **xmlsimulations** مشابه سرولت **simulations** است که قبلاً مورد بحث قرار گرفته است. تفاوت اصلی این است که باید به جای HTML، XML را تولید کند. این امر منجر به حذف فایل‌های JSP مورد استفاده در برنامه‌های قبلی خواهد شد. هدف اصلی آن‌ها بهبود خوانایی کد تولیدشده HTML با جلوگیری از پنهان شدن آن در میان کد جاوا سرولت بود. این هدف دیگر ضروری نیست. سرولت دو نوع کد XML برای تولید دارد:

  • یکی برای شبیه‌سازی‌ها
  • و آن برای خطاها

ما پیش‌تر دو نوع پاسخ XML را که باید در این دو حالت ارائه شوند، و همچنین صفحات سبک (style sheets) همراه آن‌ها را معرفی و بررسی کرده‌ایم. کد servlet به شرح زیر است:

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

public class xmlsimulations extends HttpServlet{

    // متغیرهای نمونه
    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{

        //جریان نوشتن را برای کلاینت بازیابی می‌کند
        PrintWriter out=response.getWriter();

        // نوع پاسخ را مشخص می‌کند
        response.setContentType("text/xml");

         // فهرست خطاها
        ArrayList erreurs=new ArrayList();

         // آیا فرایند راه‌اندازی به‌خوبی انجام شد؟
        if(msgErreur!=null){
             // تمام شد – ارسال پاسخ همراه با خطاها به سرور
            erreurs.add(msgErreur);
            sendErreurs(out,xslErreurs,erreurs);
             //انجام شد
            return;
        }

         // بازیابی شبیه‌سازی‌های قبلی از جلسه
        HttpSession session=request.getSession();
        ArrayList simulations=(ArrayList)session.getAttribute("simulations");
        if(simulations==null) simulations=new ArrayList();

        //در حال بازیابی پارامترهای درخواست فعلی
        String optMarie=request.getParameter("optMarie");              //وضعیت تأهل
        String txtEnfants=request.getParameter("txtEnfants");       // تعداد فرزندان
        String txtSalaire=request.getParameter("txtSalaire");       // حقوق سالانه

         //آیا همه پارامترهای مورد انتظار موجود هستند؟
        if(optMarie==null || txtEnfants==null || txtSalaire==null){
             // پارامترها ناقص هستند
             // ارسال پاسخ همراه با خطاها
            erreurs.add("Demande incomplète. Il manque des paramètres");
            sendErreurs(out,xslErreurs,erreurs);
             // تمام شد
            return;
        }

         // تمام پارامترها موجود هستند – در حال بررسی آنها
         //وضعیت تأهل
        if( ! optMarie.equals("oui") && ! optMarie.equals("non")){
             //خطا
            erreurs.add("Etat marital incorrect");
        }
         // تعداد فرزندان
        txtEnfants=txtEnfants.trim();
        if(! Pattern.matches("^\\d+$",txtEnfants)){
            // خطا
            erreurs.add("Nombre d'enfants incorrect");
        }
         // حقوق
        txtSalaire=txtSalaire.trim();
        if(! Pattern.matches("^\\d+$",txtSalaire)){
            // خطا
            erreurs.add("Salaire incorrect");
        }

        if(erreurs.size()!=0){
            // اگر خطایی وجود داشته باشد، علامت‌گذاری می‌شود
            sendErreurs(out,xslErreurs,erreurs);
        }else{
             // بدون خطا
            try{
                 // مالیات قابل پرداخت را می‌توان محاسبه کرد
                int nbEnfants=Integer.parseInt(txtEnfants);
                int salaire=Integer.parseInt(txtSalaire);
                String txtImpots=""+impots.calculer(optMarie.equals("oui"),nbEnfants,salaire);
                 // نتیجهٔ فعلی به شبیه‌سازی‌های قبلی اضافه می‌شود
                String[] simulation={optMarie.equals("oui") ? "oui" : "non",txtEnfants, txtSalaire, txtImpots};
                simulations.add(simulation);
                 //پاسخ را همراه با شبیه‌سازی‌ها ارسال کنید
                sendSimulations(out,xslSimulations,simulations);
            }catch(Exception ex){}
        }//if-else
         // فهرست شبیه‌سازی‌ها به جلسه بازگردانده می‌شود
        session.setAttribute("simulations",simulations);
    }//GET

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

     //-------- INIT
    public void init(){
         //در حال بازیابی پارامترهای راه‌اندازی
        ServletConfig config=getServletConfig();
        xslSimulations=config.getInitParameter("xslSimulations");
        xslErreurs=config.getInitParameter("xslErreurs");
        DSNimpots=config.getInitParameter("DSNimpots");
        admimpots=config.getInitParameter("admimpots");
        mdpimpots=config.getInitParameter("mdpimpots");

         //پارامترها OK هستند؟
        if(xslSimulations==null || DSNimpots==null || admimpots==null || mdpimpots==null){
            msgErreur="Configuration incorrecte";
            return;
        }

         //ایجاد یک نمونه از impotsJDBC
        try{
            impots=new impotsJDBC(DSNimpots,admimpots,mdpimpots);
        }catch(Exception ex){
            msgErreur=ex.getMessage();
        }
    }//init

     //-------- 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";
        }//برای
        réponse+="</erreurs>\n";
         //پاسخ ارسال می‌شود
        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++){
            // شبیه‌سازی شماره ۱
            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";
        }//برای
        réponse+="</simulations>\n";
         //پاسخ ارسال می‌شود
        out.println(réponse);
    }
}

بیایید نگاهی دقیق‌تر به ویژگی‌های اصلی جدید این کد در مقایسه با آنچه قبلاً می‌دانستیم بیندازیم:

  • روند `init` پارامترهای جدید را از فایل پیکربندی `web.xml` بازیابی می‌کند: نام دو صفحه‌سبک `XSL` که باید همراه پاسخ باشند، در متغیرهای `xslSimulations` و `xslErreurs` ذخیره می‌شوند. این دو صفحه‌شیوه‌نامه، فایل‌های simulations.xsl و erreurs.xsl هستند که قبلاً مورد بحث قرار گرفتند. این فایل‌ها در دایرکتوری برنامهٔ 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
  • روند GET با بررسی اینکه آیا خطایی در حین راه‌اندازی رخ داده است، آغاز می‌شود. در صورت مثبت بودن پاسخ، این روند، روند sendErreurs را فراخوانی می‌کند که پاسخ XML مناسب برای این مورد را تولید کرده و سپس متوقف می‌شود. در این پاسخ، دستور مشخص‌کنندهٔ شیوه‌نامهٔ مورد استفاده، یعنی XML، درج می‌شود.
  • اگر خطایی رخ نداده باشد، رویه GET پارامترهای درخواست مشتری را تحلیل می‌کند. اگر خطایی بیابد، آن را با استفاده مجدد از رویه sendErreurs گزارش می‌دهد. در غیر این صورت، شبیه‌سازی جدید را محاسبه می‌کند، آن را به شبیه‌سازی‌های قبلی که در جلسه جاری ذخیره شده‌اند اضافه می‌کند و در نهایت پاسخ خود XML را از طریق رویه sendSimulations ارسال می‌کند. رویه اخیر به روشی مشابه رویه sendErreurs عمل می‌کند.
  • شایان ذکر است که سرولت پاسخ خود را از نوع text/xml اعلام می‌کند:
         // نوع پاسخ مشخص شده است
        response.setContentType("text/xml");

در اینجا چند مثال از اجرا آورده شده است. فرم اولیه به شرح زیر پر شده است:

Image

پایگاه داده MySQL راه‌اندازی نشده است، که ساختن شیء «impots» در متد init سرولت را غیرممکن می‌سازد. بنابراین پاسخ سرولت به شرح زیر است:

Image

کدی که مرورگر دریافت می‌کند (View/Source) به شرح زیر است:

Image

اگر اکنون دو شبیه‌سازی دیگر را پس از آغاز تراکنش پایگاه داده MySQL اجرا کنیم، نتیجه زیر را به دست می‌آوریم:

Image

این بار، مرورگر کد زیر را دریافت کرد:

Image

شایان ذکر است که اپلیکیشن جدید ما به دلیل حذف فایل‌های JSP، ساده‌تر از قبل است. بخشی از کارهایی که قبلاً توسط این صفحات انجام می‌شد به صفحات سبک XSL منتقل شده است. مزیت تقسیم وظایف جدید ما این است که پس از نهایی شدن قالب XML پاسخ‌های سرولت، توسعه صفحات سبک از توسعه سرولت مستقل می‌شود.

6.3. پارس کردن یک سند XML در جاوا

نسخه‌های ۷ و ۸ از اپلیکیشن «impots» ما، کلاینت‌هایی خواهند بود که برای استفاده از سروِلِت «xmlsimulations» قبلی برنامه‌نویسی شده‌اند. این‌ها کد XML را دریافت خواهند کرد که باید آن را تجزیه کنند تا اطلاعات مورد نیاز خود را استخراج نمایند. اکنون از نسخه‌های مختلف خود فاصله می‌گیریم تا بیاموزیم چگونه یک سند XML را در جاوا تجزیه کنیم. این کار را با استفاده از مثالی که همراه با JBuilder 7 ارائه شده و MySaxParser نام دارد، انجام خواهیم داد. نام برنامه به شرح زیر است:

dos>java MySaxParser
Usage: java MySaxParser [URI]

برنامه MySaxParser یک پارامتر را می‌پذیرد: URI (شناسگر یکنواخت منبع) سند XML که باید تحلیل شود. در مثال ما، این URI صرفاً نام یک فایل XML خواهد بود که در دایرکتوری برنامه MySaxParser قرار دارد. بیایید دو مثال از اجرا را در نظر بگیریم. در مثال اول، فایل XML که در حال تحلیل است، فایل 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>

تحلیل نتایج زیر را به دست می‌دهد:

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 چه کاری انجام می‌دهد، اما در اینجا می‌توانیم ببینیم که ساختار سند تحلیل‌شده XML را نمایش می‌دهد. مثال دوم فایل 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>

تحلیل نتایج زیر را به دست می‌دهد:

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 شامل همه چیزهایی است که در برنامه مالیاتی خود به آن نیاز داریم، زیرا توانسته است هم خطاها و هم شبیه‌سازی‌هایی را که سرور وب ممکن است ارسال کند، بازیابی نماید. بیایید نگاهی به کد آن بیندازیم:

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

// کلاس
public class MySaxParser extends DefaultHandler {

     //مقدار یک عنصر در درخت XML
    private StringBuffer valeur=new StringBuffer();
    // یک عبارت منظم برای مقدار یک عنصر زمانی که می‌خواهید نادیده بگیرید
     // فاصله‌ی خالی قبل یا بعد از آن
    private static Pattern ptnValeur=null;
    private static Matcher résultats=null;

    // -------- اصلی
    public static void main(String[] argv) {
        //بررسی تعداد پارامترها
        if (argv.length != 1) {
            System.out.println("Usage: java MySaxParser [URI]");
            System.exit(0);
        }
         //بازیابی URI از فایل XML برای تجزیه و تحلیل
        String uri = argv[0];
        try {
            // ایجاد یک تحلیلگر (پارسر) XML
            XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
             // به پارسر گفته می‌شود که کدام شیء متدها را پیاده‌سازی خواهد کرد
             // startDocument, endDocument, startElement, endElement، کاراکترها
            MySaxParser MySaxParserInstance = new MySaxParser();
            parser.setContentHandler(MySaxParserInstance);
             // قالب مقدار برای یک عنصر مقداردهی اولیه می‌شود
            ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
             // به تجزیه‌گر دستور داده می‌شود تا سند را تحلیل کند XML
            parser.parse(uri);
        }
        catch(Exception ex) {
             // خطا
            System.err.println("Erreur : " + ex);
             // لاگ
            ex.printStackTrace();
        }
    }//اصلی


     // -------- startDocument
    public void startDocument() throws SAXException {
         //روشی که وقتی پارسر با شروع سند مواجه می‌شود، فراخوانی می‌شود
        System.out.println("Début du document");
    }//startDocument

     // -------- endDocument
    public void endDocument() throws SAXException {
         //روشی که وقتی تجزیه‌گر به انتهای سند می‌رسد، فراخوانده می‌شود

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

     // -------- startElement
    public void startElement(String uri, String localName, String qName,
        Attributes attributes) throws SAXException {
         // روشی که هنگام برخورد پارسر با ابتدای یک تگ فراخوانی می‌شود
         // URI: URI از سند در حال تجزیه؟
         // localName: نام عنصری که در حال حاضر در حال تجزیه است
         // qName: مشابه مورد بالا، اما در صورت وجود فضای نام، «تأییدشده» با آن
         // ویژگی‌ها: فهرست ویژگی‌های عنصر

         // ردیابی
        System.out.println("Début élément <"+localName+">");
         //آیا عنصر هیچ صفتی دارد؟
        for (int i = 0; i < attributes.getLength(); i++) {
            System.out.println(attributes.getLocalName(i) + " = " + attributes.getValue(i));
        }//برای
    }//startElement

    // -------- کاراکترها
    public void characters(char[] ch, int start, int length) throws SAXException {
        // روشی که هنگام برخورد با متن، بارها توسط تجزیه‌گر فراخوانی می‌شود
         // بین دو برچسب <tag>text</tag>
         // متن در ch قرار دارد، از کاراکتر شروع تا کاراکتر با طول

         // متن به مقدار بافر اضافه می‌شود
        valeur.append(ch, start, length);
    }//کاراکترها

        // -------- endElement
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
         // رونده‌ای که هنگام برخورد با پایان تگ توسط تجزیه‌گر فراخوانی می‌شود
         // URI: URI از سند تجزیه‌شده؟
         // localName: نام عنصری که در حال حاضر در حال تجزیه است
         // qName: مشابه مورد بالا، اما در صورت وجود فضای نام، «تعیین‌شده»

         // مقدار عنصر نمایش داده می‌شود
        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)+"]");
        }//اگر
         //مقدار عنصر را به یک رشتهٔ خالی تنظیم می‌کند
        valeur.setLength(0);

         // دنباله
        System.out.println("Fin élément <"+localName+">");
    }//endElement

}//کلاس

ابتدا بیایید یک مخفف را که اغلب در تحلیل اسناد XML ظاهر می‌شود، تعریف کنیم: SAX که مخفف Simple API for XML است. این مجموعه‌ای از کلاس‌های جاوا است که برای تسهیل کار با اسناد XML طراحی شده است. دو نسخه از API وجود دارد: SAX1 و SAX2. برنامهٔ فوق از API و SAX2 استفاده می‌کند.

این برنامه تعدادی بسته را وارد می‌کند:

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

دو مورد اول همراه با JDK 1.4 گنجانده شده‌اند، اما مورد سوم گنجانده نشده است. پکیج xerces.jar در وب‌سایت سرور وب آپاچی در دسترس است. این پکیج همراه با JBuilder نسخه ۷ ارائه می‌شود اما همچنین با Tomcat 4.x:

Image

بنابراین، اگر می‌خواهید برنامه قبلی را خارج از JBuilder 7 کامپایل کنید و JDK 1.4 و Tomcat 4.x را دارید، می‌توانید بنویسید:

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

هنگام اجرای برنامه، شما همین کار را انجام می‌دهید:

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

کلاس MySaxParser از کلاس DefaultHandler ارث می‌برد. بعداً به این موضوع باز خواهیم گشت. بیایید کد رویه main را بررسی کنیم:

         // بازیابی URI از فایل XML برای تجزیه و تحلیل
        String uri = argv[0];
        try {
            // ایجاد یک تحلیلگر (پارسر) XML
            XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
             // به تجزیه‌گر گفته می‌شود که کدام شیء متدها را پیاده‌سازی خواهد کرد
             // startDocument, endDocument, startElement, endElement، کاراکترها
            MySaxParser MySaxParserInstance = new MySaxParser();
            parser.setContentHandler(MySaxParserInstance);
             //قالب مقدار برای یک عنصر مقداردهی اولیه می‌شود
            ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
             // به تجزیه‌گر دستور داده می‌شود تا سند را تحلیل کند XML
            parser.parse(uri);
        }
        catch(Exception ex) {
             // خطا
            System.err.println("Erreur : " + ex);
             // لاگ
            ex.printStackTrace();
        }

برای تحلیل یک سند XML، برنامه ما به یک تحلیلگر کد XML نیاز دارد که به «پارسر» معروف است.

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

پارسر XML مورد استفاده، همان پارسری است که توسط بسته xerces.jar ارائه می‌شود. شیء بازیابی‌شده از نوع XMLReader است. XMLReader یک رابط است که در اینجا از دو متد آن استفاده می‌کنیم:

void setContentHandler(ContentHandler handler)
به پارسر می‌گوید که کدام شیء از نوع ContentHandler رویدادهایی را که هنگام تجزیه سند XML تولید می‌کند، مدیریت خواهد کرد.
void parse(InputSource input)
پردازش سند XML را که به‌عنوان پارامتر ارسال شده است، آغاز می‌کند

هنگامی که تجزیه‌گر سند XML را تجزیه می‌کند، رویدادهایی مانند: «با شروع سند مواجه شدم، شروع یک تگ، یک ویژگی تگ، محتوای یک تگ، پایان یک تگ، پایان سند، …» را صادر می‌کند. این رویدادها را به شیء ContentHandler که به آن ارائه شده است، ارسال می‌کند. ContentHandler یک رابط است که متدهایی را که باید برای رسیدگی به تمام رویدادهایی که پارسر XML می‌تواند تولید کند، پیاده‌سازی شوند، تعریف می‌کند. DefaultHandler کلاسی است که پیاده‌سازی پیش‌فرض این متدها را فراهم می‌کند. متدهای پیاده‌سازی‌شده در DefaultHandler هیچ کاری انجام نمی‌دهند، اما وجود دارند. هنگامی که پارسر نیاز دارد بداند کدام شیء رویدادهایی را که تولید می‌کند، مدیریت خواهد کرد، این کار با استفاده از دستور انجام می‌شود

void setContentHandler(ContentHandler handler)

، راحت است که یک شیء از نوع DefaultHandler را به‌عنوان پارامتر ارسال کنیم. اگر در اینجا متوقف شویم، هیچ‌یک از رویدادهای پارسر پردازش نخواهند شد، اما برنامه ما از نظر نحوی صحیح خواهد بود. در عمل، ما یک شیء مشتق‌شده از کلاس DefaultHandler را به‌عنوان پارامتر به تجزیه‌گر می‌دهیم، که در آن متدهایی که تنها رویدادهای موردنظر ما را مدیریت می‌کنند، بازتعریف شده‌اند. این کاری است که در اینجا انجام شده است:

             // به تجزیه‌گر گفته می‌شود که کدام شیء متدها را پیاده‌سازی خواهد کرد
             //startDocument, endDocument, startElement, endElement، کاراکترها
            MySaxParser MySaxParserInstance = new MySaxParser();
            parser.setContentHandler(MySaxParserInstance);
             // به تجزیه‌گر دستور داده می‌شود تا سند XML را تجزیه و تحلیل کند
            parser.parse(uri);

ما یک نمونه از کلاس mySaxParser را پاس می‌کنیم – که کلاس ماست و قبلاً با اعلامیه تعریف شده بود

public class MySaxParser extends DefaultHandler {

و ما با ارسال URI به‌عنوان پارامتر، شروع به تجزیهٔ سند می‌کنیم. از آنجا، تحلیل سند XML آغاز می‌شود. پارسر رویدادها را تولید می‌کند و برای هر یک، متد خاصی از ابجکتی را که مسئول رسیدگی به این رویدادها است فراخوانی می‌کند – در این مورد، ابجکت ما MySaxParser. این ابجکت به پنج رویداد خاص رسیدگی می‌کند؛ سایر رویدادها نادیده گرفته می‌شوند:

رویدادی که توسط تجزیه‌گر منتشر می‌شود
متد رسیدگی
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: نام عنصر تجزیه‌شده. اگر عنصر برخوردشده <simulations> باشد، آنگاه localName="simulations".
qName: نام فضای نامی-معتبر عنصر تجزیه‌شده. یک سند XML ممکن است یک فضای نامی، مانند XX را تعریف کند. نام واجد صلاحیت تگ قبلی در این صورت XX:simulations خواهد بود.
attributes: فهرست ویژگی‌ها برای تگ
valeur d'un élément :
<balise>valeur</balise>
public void characters(char[] ch, int start, int length)
ch: آرایه کاراکتر
start: نمایه اولین کاراکتری که در آرایه ch استفاده می‌شود
length: تعداد کاراکترهایی که باید از آرایه ch برداشته شود
روش characters را می‌توان بارها فراخوانی کرد. برای ساختن مقدار یک عنصر، از بافر زیر استفاده می‌شود:
  • در ابتدا خالی است
  • در هر فراخوانی جدید متد characters پر می‌شود
  • در پایان عنصر نهایی می‌شود
fin d'un élément : </balise> ou <balise .../>
void endElement(String uri, String localName, String qName)
پارامترها متعلق به متد startElement هستند.

روش startElement به شما امکان می‌دهد تا ویژگی‌های عنصر را با استفاده از پارامتر attributes از نوع **Attributes** بازیابی کنید:

  • تعداد ویژگی‌ها در attributes.getLength() موجود است.
  • نام ویژگی i در attributes.getLocalName(i) در دسترس است
  • مقدار ویژگی i در attributes.getValue(i) در دسترس است
  • مقدار ویژگی با نام localName در attributes.getValue(localName)

با توضیح این مطلب، برنامه قبلی به همراه مثال‌های اجرایی، خودگویا است. از یک عبارت منظم برای بازیابی مقادیر عناصر استفاده شد تا متنی مانند XML:

<erreur>
    erreur 1
</erreur>

نتیجه می‌دهد که متن «error با تگ <erreur> مرتبط شود، در حالی که هرگونه فاصله و خط شکست که ممکن است قبل و/یا بعد از آن وجود داشته باشد، حذف می‌شوند.

6.4. برنامه مالیات: نسخهٔ ۷

اکنون تمام عناصر لازم برای نوشتن کلاینت‌هایی که برای سرویس مالیاتی ما برنامه‌نویسی شده‌اند را در اختیار داریم، که خروجی XML را تولید می‌کند. ما از نسخهٔ ۴ برنامه‌مان برای ایجاد کلاینت استفاده می‌کنیم و نسخهٔ ۶ را برای سرور حفظ می‌کنیم. در این برنامهٔ کلاینت-سرور:

  • خدمات شبیه‌سازی محاسبه مالیات توسط سرولت xmlsimulations مدیریت می‌شود. بنابراین پاسخ سرور در قالب XML است، همان‌طور که در نسخه ۶ دیدیم.
  • کلاینت دیگر یک مرورگر وب نیست، بلکه یک کلاینت مستقل جاوا است. رابط کاربری گرافیکی آن مربوط به نسخه ۴ است.

در اینجا چند مثال از عملکرد برنامه آورده شده است. اول، یک سناریوی خطا: کلاینت از سرولت xmlsimulations استعلام می‌گیرد، در حالی که به دلیل راه‌اندازی نشدن فرآیندهای SGBD و MySQL، قادر به inicialize شدن به درستی نبوده است:

Image

ما MySQL را راه‌اندازی می‌کنیم و چند شبیه‌سازی را اجرا می‌کنیم:

Image

کلاینت در این نسخه جدید تنها در نحوه پردازش پاسخ سرور با کلاینت نسخه ۴ متفاوت است. هیچ چیز دیگری تغییر نمی‌کند. در نسخه ۴، کلاینت کد HTML را دریافت می‌کرد که از آن با استفاده از عبارت‌های منظم، اطلاعات مورد نیاز خود را استخراج می‌نمود. در اینجا، کلاینت کد XML را دریافت می‌کند و با استفاده از یک پارسر XML، اطلاعات مورد نیاز خود را از آن استخراج می‌نماید.

بیایید مراحل اصلی مربوط به منوی «حساب کردن» در نسخه ۴ کلاینت خود را مرور کنیم، زیرا تغییرات عمدتاً در اینجا اعمال شده‌اند:

  void mnuCalculer_actionPerformed(ActionEvent e) {
....
    try{
             // مالیات محاسبه می‌شود
            calculerImpots(urlImpots,rdOui.isSelected(),nbEnfants.intValue(),salaire);
    }catch (Exception ex){
             //خطا نمایش داده می‌شود
            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{
         // محاسبه مالیات
         //urlImpots: URL از اداره مالیات
         // متأهل: true اگر متأهل باشد، false در غیر این صورت
         //nbEnfants: تعداد فرزندان
         // حقوق: حقوق سالانه

         // اطلاعات مورد نیاز برای اتصال به سرور اداره مالیات از urlImpots استخراج می‌شود
....
        try{
             //اتصال به سرور
....

             //جریان‌های پرداخت ورودی و خروجی مشتری را ایجاد کنید TCP
....

             // درخواست URL  ارسال سربرگ‌ها HTTP
....

             // خط اول پاسخ را بخوانید
....
             //پاسخ را تا انتهای هدرها می‌خواند و به دنبال هرگونه کوکی می‌گردد
            while((réponse=IN.readLine())!=null){
....            }//در حالی که

            // سربرگ‌ها پایان یافته‌اند HTTP  به کد ادامه دهید HTML
             // برای بازیابی شبیه‌سازی‌ها
            ArrayList listeSimulations=getSimulations(IN,OUT,simulations);
            simulations.clear();
            for (int i=0;i<listeSimulations.size();i++){
                simulations.addElement(listeSimulations.get(i));
            }

            //همین
....
    }//calculerImpots

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

تمام این کدها در نسخه جدید معتبر باقی می‌مانند. تنها پردازش پاسخ HTML سرور (بخش کادر شده در بالا) و نمایش آن باید با پردازش پاسخ XML سرور و نمایش آن جایگزین شود:

             // برای سربرگ‌ها همین بود HTTP  حالا به کد XML می‌رویم
             //برای بازیابی شبیه‌سازی‌ها یا پیام‌های خطا
            ImpotsSaxParser parseur=new ImpotsSaxParser(IN);
            ArrayList listeErreurs=parseur.getErreurs();
            ArrayList listeSimulations=parseur.getSimulations();
             // بستن اتصال به سرور
            client.close();

             //پاک کردن لیست نمایش
            simulations.clear();
             //خطاها
            if(listeErreurs.size()!=0){
                 // ادغام تمام خطاها
                String msgErreur="Le serveur a signalé les erreurs suivantes :\n";
                for(int i=0;i<listeErreurs.size();i++){
                    msgErreur+=" - "+(String)listeErreurs.get(i);
                }
                 //نمایش خطاها
                throw new Exception(msgErreur);
            }//اگر

            // شبیه‌سازی‌ها
            for (int i=0;i<listeSimulations.size();i++){
                simulations.addElement(listeSimulations.get(i));
            }
            return;

قطعه کد بالا چه کاری انجام می‌دهد؟

  • یک پارسر XML ایجاد می‌کند و جریان IN را که حاوی کد XML ارسال‌شده توسط سرور است، به آن می‌سپارد. این استریم همچنین شامل سربرگ‌های HTTP بود، اما این سربرگ‌ها قبلاً خوانده و پردازش شده‌اند. بنابراین، تنها بخش XML از پاسخ باقی مانده است. پارسر دو لیست از رشته‌های کاراکتری تولید می‌کند: لیست خطاها، در صورت وجود، و در غیر این صورت لیست شبیه‌سازی‌ها. این دو لیست یکدیگر را مستثنی می‌کنند.
  • اگر لیست خطاها خالی نباشد، پیام‌های موجود در لیست به هم متصل شده و یک پیام خطای واحد تشکیل می‌دهند و یک استثنا با این پیام به‌عنوان پارامتر پرتاب می‌شود. این استثنا در رویه mnuCalculer_actionPeformed که calculerImpots را فراخوانی کرده است، نمایش داده می‌شود.
  • اگر فهرست شبیه‌سازی‌ها خالی نباشد، در مؤلفه jList رابط کاربری گرافیکی نمایش داده می‌شود.

اکنون بیایید پارسر پاسخ XML سرور را بررسی کنیم، پارسری که مستقیماً از مطالعه قبلی ما در مورد نحوه تحلیل یک سند XML در جاوا نشأت می‌گیرد:

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.*;

// کلاس
public class ImpotsSaxParser extends DefaultHandler {

    //مقدار یک عنصر درختی XML
    private StringBuffer valeur=new StringBuffer();
    // یک عبارت منظم برای مقدار یک عنصر وقتی میخواهید نادیده بگیرید
     // فاصلههای خالی قبل یا بعد از آن
    private Pattern ptnValeur=null;
    private Matcher résultats=null;
     // فهرستهای عناصر XML
    private ArrayList listeSimulations=new ArrayList();
    private ArrayList listeErreurs=new ArrayList();
     //اجزا XML
    private ArrayList éléments=new ArrayList();
    String élément="";

     // -------- سازنده
    public ImpotsSaxParser(BufferedReader IN) throws Exception{
        // ایجاد یک پارسر XML
        XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
         // به تجزیهگر گفته میشود که کدام شیء متدها را پیادهسازی خواهد کرد
         //startDocument، endDocument، startElement، endElement، کاراکترها
        parser.setContentHandler(this);
        //قالب مقدار برای یک عنصر مقداردهی اولیه میشود
        ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
         //در ابتدا، هیچ عنصر جاریای وجود ندارد XML
        éléments.add("");
         //سند تحلیل میشود
        parser.parse(new InputSource(IN));
    }//سازنده

     // -------- startElement
    public void startElement(String uri, String localName, String qName,
        Attributes attributes) throws SAXException {
         // روشی که توسط تجزیهگر هنگام برخورد با ابتدای یک تگ فراخوانی میشود
         // URI: URI از سند در حال تجزیه؟
         // localName: نام عنصری که در حال حاضر در حال تجزیه است
         // qName: مشابه مورد بالا، اما در صورت وجود فضای نام، «تخصصی» شده است
         //ویژگیها: فهرست ویژگیهای عنصر

         // نام عنصر ثبت میشود
        élément=localName.toLowerCase();
        éléments.add(élément);
         //آیا عنصر هیچ صفتی دارد؟
        if(élément.equals("simulation") && attributes.getLength()==4){
            // این یک شبیهسازی است  ویژگیها را بازیابی کنید
            String simulation=attributes.getValue("marie")+","+
                                         attributes.getValue("enfants")+","+
                                         attributes.getValue("salaire")+","+
                                         attributes.getValue("impot");
             // شبیهسازی را به فهرست شبیهسازیها اضافه کنید
            listeSimulations.add(simulation);
        }//اگر
    }//startElement

    // -------- کاراکترها
    public void characters(char[] ch, int start, int length) throws SAXException {
        // روندهای که هنگام برخورد پارسر با متن بارها فراخوانی میشود
         // بین دو برچسب <tag>text</tag>
         // متن در ch قرار دارد، از کاراکتر شروع تا کاراکتر با طول ch

         // متن با مقدار s به بافر اضافه میشود اگر این عنصر خطا باشد
        if (élément.equals("erreur"))
           valeur.append(ch, start, length);
    }//کاراکترها

        // -------- endElement
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
         // روندهای که توسط تجزیهگر هنگام برخورد با پایان تگ فراخوانده میشود
         // URI: URI از سند تجزیهشده؟
         // localName: نام عنصری که در حال حاضر در حال تجزیه است
         // qName: مشابه مورد بالا، اما در صورت وجود فضای نام، «تخصصیافته»

         //در صورت بروز خطا
        if(élément.equals("erreur")){
             // بازیابی مقدار عنصر خطا
            String strValeur=valeur.toString();
             // ما هرگونه «فضاهای خالی» غیرضروری را حذف کرده و آن را به لیست اضافه میکنیم
             //خطاها را اگر خالی نباشد حذف میکند
            résultats=ptnValeur.matcher(strValeur);
            if (résultats.find() && ! résultats.group(1).equals("")){
                listeErreurs.add(résultats.group(1));
            }//اگر
        }
         //مقدار عنصر را به خالی تنظیم میکند
        valeur.setLength(0);
         // نام عنصر را ریست کنید
        é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;
    }

}//class
  • سازنده جریان‌های XML و IN را برای تحلیل دریافت می‌کند و این تحلیل را بلافاصله انجام می‌دهد. پس از اتمام این مرحله، شیء ایجاد شده و لیست‌های خطا (ArrayList) و شبیه‌سازی‌ها (listeSimulations) تولید شده‌اند. تمام کاری که برای رویه‌ای که شیء را ساخت باقی مانده، بازیابی دو لیست با استفاده از متدهای getErreurs و getSimulations است.
  • تنها سه رویداد تولیدشده توسط تجزیه‌گر XML در اینجا مورد توجه هستند:
    • شروع یک عنصر XML، رویدادی که توسط رویه startElement پردازش خواهد شد. این رویه برچسب‌های <simulation marie=".." enfants=".." salaire=".." impot=".."> و <erreur>...</erreur> را مدیریت خواهد کرد.
    • مقدار یک عنصر XML، رویدادی که توسط رویه کاراکترها پردازش خواهد شد.
    • پایان یک عنصر XML، رویدادی که توسط رویه endElement مدیریت خواهد شد.
  • در رویه startElement، اگر عنصر مورد نظر <simulation marie=".." enfants=".." salaire=".." impot=".."> باشد، چهار ویژگی با استفاده از attributes.getValue("نام ویژگی") بازیابی می‌شوند. در همه موارد، نام عنصر در متغیری به نام «element» ذخیره شده و به فهرستی (ArrayList) از عناصر اضافه می‌شود: elem1, elem2, …, elemn. این لیست به صورت یک پشته مدیریت می‌شود، به طوری که آخرین عنصر، عنصر XML است که در حال حاضر در حال تحلیل است. هنگامی که رویداد «پایان عنصر» رخ می‌دهد، آخرین عنصر لیست حذف شده و عنصر جدید جاری به‌روزرسانی می‌شود. این کار در رویه endElement انجام می‌شود.
  • روند `characters` دقیقاً مشابه روندی است که در مثال قبلی مورد بحث قرار گرفت. ما صرفاً اطمینان حاصل می‌کنیم که عنصر فعلی واقعاً عنصر <erreur> است، اقدامی احتیاطی که معمولاً در اینجا ضروری نیست. این نوع اقدام احتیاطی در رویه startElement نیز برای تأیید اینکه با عنصر <simulation> سروکار داریم، انجام شد.

6.5. نتیجه‌گیری

به لطف پاسخ XML، مدیریت برنامهٔ واردات هم برای طراح آن و هم برای طراحان برنامه‌های کلاینت آسان‌تر شده است.

  • اکنون می‌توان طراحی اپلیکیشن سمت سرور را به دو نوع شخص واگذار کرد: توسعه‌دهنده جاوا که روی سرولت کار می‌کند و طراح گرافیکی که ظاهر پاسخ سرور را در مرورگرها مدیریت خواهد کرد. شخص دوم صرفاً باید با ساختار پاسخ XML سرور آشنا باشد تا بتواند صفحات سبک (style sheets) همراه آن را ایجاد کند. شایان ذکر است که این موارد در فایل‌های جداگانه XSL، مستقل از سرولتی که با جاوا نوشته شده، قرار دارند. بنابراین طراح گرافیک می‌تواند مستقل از توسعه‌دهنده جاوا کار کند.
  • طراحان برنامه‌های کاربردی کلاینت نیز به سادگی تنها باید ساختار پاسخ XML سرور را بشناسند. هر تغییری که طراح گرافیک ممکن است در صفحات سبک ایجاد کند، هیچ تأثیری بر این پاسخ XML ندارد، که همیشه یکسان باقی می‌ماند. این یک مزیت بزرگ است.
  • توسعه‌دهنده چگونه می‌تواند سروِلِت جاوا خود را بدون از کار انداختن هیچ چیزی به‌روزرسانی کند؟ اولاً، تا زمانی که پاسخ XML بدون تغییر باقی بماند، توسعه‌دهنده می‌تواند سرولت خود را هر طور که می‌خواهد سازماندهی کند. آنها همچنین می‌توانند پاسخ XML را به‌روزرسانی کنند، به شرطی که عناصر <error> و <simulation> مورد انتظار کلاینت‌های خود را حفظ کنند. بنابراین می‌توانند تگ‌های جدیدی به این پاسخ اضافه کنند. طراح گرافیک این موارد را در سبک‌های خود در نظر خواهد گرفت و مرورگرها قادر به نمایش نسخه‌های جدید پاسخ خواهند بود. با این حال، کلاینت‌های کنترل‌شده از طریق برنامه، به کار با مدل قدیمی ادامه خواهند داد و تگ‌های جدید به سادگی نادیده گرفته می‌شوند. برای امکان‌پذیر شدن این کار، تگ‌های مورد جستجو باید در تحلیل XML پاسخ سرور به درستی شناسایی شوند. این کاری است که در کلاینت XML ما برای برنامه مالیات انجام شد، جایی که رویه‌ها صراحتاً بیان می‌کردند که تگ‌های <erreur> و <simulation> باید پردازش شوند. در نتیجه، سایر تگ‌ها نادیده گرفته می‌شوند.