تنسيق JSTL والدوال
تنسيق JSTL والدوال
تناول الدرس السابق وسوم JSTL Core — العمود الفقري لتدفق التحكم في أي صفحة JSP. يتناول هذا الدرس مكتبتين بالغتي الأهمية: مكتبة وسوم fmt لتنسيق الأرقام والتواريخ بما يتوافق مع الإعدادات المحلية، ومكتبة دوال fn لمعالجة النصوص داخل تعبيرات EL. تُلغي هاتان المكتبتان كود Java القبيح الذي اضطر المطوّرون تاريخيًا لحشوه في صفحات JSP لتنسيق المخرجات.
الإعداد: إضافة مكتبتَي fmt و fn
كلتا المكتبتين تأتيان في نفس ملف JAR الخاص بـ jakarta.servlet.jsp.jstl (JSTL 3.x لـ Jakarta EE 10+). لديك هذا الملف بالفعل من درس Core. أعلن عنهما في أعلى كل صفحة JSP تحتاجهما:
http://java.sun.com/jsp/jstl/... إلى jakarta.tags.*. إن كنت تعمل على Tomcat 9 القديم أو Java EE فلا تزال تستخدم عناوين URI القديمة. سلوك الوسوم متطابق في الحالتين.
تحديد نطاق اللغة والمنطقة الزمنية بـ fmt:setLocale و fmt:setTimeZone
تحترم جميع وسوم تنسيق fmt اللغة المحلية والمنطقة الزمنية الحالية، اللتين تُحددان مرة واحدة لكل صفحة (أو لكل طلب) بدلًا من تكرارهما في كل وسم:
حين لا يظهر أيٌّ من الوسمين، يتراجع محرك JSP إلى لغة JVM للخادم وUTC، وهو ما لا يريده المستخدمون النهائيون في الغالب. احرص دائمًا على ضبطهما صراحةً في صفحات الإنتاج.
تنسيق الأرقام بـ fmt:formatNumber
يُحوّل <fmt:formatNumber> أي قيمة رقمية إلى سلسلة نصية صحيحة وفق الإعدادات المحلية. تتحكم سمة type في أسلوب المخرجات:
type="number"— عشري عادي مع فواصل تجميع (القيمة الافتراضية)type="currency"— رمز العملة + التجميع + المنازل العشرية وفق الإعدادات المحليةtype="percent"— يضرب في 100 ويُلحق علامة النسبة المئوية
مع الإعدادات المحلية en_US وقيمة price = 1299.9، ينتج وسم العملة $1,299.90. بالتبديل إلى de_DE ينتج 1.299,90 € — يعالج الوسم تلقائيًا رمز العملة وعلامات الترقيم المناسبين.
currencyCode="EUR" (أو أي رمز ISO 4217) لعرض الرمز الصحيح بصرف النظر عن الإعدادات المحلية.
لالتقاط السلسلة المُنسَّقة في متغير بدلًا من كتابتها مباشرةً، استخدم سمتَي var وscope — نمط تشترك فيه معظم وسوم JSTL:
تحليل الأرقام بـ fmt:parseNumber
العملية العكسية — تحويل سلسلة نصية مُنسَّقة وفق إعدادات محلية إلى رقم — يتولّاها <fmt:parseNumber>. نادرًا ما تحتاجه في طبقة العرض، لكنه مفيد في الفلاتر أو ملفات الوسوم التي تستقبل معاملات نصية من سلاسل الاستعلام:
تنسيق التواريخ والأوقات بـ fmt:formatDate
يقبل <fmt:formatDate> كائنًا من نوع java.util.Date (أو قيمة يُرجعها EL بعد التحويل) وينسّقه وفق اللغة المحلية والمنطقة الزمنية المُعرَّفتين. تتحكم سمة type في الجزء المعروض من الطابع الزمني:
تقبل سمتا dateStyle وtimeStyle القيم short وmedium وlong وfull — نفس ثوابت java.text.DateFormat. تأخذ سمة pattern نمط SimpleDateFormat وتُلغي سمات الأسلوب حين يجتمعان.
java.time.* (LocalDate وZonedDateTime وInstant). لا يفهم fmt:formatDate إلا java.util.Date، فيلزمك التحويل: Date.from(instant) أو Date.from(localDate.atStartOfDay(ZoneId.of("UTC")).toInstant()). في Spring MVC يمكنك تسجيل محوّل مخصص لكي يُحلّل EL كائنات java.time مباشرةً، لكن fmt:formatDate ذاته لا يزال يحتاج النوع القديم.
تحليل التواريخ بـ fmt:parseDate
يُحوّل الوسم المُصاحب <fmt:parseDate> سلسلة نصية إلى java.util.Date:
تدويل النصوص بـ fmt:message
تمتلك مكتبة fmt أيضًا وسم رسائل i18n. حمّل حزمة موارد وابحث عن المفاتيح بالاسم:
سيحتوي ملف الحزمة messages_en_US.properties على أسطر مثل order.confirmation=Your order #{0} has been placed.. يتكامل هذا مع عناصر نائبة خاصة بـ MessageFormat في Java.
مكتبة دوال fn
توفّر مكتبة fn دوال مساعدة للنصوص والمجموعات يمكن استدعاؤها مباشرةً داخل تعبيرات EL. لا يوجد وسم منفصل — كل دالة تُستدعى بالشكل ${fn:functionName(arg1, arg2)}.
أكثر الدوال استخدامًا:
fn:length(collection)— طول سلسلة نصية أو مصفوفة أو مجموعةfn:toUpperCase(str)/fn:toLowerCase(str)fn:trim(str)fn:substring(str, begin, end)— صفري الأساس، النهاية غير شاملة (يشابهString.substring)fn:substringBefore(str, delimiter)/fn:substringAfter(str, delimiter)fn:replace(str, before, after)fn:split(str, delimiter)— ترجعString[]fn:join(array, separator)— عكس splitfn:contains(str, substring)— حساس لحالة الأحرف؛ يرجع قيمة منطقيةfn:containsIgnoreCase(str, substring)fn:startsWith(str, prefix)/fn:endsWith(str, suffix)fn:indexOf(str, substring)fn:escapeXml(str)— يُشفّر الكيانات<و>و&و"و'
دوال fn في التطبيق العملي
مقتطف عملي: عرض وصف منتج مُختصر مع نقاط إضافية، والحراسة من القيم الخالية، وإظهار شارة فقط حين تكون قائمة الوسوم غير فارغة:
${review.body} دون تشفير يُشكّل ثغرة XSS مُخزَّنة. يُعدّ fn:escapeXml خط الدفاع الأخير في طبقة العرض.
الجمع بين fmt و fn معًا
تتكامل دوال fn مع وسوم fmt عبر سمة var. نمط شائع هو بناء سلسلة نصية بـ fn، تخزينها في متغير، ثم تمريرها معاملًا لوسم رسالة:
المقايضات ومتى تنقل المنطق إلى المتحكم
تعالج مكتبتا fmt و fn تحويلات العرض النقي بشكل أنيق. غير أن ثمة حالات ينبغي فيها نقل التنسيق إلى طبقة Java:
- إعادة الاستخدام عبر صفحات متعددة — إن عرضت ست صفحات JSP مختلفة السعر بنفس الأسلوب، نسّقه مرة واحدة في المتحكم ومرّر سلسلة نصية جاهزة، أو استخدم ملف وسم مشتركًا.
- منطق أعمال في التنسيق — اختيار السعر شاملًا أو مستبعدًا الضريبة بناءً على موقع المستخدم الجغرافي ليس تقديمًا بل ينتمي إلى فئة خدمة.
- أنواع java.time — نسّق بـ
DateTimeFormatterفي المتحكم ومرّر سلسلة نصية بسيطة للعرض لتجنّب الحاجة لتحويلjava.util.Date.
الخلاصة
تمنحك مكتبة fmt تنسيقًا يراعي الإعدادات المحلية والمنطقة الزمنية للأرقام والعملات والنسب المئوية والتواريخ دون سطر Java واحد في الصفحة. وتجلب مكتبة fn أدوات النصوص — الطول والمقطع والتنظيف والاستبدال والتقسيم والدمج وتشفير XML — مباشرةً إلى تعبيرات EL. يُغطيان معًا تقريبًا كل احتياجات تنسيق المخرجات في طبقة عرض JSP دون أي scriptlet. يتناول الدرس القادم نمط MVC الذي يحكم كيفية تعاون Servlets وJSP.