javax.faces.webapp.UIComponentTag.java Source code

Java tutorial

Introduction

Here is the source code for javax.faces.webapp.UIComponentTag.java

Source

/**
 * Copyright (C) 2009 GIP RECIA http://www.recia.fr
 * @Author (C) 2009 GIP RECIA <contact@recia.fr>
 * @Contributor (C) 2009 SOPRA http://www.sopragroup.com/
 * @Contributor (C) 2011 Pierre Legay <pierre.legay@recia.fr>
 *
 * 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.
 */
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.application.Application;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.context.ExternalContext;
import javax.faces.el.ValueBinding;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.Tag;
import java.io.IOException;
import java.util.*;

/**
 * Base class for all JSP tags that represent a JSF UIComponent.
 * <p>
 * <i>Disclaimer</i>: The official definition for the behaviour of
 * this class is the JSF specification but for legal reasons the
 * specification cannot be replicated here. Any javadoc present on this
 * class therefore describes the current implementation rather than the
 * officially required behaviour, though it is believed that this class
 * does comply with the specification.
 * 
 * see Javadoc of <a href="http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/index.html">JSF Specification</a> for more.
 * 
 * @author Manfred Geiler (latest modification by $Author: grantsmith $)
 * @version $Revision: 472558 $ $Date: 2006-11-08 18:36:53 +0100 (Mi, 08 Nov 2006) $
 */
public abstract class UIComponentTag implements Tag {
    private static final String FORMER_CHILD_IDS_SET_ATTR = UIComponentTag.class.getName() + ".FORMER_CHILD_IDS";
    private static final String FORMER_FACET_NAMES_SET_ATTR = UIComponentTag.class.getName()
            + ".FORMER_FACET_NAMES";
    private static final String COMPONENT_STACK_ATTR = UIComponentTag.class.getName() + ".COMPONENT_STACK";

    private static final String UNIQUE_ID_COUNTER_ATTR = UIComponentTag.class.getName() + ".UNIQUE_ID_COUNTER";

    protected PageContext pageContext = null;
    private Tag _parent = null;

    //tag attributes
    private String _binding = null;
    private String _id = null;
    private String _rendered = null;

    private FacesContext _facesContext = null;
    private UIComponent _componentInstance = null;
    private boolean _created = false;
    private Boolean _suppressed = null;
    private ResponseWriter _writer = null;
    private Set _childrenAdded = null;
    private Set _facetsAdded = null;

    private static Log log = LogFactory.getLog(UIComponentTag.class);

    public UIComponentTag() {

    }

    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;

        // Reset tag attribute members. These are reset here rather than in
        // internalRelease because of some Resin-related issue. See commit
        // r166747.
        _binding = null;
        _id = null;
        _rendered = 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;
        _suppressed = null;
        _writer = null;
        _childrenAdded = null;
        _facetsAdded = null;
    }

    /** Setter for common JSF xml attribute "binding". */
    public void setBinding(String binding) throws JspException {
        if (!isValueReference(binding)) {
            throw new IllegalArgumentException("not a valid binding: " + binding);
        }
        _binding = binding;
    }

    /** Setter for common JSF xml attribute "id". */
    public void setId(String id) {
        _id = id;
    }

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

    /** Setter for common JSF xml attribute "rendered". */
    public void setRendered(String rendered) {
        _rendered = rendered;
    }

    /**
     * Specify the "component type name" used together with the component's
     * family and the Application object to create a UIComponent instance for
     * this tag. This method is called by other methods in this class, and is
     * intended to be overridden in subclasses to specify the actual component
     * type to be created.
     * 
     * @return a registered component type name, never null.
     */
    public abstract String getComponentType();

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

    /**
     * Return true if this tag created the associated UIComponent (rather
     * than locating an existing instance of the UIComponent in the view).
     */
    public boolean getCreated() {
        return _created;
    }

    /**
     * Return the nearest JSF tag that encloses this tag.
     */
    public static UIComponentTag getParentUIComponentTag(PageContext pageContext) {
        // Question: why not just walk up the _parent chain testing for
        // instanceof UIComponentTag rather than maintaining a separate
        // stack with the pushTag and popTag methods?
        List list = (List) pageContext.getAttribute(COMPONENT_STACK_ATTR, PageContext.REQUEST_SCOPE);
        if (list != null) {
            return (UIComponentTag) list.get(list.size() - 1);
        }
        return null;
    }

    /** See documentation for pushTag. */
    private void popTag() {
        List list = (List) pageContext.getAttribute(COMPONENT_STACK_ATTR, PageContext.REQUEST_SCOPE);
        if (list != null) {
            int size = list.size();
            list.remove(size - 1);
            if (size <= 1) {
                pageContext.removeAttribute(COMPONENT_STACK_ATTR, PageContext.REQUEST_SCOPE);
            }
        }
    }

    /**
     * Push this tag onto the stack of JSP tags seen.
     * <p>
     * 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 pushTag() {
        List list = (List) pageContext.getAttribute(COMPONENT_STACK_ATTR, PageContext.REQUEST_SCOPE);
        if (list == null) {
            list = new ArrayList();
            pageContext.setAttribute(COMPONENT_STACK_ATTR, list, PageContext.REQUEST_SCOPE);
        }
        list.add(this);
    }

    /**
     * Specify the "renderer type name" used together with the current
     * renderKit to get a Renderer instance for the corresponding UIComponent.
     * <p>
     * A JSP tag can return null here to use the default renderer type string.
     * If non-null is returned, then the UIComponent's setRendererType method
     * will be called passing this value, and this will later affect the
     * type of renderer object returned by UIComponent.getRenderer().
     */
    public abstract String getRendererType();

    /**
     * Return true if the specified string contains an EL expression.
     * <p>
     * UIComponent properties are often required to be value-binding
     * expressions; this method allows code to check whether that is
     * the case or not.
     */
    public static boolean isValueReference(String value) {
        if (value == null)
            throw new NullPointerException("value");

        int start = value.indexOf("#{");
        if (start < 0)
            return false;

        int end = value.lastIndexOf('}');
        return (end >= 0 && start < end);
    }

    /**
     * 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 parent) {
        _parent = parent;
    }

    /**
     * Invoked by the standard jsp processing mechanism when the opening
     * tag of a JSF component element is found.
     * <p>
     * The UIComponent associated with this tag is created (if the view
     * doesn't exist) or located if the view is being re-rendered. If
     * the component is not "suppressed" then its encodeBegin method is
     * called (see method isSuppressed). Note also that method
     * encodeBegin is <i>not</i> called for components for which
     * getRendersChildren returns true; that occurs only in doEndTag.
     */
    public int doStartTag() throws JspException {
        setupResponseWriter();
        FacesContext facesContext = getFacesContext();
        UIComponent component = findComponent(facesContext);
        if (!component.getRendersChildren() && !isSuppressed()) {
            try {
                encodeBegin();
                _writer.flush();
            } catch (IOException e) {
                throw new JspException(e.getMessage(), e);
            }
        }
        pushTag();
        return getDoStartValue();
    }

    /**
     * Invoked by the standard jsp processing mechanism when the closing
     * tag of a JSF component element is found.
     * <p>
     * When the view is being re-rendered, any former children of this tag's
     * corresponding component which do not have corresponding tags
     * as children of this tag are removed from the view. This isn't likely
     * to be a common occurrence: wrapping JSF tags in JSTL tag "c:if" is
     * one possible cause. Programmatically created components are not affected
     * by this.
     * <p>
     * If the corresponding component returns true from getRendersChildren
     * then its encodeBegin and encodeChildren methods are called here.
     * <p>
     * The component's encodeEnd method is called provided the component
     * is not "suppressed".
     */
    public int doEndTag() throws JspException {
        popTag();
        UIComponent component = getComponentInstance();
        removeFormerChildren(component);
        removeFormerFacets(component);

        try {
            if (!isSuppressed()) {
                if (component.getRendersChildren()) {
                    encodeBegin();
                    encodeChildren();
                }
                encodeEnd();
            }
        } catch (IOException e) {
            throw new JspException(e.getMessage(), e);
        }

        int retValue = getDoEndValue();
        internalRelease();
        return retValue;
    }

    /**
     * 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) {
        Set formerChildIdsSet = (Set) component.getAttributes().get(FORMER_CHILD_IDS_SET_ATTR);
        if (formerChildIdsSet != null) {
            for (Iterator iterator = formerChildIdsSet.iterator(); iterator.hasNext();) {
                String childId = (String) iterator.next();
                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) {
        Set formerFacetNamesSet = (Set) component.getAttributes().get(FORMER_FACET_NAMES_SET_ATTR);
        if (formerFacetNamesSet != null) {
            for (Iterator iterator = formerFacetNamesSet.iterator(); iterator.hasNext();) {
                String facetName = (String) iterator.next();
                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);
            }
        }
    }

    /**
     * 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.isDebugEnabled())
            log.debug("Entered encodeBegin for client-Id: " + _componentInstance.getClientId(getFacesContext()));
        _componentInstance.encodeBegin(getFacesContext());
        if (log.isDebugEnabled())
            log.debug("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.isDebugEnabled())
            log.debug("Entered encodeChildren for client-Id: " + _componentInstance.getClientId(getFacesContext()));
        _componentInstance.encodeChildren(getFacesContext());
        if (log.isDebugEnabled())
            log.debug("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.isDebugEnabled())
            log.debug("Entered encodeEnd for client-Id: " + _componentInstance.getClientId(getFacesContext()));
        _componentInstance.encodeEnd(getFacesContext());
        if (log.isDebugEnabled())
            log.debug("Exited encodeEnd for client-Id: " + _componentInstance.getClientId(getFacesContext()));

    }

    /**
     * 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 component instance found last time.
     * <p>
     * If this is not the first time this view has been seen, then
     * locate the existing component 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 component, populate its properties from
     * the xml attributes on this JSP tag and attach it to its parent.
     * <p>
     * When a component is found or created the parent JSP tag is also
     * told that the component 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 {
        if (_componentInstance != null)
            return _componentInstance;
        UIComponentTag parentTag = getParentUIComponentTag(pageContext);
        if (parentTag == null) {
            //This is the root
            _componentInstance = context.getViewRoot();
            setProperties(_componentInstance);
            return _componentInstance;
        }

        UIComponent parent = parentTag.getComponentInstance();
        //TODO: what if parent == null?
        if (parent == null)
            throw new IllegalStateException("parent is null?");

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

            addFacetNameToParentTag(parentTag, facetName);
            return _componentInstance;
        } else {
            //Child
            //
            // Note that setProperties is called only when we create the
            // component; on later passes, the attributes defined on the
            // JSP tag are set on this Tag object, but then completely
            // ignored.

            String id = getOrCreateUniqueId(context);

            // 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.
            //
            // we throw an exception here - RI issues a warning.
            if (parentTag._childrenAdded != null && parentTag._childrenAdded.contains(id)) {
                throw new FacesException("There is more than one JSF tag with id : " + id
                        + " for parent component with id : '" + parent.getId() + "'");
            }

            _componentInstance = findComponent(parent, id);
            if (_componentInstance == null) {
                _componentInstance = createComponentInstance(context, id);
                setProperties(_componentInstance);
                int index = getAddedChildrenCount(parentTag);
                List children = parent.getChildren();
                if (index <= children.size()) {
                    children.add(index, _componentInstance);
                } else {
                    throw new FacesException("cannot add component with id '" + _componentInstance.getId()
                            + " to its parent component with id : '" + parent.getId() + "' and path '"
                            + getPathToComponent(parent) + "'at position :" + index + " in list of children. "
                            + "This might be a problem due to a duplicate id in a previously added component,"
                            + "if this is the case, the problematic id might be one of : "
                            + printSet(parentTag._childrenAdded));
                }
            }
            addChildIdToParentTag(parentTag, id);
            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;
    }

    /**
     * Utility method for creating diagnostic output.
     */
    private String printSet(Set childrenAdded) {
        StringBuffer buf = new StringBuffer();

        if (childrenAdded != null) {
            Iterator it = childrenAdded.iterator();

            while (it.hasNext()) {
                Object obj = it.next();
                buf.append(obj);

                if (it.hasNext())
                    buf.append(",");
            }
        }
        return buf.toString();
    }

    private String getOrCreateUniqueId(FacesContext context) {
        String id = getId();
        if (id != null) {
            return id;
        } else {
            //we've been calling
            //return context.getViewRoot().createUniqueId(); - don't want that anymore
            Long currentCounter = (Long) context.getExternalContext().getRequestMap().get(UNIQUE_ID_COUNTER_ATTR);
            long lCurrentCounter = 0;

            if (currentCounter != null) {
                lCurrentCounter = currentCounter.longValue();
            }

            StringBuffer retValue = new StringBuffer(UIViewRoot.UNIQUE_ID_PREFIX);
            retValue.append("Jsp");
            retValue.append(lCurrentCounter);

            lCurrentCounter++;

            context.getExternalContext().getRequestMap().put(UNIQUE_ID_COUNTER_ATTR, new Long(lCurrentCounter));

            ExternalContext extCtx = FacesContext.getCurrentInstance().getExternalContext();
            return extCtx.encodeNamespace(retValue.toString());
        }
    }

    /**
     * Create a UIComponent. Abstract method getComponentType is invoked to
     * determine the actual type name for the component to be created.
     * 
     * If this tag has a "binding" attribute, then that is immediately
     * evaluated to store the created component in the specified property.
     */
    private UIComponent createComponentInstance(FacesContext context, String id) {
        String componentType = getComponentType();
        if (componentType == null) {
            throw new NullPointerException("componentType");
        }

        if (_binding != null) {
            Application application = context.getApplication();
            ValueBinding componentBinding = application.createValueBinding(_binding);
            UIComponent component = application.createComponent(componentBinding, context, componentType);
            component.setId(id);
            component.setValueBinding("binding", componentBinding);
            recurseFacetsAndChildrenForId(component.getFacetsAndChildren(), id + "_", 0);
            _created = true;
            return component;
        } else {
            UIComponent component = context.getApplication().createComponent(componentType);
            component.setId(id);
            _created = true;
            return component;
        }
    }

    /**
     * Recurse all facets and children and assign them an unique ID if
     * necessary. We must *not* use UIViewRoot#createUniqueId here,
     * because this would affect the order of the created ids upon
     * rerendering the page!
     */
    private int recurseFacetsAndChildrenForId(Iterator facetsAndChildren, String idPrefix, int cnt) {
        while (facetsAndChildren.hasNext()) {
            UIComponent comp = (UIComponent) facetsAndChildren.next();
            if (comp.getId() == null) {
                ++cnt;
                comp.setId(idPrefix + cnt);
            }
            cnt = recurseFacetsAndChildrenForId(comp.getFacetsAndChildren(), idPrefix, cnt);
        }
        return cnt;
    }

    /**
     * Notify the enclosing JSP tag of the id of this component's id. The
     * parent tag will later delete any existing view components that were
     * not seen during this rendering phase; see doEndTag for details.
     */
    private void addChildIdToParentTag(UIComponentTag parentTag, String id) {
        if (parentTag._childrenAdded == null) {
            parentTag._childrenAdded = new HashSet();
        }
        parentTag._childrenAdded.add(id);
    }

    /**
     * check if the facet is already added to the parent
     */
    private boolean checkFacetNameOnParentExists(UIComponentTag 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(UIComponentTag parentTag, String facetName) {
        if (parentTag._facetsAdded == null) {
            parentTag._facetsAdded = new HashSet();
        }
        parentTag._facetsAdded.add(facetName);
    }

    private int getAddedChildrenCount(UIComponentTag parentTag) {
        return parentTag._childrenAdded != null ? parentTag._childrenAdded.size() : 0;
    }

    /**
     * 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 Tag.EVAL_BODY_INCLUDE
     */
    protected int getDoStartValue() throws JspException {
        return Tag.EVAL_BODY_INCLUDE;
    }

    /**
     * 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 FacesContext getFacesContext() {
        if (_facesContext == null) {
            _facesContext = FacesContext.getCurrentInstance();
        }
        return _facesContext;
    }

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

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

    /**
     * Determine whether this component renders itself. A component
     * is "suppressed" when it is either not rendered, or when it is
     * rendered by its parent component at a time of the parent's choosing.
     */
    protected boolean isSuppressed() {
        if (_suppressed == null) {
            // we haven't called this method before, so determine the suppressed
            // value and cache it for later calls to this method.

            if (isFacet()) {
                // facets are always rendered by their parents --> suppressed
                _suppressed = Boolean.TRUE;
                return true;
            }

            UIComponent component = getComponentInstance();

            // Does any parent render its children?
            // (We must determine this first, before calling any isRendered method
            //  because rendered properties might reference a data var of a nesting UIData,
            //  which is not set at this time, and would cause a VariableResolver error!)
            UIComponent parent = component.getParent();
            while (parent != null) {
                if (parent.getRendersChildren()) {
                    //Yes, parent found, that renders children --> suppressed
                    _suppressed = Boolean.TRUE;
                    return true;
                }
                parent = parent.getParent();
            }

            // does component or any parent has a false rendered attribute?
            while (component != null) {
                if (!component.isRendered()) {
                    //Yes, component or any parent must not be rendered --> suppressed
                    _suppressed = Boolean.TRUE;
                    return true;
                }
                component = component.getParent();
            }

            // else --> not suppressed
            _suppressed = Boolean.FALSE;
        }
        return _suppressed.booleanValue();
    }

    protected void setProperties(UIComponent component) {
        if (getRendererType() != null) {
            _componentInstance.setRendererType(getRendererType());
        }

        if (_rendered != null) {
            if (isValueReference(_rendered)) {
                ValueBinding vb = getFacesContext().getApplication().createValueBinding(_rendered);
                component.setValueBinding("rendered", vb);
            } else {
                boolean b = Boolean.valueOf(_rendered).booleanValue();
                component.setRendered(b);
            }
        }
    }

    protected void setupResponseWriter() {
        FacesContext facesContext = getFacesContext();

        if (facesContext == null) {
            throw new FacesException("Faces context not found. getResponseWriter will fail. "
                    + "Check if the FacesServlet has been initialized at all in your web.xml configuration file"
                    + "and if you are accessing your jsf-pages through the correct mapping. E.g.: if your FacesServlet is mapped to "
                    + " *.jsf (with the <servlet-mapping>-element), you need to access your pages as 'sample.jsf'. If you tried to access 'sample.jsp', you'd get this error-message.");
        }

        _writer = facesContext.getResponseWriter();
        if (_writer == null) {
            RenderKitFactory renderFactory = (RenderKitFactory) FactoryFinder
                    .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
            RenderKit renderKit = renderFactory.getRenderKit(facesContext,
                    facesContext.getViewRoot().getRenderKitId());

            _writer = renderKit.createResponseWriter(new _PageContextOutWriter(pageContext),
                    null /*Default: get the allowed content-types from the accept-header*/,
                    pageContext.getRequest().getCharacterEncoding());
            facesContext.setResponseWriter(_writer);
        }
    }

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

}