Package javax.faces.webapp

Source Code of javax.faces.webapp.UIComponentClassicTagBase

/*
* Copyright 2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package javax.faces.webapp;

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.faces.render.ResponseStateManager;
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.util.*;
import java.util.logging.Level;

/**
* @author Bruno Aranda (latest modification by $Author: baranda $)
* @author Manfred Geiler
* @author Dennis Byrne
* @version $Revision$ $Date$
*
* @since 1.2
*/

public abstract class UIComponentClassicTagBase extends UIComponentTagBase
        implements BodyTag, JspIdConsumer
{
 
    //  do not change this w/out doing likewise in UIComponentTag
    private static final String COMPONENT_STACK_ATTR = "org.apache.myfaces.COMPONENT_STACK";

    private static final String REQUEST_FACES_CONTEXT = "org.apache.myfaces.REQUEST_FACES_CONTEXT";

    private static final String VIEW_IDS = "org.apache.myfaces.VIEW_IDS";

    private static final String FORMER_CHILD_IDS_SET_ATTR = "org.apache.myfaces.FORMER_CHILD_IDS";
    private static final String FORMER_FACET_NAMES_SET_ATTR = "org.apache.myfaces.FORMER_FACET_NAMES";

    private static final String PREVIOUS_JSP_IDS_SET = "org.apache.myfaces.PREVIOUS_JSP_IDS_SET";

    private static final String BOUND_VIEW_ROOT = "org.apache.myfaces.BOUND_VIEW_ROOT";

    protected static final String UNIQUE_ID_PREFIX = UIViewRoot.UNIQUE_ID_PREFIX + "_";

    protected PageContext pageContext = null;
    protected BodyContent bodyContent = null;

    private boolean _created = false;

    private String _jspId = null;
    private String _facesJspId = null;

    private List<String> _childrenAdded = null;
    private List<String> _facetsAdded = null;

    private UIComponent _componentInstance = null;
    private String _id = null;

    private boolean isInAnIterator;

    // the parent tag
    private Tag _parent = null;

    // the enclosing "classic" parent tag
    private UIComponentClassicTagBase _parentClassicTag = null;

    private FacesContext _facesContext = null;

    protected abstract void setProperties(UIComponent component);

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

    public void release()
    {
        internalRelease();

        //members, that must/need only be reset when there is no more risk, that the container
        //wants to reuse this tag
        pageContext = null;
        _parent = null;
        _jspId = null;
        _id = null;
        _facesJspId = null;
        bodyContent = null;
    }


    /**
     * Reset any members that apply to the according component instance and
     * must not be reused if the container wants to reuse this tag instance.
     * This method is called when rendering for this tag is finished
     * ( doEndTag() ) or when released by the container.
     */
    private void internalRelease()
    {
        _facesContext = null;
        _componentInstance = null;
        _created = false;

         _childrenAdded = null;
        _facetsAdded = null;
    }

    /**
     * @see http://java.sun.com/javaee/5/docs/api/javax/faces/webapp/UIComponentClassicTagBase.html#getCreated()
     */
    public boolean getCreated()
    {
        return _created;
    }

     protected List<String> getCreatedComponents() {
         return _childrenAdded;
    }

    /**
     * @see http://java.sun.com/javaee/5/docs/api/javax/faces/webapp/UIComponentClassicTagBase.html#getParentUIComponentClassicTagBase(javax.servlet.jsp.PageContext)
     * @param pageContext
     * @return
     */
    public static UIComponentClassicTagBase getParentUIComponentClassicTagBase(
            PageContext pageContext)
    {
        Stack<UIComponentClassicTagBase> stack = getStack(pageContext);

        int size = stack.size();

        return size > 0 ? stack.get(size - 1) : null;
    }

    /**
     * @see http://java.sun.com/javaee/5/docs/api/javax/faces/webapp/UIComponentClassicTagBase.html#getFacesJspId()
     * @return
     */
    public String getJspId()
    {
        return _jspId;
    }

    public void setJspId(String jspId)
    {
        _jspId = jspId;
        _facesJspId = null;
        checkIfItIsInAnIterator(jspId);
    }


    /**
     * @param child
     * @see http://java.sun.com/javaee/5/docs/api/javax/faces/webapp/UIComponentTagBase.html#addChild(javax.faces._componentInstance.UIComponent)
     */

    protected void addChild(UIComponent child)
    {
        if (_childrenAdded == null)
        {
            _childrenAdded = new ArrayList<String>();
        }

        _childrenAdded.add(child.getId());
    }

    /**
     * @param name
     * @see http://java.sun.com/javaee/5/docs/api/javax/faces/webapp/UIComponentTagBase.html#addFacet(java.lang.String)
     */
    protected void addFacet(String name)
    {
        if (_facetsAdded == null)
        {
            _facetsAdded = new ArrayList<String>();
        }

        _facetsAdded.add(name);
    }

    /**
     * Return the UIComponent instance associated with this tag.
     * @return a UIComponent, never null.
     */
    public UIComponent getComponentInstance()
    {
        return _componentInstance;
    }

    /**
     * @return
     * @see http://java.sun.com/javaee/5/docs/api/javax/faces/webapp/UIComponentTagBase.html#getFacesContext()
     */

    protected FacesContext getFacesContext()
    {
        if (_facesContext != null)
        {
            return _facesContext;
        }

        _facesContext = pageContext == null ? null : (FacesContext) pageContext.getAttribute(REQUEST_FACES_CONTEXT);

        if (_facesContext != null)
        {
            return _facesContext;
        }

        _facesContext = FacesContext.getCurrentInstance();

        if (_facesContext != null)
        {
            if(pageContext != null)
            {
                pageContext.setAttribute(REQUEST_FACES_CONTEXT, _facesContext);
            }
            return _facesContext;
        }

        // should never be reached
        throw new RuntimeException("FacesContext not found");
    }

    /**
     * @return
     * @see http://java.sun.com/javaee/5/docs/api/javax/faces/webapp/UIComponentTagBase.html#getIndexOfNextChildTag()
     */

    protected int getIndexOfNextChildTag()
    {
        if (_childrenAdded == null)
        {
            return 0;
        }

        return _childrenAdded.size();
    }

    /**
     * @param id
     * @see http://java.sun.com/javaee/5/docs/api/javax/faces/webapp/UIComponentTagBase.html#setId(java.lang.String)
     */
    public void setId(String id)
    {
        if (id != null && id.startsWith(UIViewRoot.UNIQUE_ID_PREFIX))
        {
            throw new IllegalArgumentException("Id is non-null and starts with UIViewRoot.UNIQUE_ID_PREFIX: "+id);
        }

        _id = id;
    }

    /**
     * Return the id (if any) specified as an xml attribute on this tag.
     */
    protected String getId()
    {
        return _id;
    }

    protected String getFacesJspId() {
        if (_facesJspId == null)
        {
            if (_jspId != null)
            {
                _facesJspId = UNIQUE_ID_PREFIX + _jspId;

                if (isIdDuplicated(_facesJspId) || isIncludedOrForwarded()) {
                    _facesJspId = createNextId(_facesJspId);
                }
            }
            else
            {
                _facesJspId = _facesContext.getViewRoot().createUniqueId();
            }
        }

        return _facesJspId;
    }

    public void setBodyContent(BodyContent bodyContent)
    {
        this.bodyContent = bodyContent;
    }

    public void doInitBody() throws JspException
    {
        // nothing by default
    }

    public int doAfterBody() throws JspException
    {
        UIComponentClassicTagBase parentTag = getParentUIComponentClassicTagBase(pageContext);

        if (isRootTag(parentTag) || isInRenderedChildrenComponent(parentTag))
        {
            UIComponent verbatimComp = createVerbatimComponentFromBodyContent();

            if (verbatimComp != null)
            {
                List<String> childrenAddedIds = (List<String>)
                  _componentInstance.getAttributes().get(FORMER_CHILD_IDS_SET_ATTR);

                if (childrenAddedIds == null)
                {
                    _componentInstance.getChildren().add(verbatimComp);
                }
                else
                {
                    int index = _componentInstance.getChildCount();
                    if (childrenAddedIds.size() == index)
                    {
                        // verbatim already present, replace it
                        _componentInstance.getChildren().add(index-1, verbatimComp);
                    }
                    else
                    {
                        _componentInstance.getChildren().add(verbatimComp);
                    }
                }
            }
        }

        return getDoAfterBodyValue();
    }

    /**
     * Standard method invoked by the JSP framework to inform this tag
     * of the PageContext associated with the jsp page currently being
     * processed.
     */
    public void setPageContext(PageContext pageContext)
    {
        this.pageContext = pageContext;
    }

    /**
     * Returns the enclosing JSP tag object. Note that this is not
     * necessarily a JSF tag.
     */
    public Tag getParent()
    {
        return _parent;
    }

    /**
     * Standard method invoked by the JSP framework to inform this tag
     * of the enclosing JSP tag object.
     */
    public void setParent(Tag tag)
    {
        this._parent = tag;
    }

    public BodyContent getBodyContent()
    {
        return bodyContent;
    }

    public int doStartTag() throws JspException
    {
        this._facesContext = getFacesContext();

        if (_facesContext == null)
        {
            throw new JspException("FacesContext not found");
        }

        _childrenAdded = null;
        _facetsAdded = null;

        _parentClassicTag = getParentUIComponentClassicTagBase(pageContext);

        UIComponent verbatimComp = null;

        // create the verbatim component if not inside a facet (facets are rendered
        // by their parents) and in a component that renders children
        if (!isFacet())
        {
            Tag parent = getParent();

            // flush if in a loop tag and not in a jsp tag
            if (parent != null && parent instanceof LoopTag)
            {
                JspWriter outWriter = pageContext.getOut();
                boolean insideJspTag = (outWriter instanceof BodyContent);

                if (!insideJspTag)
                {
                    try
                    {
                        outWriter.flush();
                    }
                    catch (IOException e)
                    {
                        throw new JspException("Exception flushing when creating verbatim _componentInstance", e);
                    }
                }
            }

            // create the transient _componentInstance
            if (_parentClassicTag != null)
            {
                verbatimComp = _parentClassicTag.createVerbatimComponentFromBodyContent();
            }
        }

        // find the _componentInstance for this tag
        _componentInstance = findComponent(_facesContext);

        // add the verbatim component
        if (verbatimComp != null && _parentClassicTag != null)
        {
            addVerbatimBeforeComponent(_parentClassicTag, verbatimComp, _componentInstance);
        }

        Map<String,Object> viewComponentIds = getViewComponentIds();

        // check that the instance returned by the client ID for the viewComponentIds
        // is the same like this one, so we do not perform again the check for duplicated ids
        Object tagInstance = null;
        String clientId = null;
        if (_id != null) {
            clientId = _componentInstance.getClientId(_facesContext);
            tagInstance = (viewComponentIds.get(clientId) == this) ? this : null;
        }

        if (tagInstance == null)
        {
            // check for duplicated IDs
            if (_id != null)
            {
                if (clientId != null)
                {
                    if (viewComponentIds.containsKey(clientId))
                    {
                        throw new JspException("Duplicated component Id: '"+clientId+"' " +
                                "for component: '"+getPathToComponent(_componentInstance)+"'.");
                    }

                    viewComponentIds.put(clientId, this);
                }
            }

            // add to the component or facet to parent
            if (_parentClassicTag != null)
            {
                if (isFacet())
                {
                    _parentClassicTag.addFacet(getFacetName());
                }
                else
                {
                     _parentClassicTag.addChild(_componentInstance);
                }
            }
        }

        // push this tag on the stack
        pushTag();

        return getDoStartValue();
    }

    public int doEndTag() throws JspException
    {
        popTag();
        UIComponent component = getComponentInstance();

        removeFormerChildren(component);
        removeFormerFacets(component);

        try
        {
            UIComponentClassicTagBase parentTag =
                    getParentUIComponentClassicTagBase(pageContext);

            UIComponent verbatimComp = createVerbatimComponentFromBodyContent();

             if (verbatimComp != null)
            {
                component.getChildren().add(verbatimComp);

                if (parentTag != null)
                {
                    parentTag.addChild(verbatimComp);
                }
            }
        }
        catch (Throwable e)
        {
            throw new JspException(e);
        }
        finally
        {
            component = null;
        }

        int retValue = getDoEndValue();

        internalRelease();

        return retValue;
    }

    protected int getDoAfterBodyValue() throws javax.servlet.jsp.JspException
    {
        return SKIP_BODY;
    }

    /**
     * Get the value to be returned by the doStartTag method to the
     * JSP framework. Subclasses which wish to use the inherited
     * doStartTag but control whether the tag is permitted to contain
     * nested tags or not can just override this method to return
     * Tag.SOME_CONSTANT.
     *
     * @return BodyTag.EVAL_BODY_BUFFERED
     */
    protected int getDoStartValue()
            throws JspException
    {
        return BodyTag.EVAL_BODY_BUFFERED;
    }

    /**
     * Get the value to be returned by the doEndTag method to the
     * JSP framework. Subclasses which wish to use the inherited
     * doEndTag but control whether the tag is permitted to contain
     * nested tags or not can just override this method to return
     * Tag.SOME_CONSTANT.
     *
     * @return Tag.EVAL_PAGE
     */
    protected int getDoEndValue()
            throws JspException
    {
        return Tag.EVAL_PAGE;
    }

    protected String getFacetName()
    {
        return isFacet() ? ((FacetTag)_parent).getName() : null;
    }


    /**
     * Creates a UIComponent from the BodyContent
     */
    protected UIComponent createVerbatimComponentFromBodyContent()
    {
        UIOutput verbatimComp = null;

        if (bodyContent != null)
        {
            String strContent = bodyContent.getString();

            if (strContent != null)
            {
                String trimmedContent = strContent.trim();
                if (trimmedContent.length() > 0 && !isComment(strContent))
                {
                    verbatimComp = createVerbatimComponent();
                    verbatimComp.setValue(strContent);
                }
            }

            bodyContent.clearBody();
        }

        return verbatimComp;
    }

    private static boolean isComment(String bodyContent)
    {
        return (bodyContent.startsWith("<!--") && bodyContent.endsWith("-->"));
    }

    /**
     * <p>Creates a transient UIOutput using the Application, with the following characteristics:</p>
     * <p/>
     * <p><code>componentType</code> is
     * <code>javax.faces.HtmlOutputText</code>.</p>
     * <p/>
     * <p><code>transient</code> is <code>true</code>.</p>
     * <p/>
     * <p><code>escape</code> is <code>false</code>.</p>
     * <p/>
     * <p><code>id</code> is
     * <code>FacesContext.getViewRoot().createUniqueId()</code></p>
     */
    protected UIOutput createVerbatimComponent()
    {
        UIOutput verbatimComp = (UIOutput) getFacesContext().getApplication()
                .createComponent("javax.faces.HtmlOutputText");
        verbatimComp.setTransient(true);
        verbatimComp.getAttributes().put("escape", Boolean.FALSE);
        verbatimComp.setId(getFacesContext().getViewRoot().createUniqueId());

        return verbatimComp;
    }

    protected void addVerbatimBeforeComponent(
            UIComponentClassicTagBase parentTag,
            UIComponent verbatimComp,
            UIComponent component)
    {
        UIComponent parent = component.getParent();

        if (parent == null)
        {
            return;
        }

        List<UIComponent> children = parent.getChildren();
        // EDGE CASE:
        // Consider CASE 1 or 2 where the _componentInstance is provided via a
        // _componentInstance 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<String> childrenAddedIds = (List<String>)
                parent.getAttributes().get(FORMER_CHILD_IDS_SET_ATTR);


        int parentIndex = children.indexOf(component);

         if (childrenAddedIds != null)
         {
             if (parentIndex > 0 && childrenAddedIds.size() == parentIndex)
             {
                 UIComponent formerVerbatim = children.get(parentIndex - 1);

                 if (formerVerbatim instanceof UIOutput && formerVerbatim.isTransient())
                 {
                     children.set(parentIndex - 1, verbatimComp);
                 }
             }
         }

        children.add(parentIndex, verbatimComp);

        parentTag.addChild(verbatimComp);
    }

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

    protected void addVerbatimAfterComponent(UIComponentClassicTagBase parentTag,
                                             UIComponent verbatim,
                                             UIComponent component)
    {
        int indexOfComponentInParent = 0;
        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);
    }

    /**
     * @deprecated the ResponseWriter is now set by {@link
     * javax.faces.application.ViewHandler#renderView}
     */
    protected void setupResponseWriter() {
    }

    /**
     * Invoke encodeBegin on the associated UIComponent. Subclasses can
     * override this method to perform custom processing before or after
     * the UIComponent method invocation.
     */
    protected void encodeBegin()
            throws IOException
    {
        if (log.isLoggable(Level.FINE))
            log.fine("Entered encodeBegin for client-Id: " + _componentInstance.getClientId(getFacesContext()));
        _componentInstance.encodeBegin(getFacesContext());
        if (log.isLoggable(Level.FINE))
            log.fine("Exited encodeBegin");
    }

    /**
     * Invoke encodeChildren on the associated UIComponent. Subclasses can
     * override this method to perform custom processing before or after
     * the UIComponent method invocation. This is only invoked for components
     * whose getRendersChildren method returns true.
     */
    protected void encodeChildren()
            throws IOException
    {
        if (log.isLoggable(Level.FINE))
            log.fine("Entered encodeChildren for client-Id: " + _componentInstance.getClientId(getFacesContext()));
        _componentInstance.encodeChildren(getFacesContext());
        if (log.isLoggable(Level.FINE))
            log.fine("Exited encodeChildren for client-Id: " + _componentInstance.getClientId(getFacesContext()));
    }

    /**
     * Invoke encodeEnd on the associated UIComponent. Subclasses can override this
     * method to perform custom processing before or after the UIComponent method
     * invocation.
     */
    protected void encodeEnd()
            throws IOException
    {
        if (log.isLoggable(Level.FINE))
            log.fine("Entered encodeEnd for client-Id: " + _componentInstance.getClientId(getFacesContext()));
        _componentInstance.encodeEnd(getFacesContext());
        if (log.isLoggable(Level.FINE))
            log.fine("Exited encodeEnd for client-Id: " + _componentInstance.getClientId(getFacesContext()));

    }



    private boolean isRootTag(UIComponentClassicTagBase parentTag)
    {
        return (parentTag == this);
    }

    private boolean isInRenderedChildrenComponent (UIComponentClassicTagBase tag)
    {
        return (_parentClassicTag != null && tag.getComponentInstance().getRendersChildren());
    }

    private boolean isFacet()
    {
        return _parent != null && _parent instanceof FacetTag;
    }

    /** Map of <ID,Tag> in the view */
    private Map<String,Object> getViewComponentIds()
    {
        Map<String, Object> requestMap = _facesContext.getExternalContext().getRequestMap();
        Map<String, Object> viewComponentIds;

        if (_parent == null)
        {
            // top level _componentInstance
            viewComponentIds = new HashMap<String,Object>();
            requestMap.put(VIEW_IDS, viewComponentIds);
        }
        else
        {
            viewComponentIds = (Map<String, Object>) requestMap.get(VIEW_IDS);
        }

        return viewComponentIds;
    }


    private static final Stack<UIComponentClassicTagBase> getStack(PageContext pageContext)
    {
        Stack<UIComponentClassicTagBase> stack = (Stack<UIComponentClassicTagBase>) pageContext.getAttribute(COMPONENT_STACK_ATTR,
                PageContext.REQUEST_SCOPE);

        if (stack == null)
        {
            stack = new Stack<UIComponentClassicTagBase>();
            pageContext.setAttribute(COMPONENT_STACK_ATTR,
                    stack, PageContext.REQUEST_SCOPE);
        }

        return stack;
    }

    /**
     * The pageContext's request scope map is used to hold a stack of
     * JSP tag objects seen so far, so that a new tag can find the
     * parent tag that encloses it. Access to the parent tag is used
     * to find the parent UIComponent for the component associated
     * with this tag plus some other uses.
     */
    private void popTag()
    {
        Stack<UIComponentClassicTagBase> stack = getStack(pageContext);

        int size = stack.size();
        stack.remove(size -1);
        if (size <= 1)
            pageContext.removeAttribute(COMPONENT_STACK_ATTR,
                                         PageContext.REQUEST_SCOPE);

    }

    private void pushTag()
    {
        getStack(pageContext).add(this);
    }

    private boolean isIncludedOrForwarded() {
        return getFacesContext().getExternalContext().getRequestMap().
                containsKey("javax.servlet.include.request_uri");
    }

     /** Generate diagnostic output. */
    private String getPathToComponent(UIComponent component)
    {
        StringBuffer buf = new StringBuffer();

        if(component == null)
        {
            buf.append("{Component-Path : ");
            buf.append("[null]}");
            return buf.toString();
        }

        getPathToComponent(component,buf);

        buf.insert(0,"{Component-Path : ");
        buf.append("}");

        return buf.toString();
    }

    /** Generate diagnostic output. */
    private static void getPathToComponent(UIComponent component, StringBuffer buf)
    {
        if(component == null)
            return;

        StringBuffer intBuf = new StringBuffer();

        intBuf.append("[Class: ");
        intBuf.append(component.getClass().getName());
        if(component instanceof UIViewRoot)
        {
            intBuf.append(",ViewId: ");
            intBuf.append(((UIViewRoot) component).getViewId());
        }
        else
        {
            intBuf.append(",Id: ");
            intBuf.append(component.getId());
        }
        intBuf.append("]");

        buf.insert(0,intBuf);

        getPathToComponent(component.getParent(),buf);
    }

    /**
     * Remove any child components of the associated components which do not
     * have corresponding tags as children of this tag. This only happens
     * when a view is being re-rendered and there are components in the view
     * tree which don't have corresponding JSP tags. Wrapping JSF tags in
     * JSTL "c:if" statements is one way this can happen.
     * <br />
     * Attention: programmatically added components are are not affected by this:
     * they will not be on the old list of created components nor on the new list
     * of created components, so nothing will happen to them.
     */
    private void removeFormerChildren(UIComponent component)
    {
        List<String> formerChildIds = (List<String>)component.getAttributes().get(FORMER_CHILD_IDS_SET_ATTR);
        if (formerChildIds != null)
        {
            for (String childId : formerChildIds)
            {
                if (_childrenAdded == null || !_childrenAdded.contains(childId))
                {
                    UIComponent childToRemove = component.findComponent(childId);
                    if (childToRemove != null)
                    {
                        component.getChildren().remove(childToRemove);
                    }
                }
            }
            if (_childrenAdded == null)
            {
                component.getAttributes().remove(FORMER_CHILD_IDS_SET_ATTR);
            }
            else
            {
                component.getAttributes().put(FORMER_CHILD_IDS_SET_ATTR, _childrenAdded);
            }
        }
        else
        {
            if (_childrenAdded != null)
            {
                component.getAttributes().put(FORMER_CHILD_IDS_SET_ATTR, _childrenAdded);
            }
        }
    }

    /** See removeFormerChildren. */
    private void removeFormerFacets(UIComponent component)
    {
        List<String> formerFacetNames = (List<String>)component.getAttributes().get(FORMER_FACET_NAMES_SET_ATTR);
        if (formerFacetNames != null)
        {
            for (String facetName : formerFacetNames)
            {
                if (_facetsAdded == null || !_facetsAdded.contains(facetName)) {
                    component.getFacets().remove(facetName);
                }
            }
            if (_facetsAdded == null)
            {
                component.getAttributes().remove(FORMER_FACET_NAMES_SET_ATTR);
            }
            else
            {
                component.getAttributes().put(FORMER_FACET_NAMES_SET_ATTR, _facetsAdded);
            }
        }
        else
        {
            if (_facetsAdded != null)
            {
                component.getAttributes().put(FORMER_FACET_NAMES_SET_ATTR, _facetsAdded);
            }
        }
    }

    /**
     * Return the corresponding UIComponent for this tag, creating it
     * if necessary.
     * <p>
     * If this is not the first time this method has been called, then
     * return the cached _componentInstance instance found last time.
     * <p>
     * If this is not the first time this view has been seen, then
     * locate the existing _componentInstance using the id attribute assigned
     * to this tag and return it. Note that this is simple for
     * components with user-assigned ids. For components with
     * generated ids, the "reattachment" relies on the fact that
     * UIViewRoot will generate the same id values for tags in
     * this page as it did when first generating the view. For this
     * reason all JSF tags within a JSTL "c:if" are required to have
     * explicitly-assigned ids.
     * <p>
     * Otherwise create the _componentInstance, populate its properties from
     * the xml attributes on this JSP tag and attach it to its parent.
     * <p>
     * When a _componentInstance is found or created the parent JSP tag is also
     * told that the _componentInstance has been "seen". When the parent tag
     * ends it will delete any components which were in the view
     * previously but have not been seen this time; see doEndTag for
     * more details.
     */
    protected UIComponent findComponent(FacesContext context)
            throws JspException
    {
        // 1. If we have previously located this component, return it.
        if (_componentInstance != null)
        {
            return _componentInstance;
        }

        // 2. Locate the parent component by looking for a parent UIComponentTag instance,
        // and ask it for its component. If there is no parent UIComponentTag instance,
        // this tag represents the root component, so get it from the current Tree and return it.
        UIComponentClassicTagBase parentTag = getParentUIComponentClassicTagBase(pageContext);

        if (parentTag == null)
        {
            // This is the root
            _componentInstance = context.getViewRoot();

            // check if the view root is already bound to the tag
            Object alreadyBoundViewRootFlag = _componentInstance.getAttributes().get(BOUND_VIEW_ROOT);

            if (alreadyBoundViewRootFlag == null)
            {
                try
                {
                    setProperties(_componentInstance);
                }
                catch (Throwable e)
                {
                    throw new JspException(e);
                }

                if (_id != null)
                {
                    _componentInstance.setId(_id);
                }
                else
                {
                    _componentInstance.setId(getFacesJspId());
                }
                _componentInstance.getAttributes().put(BOUND_VIEW_ROOT, true);
                _created = true;

            }
            else if (hasBinding())
            {
                setProperties(_componentInstance);
            }

            return _componentInstance;
        }

        UIComponent parent = parentTag.getComponentInstance();

        if (parent == null)
        {
            throw new IllegalStateException("parent is null?");
        }

        String facetName = getFacetName();
        if (facetName != null)
        {
            //Facet
            String id = createUniqueId(context, parent);
            _componentInstance = parent.getFacet(facetName);
            if (_componentInstance == null)
            {
                _componentInstance = createComponent(context, id);
                _created = true;
                setProperties(_componentInstance);
                parent.getFacets().put(facetName, _componentInstance);
            }
            else
            {
                if (checkFacetNameOnParentExists(parentTag, facetName))
                {
                    throw new IllegalStateException("facet '" + facetName + "' already has a child associated. current associated _componentInstance id: "
                            + _componentInstance.getClientId(context) + " class: " + _componentInstance.getClass().getName());
                }
            }

            addFacetNameToParentTag(parentTag, facetName);
            return _componentInstance;
        }

        //Child
        //
        // Note that setProperties is called only when we create the
        // _componentInstance; on later passes, the attributes defined on the
        // JSP tag are set on this Tag object, but then completely
        // ignored.

        String id = createUniqueId(context, parent);

        // Warn users that this tag is about to find/steal the UIComponent
        // that has already been created for a sibling tag with the same id value .
        // _childrenAdded is a Set, and we will stomp over a past id when calling
        // addChildIdToParentTag.
        //
        // It would also be reasonable to throw an exception here rather than
        // just issue a warning as this is a pretty serious problem. However the
        // Sun RI just issues a warning...
        if(parentTag._childrenAdded != null && parentTag._childrenAdded.contains(id))
        {
            if(log.isLoggable(Level.WARNING))
                log.warning("There is more than one JSF tag with an id : " + id);
        }

        _componentInstance = findComponent(parent,id);
        if (_componentInstance == null)
        {
            _componentInstance = createComponent(context, id);
            _created = true;
            setProperties(_componentInstance);
            int index = parentTag.getIndexOfNextChildTag();
            if (index > parent.getChildCount())
            {
                index = parent.getChildCount();
            }

            List<UIComponent> children = parent.getChildren();
            children.add(index, _componentInstance);
        }

        return _componentInstance;

    }

    private UIComponent findComponent(UIComponent parent, String id)
    {
        List li = parent.getChildren();

        for (int i = 0; i < li.size(); i++)
        {
            UIComponent uiComponent = (UIComponent) li.get(i);
            if(uiComponent.getId()!=null && uiComponent.getId().equals(id))
            {
                return uiComponent;
            }
        }

        return null;
    }


    private String createUniqueId(FacesContext context, UIComponent parent)
            throws JspException
    {
        String id = getId();
        if (id == null)
        {
            id = getFacesJspId();
        }
        else if (isIdDuplicated(id))
        {
            if (isInAnIterator)
            {
                setId(createNextId(id));
            }
            else
            {
                if (parent != null)
                {

                    UIComponent namingContainer;

                    if (parent instanceof NamingContainer)
                    {
                        namingContainer = parent;
                    }
                    else
                    {
                        namingContainer = parent.getParent();
                    }

                    if (namingContainer != null)
                    {
                        UIComponent component = namingContainer.findComponent(id);

                        if (component == null || isPostBack(context))
                        {
                            return id;
                        }
                    }
                }

                throw new JspException("Duplicated Id found in the view: " + id);
            }
        }

        return id;
    }

    private String createNextId(String componentId)
    {
        Integer currentCounter = (Integer) getFacesContext().getExternalContext().getRequestMap().get(componentId);

        int iCurrentCounter = 0;

        if (currentCounter != null)
        {
            iCurrentCounter = currentCounter.intValue();
        }

        getFacesContext().getExternalContext().getRequestMap().put(componentId, new Integer(iCurrentCounter));

        componentId = componentId + UNIQUE_ID_PREFIX + iCurrentCounter;

        return componentId;
    }

    private void checkIfItIsInAnIterator(String jspId)
    {
        Set<String> previousJspIdsSet = getPreviousJspIdsSet();

        if (previousJspIdsSet.contains(jspId) && isIncludedOrForwarded())
        {
            isInAnIterator = true;
        }
        else
        {
            previousJspIdsSet.add(jspId);
            isInAnIterator = false;
        }
    }

    private Set<String> getPreviousJspIdsSet()
    {
        Set<String> previousJspIdsSet = (Set<String>) getFacesContext().getExternalContext()
                .getRequestMap().get(PREVIOUS_JSP_IDS_SET);

        if (previousJspIdsSet == null)
        {
            previousJspIdsSet = new HashSet<String>();
        }

        return previousJspIdsSet;
    }

    private boolean isIdDuplicated(String componentId)
    {
        boolean result = false;
        if (_parentClassicTag != null)
        {
            if (_parentClassicTag.isInAnIterator)
            {
                return true;
            }
            List childComponents = _parentClassicTag.getCreatedComponents();

            if (childComponents != null)
            {
                result = childComponents.contains(componentId);
                if (result && (!isInAnIterator))
                {
                    return true;
                }
            }
        }

        return result;
    }

    private boolean isPostBack(FacesContext facesContext)
    {
        return facesContext.getExternalContext().getRequestParameterMap().
              containsKey(ResponseStateManager.VIEW_STATE_PARAM);
    }


    /**
     * check if the facet is already added to the parent
     */
    private boolean checkFacetNameOnParentExists(UIComponentClassicTagBase parentTag, String facetName)
    {
        return parentTag._facetsAdded != null && parentTag._facetsAdded.contains(facetName);
    }

    /**
     * Notify the enclosing JSP tag of the id of this facet's id. The parent
     * tag will later delete any existing view facets that were not seen
     * during this rendering phase; see doEndTag for details.
     */
    private void addFacetNameToParentTag(UIComponentClassicTagBase parentTag, String facetName)
    {
        if (parentTag._facetsAdded == null)
        {
            parentTag._facetsAdded = new ArrayList<String>();
        }
        parentTag._facetsAdded.add(facetName);
    }

    protected abstract boolean hasBinding();

    public JspWriter getPreviousOut() {
        return bodyContent.getEnclosingWriter();
    }
}
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.