Thanks all for your replies, I solved the problem!

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

Reply via email to