Package javax.faces.webapp

Source Code of javax.faces.webapp.UIComponentClassicTagBase

/*
*/

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2010 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code.  If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/

package javax.faces.webapp;
import javax.faces.FacesException;
import javax.faces.application.Application;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIComponent;
import javax.faces.component.UIOutput;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.jstl.core.LoopTag;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTag;
import javax.servlet.jsp.tagext.JspIdConsumer;
import javax.servlet.jsp.tagext.Tag;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.RandomAccess;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;


/**
* <p><strong><code>UIComponentTagBase</code></strong> is the base class
* for all JSP tags that use the "classic" JSP tag interface that
* correspond to a {@link javax.faces.component.UIComponent} instance in
* the view.  In Faces 1.2, all component tags are <code>BodyTag</code>
* instances to allow for the execution of the page to build the
* component tree, but not render it.  Rendering happens only after the
* component tree is completely built.</p>
*
* <p>{@link UIComponentTag} extends
* <code>UIComponentClassicTagBase</code> to add support for properties
* that conform to the Faces 1.1 EL.</p>
*
* <p>{@link UIComponentELTag} extends
* <code>UIComponentClassicTagBase</code> class to add support for
* properties that conform to the EL API.</p>
*
* <p>The default implementation allows the proper interweaving of
* template text, non-Faces JSP tag output, and Faces component tag
* output in the same page, as expected by the page author.</p>
*
* <p>The CASE markers in the following example will be cited in the
* method descriptions of this class.</p>
*
* <ul>
*
* <li><p>CASE 1 describes template text and/or non-component custom tag
* output occurring as the child of a component tag, but before the
* first component tag child of that component tag.</p></li>
*
* <li><p>CASE 2 describes template text and/or non-component custom tag
* output occurring between two sibling component tags.</p></li>
*
* <li><p>CASE 3 describes template text and/or non-component custom tag
* output occurring as the child content of an &lt;f:verbatim&gt;
* tag at any point in the page.</p></li>
*
* <li><p>CASE 4 describes template text and/or non-component custom tag
* output occurring between the last child component tag and its
* enclosing parent component tag's end tag.</p></li>
*
* </ul>
*
* <code><pre>

    &lt;h:panelGrid style="color:red" border="4" columns="2"&gt;
      CASE 1
      &lt;h:outputText value="component 1"/&gt;
      CASE 2
      &lt;h:outputText value="component 2"/&gt;
      &lt;f:verbatim&gt;CASE 3&lt;/f:verbatim&gt;
      &lt;c:out value="${pageScope.CASE4}" /&gt;
    &lt;/h:panelGrid&gt;

* </pre></code>
*
* <p>The preceding arrangement of faces component tags, must yield
* markup that will render identically to the following (assuming that
* <code>${pageScope.CASE4}</code> evaluates to "<code>CASE 4</code>"
* without the quotes).</p>
*
* <code><pre>

&lt;table border="4" style="color:red"&gt;

  &lt;tbody&gt;

    &lt;tr&gt;&lt;td&gt;CASE 1&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt;&lt;td&gt;component 1&lt;/td&gt;&lt;/tr&gt;

    &lt;tr&gt;&lt;td&gt;CASE 2&lt;/td&gt; &lt;tr&gt;&lt;td&gt;component 2&lt;/td&gt;&lt;/tr&gt;

    &lt;tr&gt;&lt;td&gt;CASE 3&lt;/td&gt; &lt;td&gt;CASE 4&lt;/td&gt;&lt;/tr&gt;

  &lt;/tbody&gt;

&lt;/table&gt;

* </pre></code>

*
*/

public abstract class UIComponentClassicTagBase extends UIComponentTagBase implements JspIdConsumer, BodyTag {

    // ------------------------------------------------------ Manifest Constants
    /**
     * <p>The request scope attribute under which a component tag stack
     * for the current request will be maintained.</p>
     */
    private static final String COMPONENT_TAG_STACK_ATTR =
        "javax.faces.webapp.COMPONENT_TAG_STACK";

    /**
     * <p>A request scoped map that takes the place of calls to the request
     * map directly.</p>
     * <p>IMPORTANT.  This map is accessed via literal string in
     * ViewTag.doEndTag().</p>
     */
    private static final String CONTEXT_MAP_ATTR =
        "com.sun.faces.CONTEXT_MAP";

    /**
     * <p>The {@link UIComponent} attribute under which we will store a
     * <code>List</code> of the component identifiers of child components
     * created on the previous generation of this page (if any).</p>
     */
    private static final String JSP_CREATED_COMPONENT_IDS =
        "javax.faces.webapp.COMPONENT_IDS";


    /**
     * <p>The {@link UIComponent} attribute under which we will store a
     * <code>List</code> of the facet names of facets created on the previous
     * generation of this page (if any).
     */
    private static final String JSP_CREATED_FACET_NAMES =
        "javax.faces.webapp.FACET_NAMES";


    /**
     * <p>The attribute name under which we will store the {@link FacesContext}
     * for this request.</p>
     */
    private static final String CURRENT_FACES_CONTEXT =
        "javax.faces.webapp.CURRENT_FACES_CONTEXT";

    /**
     * <p>The attribute name under which we will store the {@link UIViewRoot}
     * for this request.</p>
     */
    private static final String CURRENT_VIEW_ROOT =
        "javax.faces.webapp.CURRENT_VIEW_ROOT";

    /**
     * Used as the prefix for ids.  This is necessary to avoid
     * uniqueness conflicts with the transient verbatim components.
     */
    protected static final String UNIQUE_ID_PREFIX =
  UIViewRoot.UNIQUE_ID_PREFIX + '_';

    /**
     * Used to store the previousJspId Map in requestScope
     */
    private static final String PREVIOUS_JSP_ID_SET =
  "javax.faces.webapp.PREVIOUS_JSP_ID_SET";

    /**
     * This is a <code>Page</code> scoped marker to help us
     * keep track of the different execution context we could
     * be operating within, e.g. an include, or a tag file.
     * The value of the attribute is an Integer that is unqiue
     * to this page context.
     */
    private static final String JAVAX_FACES_PAGECONTEXT_MARKER =
         "javax.faces.webapp.PAGECONTEXT_MARKER";

    /**
     * This is a <code>request</code> scoped attribute which contains
     * an AtomicInteger which we use to increment the PageContext
     * count.
     */
    private static final String JAVAX_FACES_PAGECONTEXT_COUNTER =
         "javax.faces.webapp.PAGECONTEXT_COUNTER";

   
    // ------------------------------------------------------ Instance Variables
    /**
     * <p>The <code>bodyContent</code> for this tag handler.</p>
     */
    protected BodyContent bodyContent = null;

    /**
     * <p>The {@link UIComponent} that is being encoded by this tag,
     * if any.</p>
     */
    private UIComponent component = null;


    /**
     * <p>The {@link FacesContext} for the request being processed, if any.
     * </p>
     */
    private FacesContext context = null;


    /**
     * <p>Was a new component instance dynamically created when our
     * <code>findComponent()</code> method was called.</p>
     */
    private boolean created = false;


    /**
     * <p>The <code>Lst</code> of {@link UIComponent} ids created or located
     * by nested {@link UIComponentTag}s while processing the current
     * request.</p>
     */
    private List<String> createdComponents = null;


    /**
     * <p>The <code>List</code> of facet names created or located by nested
     * {@link UIComponentTag}s while processing the current request.</p>
     */
    private List<String> createdFacets = null;


    /**
     * <p>The JSP <code>PageContext</code> for the page we are embedded in.</p>
     */
    protected PageContext pageContext = null;


    /**
     * <p>The JSP <code>Tag</code> that is the parent of this tag.</p>
     */
    private Tag parent = null;

    /**
     * {@link #setJspId}
     */

    private String jspId = null;

    /**
     * Only consulted in setJspId to detect the iterator case.
     * Set in {@link #release}.  Never cleared.
     */

    //private String oldJspId = null;

    /**
     * This is simply the jspId prefixed by {@link #UNIQUE_ID_PREFIX}.
     */

    private String facesJspId = null;

    /**
     * <p>The component identifier for the associated component.</p>
     */
    private String id = null;

    /**
     * Caches the nearest enclosing {@link UIComponentClassicTagBase} of this
     * tag. This is used for duplicate id detection.
     */
    private UIComponentClassicTagBase parentTag = null;

    /**
     * Set to true if this component is nested inside of an iterating
     * tag
     */
    private boolean isNestedInIterator = false;

  /**
   * The next child index to get in getChild()
   */
  private int _nextChildIndex = 0;

    /**
     * private context Map For Lookups
     */
    Map<String, Object> contextMap = null;

    Map<String, Map<String, UIComponentTagBase>> namingContainerChildIds = null;

    public UIComponentClassicTagBase() {}
   
    UIComponentClassicTagBase(PageContext pageContext, FacesContext facesContext) {
        this.pageContext = pageContext;
        this.context = facesContext;
    }


    // --------------------------------------------- Support Methods for Tag

    //
    // Simple methods to be overridden by subclasses if necessary
    //

    /**
     * <p>Return the flag value that should be returned from the
     * <code>doStart()</code> method when it is called.  Subclasses
     * may override this method to return the appropriate value.</p>
     *
     * @throws JspException to cause <code>doStart()</code> to
     *  throw an exception
     */
    protected int getDoStartValue() throws JspException {

  int result = EVAL_BODY_BUFFERED;

  return result;
    }

    /**
     * <p>Return the flag value that should be returned from the
     * <code>doEnd()</code> method when it is called.  Subclasses
     * may override this method to return the appropriate value.</p>
     *
     * @throws JspException to cause <code>doEnd()</code> to
     *  throw an exception
     */
    protected int getDoEndValue() throws JspException {

        return (EVAL_PAGE);

    }

    /**
     * <p>Delegate to the <code>encodeBegin()</code> method of our
     * corresponding {@link UIComponent}.  This method is called from
     * <code>doStartTag()</code>.  Normally, delegation occurs unconditionally;
     * however, this method is abstracted out so that advanced tags can
     * conditionally perform this call.
     *
     * @throws IOException if an input/output error occurs
     *
     * @deprecated No encoding is done during JSP page execution.
     * Encoding is deferred until the page has completed executing to
     * allow the entire tree to be built before any encoding occurs.
     */
    protected void encodeBegin() throws IOException {

        component.encodeBegin(context);

    }


    /**
     * <p>Delegate to the <code>encodeChildren()</code> method of our
     * corresponding {@link UIComponent}.  This method is called from
     * <code>doStartTag()</code>.  Normally, delegation occurs unconditionally;
     * however, this method is abstracted out so that advanced tags can
     * conditionally perform this call.
     *
     * @throws IOException if an input/output error occurs
     *
     * @deprecated No encoding is done during JSP page execution.
     * Encoding is deferred until the page has completed executing to
     * allow the entire tree to be built before any encoding occurs.
     */
    protected void encodeChildren() throws IOException {

        component.encodeChildren(context);

    }


    /**
     * <p>Delegate to the <code>encodeEnd()</code> method of our
     * corresponding {@link UIComponent}.  This method is called from
     * <code>doStartTag()</code>.  Normally, delegation occurs unconditionally;
     * however, this method is abstracted out so that advanced tags can
     * conditionally perform this call.
     *
     * @throws IOException if an input/output error occurs
     *
     * @deprecated No encoding is done during JSP page execution.
     * Encoding is deferred until the page has completed executing to
     * allow the entire tree to be built before any encoding occurs.
     */
    protected void encodeEnd() throws IOException {

        component.encodeEnd(context);

    }

    // --------------------------------------------------------- Tag Properties


    /**
     * <p>Set the <code>PageContext</code> of the page containing this
     * tag instance.</p>
     *
     * @param pageContext The enclosing <code>PageContext</code>
     */
    public void setPageContext(PageContext pageContext) {

        this.pageContext = pageContext;

    }


    /**
     * <p>Return the <code>Tag</code> that is the parent of this instance.</p>
     */
    public Tag getParent() {

        return (this.parent);

    }


    /**
     * <p>Set the <code>Tag</code> that is the parent of this instance.</p>
     *
     * @param parent The new parent <code>Tag</code>
     */
    public void setParent(Tag parent) {

        this.parent = parent;

    }




    //
    // Complex methods to support Tag
    //

    /**
     * <p>Set up the {@link javax.faces.context.ResponseWriter} for the
     * current response, if this has not been done already.</p>
     *
     * <p>@deprecated{@link
     * javax.faces.application.ViewHandler#renderView} is now
     * responsible for setting up the response writer.  This method is
     * now a no-op.</p>

     */
    protected void setupResponseWriter() {
    }


    /**
     * <p>Create a new child component using <code>createComponent</code>,
     * initialize its properties, and add it to its parent as a child.
     * </p>
     * @param context {@link FacesContext} for the current request
     * @param parent Parent {@link UIComponent} for the new child
     * @param componentId Component identifier for the new child,
     *  or <code>null</code> for no explicit identifier
     */
    private UIComponent createChild(
     FacesContext context,
     UIComponent parent,
     UIComponentClassicTagBase parentTag,
     String componentId) throws JspException {

        UIComponent component = createComponent(context, componentId);
             
        int indexOfNextChildTag = parentTag.getIndexOfNextChildTag();
        if (indexOfNextChildTag > parent.getChildCount()) {
            indexOfNextChildTag = parent.getChildCount();
        }
        parent.getChildren().add(indexOfNextChildTag, component);
        created = true;
        return (component);

    }

    /**
     * <p>Create a new child component using <code>createComponent</code>,
     * initialize its properties, and add it to its parent as a facet.
     * </p>
     * @param context {@link FacesContext} for the current request
     * @param parent Parent {@link UIComponent} of the new facet
     * @param name Name of the new facet
     * @param newId id of the new facet
     */
    private UIComponent createFacet(FacesContext context, UIComponent parent,
                                    String name, String newId) throws JspException {

        UIComponent component = createComponent(context, newId);
        parent.getFacets().put(name, component);
        created = true;
        return (component);

    }

    /**
     * <p>Return a child with the specified component id from the specified
     * component, if any; otherwise, return <code>null</code>.</p>
     *
     * @param component {@link UIComponent} to be searched
     * @param componentId Component id to search for
     */
    private static UIComponent getChild(
      UIComponentClassicTagBase tag, UIComponent component, String componentId)
    {
      int childCount = component.getChildCount();
     
      // we only need to bother to check if we even have children
      if (childCount > 0)
      {
        List<UIComponent> children = component.getChildren();
       
        // Most Lists implement RandomAccess, so iterate directly rather than creating
        // and iterator
        if (children instanceof RandomAccess)
        {
          // in the most common case, the first component we are asked for will be the
          // our first child, the second, our second, etc.  Take advantage of this by
          // remembering the index to check for the next child.  This changes this code
          // from O(n^2) for all of the children to O(n)
          int startIndex;
         
          if (tag != null)
            startIndex = tag._nextChildIndex;
          else
            startIndex = 0;
         
          // start searching from location remembered from last time
          for (int i = startIndex; i < childCount; i++)
          {
            UIComponent child = children.get(i);
           
            if (componentId.equals(child.getId()))
            {
              // bump up the index to search next and wrap around
              i++;
             
              tag._nextChildIndex = (i < childCount) ? i : 0;
              return child;
            }
          }
         
          // handle case where we started past the first item and didn't find our
          // child.  Now search from the beginning to where we started
          if (startIndex > 0)
          {
            for (int i = 0; i < startIndex; i++)
            {
              UIComponent child = children.get(i);
             
              if (componentId.equals(child.getId()))
              {             
                i++;
               
                tag._nextChildIndex = i;
                return child;
              }
            }           
          }
        }
        else
        {
          // List doesn't support RandomAccess, do it the iterator way
          for (UIComponent child : children)
          {
            if (componentId.equals(child.getId()))
              return child;           
          }
        }
      }
     
      return null;
    }

    /**
     * <p>Find and return the {@link UIComponent}, from the component
     * tree, that corresponds to this tag handler instance.  If there
     * is no such {@link UIComponent}, create one
     * and add it as a child or facet of the {@link UIComponent} associated
     * with our nearest enclosing {@link UIComponentTag}.  The process for
     * locating or creating the component is:</p>
     * <ol>
     * <li>If we have previously located this component, return it.</li>
     * <li>Locate the parent component by looking for a parent
     *     {@link UIComponentTag} instance, and ask it for its component.
     *     If there is no parent {@link UIComponentTag} instance, this tag
     *     represents the root component, so get it from the current
     *     <code>Tree</code> and return it.</li>
     * <li>If this {@link UIComponentTag} instance has the
     *     <code>facetName</code> attribute set, ask the parent
     *     {@link UIComponent} for a facet with this name.  If not found,
     *     create one, call <code>setProperties()</code> with the new
     *     component as a parameter, and register it under this name.
     *     Return the found or created facet {@link UIComponent}.</li>
     * <li>Determine the component id to be assigned to the new
     *     component, as follows:  if this {@link UIComponentTag} has
     *     an <code>id</code> attribute set, use that value; otherwise,
     *     generate an identifier that is guaranteed to be the same for
     *     this {@link UIComponent} every time this page is processed
     *     (i.e. one based on the location of all {@link UIComponentTag}
     *     instances without an <code>id</code> attribute set).</li>
     * <li>Ask the parent {@link UIComponent} for a child with this identifier.
     *     If not found, create one, call <code>setProperties()</code>
     *     with the new component as a parameter, and register it as a child
     *     with this identifier.  Return the found or created
     *     child {@link UIComponent}.</li>
     * </ol>
     * <p>When creating a component, the process is:</p>
     * <ol>
     * <li>Retrieve the component type by calling
     * {@link UIComponentTag#getComponentType}</li>
     * <li>If the component has a <code>binding</code> attribute,
     * create an expression from it, and call
     * {@link Application#createComponent} with that expression,
     * the {@link FacesContext}, and the component type.  Store the
     * expression using the key <code>"binding"</code>.</li>
     * <li>Otherwise, call {@link Application#createComponent} with
     * only the component type.
     * <li>Call <code>setProperties()</code>.
     * <li>Add the new component as a child or facet of its parent</li>
     * </ol>
     */
  protected UIComponent findComponent(FacesContext context) throws JspException
  {
    // Step 1 -- Have we already found the relevant component?
    if (component != null)
    {
      return (component);
    }

    // Step 2 -- Identify the component that is, or will be, our parent
    UIComponentClassicTagBase parentTag = _getParentUIComponentClassicTagBase(contextMap);
    UIComponent parentComponent;
   
    if (parentTag != null)
    {
      parentComponent = parentTag.getComponentInstance();
    }
    else
    {     
      // Special case.  The component to be found is the
      // UIViewRoot.
      // see if this is the first time this tag instance is trying
      // to be bound to the UIViewRoot
      parentComponent = context.getViewRoot();
     
      // Has this UIViewRoot instance had a tag bound to it
      // before?
      if (null == parentComponent.getAttributes().get(CURRENT_VIEW_ROOT))
      {
        // No it hasn't.
       
        // make sure setProperties() and setId() are called
        // once per UIViewRoot instance.
        try
        {
          setProperties(parentComponent);
        }
        catch (FacesException e)
        {
          if (e.getCause() instanceof JspException)
          {
            throw ((JspException)e.getCause());
          }
          throw e;
        }

        if (null != this.id)
        {
          parentComponent.setId(this.id);
        }
        else
        {
          assert(null != getFacesJspId());
          parentComponent.setId(getFacesJspId());
        }
       
        parentComponent.getAttributes().put(CURRENT_VIEW_ROOT, CURRENT_VIEW_ROOT);
        created = true;
      }
      else if (hasBinding())
      {
        try
        {
          setProperties(parentComponent);
        }
        catch (FacesException e)
        {
          if (e.getCause() instanceof JspException)
          {
            throw ((JspException)e.getCause());
          }
          throw e;
        }
      }

      // this is not the first time this tag instance is trying to
      // be bound to this UIViewRoot, take no extra action.
      component = parentComponent;
      return (component);
    }

    // Step 3 -- Calculate the component identifier for this component
    String newId = createId(context);

    // Step 4 -- Create or return a facet with the specified name (if any)
    String facetName = getFacetName();
    boolean created = parentTag.getCreated();
   
    if (facetName != null)
    {
      // =-= bts figure out why this didn't work
      // if (!created)
        component = parentComponent.getFacets().get(facetName);
     
      if (component == null)
      {
        component = createFacet(context, parentComponent, facetName, newId);
      }
     
      return (component);
    }
    else
    {
      // Step 5 -- Create or return a child with the specified id if the parent
      // wasn't newly created and therefore can't already contain the component
     
      // =-= bts figure out why this didn't work
      // if (!created)
      component = getChild(parentTag, parentComponent, newId);

      if (component == null)
      {
        component = createChild(context, parentComponent, parentTag, newId);
      }

      return (component);
    }
  }

    //
    // Tag tree navigation
    //

  /**
   * <p>Locate and return the nearest enclosing {@link UIComponentClassicTagBase}
   * if any; otherwise, return <code>null</code>.</p>
   *
   * @param context <code>PageContext</code> for the current page
   */
  public static UIComponentClassicTagBase getParentUIComponentClassicTagBase(PageContext context)
  {
    return _getParentUIComponentClassicTagBase(getFacesContext(context));
  }

  private static UIComponentClassicTagBase _getParentUIComponentClassicTagBase(
    FacesContext facesContext)
  {
      return _getParentUIComponentClassicTagBase(_getContextMap(facesContext));
  }

  private static UIComponentClassicTagBase _getParentUIComponentClassicTagBase(Map<String, Object> cMap) {
    List list = null;

    if (cMap != null) {
        list = (List) cMap.get(COMPONENT_TAG_STACK_ATTR);
    }

    if (list != null)
    {
      return ((UIComponentClassicTagBase) list.get(list.size() - 1));
    }
    else
    {
      return null;
    }

  }

    //
    // Methods related to the createdComponents and createdFacets lists.
    //

    protected int getIndexOfNextChildTag() {

        if (createdComponents != null) {
            return (createdComponents.size());
        } else {
            return (0);
        }

    }

    protected void addChild(UIComponent child) {

        if (createdComponents == null) {
            createdComponents = new ArrayList<String>(6);
        }
        createdComponents.add(child.getId());
    }

    /*
     * Adds argument child to component tree as a child of this component.
     */
   
    void addChildToComponentAndTag(UIComponent child) {
        UIComponent myComponent = this.getComponentInstance();

        int indexOfNextChildTag = this.getIndexOfNextChildTag();
        if (indexOfNextChildTag > myComponent.getChildCount()) {
            indexOfNextChildTag = myComponent.getChildCount();
        }
        myComponent.getChildren().add(indexOfNextChildTag, child);
        this.addChild(child);       
    }

    protected void addFacet(String name) {

        if (createdFacets == null) {
            //noinspection CollectionWithoutInitialCapacity
            createdFacets = new ArrayList<String>(3);
        }
        createdFacets.add(name);

    }

    /**
     * <p>Pop the top {@link UIComponentTag} instance off of our component tag
     * stack, deleting the stack if this was the last entry.</p>
     */
    private void popUIComponentClassicTagBase() {
        assert(null != contextMap);
        List list = (List) contextMap.get(COMPONENT_TAG_STACK_ATTR);

        // if an exception occurred in a nested  tag,
        //there could be a few tags left in the stack.
        UIComponentClassicTagBase uic = null;
        while (list != null && uic != this) {
            int idx = list.size() - 1;
            uic = (UIComponentClassicTagBase) list.get(idx);
            list.remove(idx);
            if (idx < 1) {
                contextMap.remove(COMPONENT_TAG_STACK_ATTR);
                list = null;
            }
        }
    }


    /**
     * <p>Push the specified {@link UIComponentTag} instance onto our component
     * tag stack, creating a stack if necessary.</p>
     */
    private void pushUIComponentClassicTagBase() {

        assert (null != contextMap);
        List<UIComponentClassicTagBase> list = TypedCollections.dynamicallyCastList((List)
              contextMap.get(COMPONENT_TAG_STACK_ATTR), UIComponentClassicTagBase.class);
        if (list == null) {
            //noinspection CollectionWithoutInitialCapacity
            list = new ArrayList<UIComponentClassicTagBase>();
            contextMap.put(COMPONENT_TAG_STACK_ATTR, list);
        }
        list.add(this);

    }

  /**
   * Similar to List.indexOf, except that we start searching from a specific index
   * and then wrap aroud.  For this to be performant, the List should implement
   * RandomAccess.
   * @param <T>
   * @param list List to seatch
   * @param startIndex index to start searching for value from
   * @param searchValue Value to search for (null not supported)
   * @return The index at which the value was first found, or -1 if not found
   */
  private static int _indexOfStartingFrom(List<?> list, int startIndex, Object searchValue)
  {
    int itemCount = list.size();
   
    boolean found = false;
   
    // start searching from location remembered from last time
    for (int currIndex = startIndex; currIndex < itemCount; currIndex++)
    {
      Object currId = list.get(currIndex);
     
      if ((searchValue == currId) || ((searchValue != null) && searchValue.equals(currId)))
      {
        return currIndex;
      }
    }
   
    // handle case where we started past the first item and didn't find the
    // searchValue.  Now search from the beginning to where we started
    if (startIndex > 0)
    {
      for (int currIndex = 0; currIndex < startIndex; currIndex++)
      {       
        Object currId = list.get(currIndex);
       
        if ((searchValue == currId) || ((searchValue != null) && searchValue.equals(currId)))
        {
          return currIndex;
        }
      }           
    }
   
    // didn't find it
    return -1;
  }

  /**
   * <p>Retrieve from the {@link UIComponent} corresponding to this tag
   * handler the list of all child component ids created by
   * {@link UIComponentTag} instances the previous time this tree was
   * rendered.  Compare it to the list of children created during this
   * page processing pass, and remove all children present on the old list
   * but not in the new list.  Save the list as a {@link UIComponent}
   * attribute so that it gets saved as part of the component's state.</p>
   */
  private void removeOldChildren()
  {
    Map<String, Object> attributes = component.getAttributes();
    List<String> currentComponents = createdComponents;

    // Get the old list of created component ids and update the current list as a
    // component attribute
    Object oldValue;
   
    if (currentComponents != null)
    {
      oldValue = attributes.put(JSP_CREATED_COMPONENT_IDS, currentComponents);
      createdComponents = null;
    }
    else
    {
      oldValue = attributes.remove(JSP_CREATED_COMPONENT_IDS);
    }
             
    // Remove old children that are no longer present
    if (oldValue != null)
    {
      List<String> oldList = TypedCollections.dynamicallyCastList((List)oldValue, String.class);
     
      int oldCount = oldList.size();
     
      if (oldCount > 0)
      {
        if (currentComponents != null)
        {
          int currStartIndex = 0;
           
          for (int oldIndex = 0; oldIndex < oldCount; oldIndex++)
          {
            String oldId = oldList.get(oldIndex);
           
            int foundIndex = _indexOfStartingFrom(currentComponents, currStartIndex, oldId);
           
            if (foundIndex != -1)
            {
              currStartIndex = foundIndex + 1;
            }
            else
            {
              UIComponent child = component.findComponent(oldId);
              // if a component is marked transient, it would have
              // been already removed from the child list, but the
              // oldList would still have it.  In addition, the component
              // might have manually been removed.  So, if findComponent
              // isn't successful, don't call remove child (it will NPE)
              if ( child != null)
              {
                component.getChildren().remove(child);
              }
            }
          }
        }
        else
        {
          List<UIComponent> children = component.getChildren();
         
          // All old components need to be removed
          for (String oldId : oldList)
          {
            UIComponent child = component.findComponent(oldId);
            if (child != null)
            {
              children.remove(child);
            }           
          }
        }
      }
    }
  }


  /**
   * <p>Retrieve from the {@link UIComponent} corresponding to this tag
   * handler the list of all facet names created by {@link UIComponentTag}
   * instances the previous time this tree was rendered.  Compare it to the
   * list of facets created during this page processing pass, and remove
   * all facets present on the old list but not in the new list.  Save the
   * list as a {@link UIComponent} attribute so that it gets saved as part
   * of the component's state.</p>
   */
  private void removeOldFacets()
  {
    Map<String, Object> attributes = component.getAttributes();
    List<String> currentComponents = createdFacets;

    // Get the old list of created component ids and update the current list as a
    // component attribute
    Object oldValue;
   
    if (currentComponents != null)
    {
      oldValue = attributes.put(JSP_CREATED_FACET_NAMES, currentComponents);
      createdFacets = null;
    }
    else
    {
      oldValue = attributes.remove(JSP_CREATED_FACET_NAMES);
    }
             
    // Remove old children that are no longer present
    if (oldValue != null)
    {
      List<String> oldList = TypedCollections.dynamicallyCastList((List)oldValue, String.class);

      int oldCount = oldList.size();
     
      if (oldCount > 0)
      {
        if (currentComponents != null)
        {
          int currStartIndex = 0;
           
          for (int oldIndex = 0; oldIndex < oldCount; oldIndex++)
          {
            String oldId = oldList.get(oldIndex);
           
            int foundIndex = _indexOfStartingFrom(currentComponents, currStartIndex, oldId);
           
            if (foundIndex != -1)
            {
              currStartIndex = foundIndex + 1;
            }
            else
            {
              component.getFacets().remove(oldId);
            }
          }
        }
        else
        {
          Map<String, UIComponent> facets = component.getFacets();
         
          // All old facets need to be removed
          for (String oldId : oldList)
          {
            facets.remove(oldId);
          }
        }
      }
    }
  }

    //
    // Methods to support content interweaving
    //

    /**
     *
     * <p>Create a transient UIOutput component from the body content,
     * of this tag instance or return null if there is no body content,
     * the body content is whitespace, or the body content is a
     * comment.</p>
     */

    protected UIComponent createVerbatimComponentFromBodyContent() {
        UIOutput verbatim = null;
        String bodyContentString;
        String trimString;
        if (null != bodyContent &&
             null != (bodyContentString = bodyContent.getString()) &&
             0 < (trimString = bodyContent.getString().trim()).length()) {
            if (!(trimString.startsWith("<!--") &&
                 trimString.endsWith("-->"))) {
                verbatim = createVerbatimComponent();
                verbatim.setValue(bodyContentString);
                bodyContent.clearBody();
            } else {
                StringBuilder content = new StringBuilder(trimString.length());
                int sIdx = trimString.indexOf("<!--");
                int eIdx = trimString.indexOf("-->", sIdx);
                while (sIdx >= 0 && eIdx >= 0) {
                    if (sIdx == 0) {
                        trimString = trimString.substring(eIdx + 3);
                    } else {
                        content.append(trimString.substring(0, sIdx));
                        trimString = trimString.substring(eIdx + 3);
                    }
                    sIdx = trimString.indexOf("<!--");
                    eIdx = trimString.indexOf("-->", sIdx);
                }
                content.append(trimString);
                String result = content.toString();
                if (result.trim().length() > 0) {
                    verbatim = createVerbatimComponent();
                    verbatim.setValue(content.toString());
                }
                bodyContent.clearBody();
            }
        }
        return verbatim;
    }

    /**
     * <p>Use the {@link Application} instance to create a new component
     * with the following characteristics.</p>
     *
     * <p><code>componentType</code> is
     * <code>javax.faces.HtmlOutputText</code>.</p>
     *
     * <p><code>transient</code> is <code>true</code>.</p>
     *
     * <p><code>escape</code> is <code>false</code>.</p>
     *
     * <p><code>id</code> is
     * <code>FacesContext.getViewRoot().createUniqueId()</code></p>
     *
     */

    protected UIOutput createVerbatimComponent() {
  assert(null != getFacesContext());
  UIOutput verbatim;
  Application application = getFacesContext().getApplication();
  verbatim = (UIOutput)
      application.createComponent("javax.faces.HtmlOutputText");
  verbatim.setTransient(true);
  verbatim.getAttributes().put("escape", Boolean.FALSE);
  verbatim.setId(getFacesContext().getViewRoot().createUniqueId());
  return verbatim;
    }

    /**
     * <p>Add <i>verbatim</i> as a sibling of <i>component</i> in
     * <i>component</i> in the parent's child list.  <i>verbatim</i> is
     * added to the list at the position immediatly preceding
     * <i>component</i>.</p>
     */

    protected void addVerbatimBeforeComponent(
          UIComponentClassicTagBase parentTag,
          UIComponent verbatim,
          UIComponent component) {

        UIComponent parent = component.getParent();
        if (null == parent) {
            return;
        }

        List<UIComponent> children = parent.getChildren();
        // EDGE CASE:
            // Consider CASE 1 or 2 where the component is provided via a
            // component binding in session or application scope.
            // The automatically created UIOuput instances for the template text
            // will already be present.  Check the JSP_CREATED_COMPONENT_IDS attribute,
            // if present and the number of created components is the same
            // as the number of children replace at a -1 offset from the current
            // value of indexOfComponentInParent, otherwise, call add()
        List createdIds = (List)
              parent.getAttributes().get(JSP_CREATED_COMPONENT_IDS);
        int indexOfComponentInParent = children.indexOf(component);
        boolean replace =
              (indexOfComponentInParent > 0 && createdIds != null &&
              createdIds.size() == children.size());
        if (replace) {
            UIComponent oldVerbatim = children.get(indexOfComponentInParent - 1);
            if (oldVerbatim instanceof UIOutput && oldVerbatim.isTransient()) {
                children.set((indexOfComponentInParent - 1), verbatim);
            } else {
                children.add(indexOfComponentInParent, verbatim);
            }
        } else {
            children.add(indexOfComponentInParent, verbatim);
        }
        parentTag.addChild(verbatim);
    }

    /**
     * <p>Add <i>verbatim</i> as a sibling of <i>component</i> in
     * <i>component</i> in the parent's child list.  <i>verbatim</i> is
     * added to the list at the position immediatly following
     * <i>component</i>.</p>
     */

    protected void addVerbatimAfterComponent(UIComponentClassicTagBase parentTag,
             UIComponent verbatim,
             UIComponent component) {
  int indexOfComponentInParent;
  UIComponent parent = component.getParent();

  // invert the order of this if and the assignment below.  Since this line is
  // here, it appears an early return is acceptable/desired if parent is null,
  // and, if it is null, we should probably check for that before we try to
  // access it.  2006-03-15 jdl
  if (null == parent) {
      return;
  }
  List<UIComponent> children = parent.getChildren();
  indexOfComponentInParent = children.indexOf(component);
  if (children.size() - 1 == indexOfComponentInParent) {
      children.add(verbatim);
  }
  else {
      children.add(indexOfComponentInParent + 1, verbatim);
  }
  parentTag.addChild(verbatim);
    }

    // ------------------------------------------------------------ Tag Methods

    /**
     *
     * <p>Perform any processing necessary to find (or create) the
     * {@link UIComponent} instance in the view corresponding to this
     * tag instance in the page and, if and only if a component was
     * created, insert it into the tree at the proper location as
     * expected by the page author.  Secondarily, cause a transient
     * {@link UIOutput} component to be created and placed in the tree
     * <b>before</b> the <code>UIComponent</code> instance for
     * <b>this</b> tag.  The value of this <code>UIOutput</code>
     * component must include anything covered by <code>CASE 1</code> or
     * <code>CASE 2</code> in the class description.</p>
     *
     * <p>The default implementation, which is intended to be sufficient
     * for most components, implements this secondary requirement by
     * calling {@link #getParentUIComponentClassicTagBase}, and calling
     * {@link #createVerbatimComponentFromBodyContent} on the result.
     * It then adds the returned component to the tree <b>before</b> the
     * actual component for <b>this</b> tag instance instance by calling
     * {@link #addVerbatimBeforeComponent}.</p>
     *
     * <p>Before returning, the component is pushed onto the component
     * stack for this response to enable the {@link
     * #getParentUIComponentClassicTagBase} method to work properly.</p>
     *
     * <p>The flag value to be returned is acquired by calling the
     * <code>getDoStartValue()</code> method, which tag subclasses may
     * override if they do not want the default value.</p>
     *
     * @throws JspException if an error occurs
     */
    public int doStartTag() throws JspException {
  // make sure that these ivars are reset at the beginning of the
  // lifecycle for this tag.
  createdComponents = null;
  createdFacets = null;
  UIComponent verbatim = null;

        context = getFacesContext();
  if (null == context) {
      // PENDING(edburns): I18N
      throw new JspException("Can't find FacesContext");
  }

        getContextMap(context);
        assert(null != contextMap);

        List list = (List) contextMap.get(COMPONENT_TAG_STACK_ATTR);
        if (list != null) {
            parentTag = ((UIComponentClassicTagBase) list.get(list.size() - 1));
        } else {
            parentTag = null;
        }

        Map<String,UIComponentTagBase> componentIds;

        // If we're not inside of a facet, and if we are inside of a
        // rendersChildren==true component, stuff any template text or
        // custom tag output into a transient component.
        if (null == getFacetName() &&
            null != parentTag) {
            Tag p = this.getParent();
            // If we're not inside a JSP tag or we're not inside
            // a UIComponentTag flush the buffer
            if (null == p || !(p instanceof UIComponentTagBase)) {
                JspWriter out = pageContext.getOut();
                if (!(out instanceof BodyContent)) {
                    try {
                        out.flush();
                    }
                    catch (IOException ioe) {
                        throw new JspException(ioe);
                    }
                }
            }
            verbatim = parentTag.createVerbatimComponentFromBodyContent();
        }

        // Locate the UIComponent associated with this UIComponentTag,
        // creating one if necessary
        component = findComponent(context);

  // if we have a verbatim component, add it after this component.
  if (null != verbatim) {
      addVerbatimBeforeComponent(parentTag,
               verbatim, component);
  }

        Object tagInstance = null;
        String clientId = null;

        if (component instanceof NamingContainer || (parentTag == null)) {
            namingContainerChildIds = new HashMap<String, Map<String, UIComponentTagBase>>();
        }

        if (this.id != null) {
            clientId = getId();

            UIComponentClassicTagBase temp = (UIComponentClassicTagBase)
                getParentNamingContainerTag().getNamingContainerChildIds().get(clientId);

            // According to the JavaDocs for JspIdConsumer tag handlers
            // that implement this interface are not to be pooled, however
            // due to a bug in Jasper this is not the case.
            // Because of this, two tags with the same ID within the same
            // naming container will not be detected as duplicates.  So
            // in order to ensure we detect it, if the instance is the same,
            // verify the JSP IDs are different.  If they are, then continue,
            // if they aren't, then we're dealing with EVAL_BODY_AGAIN (see
            // below)
            //noinspection ObjectEquality
            if (temp == this
                 && !this.getJspId().equals(temp.getJspId())) {
                tagInstance = this;
            } else if (temp != null
                         && temp != this
                         && this.getJspId().equals(temp.getJspId())) {
                // new instance, same JSP ID - this is the EVAL_BODY_AGAIN case.
                tagInstance = temp;
            }
        }

        // If we have a tag instance, then, most likely, a tag handler
        // returned EVAL_BODY_AGAIN somewhere.  Make sure the instance
        // returned is the same as the current instance and if this is the case,
        // do not perform ID validation as it has already been done.
        if (tagInstance == null) {

            // only check for id uniqueness if the author has manually given
            // us an id.
            if (null != this.id) {

                // assert component ID uniqueness
                if (clientId != null) {
                    if (getParentNamingContainerTag().getNamingContainerChildIds().containsKey(clientId)) {
                        // PENDING i18n
                        StringWriter writer = new StringWriter(128);
                        printTree(context.getViewRoot(), clientId, writer, 0);
                        String msg = "Duplicate component id: '"
                                     + clientId
                                     + "', first used in tag: '"
                                     + getParentNamingContainerTag().getNamingContainerChildIds().get(clientId).getClass().getName()

                                     + "'\n"
                                     + writer.toString();
                        throw new JspException(new IllegalStateException(msg));
                    } else {
                        getParentNamingContainerTag().getNamingContainerChildIds().put(clientId, this);
                    }
                }
            }

            // Add to parent's list of created components or facets if needed

            if (parentTag != null) {
                if (getFacetName() == null) {
                    parentTag.addChild(component);
                } else {
                    parentTag.addFacet(getFacetName());
                }
            }
        }

  // Rendering is deferred until after the tree is completely
  // created

        // Return the appropriate control value
        pushUIComponentClassicTagBase();
        return (getDoStartValue());

    }

    private Map<String, Object> getContextMap(FacesContext context) {
        if (null == contextMap) {
            Map<String, Object> requestMap = context.getExternalContext().getRequestMap();

            contextMap = (Map<String, Object>) requestMap.get(CONTEXT_MAP_ATTR);
            if (null == contextMap) {
                contextMap = new HashMap<String, Object>(5);
                requestMap.put(CONTEXT_MAP_ATTR, contextMap);
            }
        }

        return contextMap;
    }

    private static Map<String, Object> _getContextMap(FacesContext context) {
        Map<String, Object> rMap = context.getExternalContext().getRequestMap();
        return (Map<String,Object>) rMap.get(CONTEXT_MAP_ATTR);

    }

    private void releaseContextMap(FacesContext context) {
        Map<String, Object> requestMap = context.getExternalContext().getRequestMap();

        if (null != contextMap) {
            contextMap.clear();
        }
        requestMap.remove(CONTEXT_MAP_ATTR);
    }


    /**
     *
     * <p>Perform any processing necessary to handle the content
     * implications of CASE 3 in the class description.</p>
     *
     * <p>The default implementation, which is intended to be sufficient
     * for most components, calls {@link
     * #createVerbatimComponentFromBodyContent} on <b>this</b> instance
     * and adds it as a child of the component for this tag's component
     * at the <b>end</b> of the child list.  In addition, the following
     * housekeeping steps are taken.</p>
     *
     * <ul>
     *
     * <li>Retrieve from the {@link UIComponent} the set of component
     * ids of child components created by {@link UIComponentTag}
     * instances the last time this page was processed (if any).
     * Compare it to the list of children created during this page
     * processing pass, and remove all children present in the old list
     * but not the new.  Save the new list as a component attribute so
     * that it gets saved as part of the component's state.</li>
     *
     * <li>Retrieve from the {@link UIComponent} the set of facet names
     * of facets created by {@link UIComponentTag} instances the last
     * time this page was processed (if any).  Compare it to the list of
     * facets created during this page processing pass, and remove all
     * facets present in the old list but not the new.  Save the new
     * list as a component attribute so that it gets saved as part of
     * the component's state.</li>
     *
     * <li>Release all references to the component, and pop it from the
     * component stack for this response, removing the stack if this was
     * the outermost component.</li> </ul>
     *
     * <p>The flag value to be returned is acquired by calling the
     * <code>getDoEndValue()</code> method, which tag subclasses may
     * override if they do not want the default value.</p>
     *
     * @throws JspException if an error occurs
     */
    public int doEndTag() throws JspException
    {
      // Remove old children and facets as needed
      popUIComponentClassicTagBase();
      removeOldChildren();
      removeOldFacets();

      //If we are at the end tag of a NamingContainer component, reset the Map of ids
      // for the NamingContainer tag.
      if (namingContainerChildIds != null) {
          namingContainerChildIds = null;
      }

      // Render the children (if needed) and  end of the component
      // associated with this tag
      try
      {
        UIComponent verbatim;
        UIComponentClassicTagBase parentTag = _getParentUIComponentClassicTagBase(
                                                                                 contextMap);

        if (null != (verbatim = this.createVerbatimComponentFromBodyContent()))
        {
          component.getChildren().add(verbatim);
         
          if (null != parentTag) {
            parentTag.addChild(verbatim);
        }
      }

      // else, we don't render rendersChildren==true
      // components here

        } catch (Throwable e) {
            throw new JspException(e);
        } finally {
            component = null;
            context = null;
        }

        // Return the appropriate control value

  this.release();
        return (getDoEndValue());

    }

    /**
     * <p>Release any resources allocated during the execution of this
     * tag handler.</p>
     */
    public void release() {

        this.parent = null;

        this.id = null;
        this.facesJspId = null;
        this.created = false;
  this.bodyContent = null;
        this.isNestedInIterator = false;
      _nextChildIndex = 0;
    }

    // -------------------------------------------- Support methods for BodyTag

    /**
     * <p>Return the flag value that should be returned from the
     * <code>doAfterBody()</code> method when it is called.  Subclasses
     * may override this method to return the appropriate value.</p>
     */
    protected int getDoAfterBodyValue() throws JspException {

        return (SKIP_BODY);

    }

    // -------------------------------------------------------- BodyTag Methods

    /**
     * <p>Set the <code>bodyContent</code> for this tag handler.  This method
     * is invoked by the JSP page implementation object at most once per
     * action invocation, before <code>doInitiBody()</code>.  This method
     * will not be invoked for empty tags or for non-empty tags whose
     * <code>doStartTag()</code> method returns <code>SKIP_BODY</code> or
     * <code>EVAL_BODY_INCLUDE</code>.</p>
     *
     * @param bodyContent The new <code>BodyContent</code> for this tag
     */
    public void setBodyContent(BodyContent bodyContent) {

        this.bodyContent = bodyContent;

    }

    /**
     * <p>Get the <code>JspWriter</code> from our <code>BodyContent</code>.
     * </p>
     */
    public JspWriter getPreviousOut() {

        return (this.bodyContent.getEnclosingWriter());

    }

    public BodyContent getBodyContent() {

        return (this.bodyContent);

    }



    /**
     * <p>Prepare for evaluation of the body.  This method is invoked by the
     * JSP page implementation object after <code>setBodyContent()</code>
     * and before the first time the body is to be evaluated.  This method
     * will not be invoked for empty tags or for non-empty tags whose
     * <code>doStartTag()</code> method returns <code>SKIP_BODY</code>
     * or <code>EVAL_BODY_INCLUDE</code>.</p>
     *
     * @throws JspException if an error is encountered
     */
    public void doInitBody() throws JspException {

        ; // Default implementation does nothing

    }

    /**
     *
     * <p>Perform any processing necessary to handle the content
     * implications of CASE 4 in the class description.</p>
     *
     * <p>Return result from {@link #getDoAfterBodyValue}</p>
     * @throws JspException if an error is encountered
     */
    public int doAfterBody() throws JspException {

  UIComponent verbatim;
  UIComponentClassicTagBase parentTag = _getParentUIComponentClassicTagBase(contextMap);

  // if we are the root tag, or if we are inside of a
  // rendersChildren==true component
        //noinspection ObjectEquality
        if (this == parentTag ||
      (null != parentTag &&
       parentTag.getComponentInstance().getRendersChildren())) {
      // stuff the template text or custom tag output into a
      // transient component
      if (null != (verbatim = this.createVerbatimComponentFromBodyContent())) {
            // EDGE CASE:
            // Consider CASE 4 where the component is provided via a
            // component binding in session or application scope.
            // The verbatim instance will already be present.  If we
            // add again, the user will get duplicate component ID
            // errors.  Check the JSP_CREATED_COMPONENT_IDS attribute.  If it is not present, we
            // need to add the new verbatim child.  If it is present, assume it is a
            // List and check its size.  If the size of the list is equal to the
            // number of children currently in the component, replace the replace
            // the child of this component at the index derived as follows.  If
            // indexOfChildInParent is 0, replace the child at the 0th index with
            // the new verbatim child.  Otherwise, replace the child at the
            // (indexOfChildInParent - 1)th index with the new verbatim child.
            List createdIds = (List)
                  component.getAttributes().get(JSP_CREATED_COMPONENT_IDS);
            if (createdIds != null) {
                int listIdx = component.getChildCount();
                if (createdIds.size() == listIdx) {
                    component.getChildren().set((listIdx - 1), verbatim);
                } else {
                    component.getChildren().add(verbatim);
                }
            } else {
                component.getChildren().add(verbatim);
            }
            parentTag.addChild(verbatim);
        }
  }

        return (getDoAfterBodyValue());

    }

    // ----------------------------------------------- Methods relating to Id

    /**
     * <p>Set the component identifier for our component.  If the
     * argument begins with {@link
     * UIViewRoot#UNIQUE_ID_PREFIX} throw an
     * <code>IllegalArgumentException</code></p>
     *
     * @param id The new component identifier.  This may not start with
     * {@link UIViewRoot#UNIQUE_ID_PREFIX}.
     *
     * @throws IllegalArgumentException if the argument is
     * non-<code>null</code> and starts with {@link
     * UIViewRoot#UNIQUE_ID_PREFIX}.
     */
    public void setId(String id) {
  if (null != id && id.startsWith(UIViewRoot.UNIQUE_ID_PREFIX)) {
      throw new IllegalArgumentException();
  }

        this.id = id;

    }

    /**
     * <p>Return the <code>id</code> value assigned by the page author.</p>
     */
    protected String getId() {

  return (id);

    }

    /**
     * <p>If this method has been called before on this tag's useful
     * lifetime (before {@link #release} was called), return the
     * previously returned value.  Otherwise, if {@link #getJspId}
     * returns non-<code>null</code>, prepend {@link #UNIQUE_ID_PREFIX}
     * to the <code>jspId</code> and return the result.</p>
     */

    protected String getFacesJspId() {
        if (null == facesJspId) {
            if (null != jspId) {
                facesJspId = UNIQUE_ID_PREFIX + jspId;
                // if this tag happens to be nested within <c:forEach>,
                //  jspId will be the same for each iteration. So it is
                // transformed into a unique "id" by appending a counter which
                // gets stored in request scope with jspId as the key for use
                // during the next iteration.
                if (isDuplicateId(facesJspId)) {
                    facesJspId = generateIncrementedId(facesJspId);
                }
            } else {
                // jspId will be null if we're running in a container
                // that doesn't support JspIdConsumer
                facesJspId = getFacesContext().getViewRoot().createUniqueId();
            }
        }
        return facesJspId;
    }

    /**
     * Returns true if a component already exists with the same
     * <code>id</code>. This will be the case if this tag is
     * nested within <code><c:forEach></code> tag or any other JSTL loop tag
     * or if the page has components with the same <code>Id</code>.
     *
     * @param componentId <code>id</code> to be looked up for possible
     * duplication.
     * @return true if this nested with <code>facesJspId</code> is duplicate.
     */
    private boolean isDuplicateId(String componentId) {
        boolean result = false;
        if (parentTag != null) {

            if (parentTag.isNestedInIterator) {
                return true;
            }
             List childComponents = parentTag.createdComponents;
            // PENDING: Need to analyze the impact of this look up on pages
            // with several levels of nesting.
            if (childComponents != null) {
                result = childComponents.contains(componentId);
                if (result && (!isNestedInIterator)) {
                    return true;
                }
            }
        }

        return result;
    }

    /*
     * Appends a counter to the passed in <code>id</code> and stores the
     * <code>id</code> and counter information in request scope.
     *
     * @return String <code>id</code> with a counter appended to it.
     */
    private String generateIncrementedId (String componentId) {
        assert (null != contextMap);
        Integer serialNum = (Integer) contextMap.get(componentId);
        if (null == serialNum) {
            serialNum = 1;
        } else {
            serialNum = serialNum.intValue() + 1;
        }
        contextMap.put(componentId, serialNum);
        componentId = componentId + UNIQUE_ID_PREFIX + serialNum.intValue();
        return componentId;
    }

    /**
     * Returns the <code>List</code> of {@link UIComponent} ids created or
     * located by nested {@link UIComponentTag}s while processing the current
     * request.</p>
     */
    protected List<String> getCreatedComponents() {
        return createdComponents;
    }

    /**
     * <p>Create the component identifier to be used for this component.</p>
     */
    private String createId(FacesContext context)
    throws JspException {

  if (this.id == null) {
      return getFacesJspId();
  } else {
            // if this tag happens to be nested within <c:forEach>, jspId
            // will be the same for each iteration. So it is
            // transformed into a unique "id" by appending a counter which gets
            // stored in request scope with jspId as the key for use during next
            // iteration.
        if (isDuplicateId(this.id)) {
            if (!isSpecifiedIdUnique(this.id)) {
                if (isNestedInIterator) {
                    this.id = generateIncrementedId(this.id);
                } else {
                    StringWriter writer = new StringWriter(128);
                    printTree(context.getViewRoot(), this.id, writer, 0);
                    String msg = "Component ID '"
                                 + this.id
                                 + "' has already been used"
                                 + " in the view.\n"
                                 + "See below for the view up to the point of"
                                 + " the detected error.\n"
                                 + writer.toString();
                    throw new JspException(msg);
                }
            }
        }
        return (this.id);
    }

    }


    /**
     * @param id the component ID
     * @return <code>true</code> if this ID is unique within the closest naming
     *  container, otherwise <code>false</code>
     */
    private boolean isSpecifiedIdUnique(String id) {

        UIComponentClassicTagBase containerTag = getParentNamingContainerTag();
        UIComponent c = containerTag.component.findComponent(id);
        if (c == null) {
            return true;
        } else {
            UIComponent parent = c.getParent();
            if (parent.equals(this.parentTag.component)) {
                // the component we found has the same parent, If we find
                // a sibling with the same ID, return true so that the
                // id is incremented, otherwise, return false.
                List<String> created = this.parentTag.createdComponents;
                return !(created != null && created.contains(id));
            } else {
                return false;
            }
        }

    }


    /**
     * @return the parent tag that represents the closest NamingContainer
     *  component.
     */
    private UIComponentClassicTagBase getParentNamingContainerTag() {
        if (this.parentTag == null) {
            return this;
        }
        UIComponentClassicTagBase parent = this.parentTag;
        while (parent != null) {
            if (parent.component instanceof NamingContainer
                    || parent.parentTag == null && parent.component instanceof UIViewRoot) {
                return parent;
            }
            parent = parent.parentTag;
        }
        return null;

    }


    // ------------------------------------------------   JspIdConsumer Methods


    /**
     * <p>Defined on {@link JspIdConsumer}.  This method is called by
     * the container before {@link #doStartTag}.  The argument is
     * guaranteed to be unique within the page.</p>
     *
     * <p>IMPLEMENTATION NOTE:  This method will detect where we
     * are in an include and assign a unique ID for each include
     * in a particular 'logical page'.  This allows us to avoid
     * possible duplicate ID situations for included pages that
     * have components without explicit IDs.</p>
     *
     * @param id the container generated id for this tag, guaranteed to
     * be unique within the page.
     */

    public void setJspId(String id) {
        // reset JSP ID here instead of release as we may need
        // to check the ID after the tag has been used
        this.jspId = null;

        Integer pcId = (Integer)
             pageContext.getAttribute(JAVAX_FACES_PAGECONTEXT_MARKER,
                                      PageContext.PAGE_SCOPE);
        if (pcId == null) {
            getContextMap(getFacesContext());
            AtomicInteger aInt = (AtomicInteger) contextMap.get(JAVAX_FACES_PAGECONTEXT_COUNTER);
            if (aInt == null) {
                aInt = new AtomicInteger();
                contextMap.put(JAVAX_FACES_PAGECONTEXT_COUNTER, aInt);
            }

            pcId = aInt.incrementAndGet();
            pageContext.setAttribute(JAVAX_FACES_PAGECONTEXT_MARKER, pcId);
        }
        if (pcId.intValue() > 1) {
            StringBuilder builder = new StringBuilder(id.length() + 3);
            builder.append(id).append("pc").append(pcId);
            jspId = builder.toString();
        } else {
            jspId = id;
        }

        facesJspId = null;
        updatePreviousJspIdAndIteratorStatus(jspId);
    }

    /**
     * <p>Called from {@link #setJspId} to update the value saved for
     * the previous call to {@link #setJspId} for this component <b>on
     * this request</b>.  If this method is presented with the same
     * argument <code>id</code> for the same tag instance more than once
     * on the same request, then we know that the tag instance lies
     * inside an iterator tag, such as <code>c:forEach</code>.  If so,
     * we set the <code>isNestedInIterator</code> ivar to
     * <code>true</code> otherwise, we set it to <code>false</code>.</p>
     *
     * <p>The implementation for this method stores a Map from tag
     * instance to id String as a request scoped attribute.  This map
     * contains the value used as the previousJspId and compared with
     * the argument <code>id</code>.
     *
     * @param id the id to be compared with the previous id, if any, for
     * this tag instance on this request.
     */

  private void updatePreviousJspIdAndIteratorStatus(String id)
  {
    Set<String> previousJspIdSet = TypedCollections.dynamicallyCastSet((Set)
        pageContext.getAttribute(PREVIOUS_JSP_ID_SET, PageContext.PAGE_SCOPE), String.class);

    if (null == previousJspIdSet)
    {
      previousJspIdSet = new HashSet<String>();
     
      //noinspection CollectionWithoutInitialCapacity
      pageContext.setAttribute(PREVIOUS_JSP_ID_SET, previousJspIdSet, PageContext.PAGE_SCOPE);
    }

    // detect the iterator case, since add will return true if the collection already
    // contains the id
    if (previousJspIdSet.add(id))
    {
      // id wasn't in Set, so we aren't nested yet
      isNestedInIterator = false;
    }
    else
    {
      // the Set didn't change, so we are nested
      if (log.isLoggable(Level.FINEST))
      {
        log.log(Level.FINEST, "Id " + id + " is nested within an iterating tag.");
      }

      isNestedInIterator = true;     
    }
  }


    public String getJspId() {
  return jspId;
    }

    // ------------------------------------------------------- Abstract methods

    /**
     * <p>Override properties and attributes of the specified component,
     * if the corresponding properties of this tag handler instance were
     * explicitly set.  This method must be called <strong>ONLY</strong>
     * if the specified {@link UIComponent} was in fact created during
     * the execution of this tag handler instance, and this call will occur
     * <strong>BEFORE</strong> the {@link UIComponent} is added to
     * the view.</p>
     *
     * <p>Tag subclasses that want to support additional set properties
     * must ensure that the base class <code>setProperties()</code>
     * method is still called.  A typical implementation that supports
     * extra properties <code>foo</code> and <code>bar</code> would look
     * something like this:</p>
     * <pre>
     * protected void setProperties(UIComponent component) {
     *   super.setProperties(component);
     *   if (foo != null) {
     *     component.setAttribute("foo", foo);
     *   }
     *   if (bar != null) {
     *     component.setAttribute("bar", bar);
     *   }
     * }
     * </pre>
     *
     * <p>The default implementation overrides the following properties:</p>
     * <ul>
     * <li><code>rendered</code> - Set if a value for the
     *     <code>rendered</code> property is specified for
     *     this tag handler instance.</li>
     * <li><code>rendererType</code> - Set if the <code>getRendererType()</code>
     *     method returns a non-null value.</li>
     * </ul>
     *
     * @param component {@link UIComponent} whose properties are to be
     *  overridden
     */
    protected abstract void setProperties(UIComponent component);


    /**
     * <p>Create and return a new child component of the type returned
     * by calling <code>getComponentType()</code>.  If this {@link
     * UIComponentTag} has a non-null <code>binding</code> attribute,
     * this is done by call {@link Application#createComponent} with the
     * expression created for the <code>binding</code> attribute, and
     * the expression will be stored on the component.  Otherwise,
     * {@link Application#createComponent} is called with only the
     * component type.  Finally, initialize the components id and other
     * properties.  </p>
     * @param context {@link FacesContext} for the current request
     * @param newId id of the component
     */

    protected abstract UIComponent createComponent(FacesContext context,
               String newId) throws JspException;

    /**
     * <p>Return <code>true</code> if this component has a
     * non-<code>null</code> binding attribute.  This method is
     * necessary to allow subclasses that expose the
     * <code>binding</code> property as an Faces 1.1 style EL property
     * as well as subclasses that expose it as an EL API property.</p>
     */

    protected abstract boolean hasBinding();

    // --------------------------------------------------------- Properties

    /**
     * <p>Return the {@link UIComponent} instance that is associated with
     * this tag instance.  This method is designed to be used by tags nested
     * within this tag, and only returns useful results between the
     * execution of <code>doStartTag()</code> and <code>doEndTag()</code>
     * on this tag instance.</p>
     */
    public UIComponent getComponentInstance() {

        return (this.component);

    }


    /**
     * <p>Return <code>true</code> if we dynamically created a new component
     * instance during execution of this tag.  This method is designed to be
     * used by tags nested within this tag, and only returns useful results
     * between the execution of <code>doStartTag()</code> and
     * <code>doEndTag()</code> on this tag instance.</p>
     */
    public boolean getCreated() {

        return (this.created);

    }

    private Map getNamingContainerChildIds() {
        return (this.namingContainerChildIds);
    }

    protected FacesContext getFacesContext() {

  if (context == null) {
            if (null == (context = (FacesContext)
                    pageContext.getAttribute(CURRENT_FACES_CONTEXT))) {
                context = FacesContext.getCurrentInstance();

                if (context == null) { // PENDING - i18n
                    throw new RuntimeException("Cannot find FacesContext");
                }

                // store the current FacesContext for use by other
                // UIComponentTags in the same page
                pageContext.setAttribute(CURRENT_FACES_CONTEXT, context);
            }
  }

  return (context);

    }


    /**
     * <p>Return the facet name that we should be stored under, if any;
     * otherwise, return null (indicating that we will be a child component).
     * </p>
     */
    protected String getFacetName() {

        Tag parent = getParent();
        if (parent instanceof FacetTag) {
            return (((FacetTag) parent).getName());
        } else {
            return (null);
        }

    }


    private static FacesContext getFacesContext(PageContext pageContext) {

        FacesContext context = (FacesContext)
             pageContext.getAttribute(CURRENT_FACES_CONTEXT);
        if (context == null) {
            context = FacesContext.getCurrentInstance();
            if (context == null) {
                 throw new RuntimeException("Cannot find FacesContext");
            } else {
                pageContext.setAttribute(CURRENT_FACES_CONTEXT, context);
            }
        }

        return (context);

    }

    private static void printTree(UIComponent root,
                                  String duplicateId,
                                  Writer out,
                                  int curDepth) {
        if (null == root) {
            return;
        }

        if (duplicateId.equals(root.getId())) {
            indentPrintln(out, "+id: " + root.getId() + "  <===============",
                          curDepth);
        } else {
            indentPrintln(out, "+id: " + root.getId(), curDepth);
        }
        //noinspection ObjectToString
        indentPrintln(out, " type: " + root.toString(), curDepth);

        curDepth++;
        // print all the facets of this component
        for (UIComponent uiComponent : root.getFacets().values()) {
            printTree(uiComponent, duplicateId, out, curDepth);
        }
        // print all the children of this component
        for (UIComponent uiComponent : root.getChildren()) {
            printTree(uiComponent, duplicateId, out, curDepth);
        }

    }

    private static void indentPrintln(Writer out, String str, int curDepth) {

        // handle indentation
        try {
            for (int i = 0; i < curDepth; i++) {
                out.write("  ");
            }
            out.write(str + '\n');
        } catch (IOException ex) {
            // ignore
        }
    }

}
TOP

Related Classes of javax.faces.webapp.UIComponentClassicTagBase

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.