Java 3 - 2025

Week 13

Phone number custom format

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

custom-formats.tld

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

PhoneNumber TagSupport class

  • 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;
    }

}

PhoneNumber TagSupport class

  • 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;
    }
}

convert method

  • 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));
}

convert method

  • 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;
}

custom-formats.tld

  • 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>
  • Note that the <tag> tag is demonstrated in fmt.tld.
    • <description> is an optional description for the function.
    • <name> is a required element that indicates the name of the function. The name must be a valid Java identifier.
    • <tag-class> is a required element that provides a fully-qualified name of a standard Java class.
    • <body-content> Specifies whether content goes between opening and closing tags

custom-formats.tld

  • <attribute> specifies a single tag attribute.
  • <description> is an optional description for the attribute
  • <name> is a required element that indicates the name of the attribute. The name must be a valid Java identifier.
  • <required> Specifies whether the attribute is required.

top.jspf

  • Add this custom taglib directive to the top.jspf file.

<%@ taglib prefix="cfmt" uri="/WEB-INF/tld/custom-formats.tld" %>
  • Now we can call our custom formats. 

admin-users.jsp

<cfmt:formatPhone value="${user.phone}" />
  • Run the program and test the admin users page.

shop.jsp In Cart button

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

Internationalization/Resource Bundles

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

ResourceBundle Class

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

ResourceBundle Class

  • 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")

Properties files

  • 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

  • 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).

Messages

  • 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=تعديل الملف الشخصي

2026

  • https://github.com/mlhaus/toy-store/commit/111cb1ad539778b3bb2f1324c7bd44e5a516d0d8

Twillio

  • X

Other

  • Spotify

  • Socket Tic-Tac-Toe

  • Upload images

  • download PDF/CSV

Java 3 - Week 13

By Marc Hauschildt

Java 3 - Week 13

  • 172