Marc Hauschildt
Web Technologies and Computer Software Development Instructor at Kirkwood Community College in Cedar Rapids, IA.
Week 13
In week 9, we created a custom function tag library.
Today we will create a custom formatting tag library.
As previously mentioned, a tag library descriptor is an XML document that contains information about a tag library and each function or tag included in the library.
Inspect the TLD files for these libraries. Notice the common tags. Compare the tld files with the documentation.
In the "WEB-INF/tld/" folder, rceate a file called "custom-formats.tld". Add this code.
cfmt is short for custom format.
See the week 9 presentation for explanations of these tags.
<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-jsptaglibrary_3_0.xsd"
version="3.0">
<tlib-version>1.0</tlib-version>
<short-name>cfmt</short-name>
<uri>/WEB-INF/tld</uri>
</taglib>
Create a "format_tags" package in the "shared" package.
Inside that package, create a class called "PhoneNumber".
Update this class to extend the TagSupport class that comes from the jakarta.servlet.jsp.jstl-api dependency.
Set a required attribute called "value".
A setter method is required. Constructors and getter methods are not needed.
package edu.kirkwood.shared.format_tags;
import jakarta.servlet.jsp.tagext.TagSupport;
public class PhoneNumber extends TagSupport {
private String value;
public void setValue(String value) {
this.value = value;
}
}
Add a doEndTag method. This method is invoked when Tomcat is ready to close the tag.
Inside doEndTag, call a method to perform a conversion.
Create a second instance variable to store a converted String.
Create a convert method that will be used to write logic to perform the needed operation.
import jakarta.servlet.jsp.JspException;
import jakarta.servlet.jsp.tagext.TagSupport;
public class PhoneNumber extends TagSupport {
private String value;
private String converted = "";
public void setValue(String value) {
this.value = value;
}
private void convert() {
}
@Override
public int doEndTag() throws JspException {
convert();
return 0;
}
}
Inside the convert method, first assign the value of the text variable to the converted value.
If the value doesn't pass validation requirements, I want the converted value to be the original value.
Then, write the logic to convert the string into a properly formatted phone number.
private void convert() {
converted = value;
if(!Validators.isValidPhone(value)) {
return;
}
// Remove all non-digit characters
String digits = converted.replaceAll("\\D", "");
// Format into (XXX) XXX-XXXX
converted = String.format("(%s) %s-%s",
digits.substring(0, 3),
digits.substring(3, 6),
digits.substring(6, 10));
}
Back in the doEnd() method, add this code.
import jakarta.servlet.jsp.JspWriter
PageContext provides access to the namespaces associated with a JSP page. They are the PAGE_SCOPE, APPLICATION_SCOPE, REQUEST_SCOPE, and SESSION_SCOPE.
The getOut method returns a JspWriter object that has a print method that is used to display the content.
@Override
public int doEndTag() throws JspException {
convert();
try {
JspWriter out = pageContext.getOut();
out.print(converted);
} catch (IOException ex) {
}
return SKIP_BODY;
}
Add these tags between the taglib tags of the custom-formats.tld file. Replace "edu.kirkwood.shared" with the path of your Formats class.
<tag>
<description>Takes 10 numerical text characters and converts to a phone number.</description>
<name>formatPhone</name>
<tag-class>edu.kirkwood.shared.format_tags.PhoneNumber</tag-class>
<body-content>empty</body-content>
<attribute>
<description>
The phone number to be formatted.
</description>
<name>value</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
Add this custom taglib directive to the top.jspf file.
<%@ taglib prefix="cfmt" uri="/WEB-INF/tld/custom-formats.tld" %>
<cfmt:formatPhone value="${user.phone}" />
Write a c:choose, c:when, c:otherwise tag that checks if the product is already in the cart.
If the product is not added, display a button saying "Add to Cart".
If the product is added, display a disabled button saying "In Cart".
The following will return true if the Map contains the Product key.
${map-attribute-name.containsKey(course)}
<div class="d-flex justify-content-between align-items-center">
<small class="fw-bold"><fmt:formatNumber value="${product.price}" type="currency" /></small>
<c:choose>
<c:when test="${sessionScope.cart.contents.containsKey(product)}">
<button disabled class="btn btn-outline-primary btn-sm">In Cart</button>
</c:when>
<c:otherwise>
<form method="POST" action="${appURL}/add-to-cart" class="w-50">
<input type="hidden" name="prod_id" value="${product.id}">
<div class="input-group">
<div class="form-floating">
<input type="number" min="0" class="form-control" id="qty" name="qty" value="1">
<label for="qty">Qty</label>
</div>
<button type="submit" class="btn btn-outline-primary btn-sm">Add to Cart</button>
</div>
</form>
</c:otherwise>
</c:choose>
</div>
The formatting tag library is also used for Internationalization. If you intend to make your application available to international audiences you will want to localize text, dates, numbers, and prices to particular regions of the world.
"i18n" is sometimes used as an abbreviation for internationalization because internationalization begins with an i, followed by 18 letters, followed by an n.
Resource bundles are used to store and access strings and settings for our program.
The ResourceBundle class allows you to write programs that can be easily translated into different languages.
The Locale class allows you to define a specific geographical region in which you want to translate content.
In the Locale class, note the Field Summary that contains a list of predefined locales and languages.
Some languages, like Spanish, do not have a predefined constant since there are so many variations. You will have to define your own custom Locale object for those that are not provided.
Country | Locale.COUNTRY | Locale.LANGUAGE | new Locale |
---|---|---|---|
United States | Locale.US | Locale.ENGLISH | new Locale("en", "US") |
France | Locale.FRANCE | Local.FRENCH | new Locale("fr", "FR") |
Germany | Locale.GERMANY | Locale.GERMAN | new Locale("de", "DE") |
Iceland | Not available | Not available | new Locale("is") |
Spain | Not available | Not available | new Locale("es", "ES") |
Russia | Not available | Not available | new Locale("ru", "RU") |
China | Locale.CHINA | Locale.CHINESE | new Locale("zh", "CN") |
Create a .properties file in a "src/main/resources" folder. Call it something like "translations.properties".
Add the following key-value pairs.
A properties file is in plain-text format.
Properties are entered as key-value pairs.
The key is always string text. Do not use quotation marks.
I recommend naming keys with the jsp file name, followed by a period, followed by the word/phrase.
Follow the key with an equal sign.
The value can be text, a number, or an object. Do not use quotation marks.
Do not include a semi-colon at the end.
topNav.home=Home
topNav.shop=Shop
topNav.login=Login
topNav.signup=Sign-up
topNav.signout=Sign out
topNav.edit-profile=Edit Profile
Messages in the properties files can be translated into various languages. Create as many as you need or want to support by copying and pasting the properties file.
The resource bundle filename should adhere the following pattern: "name_ll_CC.properties
".
The "_ll
" part should be the lowercase ISO 693-1 language code. It is optional and only required whenever the "_CC
" part is present.
The "_CC
" part should be the uppercase ISO 3166-1 Alpha-2 country code. It is optional and often only used to distinguish between country-specific language dialects, like American English (en_US) and British English (en_GB).
In IntelliJ, go to File > Settings. Choose "Editor" > "File Encodings", check “Transparent native-to-ascii conversion”. Apply changes. IntelliJ will now support all utf-8 characters, such as Arabic.
Create a translations_fr.properties file in the same folder.
File name structures should match. The only difference will be the two-letter language codes.
Create a translations_ar.properties file in the same folder.
Add as many more properties that you need.
topNav.home=Accueil
topNav.shop=Boutique
topNav.login=Connexion
topNav.signup=Inscription
topNav.signout=Déconnexion
topNav.edit-profile=Modifier le profil
topNav.home=الرئيسية
topNav.shop=محل
topNav.login=تسجيل الدخول
topNav.signup=التسجيل
topNav.signout=تسجيل الخروج
topNav.edit-profile=تعديل الملف الشخصي
X
Spotify
Socket Tic-Tac-Toe
Upload images
download PDF/CSV
By Marc Hauschildt
Web Technologies and Computer Software Development Instructor at Kirkwood Community College in Cedar Rapids, IA.