Thursday, February 2, 2012

Java Pattern Formatter

If you have worked in Java for a healthy amount of time you have probably come across Format, specifically DateFormat, NumberFormat and MessageFormat.  All of these are crazy awesome but I find that I need another type of formatter that isn't part of the standard Java offerings - PatternFormatter.  I love how you can use SimpleDateFormat to customize the layout of a date but I come across several situations that I wish I could come up with my own formatting tokens to format anything - Phone Numbers, Addresses, ZIP codes or any other varying but standardized format.  I've spent many a night trying to solve this problem but I think I finally have something that is workable and simple...

import java.util.regex.Matcher;
import java.util.regex.Pattern;



public class PatternFormatter {
    public static interface TokenEditor {
        String replace(String token);
    }

    private static final String EMPTY_STRING = "";

    private TokenEditor _editor;

    private Pattern _pattern;

    public PatternFormatter(Pattern pattern, TokenEditor editor) {
        _pattern = pattern;
        _editor = editor;
    }

    public PatternFormatter(String regex, TokenEditor editor) {
        this(Pattern.compile(regex), editor);
    }

    /**
     * Finds all matching pattern tokens and delegates the replacement to the
     * {@link TokenEditor}. The pattern token is passed along to the editor so it
     * can determine the appropriate replacement.
     *
     * @param mask
     *            to use against the pattern
     * @return the formatted string
     */

    public String format(String mask) {
        Matcher matcher = _pattern.matcher(mask);
        StringBuffer sb = new StringBuffer();

        while (matcher.find()) {
            String replacement = _editor.replace(matcher.group());
            matcher.appendReplacement(sb, replacement == null ? EMPTY_STRING : replacement);
        }
        matcher.appendTail(sb);
        return sb.toString();
    }
}


Then to compose the Pattern formatter in a class...
public class Phone implements TokenEditor {
    private Integer _areaCode, _prefix, _lineNumber;

    public String replace(String token){
        if(isNotBlank(token)){
            if(contains(token, "a")){
                if(_areaCode != null) return leftPad(_areaCode.toString(), length(token), "0");
            } else if(contains(token, "p")){
                if(_prefix != null) return leftPad(_prefix.toString(), length(token), "0");
            } else if(contains(token, "n")){
                if(_lineNumber != null)) return leftPad(_lineNumber.toString(), length(token), "0");
            }
            return null;
        }
    }
    
    private PatternFormatter _formatter = new PatternFormatter("[apn]+|\"[^\"]*\"|'[^']*'", this);
    public String format(String mask){
        _formatter.format(mask);
    }
}

Finally to use...
new Phone(555, 222, 3333).format("(aaa) ppp-nnnn");  = "(555) 222-3333"
new Phone(555, 2, 33).format("(aaa) ppp-nnnn");  = "(555) 002-0033"

No comments: