Ok, here is my workaround solution without modifying jars or exchanging classes, using reflection to access private fields and lots of wreid class-casting stuff. (To get around these workaround the MessageInterpolator should provide direct access to these fields ?)
Code:
public class TranslatingInterpolator implements MessageInterpolator {
String interpolateMessage = null;
private DefaultMessageInterpolator interpolator;
static Map<Locale, ResourceBundle> resourceBundles = new HashMap<Locale,ResourceBundle>();
private String replace(String message) {
StringTokenizer tokens = new StringTokenizer( message, "#{}", true );
StringBuilder buf = new StringBuilder( 30 );
boolean escaped = false;
boolean el = false;
while ( tokens.hasMoreTokens() ) {
String token = tokens.nextToken();
if ( !escaped && "#".equals( token ) ) {
el = true;
}
if ( !el && "{".equals( token ) ) {
escaped = true;
}
else if ( escaped && "}".equals( token ) ) {
escaped = false;
}
else if ( !escaped ) {
if ( "{".equals( token ) ) el = false;
buf.append( token );
}
else {
Object variable = getAnnotationParameters().get( token );
if ( variable != null ) {
buf.append( variable );
}
else {
String string = null;
try {
string = getMessageBundle() != null ? getMessageBundle().getString( token ) : null;
}
catch( MissingResourceException e ) {
//give a second chance with the default resource bundle
}
if (string == null) {
try {
string = getDefaultMessageBundle().getString( token );
}
catch( MissingResourceException e) {
//return the unchanged string
buf.append('{').append(token).append('}');
}
}
if ( string != null ) buf.append( replace( string ) );
}
}
}
return buf.toString();
}
public String interpolate(String message, Validator validator, MessageInterpolator defaultInterpolator) {
DefaultMessageInterpolatorAggerator defaultInterpolatorAggregator = (DefaultMessageInterpolatorAggerator) defaultInterpolator;
try {
interpolator = (DefaultMessageInterpolator) ((Map)getField(defaultInterpolatorAggregator, "interpolators")).get(validator);
} catch (Throwable e) {
throw new IllegalAccessError("Workaround failed!");
}
if ( getAnnotationMessage().equals( message ) ) {
//short cut
if (interpolateMessage == null) {
interpolateMessage = replace( getAnnotationMessage() );
}
return interpolateMessage;
}
else {
//TODO keep them in a weak hash map, but this might not even be useful
return replace( message );
}
}
private ResourceBundle getMessageBundle() {
Locale locale = RequestContext.getLocale();
if (resourceBundles.containsKey(locale)) return resourceBundles.get(locale);
Locale oldDefault = Locale.getDefault();
ResourceBundle rb;
try {
Locale.setDefault(locale);
rb = ResourceBundle.getBundle("org.hibernate.validator.resources.DefaultValidatorMessages", locale, getClass().getClassLoader());
resourceBundles.put(locale,rb);
return rb;
} catch( Throwable t) {
return null;
}finally {
Locale.setDefault(oldDefault);
}
}
public String getAnnotationMessage() {
return (String) getField(interpolator,"annotationMessage");
}
public Map getAnnotationParameters() {
return (Map) getField(interpolator,"annotationParameters");
}
public ResourceBundle getDefaultMessageBundle() {
return (ResourceBundle) getField(interpolator,"defaultMessageBundle");
}
public Object getField(Object o, String name){
try {
Field field = o.getClass().getDeclaredField(name);
field.setAccessible(true);
return field.get(o);
} catch (Throwable e) {
throw new IllegalAccessError("MessageInterpolator Workaround Failed!");
}
}
Thanks for the help, problem solved.