Martin Marinschek schrieb:
Can you post your suggested changes again, and interweave the existing
code, so that I can look at it step by step and see what your changes
would mean?
regards,
Martin
Hi Martin,
here is a 'little' peace of code for RendererUtils :) It implements,
what I've talking about in my early posting. The
getBooleanValueForCheckbox() method is intended to replace existing
getBooleanValue(), which is currently called from renderer in
RenderUtils however wit one more parameter, so this call should be
changed too (HtmlCheckboxRendererBase line 62).
It is something like proof-of-concept. I've not implement yet the
controversy case when custom converter provides the same type as our
'expected' (see comments in code) since IMHO it should be really
clarified first. The String2Boolean() can be probably implemented some
smarter way too. But I think, this approach can be used in
UIComponentTagUtils as well, since it should work in both way.
Regards,
paul
/**
* Envelope for results of obtaining converter.
*/
public static class FoundConverter{
private int converterType = 0; //int due to Java 1.42
compatibility! (Use Enum in 5+ ?)
private Converter converter = null;
private Class customClass = null;
/**
* @return found conwerter if type > 0, otherweis null.
*/
public final Converter getConverter() {
return converter;
}
public final void setConverter(Converter converter) {
this.converter = converter;
}
/**
* @return -1 not required;<br>
* 0 not found;<br>
* 1 custom;<br>
* 2 standard for object's class<br>
* 3 standard for target class (last choice!)
*/
public final int getConverterType() {
return converterType;
}
public final void setConverterType(int converterType) {
this.converterType = converterType;
}
public final Class getCustomClass() {
return customClass;
}
public final void setCustomClass(Class customClass) {
this.customClass = customClass;
}
}
/**
* @param value - object, usually returned by getValue() or
getSubmittedValue()<br>
* @param targetClass - class to which value should be converted<br>
* @param facesContext - Faces context<br>
* @param component - component<br>
* @param probe - string to be used for determining return type of
getAsObject() of custom converter<br>
*
* @return an instance of envelope class FoundConverter, containing
evtl. found converter and
* information on type of this converter.
*
* Obtains apropriative converter in order:<br>
* custom converter,<br>
* standard converter registered for value's class,<br>
* standard converter registered for target class.<br>
*
* Returns no converter if:<br>
* - no custom converter provided but value is already of suitable
type;<br>
* - no custom converter provided and value is null.<br>
* If custom converter provided, tries also to call getAsObject()
with probe
* as argument to determine custom type.
*
*
* @throws IllegalArgumentException if value is not null, not of
suitable type and no converter can be found.
*/
public static FoundConverter obtainConverter(Object value, Class
targetClass,
FacesContext facesContext,
UIComponent component,
String probe) throws
IllegalArgumentException{
FoundConverter result = new FoundConverter();//result envelope
//--- check if component is a ValueHolder ---
if (! (component instanceof ValueHolder))
throw new IllegalArgumentException("Component : " +
getPathToComponent(component) + "is not a ValueHolder");
Converter converter = ((ValueHolder) component).getConverter();
//--- custom converter found ---
if (converter != null){
try{
//--- try to determine, which type returns getAsObject()
of this converter
Object x = converter.getAsObject(facesContext,
component, probe);
result.setCustomClass(x.getClass());
}
catch(Exception ex){
log.warn("Unable to determine object type returned by
custom converter: "+
converter.getClass().getName()+
" with probe string \""+probe+"\"");
}
result.setConverterType(1);//custom converter found
result.setConverter(converter);
return result;
}
//--- NO custom converter ---
if(value == null){
result.setConverterType(0);//not found, undefined for null
return result;
}
//--- value not null but already of the same type ---
if ( targetClass.isAssignableFrom(value.getClass()) ){
result.setConverterType(-1); //not found, not required!
return result;
}
/*
* If component provides no custom converter, we
* cann still try to obtain a standard one from Application
*/
try {
//--- try first for type of value... ---
converter = facesContext.getApplication()
.createConverter(value.getClass());
result.setConverterType(2);//standard for value's type
result.setConverter(converter);
return result;
} catch (FacesException ex) {
//--- ...then for target type ---
try{
converter = facesContext.getApplication()
.createConverter(targetClass);
result.setConverterType(3);//standard for target type
result.setConverter(converter);
return result;
}
catch(FacesException ex2){
throw new IllegalArgumentException("Component : "
+ getPathToComponent(component)+
" expects value of type "+ targetClass.getName() +
" but " + value.getClass().getName() + " was
passed and"+
" no suitable converter found.");
}
}
}
/**
* @param str - string to convert into Boolean
* @return Boolean
*
* null-safe conversion of strings like "true","yes", "on" etc. to
Boolean.
*/
public static Boolean String2Boolean(String str){
if(null != str &&
(
str.equalsIgnoreCase("true") ||
str.equalsIgnoreCase("yes") ||
str.equalsIgnoreCase("on") ||
str.equalsIgnoreCase("1")
)
)
return Boolean.TRUE;
else
return Boolean.FALSE;
}
public static Boolean getBooleanValueForCheckbox(FacesContext
facesContext,
UIComponent component) {
/*
* The value is returned in order: getSubmittedValue(), getValue()
*/
Object value = getObjectValue(component);
FoundConverter xConverter =
obtainConverter(value,Boolean.class,facesContext,component,"true");
String valueClassName = (null != value) ?
value.getClass().getName() : "null";
String strExceptionMsg = "Component: " +
getPathToComponent(component)+
" expects value of type Boolean, but "+
valueClassName + " passed.";
try{
switch (xConverter.getConverterType()){
case -1:
return (Boolean)value;
case 0:
log.warn(strExceptionMsg + " FALSE assumed since no
converter provided.");
return Boolean.FALSE; // Boolean(null) -> false
/*
* Custom converter(1) as well as standard converter for
value's type(2)
* provide conversion [String <-> custom-type] so if we
are going
* to use it, we have to call getAsString() and then
* try to convert that String into our expected type
*/
case 1:
//The conversion logic in case when custom converter
produce the same
//type of 'object' as our expected type, that I've
describe in my posting
//early, can be implemented here... i.e.:
//
//if(xConverter.getCustomClass() != null){
// if
(Boolean.class.isAssignableFrom(xConverter.getCustomClass())){
// ...
// }
//}
case 2:
String xString =
xConverter.getConverter().getAsString(facesContext, component, value);
return String2Boolean(xString);
/*
* Standard converter for target type(3) provide
conversion [String <-> our-expected-type]
* so we can get value.toString() first and then call
getAsObject()
*/
case 3:
String yString = value.toString();
return
(Boolean)xConverter.getConverter().getAsObject(facesContext, component,
yString);
default:
throw new IllegalArgumentException(strExceptionMsg +
" No suitable converter available.");
}//case
}
catch(ConverterException ex){
throw new IllegalArgumentException(strExceptionMsg +
" Conversion with " +
xConverter.getConverter().getClass().getName()+
" failed."
);
}
}
//----------------------- END OF CODE --------------------------------