Looking at the APIs I found out that the class I was extending to implement the converter does something I actually don't want...
In fact I extend javax.faces.webapp.ConverterTag... And the documentation says: "To avoid creating duplicate instances when a page is redisplayed, creation and registration of a Converter occurs only if the corresponding UIComponent was created (by the owning UIComponentTag) during the execution of the current page."
I looked at the ConverterTag source code and found out that the createConverter method is called only when the parent component is istantiated.
To solve the problem I override the doStartTag method...
Here's the source code:
---------------------- The Tag ----------------------
import ...;
public class StringMatcherTag extends ConverterTag {
private String match = null;
private String highlightClass = null;
/** Creates a new instance of StringConverterTag */
public StringMatcherTag() {
super();
setConverterId(StringMatcher.CONVERTER_ID);
}
public int doStartTag() throws JspException {
UIComponentTag componentTag = UIComponentTag.getParentUIComponentTag(pageContext);
if (componentTag == null) {
throw new JspException("no parent UIComponentTag found");
}
Converter converter = createConverter();
UIComponent component = componentTag.getComponentInstance();
if (component == null) {
throw new JspException("parent UIComponentTag has no UIComponent");
}
if (!(component instanceof ValueHolder)) {
throw new JspException("UIComponent is no ValueHolder");
}
((ValueHolder)component).setConverter(converter);
return Tag.SKIP_BODY;
}
protected Converter createConverter()
throws JspException
{
StringMatcher matcher = (StringMatcher)super.createConverter();
FacesContext facesContext = FacesContext.getCurrentInstance();
setMatch(facesContext, matcher, getMatch());
setHighlightClass(facesContext, matcher, getHighlightClass());
return matcher;
}
private static void setMatch(FacesContext facesContext, StringMatcher matcher, String match) {
if (UIComponentTag.isValueReference(match)) {
ValueBinding vb = facesContext.getApplication().createValueBinding(match);
matcher.setMatch((String)vb.getValue(facesContext));
}
else {
matcher.setMatch(match);
}
}
private static void setHighlightClass(FacesContext facesContext, StringMatcher matcher, String highlightClass) {
if ( (highlightClass != null) && (UIComponentTag.isValueReference(highlightClass)) ) {
ValueBinding vb = facesContext.getApplication().createValueBinding(highlightClass);
matcher.setHighlightClass((String)vb.getValue(facesContext));
}
else {
matcher.setHighlightClass(highlightClass);
}
}
public String getMatch() {
return match;
}
public void setMatch(String match) {
this.match = match;
}
public String getHighlightClass() {
return highlightClass;
}
public void setHighlightClass(String highlightClass) {
this.highlightClass = highlightClass;
}
}
---------------------- The Converter ----------------------
public class StringMatcher implements Converter {
public static final String CONVERTER_ID = "StringMatcher";
private String match = null;
private String highlightClass = null;
/** Creates a new instance of StringMatcher */
public StringMatcher() {
}
public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String value)
throws ConverterException
{
if (facesContext == null) throw new NullPointerException("facesContext");
if (uiComponent == null) throw new NullPointerException("uiComponent");
String matchStr = null;
if( (this.match == null) || (this.match.equals("")) ) {
return value;
}
if (value != null) {
value = value.trim();
int valueLen = value.length();
if (valueLen > 0) {
matchStr = value;
StringTokenizer strTok = new StringTokenizer(this.match, " ");
while(strTok.hasMoreTokens()) {
String matchWord = strTok.nextToken();
Pattern p = Pattern.compile("(" + matchWord + ")", Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(matchStr);
if(this.highlightClass != null) {
matchStr = m.replaceAll("<span class=\"" + this.highlightClass + "\">$1</span>");
}
else {
matchStr = m.replaceAll("<strong>$1</strong>");
}
}
}
}
return matchStr;
}
public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object obj)
throws ConverterException
{
if(obj instanceof String)
return (String)(this.getAsObject(facesContext, uiComponent, (String)obj));
else
return null;
}
public String getMatch() {
return match;
}
public void setMatch(String match) {
this.match = match;
}
public String getHighlightClass() {
return highlightClass;
}
public void setHighlightClass(String highlightClass) {
this.highlightClass = highlightClass;
}
}
As you can see, it's quite simple and rude but it works!
Hope it could help someone!
Cheers,
Ennio
2005/11/3, Martin Marinschek <[EMAIL PROTECTED]>:
You are right - just looked it up. No converter state saving anywhere around.
Sorry for the misleading answer!
regards,
Martin
On 11/3/05, Mike Kienenberger < [EMAIL PROTECTED]> wrote:
> Hey Martin,
>
> I could be wrong, but I don't think it helps if converters implement
> StateHolder under JSF 1.1. I think they're recreated each lifecycle.
> JSF 1.2 does restore them instead of recreating them. Switching
> to the facelets view handler will also restore them instead of
> recreating them. This is based on info that Jacob Hook has told me
> in the past rather than any research on my own part, though.
>
> So while it's a very good idea to provide saveState/restoreState, It
> may not affect the problem here.
>
> On 11/3/05, Martin Marinschek < [EMAIL PROTECTED]> wrote:
> > Hmm...
> >
> > did you add a saveState/restoreState section where the converters
> > field are stored into/restored from the application state?
> >
> > just like in the following example for NumberConverter (fyi:
> > converters need to save/restore their state much like components).
> >
> > // STATE SAVE/RESTORE
> > public void restoreState(FacesContext facesContext, Object state)
> > {
> > Object values[] = (Object[])state;
> > _currencyCode = (String)values[0];
> > _currencySymbol = (String)values[1];
> > _locale = (Locale)values[2];
> > Integer value = (Integer)values[3];
> > _maxFractionDigits = value != null ? value.intValue() : 0;
> > value = (Integer)values[4];
> > _maxIntegerDigits = value != null ? value.intValue() : 0;
> > value = (Integer)values[5];
> > _minFractionDigits = value != null ? value.intValue() : 0;
> > value = (Integer)values[6];
> > _minIntegerDigits = value != null ? value.intValue() : 0;
> > _pattern = (String)values[7];
> > _type = (String)values[8];
> > _groupingUsed = ((Boolean)values[9]).booleanValue();
> > _integerOnly = ((Boolean)values[10]).booleanValue();
> > _maxFractionDigitsSet = ((Boolean)values[11]).booleanValue();
> > _maxIntegerDigitsSet = ((Boolean)values[12]).booleanValue();
> > _minFractionDigitsSet = ((Boolean)values[13]).booleanValue();
> > _minIntegerDigitsSet = ((Boolean)values[14]).booleanValue();
> > }
> >
> > public Object saveState(FacesContext facesContext)
> > {
> > Object values[] = new Object[15];
> > values[0] = _currencyCode;
> > values[1] = _currencySymbol;
> > values[2] = _locale;
> > values[3] = _maxFractionDigitsSet ? new
> > Integer(_maxFractionDigits) : null;
> > values[4] = _maxIntegerDigitsSet ? new
> > Integer(_maxIntegerDigits) : null;
> > values[5] = _minFractionDigitsSet ? new
> > Integer(_minFractionDigits) : null;
> > values[6] = _minIntegerDigitsSet ? new
> > Integer(_minIntegerDigits) : null;
> > values[7] = _pattern;
> > values[8] = _type;
> > values[9] = _groupingUsed ? Boolean.TRUE : Boolean.FALSE;
> > values[10] = _integerOnly ? Boolean.TRUE : Boolean.FALSE;
> > values[11] = _maxFractionDigitsSet ? Boolean.TRUE : Boolean.FALSE;
> > values[12] = _maxIntegerDigitsSet ? Boolean.TRUE : Boolean.FALSE;
> > values[13] = _minFractionDigitsSet ? Boolean.TRUE : Boolean.FALSE;
> > values[14] = _minIntegerDigitsSet ? Boolean.TRUE : Boolean.FALSE;
> > return values;
> > }
> >
> > regards,
> >
> > Martin
> >
> > On 11/3/05, Wayne Fay < [EMAIL PROTECTED]> wrote:
> > > I, for one, would be interested in the converter source code.
> > >
> > > To help see what's going on, I'd add *a ton* of logging statements to
> > > your code and turn debugging up to FINEST level and trace what happens
> > > when it works vs when it doesn't.
> > >
> > > I'm talking about log.trace() at the beginning and end of every method
> > > and in if/else/for/while/etc statements, log.info() to dump the
> > > contents of variables before and after setting them and before
> > > returning a value from a method, etc.
> > >
> > > I find this helps me find the source of any problem, generally. The
> > > MyFaces libraries have pretty good logging as well, so even if the
> > > problem is in MyFaces and not your code, you would probably be able to
> > > "see" it.
> > >
> > > Wayne
> > >
> > >
> > > On 11/3/05, Ennio Tosi <[EMAIL PROTECTED] > wrote:
> > > > Hi, I developed a custom converter to highlight certain terms in a string
> > > > that is displayed with an <h:outputText> tag.
> > > >
> > > > Here's a sample code to illustrate how it works:
> > > >
> > > > <h:outputText value="#{myBean.description}">
> > > > <q:stringMatcher match="#{myBean.queryString}" />
> > > > </h:outputText>
> > > >
> > > >
> > > > myBean.queryString property is populated by an inputText in the same page.
> > > >
> > > > So I would like to highlight matching words depending on what the user is
> > > > searching...
> > > >
> > > > The first time I access the page everything works well, but as soon as I
> > > > submit the page, nothing gets highlighted.
> > > > I'm sure myBean.queryString is not null, because in the same page I'm
> > > > displaying it...
> > > >
> > > > If it could be interesting for someone I could post the converter source
> > > > code.
> > > >
> > > > Thanks,
> > > > Ennio
> > > >
> > > >
> > >
> >
> >
> > --
> >
> > http://www.irian.at
> > Your JSF powerhouse -
> > JSF Trainings in English and German
> >
>
--
http://www.irian.at
Your JSF powerhouse -
JSF Trainings in English and German

