Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

<f:attribute value="#{some EL expression}"> not found via getAttributes().containsKey()

I cannot retrieve attributes that contain EL inside my ViewHandler.

As a minimal example, here's my ViewHandler. It just looks for an attribute "attributeName" and prints out its value.

public class MiniViewHandler extends ViewHandlerWrapper
{
  private ViewHandler defaultViewHandler = null;

  public MiniViewHandler()
  {
  }

  public MiniViewHandler(ViewHandler defaultViewHandler)
  {
    super();
    this.defaultViewHandler = defaultViewHandler;
  }

  @Override
  public void renderView(FacesContext context, UIViewRoot viewToRender) throws IOException, FacesException
  {
    viewToRender.visitTree(VisitContext.createVisitContext(context),
            new VisitCallback()
            {

              @Override
              public VisitResult visit(VisitContext context, UIComponent target)
              {
                if (target.getAttributes().containsKey("attributeName"))
                {
                  System.out.println("Found it: " + target.getAttributes().get("attributeName"));
                }

                return VisitResult.ACCEPT;
              }
            }
    );

    defaultViewHandler.renderView(context, viewToRender);
  }

  @Override
  public ViewHandler getWrapped()
  {
    return defaultViewHandler;
  }
}

This is registered in the faces-config.xml. The xhtml that I'm hitting:

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
<h:body>
    <f:view>
        <f:attribute name="attributeName" value="#{2 + 5}"/>
    </f:view>
    <f:view>
        <f:attribute name="attributeName" value="2 + 5"/>
    </f:view>
</h:body>
</html>

The logged output shows that only the non-EL attribute was picked up.

INFO  [stdout] (http-/127.0.0.1:8080-1) Found it: 2 + 5
like image 300
Evan Knowles Avatar asked Feb 03 '26 14:02

Evan Knowles


1 Answers

The mistake is here:

if (target.getAttributes().containsKey("attributeName"))

You're using containsKey() to check if the property has been specified. This however won't work if the attribute is a ValueExpression. Here's an extract from UIComponent#getAttributes() javadoc with emphasis:

getAttributes

public abstract java.util.Map<java.lang.String,java.lang.Object> getAttributes()

Return a mutable Map representing the attributes (and properties, see below) associated wth this UIComponent, keyed by attribute name (which must be a String). The returned implementation must support all of the standard and optional Map methods, plus support the following additional requirements:

  • The Map implementation must implement the java.io.Serializable interface.
  • Any attempt to add a null key or value must throw a NullPointerException.
  • Any attempt to add a key that is not a String must throw a ClassCastException.
  • If the attribute name specified as a key matches a property of this UIComponent's implementation class, the following methods will have special behavior:
    • containsKey() - Return false.
    • get() - If the property is readable, call the getter method and return the returned value (wrapping primitive values in their corresponding wrapper classes); otherwise throw IllegalArgumentException.
    • put() - If the property is writeable, call the setter method to set the corresponding value (unwrapping primitive values in their corresponding wrapper classes). If the property is not writeable, or an attempt is made to set a property of primitive type to null, throw IllegalArgumentException.
    • remove() - Throw IllegalArgumentException.

Thus, it always returns false for containsKey for component's properties (read: component's ValueExpressions). That's because dynamic properties are not stored in the attribute map, but instead in the component instance itself via UIComponent#setValueExpression() calls. They're only resolved when calling get().

You basically need to change the wrong line as follows, this also works for "static" attributes:

Object value = target.getAttributes().get("attributeName");
if (value != null) {
    System.out.println("Found it: " + value);
}

If you would like to check if the attribute is actually being set, even though it would evaluate null like value="#{bean.returnsNull}", then you'd instead like to check if UIComponent#getValueExpression() doesn't return null.

if (target.getValueExpression("attributeName") != null) {
    System.out.println("Found it: " + target.getAttributes().get("attributeName"));
}

This does in turn however not work for "static" attributes. You could just combine the checks if necessary, depending on the concrete functional requirements.

like image 59
BalusC Avatar answered Feb 05 '26 07:02

BalusC



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!