独自実装のValidator及びConverterで任意のresourceKeyを返すには?

はじめに

 先日タイトルの件について必要になって調べたのですが、意外と日本語の情報が無いのでメモ。
 私の探し方が下手だという可能性も大いにありますが。

環境

そもそもresourceKeyとは?

 ValidatorとConverterのresourceKeyは、プロパティファイルからエラーメッセージを拾うためのキーです。
 resourceKeyの部分をちゃんと実装することで、自作のValidatorとConverterについても、エラーメッセージをプロパティファイルから取得することが可能になります。
 標準のValidatorとConverterのリソースキーについては書籍の「オープンソース徹底活用WicketによるWebアプリケーション開発」に記載があります。

Validatorの場合

 AbstractValidator#resourceKey()をオーバーライドすることによって、任意のリソースキーを使用することが可能になります。
 デフォルトの実装を以下に引用します。

protected String resourceKey()
{
    return Classes.simpleName(getClass());
}

 Validatorの興味深い点としては、IValidatorインターフェースにはresourceKey()が存在しないことです。

 ということを踏まえてソースを追っていくと、一つのValidatorで複数のメッセージキーを使い分けることが可能であることに気づくのですが、そのあたりは省略。

Converterの場合

 Converterは、エラーを表示したい場合、IConverter#convertToObject(String, Locale)でConversionExceptionを送出するかと思います。
 で、throw前にConversionException#setResourceKey(String)を呼んで任意のリソースキーを設定します。

 setResourceKey(String)を呼ばなかった場合の挙動がどんな感じであるかは、FormComponent#convertInput()とValidationError#getErrorMessage(IErrorMessageSource)を読むと何となくわかります。
 以下にFormComponent#convertInput()を引用します。

protected void convertInput()
{
    if (typeName == null)
    {
        try
        {
            convertedInput = convertValue(getInputAsArray());
        }
        catch (ConversionException e)
        {
            ValidationError error = new ValidationError();
            if (e.getResourceKey() != null)
            {
                error.addMessageKey(e.getResourceKey());
            }
            if (e.getTargetType() != null)
            {
                error.addMessageKey("ConversionError." + Classes.simpleName(e.getTargetType()));
            }
            error.addMessageKey("ConversionError");
            reportValidationError(e, error);
        }
    }
    else
    {
        final IConverter converter = getConverter(getType());
        try
        {
            convertedInput = converter.convertToObject(getInput(), getLocale());
        }
        catch (ConversionException e)
        {
            ValidationError error = new ValidationError();
            if (e.getResourceKey() != null)
            {
                error.addMessageKey(e.getResourceKey());
            }
            String simpleName = Classes.simpleName(getType());
            error.addMessageKey("IConverter." + simpleName);
            error.addMessageKey("IConverter");
            error.setVariable("type", simpleName);
            reportValidationError(e, error);
        }
    }
}
 以下にValidationError#getErrorMessage(IErrorMessageSource)を引用します。
public final String getErrorMessage(IErrorMessageSource messageSource)
{
    String errorMessage = null;

    // try any message keys ...
    for (Iterator iterator = keys.iterator(); iterator.hasNext();)
    {
        errorMessage = messageSource.getMessage((String)iterator.next());
        if (errorMessage != null)
        {
            break;
        }
    }

    // ... if no keys matched try the default
    if (errorMessage == null && message != null)
    {
        errorMessage = message;
    }

    // if a message was found perform variable substitution
    if (errorMessage != null)
    {
        final Map p = (vars == null) ? Collections.EMPTY_MAP : vars;
        errorMessage = messageSource.substitute(errorMessage, p);
    }
    return errorMessage;
}

さいごに

 ValidatorとConverterで実装方法が違うのがちょっと覚えにくいですね。
 それはさておき、これを知っているとエラーメッセージをソースに埋め込まなくともよいので、ValidatorとConverterの実装が少し幸せになるかと思います。