CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-typesafe-play--play-java

Java API for the Play Framework providing web application development capabilities including form handling, validation, dependency injection, and utility libraries

Pending
Overview
Eval results
Files

formatting.mddocs/

Value Formatting and Conversion

Play Framework's formatting system provides type-safe value parsing and formatting capabilities with extensible formatter registration. The system handles conversion between strings and typed objects with locale support, enabling automatic form data binding and custom type handling.

Capabilities

Core Formatting Operations

Central formatting utilities for parsing and printing values with type safety and locale support.

/**
 * Helper class for parsing and formatting values with type safety
 */
public class Formatters {
    /** Parse string to specified type using default locale */
    public static <T> T parse(String text, Class<T> clazz);
    
    /** Parse string to specified type with field context */
    public static <T> T parse(Field field, String text, Class<T> clazz);
    
    /** Convert typed value to string representation */
    public static <T> String print(T t);
    
    /** Convert typed value to string with field context */
    public static <T> String print(Field field, T t);
    
    /** Convert typed value to string with type descriptor */
    public static <T> String print(TypeDescriptor desc, T t);
    
    /** Register simple formatter for a type */
    public static <T> void register(Class<T> clazz, SimpleFormatter<T> formatter);
    
    /** Register annotation-based formatter for a type */
    public static <A extends Annotation,T> void register(Class<T> clazz, AnnotationFormatter<A,T> formatter);
    
    /** The underlying Spring conversion service */
    public static final FormattingConversionService conversion;
}

Usage Examples:

import play.data.format.Formatters;
import java.util.Date;
import java.math.BigDecimal;

// Basic parsing and printing
String numberStr = "123.45";
BigDecimal number = Formatters.parse(numberStr, BigDecimal.class);
String formatted = Formatters.print(number); // "123.45"

// Date parsing with locale
String dateStr = "2023-12-25";
Date date = Formatters.parse(dateStr, Date.class);
String dateFormatted = Formatters.print(date); // Locale-specific format

// Custom type conversion
public class Temperature {
    private double celsius;
    public Temperature(double celsius) { this.celsius = celsius; }
    public double getCelsius() { return celsius; }
}

// Register custom formatter
Formatters.register(Temperature.class, new SimpleFormatter<Temperature>() {
    @Override
    public Temperature parse(String text, Locale locale) {
        return new Temperature(Double.parseDouble(text));
    }
    
    @Override
    public String print(Temperature temp, Locale locale) {
        return String.format("%.1f°C", temp.getCelsius());
    }
});

Simple Formatter Framework

Base class for creating simple, locale-aware formatters for custom types.

/**
 * Base class for simple formatters that handle parsing and printing
 */
public abstract class Formatters.SimpleFormatter<T> {
    /** Parse text representation to typed object */
    public abstract T parse(String text, Locale locale) throws ParseException;
    
    /** Format typed object to text representation */
    public abstract String print(T t, Locale locale);
}

Usage Examples:

import play.data.format.Formatters.SimpleFormatter;

// Custom currency formatter
public class CurrencyFormatter extends SimpleFormatter<Money> {
    @Override
    public Money parse(String text, Locale locale) throws ParseException {
        // Remove currency symbols and parse
        String cleanText = text.replaceAll("[^\\d.,]", "");
        BigDecimal amount = new BigDecimal(cleanText);
        return new Money(amount, Currency.getInstance(locale));
    }
    
    @Override
    public String print(Money money, Locale locale) {
        NumberFormat formatter = NumberFormat.getCurrencyInstance(locale);
        return formatter.format(money.getAmount());
    }
}

// Register the formatter
Formatters.register(Money.class, new CurrencyFormatter());

// Usage in forms
public class ProductForm {
    public String name;
    public Money price; // Will use CurrencyFormatter automatically
}

Annotation-Based Formatting

Advanced formatter framework for annotation-driven formatting with custom configuration.

/**
 * Base class for annotation-based formatters with custom configuration
 */
public abstract class Formatters.AnnotationFormatter<A extends Annotation, T> {
    /** Parse text with annotation configuration */
    public abstract T parse(A annotation, String text, Locale locale) throws ParseException;
    
    /** Format value with annotation configuration */
    public abstract String print(A annotation, T value, Locale locale);
}

Built-in Format Types

Pre-built formatters and annotations for common data types.

/**
 * Container for built-in formatters and format annotations
 */
public class Formats {
    // Built-in formatters are automatically registered
}

/**
 * Date formatting with custom pattern support
 */
public class Formats.DateFormatter extends Formatters.SimpleFormatter<Date> {
    /** Create date formatter with specific pattern */
    public DateFormatter(String pattern);
    
    /** Parse date string using configured pattern */
    public Date parse(String text, Locale locale) throws ParseException;
    
    /** Format date using configured pattern */
    public String print(Date value, Locale locale);
}

/**
 * Annotation for specifying date format patterns
 */
@interface Formats.DateTime {
    /** Date format pattern (e.g., "yyyy-MM-dd", "dd/MM/yyyy HH:mm") */
    String pattern();
}

/**
 * Annotation-driven date formatter using @DateTime configuration
 */
public class Formats.AnnotationDateFormatter extends Formatters.AnnotationFormatter<Formats.DateTime, Date> {
    public Date parse(Formats.DateTime annotation, String text, Locale locale) throws ParseException;
    public String print(Formats.DateTime annotation, Date value, Locale locale);
}

/**
 * Annotation for non-empty string validation and formatting
 */
@interface Formats.NonEmpty {}

/**
 * Formatter for @NonEmpty annotation handling
 */
public class Formats.AnnotationNonEmptyFormatter extends Formatters.AnnotationFormatter<Formats.NonEmpty, String> {
    public String parse(Formats.NonEmpty annotation, String text, Locale locale) throws ParseException;
    public String print(Formats.NonEmpty annotation, String value, Locale locale);
}

Usage Examples:

import play.data.format.Formats.*;

public class EventForm {
    @DateTime(pattern = "yyyy-MM-dd")
    public Date startDate;
    
    @DateTime(pattern = "yyyy-MM-dd HH:mm")
    public Date endDateTime;
    
    @NonEmpty
    public String title;
    
    public String description;
}

// The formatters will automatically handle conversion
public Result createEvent() {
    Form<EventForm> form = Form.form(EventForm.class).bindFromRequest();
    
    if (form.hasErrors()) {
        return badRequest(form.errorsAsJson());
    }
    
    EventForm event = form.get();
    // Dates are automatically parsed according to their patterns
    return ok("Event created: " + event.title);
}

Advanced Usage Patterns

Custom Annotation Formatters

// Custom annotation for phone number formatting
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface PhoneNumber {
    String region() default "US";
    boolean international() default false;
}

// Custom annotation formatter
public class PhoneNumberFormatter extends Formatters.AnnotationFormatter<PhoneNumber, String> {
    
    @Override
    public String parse(PhoneNumber annotation, String text, Locale locale) throws ParseException {
        // Parse and validate phone number based on region
        PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
        try {
            Phonenumber.PhoneNumber number = phoneUtil.parse(text, annotation.region());
            if (!phoneUtil.isValidNumber(number)) {
                throw new ParseException("Invalid phone number", 0);
            }
            return phoneUtil.format(number, PhoneNumberUtil.PhoneNumberFormat.E164);
        } catch (NumberParseException e) {
            throw new ParseException("Invalid phone number format", 0);
        }
    }
    
    @Override
    public String print(PhoneNumber annotation, String phoneNumber, Locale locale) {
        PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
        try {
            Phonenumber.PhoneNumber number = phoneUtil.parse(phoneNumber, null);
            PhoneNumberUtil.PhoneNumberFormat format = annotation.international() 
                ? PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL
                : PhoneNumberUtil.PhoneNumberFormat.NATIONAL;
            return phoneUtil.format(number, format);
        } catch (NumberParseException e) {
            return phoneNumber; // Return as-is if can't format
        }
    }
}

// Register the custom formatter
Formatters.register(String.class, new PhoneNumberFormatter());

// Usage in model
public class ContactForm {
    @PhoneNumber(region = "US", international = false)
    public String phoneNumber;
    
    @PhoneNumber(region = "US", international = true)
    public String internationalPhone;
}

Locale-Specific Formatting

// Locale-aware number formatter
public class LocalizedNumberFormatter extends SimpleFormatter<BigDecimal> {
    
    @Override
    public BigDecimal parse(String text, Locale locale) throws ParseException {
        NumberFormat format = NumberFormat.getNumberInstance(locale);
        Number number = format.parse(text);
        return new BigDecimal(number.toString());
    }
    
    @Override
    public String print(BigDecimal value, Locale locale) {
        NumberFormat format = NumberFormat.getNumberInstance(locale);
        return format.format(value);
    }
}

// Usage with different locales
public Result processLocalizedForm() {
    // Request locale affects formatting
    Locale currentLocale = request().getLocale();
    
    Form<ProductForm> form = Form.form(ProductForm.class).bindFromRequest();
    // Numbers will be parsed according to current locale
    
    return ok("Form processed with locale: " + currentLocale);
}

Complex Type Formatting

// Custom formatter for complex types
public class CoordinateFormatter extends SimpleFormatter<Coordinate> {
    
    @Override
    public Coordinate parse(String text, Locale locale) throws ParseException {
        // Parse "lat,lng" format
        String[] parts = text.split(",");
        if (parts.length != 2) {
            throw new ParseException("Invalid coordinate format", 0);
        }
        
        try {
            double lat = Double.parseDouble(parts[0].trim());
            double lng = Double.parseDouble(parts[1].trim());
            return new Coordinate(lat, lng);
        } catch (NumberFormatException e) {
            throw new ParseException("Invalid coordinate values", 0);
        }
    }
    
    @Override
    public String print(Coordinate coord, Locale locale) {
        return String.format("%.6f,%.6f", coord.getLatitude(), coord.getLongitude());
    }
}

// Model using complex formatter
public class LocationForm {
    @Required
    public String name;
    
    public Coordinate coordinates; // Uses CoordinateFormatter
    
    public String description;
}

Error Handling in Formatters

public class SafeNumberFormatter extends SimpleFormatter<Integer> {
    
    @Override
    public Integer parse(String text, Locale locale) throws ParseException {
        if (text == null || text.trim().isEmpty()) {
            return null; // Allow empty values
        }
        
        try {
            return Integer.parseInt(text.trim());
        } catch (NumberFormatException e) {
            throw new ParseException("Invalid number: " + text, 0);
        }
    }
    
    @Override
    public String print(Integer value, Locale locale) {
        return value != null ? value.toString() : "";
    }
}

// Formatter registration with error handling
public class FormatterConfig {
    public static void registerCustomFormatters() {
        try {
            Formatters.register(Coordinate.class, new CoordinateFormatter());
            Formatters.register(Integer.class, new SafeNumberFormatter());
        } catch (Exception e) {
            Logger.error("Failed to register custom formatters", e);
            // Fallback to default formatters
        }
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-com-typesafe-play--play-java

docs

dependency-injection.md

form-processing.md

formatting.md

index.md

routing.md

streaming.md

utilities.md

validation.md

tile.json