My JSF Books/Videos My JSF Tutorials OmniFaces/JSF PPTs
JSF 2.3 Tutorial
JSF Caching Tutorial
JSF Navigation Tutorial
JSF Scopes Tutorial
JSF Page Author Beginner's Guide
OmniFaces 2.3 Tutorial Examples
OmniFaces 2.2 Tutorial Examples
JSF Events Tutorial
OmniFaces Callbacks Usages
JSF State Tutorial
JSF and Design Patterns
JSF 2.3 New Features (2.3-m04)
Introduction to OmniFaces
25+ Reasons to use OmniFaces in JSF
OmniFaces Validators
OmniFaces Converters
JSF Design Patterns
Mastering OmniFaces
Reusable and less-verbose JSF code

My JSF Resources ...

Java EE Guardian
Member of JCG Program
Member MVB DZone
Blog curated on ZEEF
OmniFaces is an utility library for JSF, including PrimeFaces, RichFaces, ICEfaces ...

[OmniFaces Utilities] - Find the right JSF OmniFaces 2 utilities methods/functions

Search on blog

Petition by Java EE Guardians

Twitter

marți, 23 decembrie 2014

OmniFaces findComponentRelatively(), findComponentInParents() and findComponentInChildren() methods


[OmniFaces utilities] The findComponentRelatively() returns the UI component matching the given client ID search expression relative to the point in the component tree of the given component. For this search both parents and children are consulted, increasingly moving further away from the given component. Parents are consulted first, then children.
[OmniFaces utilities] The findComponentInParents() returns the UI component matching the given client ID search expression relative to the point in the component tree of the given component, searching only in its parents.
[OmniFaces utilities] The findComponentInChildren() returns the UI component matching the given client ID search expression relative to the point in the component tree of the given component, searching only in its children.

Besides the goodies revealed in the Showcase, OmniFaces has a lot of "hidden pearls" that can help us during development of JSF projects. A few days ago I have "discovered" the OmniFaces utilities, which are a collection of utility methods for the JSF API. More precisely, in the org.omnifaces.util.Components I found the findComponentRelatively() method:

public static <C extends UIComponent> C findComponentRelatively
                            (UIComponent component, String clientId) {
 if (isEmpty(clientId)) {
     return null;
 }

 // Search first in the naming container parents of the given component
 UIComponent result = findComponentInParents(component, clientId);

 if (result == null) {
     // If not in the parents, search from the root
     result = findComponentInChildren(getViewRoot(), clientId);
 }

 return (C) result;
}

Well, as the OmniFaces description says, this method "returns the UI component matching the given client ID search expression relative to the point in the component tree of the given component. For this search both parents and children are consulted, increasingly moving further away from the given component. Parents are consulted first, then children."

But, in order to use this method correctly, you must be aware of several aspects:

·         when searching in parents, the search will stop when the component with clientId is found or at the UIViewRoot
·         the search in children is not relatively to the "point in the component tree of the given component", is rather relative to the UIViewRoot
·         actually, the findComponentRelatively() searches the component with clientId ONLY in instances of NamingContainer (which are: HtmlDataTable, HtmlForm, UIData, UIForm, NamingContainer)
·         for accomplish its job, this method uses the findComponentInParents() and findComponentInChildren() methods
·         These methods (findComponentInParents() and findComponentInChildren()) can be called individually, they are not related to findComponentRelatively() method

The findComponentInParents() is listed below:

public static <C extends UIComponent> C findComponentInParents
                          (UIComponent component, String clientId) {

 if (isEmpty(clientId)) {
    return null;
}

for (UIComponent parent = component; parent != null; parent = parent.getParent()) {
     UIComponent result = null;
     if (parent instanceof NamingContainer) {
         try {
             result = parent.findComponent(clientId);
         } catch (IllegalArgumentException e) {
             continue;
         }
     }

     if (result != null) {
         return (C) result;
     }
 }

return null;
}

As documentation says, this method "returns the UI component matching the given client ID search expression relative to the point in the component tree of the given component, searching only in its parents.".

Well, again there are a few aspects to notice here:

·         the first inspected parent is the " given component"
·         the component matching the given clientId is searched ONLY in NamingContainer instances
·         this is NOT recursive search! Is just a parent-traversal algorithm from the " given component" to the UIViewRoot

When findComponentInParents() return null, the findComponentInChildren() is called. The code is listed below:

public static <C extends UIComponent> C findComponentInChildren
                           (UIComponent component, String clientId) {

 if (isEmpty(clientId)) {
     return null;
 }

 for (UIComponent child : component.getChildren()) {

      UIComponent result = null;
      if (child instanceof NamingContainer) {
         try {
             result = child.findComponent(clientId);
         } catch (IllegalArgumentException e) {
             continue;
          }
      }

      if (result == null) {
          result = findComponentInChildren(child, clientId);
      }

      if (result != null) {
          return (C) result;
      }
 }

 return null;
}

As documentation says, this method "returns the UI component matching the given client ID search expression relative to the point  in the component tree of the given component, searching only in its children."

The important things to notice here are:

·         the inspection doesn't start from the " given component", as in the case of findComponentInParents(). It starts from the first child.
·         the component matching the given clientId is searched ONLY in NamingContainer instances
·         this is a recursive method - the recursive process performs a clean traversal of the component's tree (or subtree) by visiting each node in a hierarchical approach.
·         the findComponentRelatively() uses this method to traverse the component tree starting from the UIViewRoot


Each of these three methods uses the JSF, UIComponent.findComponent() which acts as below:


For the sake of completeness, the source code of the findComponent() method from UIComponentBase (extracted from Mojarra implementation) is listed below - is pretty straightforward:
public UIComponent findComponent(String expr) {

 if (expr == null) {
     throw new NullPointerException();
 }

 FacesContext ctx = FacesContext.getCurrentInstance();
 final char sepChar = UINamingContainer.getSeparatorChar(ctx);
 final String SEPARATOR_STRING = String.valueOf(sepChar);

 if (expr.length() == 0) {
     // if an empty value is provided, fail fast.
     throw new IllegalArgumentException("\"\"");
 }

 // Identify the base component from which we will perform our search
 UIComponent base = this;
 if (expr.charAt(0) == sepChar) {
     // Absolute searches start at the root of the tree
     while (base.getParent() != null) {
            base = base.getParent();
     }
     // Treat remainder of the expression as relative
     expr = expr.substring(1);
 } else if (!(base instanceof NamingContainer)) {
            // Relative expressions start at the closest NamingContainer or root
            while (base.getParent() != null) {
                   if (base instanceof NamingContainer) {
                       break;
                   }
            base = base.getParent();
            }
 }

 // Evaluate the search expression (now guaranteed to be relative)
 UIComponent result = null;
 String[] segments = expr.split(SEPARATOR_STRING);
 for (int i = 0, length = (segments.length - 1);
      i < segments.length;
      i++, length--) {
      result = findComponent(base, segments[i], (i == 0));
      // the first element of the expression may match base.id
      // (vs. a child if of base)
      if (i == 0 && result == null &&
          segments[i].equals(base.getId())) {
          result = base;
      }
      if (result != null && (!(result instanceof NamingContainer)) && length > 0) {
          throw new IllegalArgumentException(segments[i]);
      }
      if (result == null) {
          break;
      }
      base = result;
 }

 // Return the final result of our search
 return (result);

}

OmniFaces, internally uses the findComponentRelatively() method. Per example, in the OutputLabel component, OmniFaces uses this method to find the component indicated by the for attribute. Let's check this example:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html id="htmlId"  xmlns="http://www.w3.org/1999/xhtml"
      xmlns:o="http://omnifaces.org/ui"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
  <h:head id="headId">
    <title>Register Form</title>       
  </h:head>
  <h:body id="bodyId">
      
  <h:form id="registerFormId">
    <h:panelGrid id="panelId" columns="3">    
      <o:outputLabel id="labelNameId" for="nameId" value="First Name" />
      <h:inputText id="nameId" required="true" value="#{dataBean.name}" />
      <h:message id="msgNameId" for="nameId"/>

      <o:outputLabel id="labelSurnameId" for="surnameId" value="Last Name" />
      <h:inputText id="surnameId" required="true" value="#{dataBean.surname}" />
      <h:message id="msgSurnameId" for="surnameId"/> 
                              
      <h:commandButton value="Register">
       <f:ajax execute="@form" render="@form" />
      </h:commandButton>
    </h:panelGrid>          
  </h:form>       
      
 </h:body>
</html>

So, OmniFaces passes the instance of the OutputLabel ("given component ") and the nameId ("given client ID "), which is the value of the for attribute, but, is also the clientId of the indicated component (the input text with this id).
The parent component that contains the " given client ID " is the form with id, registerFormId, NOT the output label with id labelNameId, or the panel grid with id, panelId.  This is happening because, the output label and the panel grid are not NamingContainers, so they don't pass this check!
In the figure below, you can see this use case - since the searched component is found by traversing the parents of the "given component ", there is no call of the findComponentInChildren() method:


A more comprehensive case can be like this (is just a re-write of the above example meant to force the call of the findComponentInChildren() method):

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html id="htmlId"  xmlns="http://www.w3.org/1999/xhtml"
      xmlns:o="http://omnifaces.org/ui"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
 <h:head id="headId">
   <title>Register Form</title>        
 </h:head>
 <h:body id="bodyId">

 <h:panelGrid id="panelId" columns="2">
  <o:outputLabel id="labelNameId" for=":registerFormNameId:nameId" value="First Name" />
  <o:outputLabel id="labelSurnameId" for=":registerFormSurnameId:surnameId" value="Last Name" />       

  <h:form id="registerFormNameId">
   <h:inputText id="nameId" required="true" value="#{dataBean.name}" />
   <h:message id="msgNameId" for="nameId"/>
   <h:commandButton value="Register Name">
    <f:ajax execute="@form" render="@form" />
   </h:commandButton> 
  </h:form>

  <h:form id="registerFormSurnameId">
   <h:inputText id="surnameId" required="true" value="#{dataBean.surname}" />
   <h:message id="msgSurnameId" for="surnameId"/>  
   <h:commandButton value="Register Surname">
    <f:ajax execute="@form" render="@form" />
   </h:commandButton> 
  </h:form>
 </h:panelGrid>    

 </h:body>
</html>

So, OmniFaces passes the instance of the OutputLabel ("given component ") and the :registerFormNameId:nameId ("given client ID "), which is the value of the for attribute, but, is also the clientId of the indicated component (the input text with this id). This time the findComponentInParents() reaches the UIViewRoot and will return true. This means that the findComponentInChildren() method enter in action. It will traverse, starting with the UIViewRoot, the component tree and will find the NamingContainer with id, registerFormNameId, as the component that contains the " given client ID". As you can see in the below figure, the findComponentInChildren() method traverse the component tree, including the nodes that already have been traversed by the findComponentInParents() - the checks, 8, 9 and 10. Obviously, calling the findComponentInParents() first is the best approach, since this methods jumps from parent to parent as a " kangaroo", while the findComponentInChildren() method involves a recursive process, which is by its nature less efficient.


You may find this dissertation useless, but many JSF developers uses the findXxx() methods, without understanding the process behind the scene. Once you understand how this process works, you can write your own findXxx() methods, optimize/adjust the existing ones to obtain more power, flexibility and performance in particular cases. There is no rational reason to ignore the time spent by the application to traverse/find components in component tree.
I hope that this dissertation revealed to you the OmniFaces, findComponentRelatively(),findComponentInParents() and findComponentInChildren() methods, and that you will find them useful in your projects. Moreover, they can inspire you to contribute with your own findXxx() methods.

Niciun comentariu :

Trimiteți un comentariu

JSF BOOKS COLLECTION

Postări populare

Follow by Email

Visitors Starting 4 September 2015

Locations of Site Visitors