Logo Search packages:      
Sourcecode: mojarra version File versions  Download package

UIComponentClassicTagBase.java

/*
 * 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.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>

 *
 */

00150 public abstract class UIComponentClassicTagBase extends UIComponentTagBase implements JspIdConsumer, BodyTag {

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

    /**
     * <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>
     */
00165     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).
     */
00174     private static final String JSP_CREATED_FACET_NAMES =
        "javax.faces.webapp.FACET_NAMES";


    /**
     * <p>The attribute name under which we will store all {@link UIComponent}
     * IDs of the current translation unit.</p>
     */
00182     private static final String GLOBAL_ID_VIEW =
        "javax.faces.webapp.GLOBAL_ID_VIEW";

    /**
     * <p>The attribute name under which we will store the {@link FacesContext}
     * for this request.</p>
     */
00189     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>
     */
00196     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.
     */
00203     protected static final String UNIQUE_ID_PREFIX =
      UIViewRoot.UNIQUE_ID_PREFIX + '_';

    /**
     * Used to store the previousJspId Map in facesContextScope
     */
00209     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.
     */
00219     private static final String JAVAX_FACES_PAGECONTEXT_MARKER =
         "javax.faces.webapp.PAGECONTEXT_MARKER";

    /**
     * This is a <code>facesContext</code> scoped attribute which contains
     * an AtomicInteger which we use to increment the PageContext
     * count.
     */
00227     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>
     */
00234     protected BodyContent bodyContent = null;

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


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


    /**
     * <p>Was a new component instance dynamically created when our
     * <code>findComponent()</code> method was called.</p>
     */
00254     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>
     */
00262     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>
     */
00269     private List<String> createdFacets = null;


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


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

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

00287     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}.
     */

00300     private String facesJspId = null;

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

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

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

    // --------------------------------------------- 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
     */
00333     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
     */
00348     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.
     */
00367     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.
     */
00387     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.
     */
00407     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>
     */
00422     public void setPageContext(PageContext pageContext) {

        this.pageContext = pageContext;

    }


    /**
     * <p>Return the <code>Tag</code> that is the parent of this instance.</p>
     */
00432     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>
     */
00444     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>

     */
00467     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
     */
00480     private UIComponent createChild(FacesContext context, UIComponent parent,
                                    String componentId) throws JspException {

        UIComponent component = createComponent(context, componentId);
        UIComponentTagBase parentTag = getParentUIComponentClassicTagBase(pageContext);
        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
     */
00504     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
     */
00521     private static UIComponent getChild(UIComponent component, String componentId) {

        Iterator<UIComponent> kids = component.getChildren().iterator();
        while (kids.hasNext()) {
            UIComponent kid = kids.next();
            if (componentId.equals(kid.getId())) {
                return (kid);
            }
        }
        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>
     */
00582     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(pageContext);
        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();
        if (facetName != null) {
            component = parentComponent.getFacets().get(facetName);
            if (component == null) {
                component = createFacet(context, parentComponent, facetName,
                        newId);
            }
            return (component);
        }

        // Step 5 -- Create or return a child with the specified id
        component = getChild(parentComponent, newId);
        if (component == null) {
            component = createChild(context, parentComponent, 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
     */
00683     public static UIComponentClassicTagBase getParentUIComponentClassicTagBase(PageContext context) {

        FacesContext facesContext = getFacesContext(context);
        List list = (List) facesContext.getAttributes()
              .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.
    //

00701     protected int getIndexOfNextChildTag() {

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

    }

00711     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);        
    }

00734     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>
     */
00748     private void popUIComponentClassicTagBase() {
        Map<Object, Object> contextMap =
              context.getAttributes();
        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>
     */
00772     private void pushUIComponentClassicTagBase() {

        Map<Object,Object> contextMap =
              context.getAttributes();
        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);

    }


    /**
     * <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>
     */
00797     private void removeOldChildren() {

        // Remove old children that are no longer present
        List<String> oldList = TypedCollections.dynamicallyCastList(
            (List) component.getAttributes().get(JSP_CREATED_COMPONENT_IDS), String.class);
        if (oldList != null && !oldList.isEmpty()) {

            if (createdComponents != null) {

                // Components not in the new list need to be removed
                Iterator<String> olds = oldList.iterator();
                while (olds.hasNext()) {
                    String old = olds.next();
                    if (!createdComponents.contains(old)) {
                        UIComponent child = component.findComponent(old);
                        // 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 {

                // All old components need to be removed
                Iterator<String> olds = oldList.iterator();
                while (olds.hasNext()) {
                    String old = olds.next();
                    UIComponent child = component.findComponent(old);
                    if (child != null) {
                        component.getChildren().remove(child);
                    }
                }

            }

        }

        // Save the current list as a component attribute
        if (createdComponents != null) {
            component.getAttributes().put(JSP_CREATED_COMPONENT_IDS,
                                          createdComponents);
        } else {
            component.getAttributes().remove(JSP_CREATED_COMPONENT_IDS);
        }
        createdComponents = null;

    }


    /**
     * <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>
     */
00860     private void removeOldFacets() {

        // Remove old facets that are no longer present
        List<String> oldList = TypedCollections.dynamicallyCastList(
            (List) component.getAttributes().get(JSP_CREATED_FACET_NAMES), String.class);
        if (oldList != null) {

            if (createdFacets != null) {

                // Facets not in the new list need to be removed
                Iterator<String> olds = oldList.iterator();
                while (olds.hasNext()) {
                    String old = olds.next();
                    if (!createdFacets.contains(old)) {
                        component.getFacets().remove(old);
                    }
                }

            } else {

                // All old facets need to be removed
                Iterator<String> olds = oldList.iterator();
                while (olds.hasNext()) {
                    String old = olds.next();
                    component.getFacets().remove(old);
                }

            }

        }

        // Save the current list as a component attribute
        if (createdFacets != null) {
            component.getAttributes().put(JSP_CREATED_FACET_NAMES,
                                          createdFacets);
        } else {
            component.getAttributes().remove(JSP_CREATED_FACET_NAMES);
        }
        createdFacets = null;

    }

    //
    // 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>
     */

00914     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>
     *
     */

00968     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>
     */

00987     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>
     */

01032     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
     */
01089     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");
      }

        parentTag = getParentUIComponentClassicTagBase(pageContext);
        Map<Object,Object> contextMap = context.getAttributes();
        Map<String,UIComponentTagBase> componentIds;
        if (parentTag == null) {
            // create the map if we're the top level UIComponentTag
            //noinspection CollectionWithoutInitialCapacity
            componentIds = new HashMap<String,UIComponentTagBase>();
            contextMap.put(GLOBAL_ID_VIEW, componentIds);
        } else {
            componentIds = TypedCollections.dynamicallyCastMap((Map)
            contextMap.get(GLOBAL_ID_VIEW), String.class, UIComponentTagBase.class);
        }

        // 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 (this.id != null) {
            clientId = component.getClientId(context);
              UIComponentClassicTagBase temp = (UIComponentClassicTagBase)
                 componentIds.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 (componentIds.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: '"
                                     + componentIds.get(clientId).getClass().getName()
                                     + "'\n"
                                     + writer.toString();
                        throw new JspException(new IllegalStateException(msg));
                    } else {
                        componentIds.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());

    }


    /**
     *
     * <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
     */
01264     public int doEndTag() throws JspException {

        // Remove old children and facets as needed
        popUIComponentClassicTagBase();
        removeOldChildren();
        removeOldFacets();

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

          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
        created = false;

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

    }

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

        this.parent = null;

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

    // -------------------------------------------- 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>
     */
01325     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
     */
01343     public void setBodyContent(BodyContent bodyContent) {

        this.bodyContent = bodyContent;

    }

    /**
     * <p>Get the <code>JspWriter</code> from our <code>BodyContent</code>.
     * </p>
     */
01353     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
     */
01377     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
     */
01391     public int doAfterBody() throws JspException {

      UIComponent verbatim;
      UIComponentClassicTagBase parentTag =
          getParentUIComponentClassicTagBase(pageContext);

      // 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}.
     */
01454     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>
     */
01466     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>
     */

01480     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.
     */
01511     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) {
        Map<Object,Object> contextMap = getFacesContext().getAttributes();
        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>
     */
01556     protected List<String> getCreatedComponents() {
        return createdComponents;
    }

    /**
     * <p>Create the component identifier to be used for this component.</p>
     */
01563     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>
     */
01603     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.
     */
01629     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.
     */

01664     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;

        Map<Object,Object> contextMap =
             getFacesContext().getAttributes();
        AtomicInteger aInt = (AtomicInteger) contextMap.get(JAVAX_FACES_PAGECONTEXT_COUNTER);
        if (aInt == null) {
            aInt = new AtomicInteger();
            contextMap.put(JAVAX_FACES_PAGECONTEXT_COUNTER, aInt);
        }

        Integer pcId = (Integer)
             pageContext.getAttribute(JAVAX_FACES_PAGECONTEXT_MARKER,
                                      PageContext.PAGE_SCOPE);
        if (pcId == null) {
            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.
     */

01715     private void updatePreviousJspIdAndIteratorStatus(String id) {
        Set<String> previousJspIdSet;

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

        // detect the iterator case
        if (previousJspIdSet.contains(id)) {
            if (log.isLoggable(Level.FINEST)) {
                log.log(Level.FINEST, "Id " + id +
                        " is nested within an iterating tag.");
            }
            isNestedInIterator = true;
        } else {
            isNestedInIterator = false;
            previousJspIdSet.add(id);
        }
    }


    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>
     */
01823     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>
     */
01837     public boolean getCreated() {

        return (this.created);

    }


01844     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>
     */
01871     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
        }
    }

}

Generated by  Doxygen 1.6.0   Back to index