All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javax.faces.webapp.UIComponentClassicTagBase Maven / Gradle / Ivy

The newest version!
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2010 Oracle and/or its affiliates. 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.java.net/public/CDDL+GPL_1_1.html
 * or packager/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 packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [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.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.List;
import java.util.Map;
import java.util.RandomAccess;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;


/**
 * 

UIComponentTagBase 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 BodyTag * 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.

* *

{@link UIComponentTag} extends * UIComponentClassicTagBase to add support for properties * that conform to the Faces 1.1 EL.

* *

{@link UIComponentELTag} extends * UIComponentClassicTagBase class to add support for * properties that conform to the EL API.

* *

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.

* *

The CASE markers in the following example will be cited in the * method descriptions of this class.

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

  • * *
  • CASE 2 describes template text and/or non-component custom tag * output occurring between two sibling component tags.

  • * *
  • CASE 3 describes template text and/or non-component custom tag * output occurring as the child content of an <f:verbatim> * tag at any point in the page.

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

  • * *
* *


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

 * 
* *

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

* *


 <table border="4" style="color:red">

 <tbody>

 <tr><td>CASE 1</td></tr> <tr><td>component 1</td></tr>

 <tr><td>CASE 2</td> <tr><td>component 2</td></tr>

 <tr><td>CASE 3</td> <td>CASE 4</td></tr>

 </tbody>

 </table>

 * 
* */ public abstract class UIComponentClassicTagBase extends UIComponentTagBase implements JspIdConsumer, BodyTag { // ------------------------------------------------------ Manifest Constants /** *

The facesContext scope attribute under which a component tag stack * for the current facesContext will be maintained.

*/ private static final String COMPONENT_TAG_STACK_ATTR = "javax.faces.webapp.COMPONENT_TAG_STACK"; /** *

The {@link UIComponent} attribute under which we will store a * List of the component identifiers of child components * created on the previous generation of this page (if any).

*/ private static final String JSP_CREATED_COMPONENT_IDS = "javax.faces.webapp.COMPONENT_IDS"; /** *

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

The attribute name under which we will store all {@link UIComponent} * IDs of the current translation unit.

*/ private static final String GLOBAL_ID_VIEW = "javax.faces.webapp.GLOBAL_ID_VIEW"; /** *

The attribute name under which we will store the {@link FacesContext} * for this request.

*/ private static final String CURRENT_FACES_CONTEXT = "javax.faces.webapp.CURRENT_FACES_CONTEXT"; /** *

The attribute name under which we will store the {@link UIViewRoot} * for this request.

*/ private static final String CURRENT_VIEW_ROOT = "javax.faces.webapp.CURRENT_VIEW_ROOT"; /** * Used as the prefix for ids. This is necessary to avoid * uniqueness conflicts with the transient verbatim components. */ protected static final String UNIQUE_ID_PREFIX = UIViewRoot.UNIQUE_ID_PREFIX + '_'; /** * Used to store the previousJspId Map in facesContextScope */ private static final String PREVIOUS_JSP_ID_SET = "javax.faces.webapp.PREVIOUS_JSP_ID_SET"; /** * This is a Page scoped marker to help us * keep track of the different execution context we could * be operating within, e.g. an include, or a tag file. * The value of the attribute is an Integer that is unqiue * to this page context. */ private static final String JAVAX_FACES_PAGECONTEXT_MARKER = "javax.faces.webapp.PAGECONTEXT_MARKER"; /** * This is a facesContext scoped attribute which contains * an AtomicInteger which we use to increment the PageContext * count. */ private static final String JAVAX_FACES_PAGECONTEXT_COUNTER = "javax.faces.webapp.PAGECONTEXT_COUNTER"; // ------------------------------------------------------ Instance Variables /** *

The bodyContent for this tag handler.

*/ protected BodyContent bodyContent = null; /** *

The {@link UIComponent} that is being encoded by this tag, * if any.

*/ private UIComponent component = null; /** *

The {@link FacesContext} for the request being processed, if any. *

*/ private FacesContext context = null; /** *

Was a new component instance dynamically created when our * findComponent() method was called.

*/ private boolean created = false; /** *

The Lst of {@link UIComponent} ids created or located * by nested {@link UIComponentTag}s while processing the current * request.

*/ private List createdComponents = null; /** *

The List of facet names created or located by nested * {@link UIComponentTag}s while processing the current request.

*/ private List createdFacets = null; /** *

The JSP PageContext for the page we are embedded in.

*/ protected PageContext pageContext = null; /** *

The JSP Tag that is the parent of this tag.

*/ private Tag parent = null; /** * {@link #setJspId} */ private String jspId = null; /** * Only consulted in setJspId to detect the iterator case. * Set in {@link #release}. Never cleared. */ //private String oldJspId = null; /** * This is simply the jspId prefixed by {@link #UNIQUE_ID_PREFIX}. */ private String facesJspId = null; /** *

The component identifier for the associated component.

*/ private String id = null; /** * Caches the nearest enclosing {@link UIComponentClassicTagBase} of this * tag. This is used for duplicate id detection. */ private UIComponentClassicTagBase parentTag = null; /** * Set to true if this component is nested inside of an iterating * tag */ private boolean isNestedInIterator = false; /** * The next child index to get in getChild() */ private int _nextChildIndex = 0; Map> namingContainerChildIds = null; public UIComponentClassicTagBase() {} UIComponentClassicTagBase(PageContext pageContext, FacesContext facesContext) { this.pageContext = pageContext; this.context = facesContext; } // --------------------------------------------- Support Methods for Tag // // Simple methods to be overridden by subclasses if necessary // /** *

Return the flag value that should be returned from the * doStart() method when it is called. Subclasses * may override this method to return the appropriate value.

* * @throws JspException to cause doStart() to * throw an exception * * @return the value to return from doStart() */ protected int getDoStartValue() throws JspException { int result = EVAL_BODY_BUFFERED; return result; } /** *

Return the flag value that should be returned from the * doEnd() method when it is called. Subclasses * may override this method to return the appropriate value.

* * @throws JspException to cause doEnd() to * throw an exception * * @return the value to return from doEnd() */ protected int getDoEndValue() throws JspException { return (EVAL_PAGE); } /** *

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

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

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

Set the PageContext of the page containing this * tag instance.

* * @param pageContext The enclosing PageContext */ @Override public void setPageContext(PageContext pageContext) { this.pageContext = pageContext; } /** *

Return the Tag that is the parent of this instance.

*/ @Override public Tag getParent() { return (this.parent); } /** *

Set the Tag that is the parent of this instance.

* * @param parent The new parent Tag */ @Override public void setParent(Tag parent) { this.parent = parent; } // // Complex methods to support Tag // /** *

Set up the {@link javax.faces.context.ResponseWriter} for the * current response, if this has not been done already.

* *

@deprecated. {@link * javax.faces.application.ViewHandler#renderView} is now * responsible for setting up the response writer. This method is * now a no-op.

*/ protected void setupResponseWriter() { } /** *

Create a new child component using createComponent, * initialize its properties, and add it to its parent as a child. *

* @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 null for no explicit identifier */ private UIComponent createChild( FacesContext context, UIComponent parent, UIComponentClassicTagBase parentTag, String componentId) throws JspException { UIComponent component = createComponent(context, componentId); int indexOfNextChildTag = parentTag.getIndexOfNextChildTag(); if (indexOfNextChildTag > parent.getChildCount()) { indexOfNextChildTag = parent.getChildCount(); } parent.getChildren().add(indexOfNextChildTag, component); created = true; return (component); } /** *

Create a new child component using createComponent, * initialize its properties, and add it to its parent as a facet. *

* @param context {@link FacesContext} for the current request * @param parent Parent {@link UIComponent} of the new facet * @param name Name of the new facet * @param newId id of the new facet */ private UIComponent createFacet(FacesContext context, UIComponent parent, String name, String newId) throws JspException { UIComponent component = createComponent(context, newId); parent.getFacets().put(name, component); created = true; return (component); } /** *

Return a child with the specified component id from the specified * component, if any; otherwise, return null.

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

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:

*
    *
  1. If we have previously located this component, return it.
  2. *
  3. 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 * Tree and return it.
  4. *
  5. If this {@link UIComponentTag} instance has the * facetName attribute set, ask the parent * {@link UIComponent} for a facet with this name. If not found, * create one, call setProperties() with the new * component as a parameter, and register it under this name. * Return the found or created facet {@link UIComponent}.
  6. *
  7. Determine the component id to be assigned to the new * component, as follows: if this {@link UIComponentTag} has * an id 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 id attribute set).
  8. *
  9. Ask the parent {@link UIComponent} for a child with this identifier. * If not found, create one, call setProperties() * with the new component as a parameter, and register it as a child * with this identifier. Return the found or created * child {@link UIComponent}.
  10. *
*

When creating a component, the process is:

*
    *
  1. Retrieve the component type by calling * {@link UIComponentTag#getComponentType}
  2. *
  3. If the component has a binding 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 "binding".
  4. *
  5. Otherwise, call {@link Application#createComponent} with * only the component type. *
  6. Call setProperties(). *
  7. Add the new component as a child or facet of its parent
  8. *
* * @param context the {@code FacesContext} for the current request. * * @return the found component * * @throws JspException if an unexpected condition arises while finding the component */ 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(context.getAttributes()); UIComponent parentComponent; if (parentTag != null) { parentComponent = parentTag.getComponentInstance(); } else { // Special case. The component to be found is the // UIViewRoot. // see if this is the first time this tag instance is trying // to be bound to the UIViewRoot parentComponent = context.getViewRoot(); // Has this UIViewRoot instance had a tag bound to it // before? if (null == parentComponent.getAttributes().get(CURRENT_VIEW_ROOT)) { // No it hasn't. // make sure setProperties() and setId() are called // once per UIViewRoot instance. try { setProperties(parentComponent); } catch (FacesException e) { if (e.getCause() instanceof JspException) { throw ((JspException)e.getCause()); } throw e; } if (null != this.id) { parentComponent.setId(this.id); } else { assert(null != getFacesJspId()); parentComponent.setId(getFacesJspId()); } parentComponent.getAttributes().put(CURRENT_VIEW_ROOT, CURRENT_VIEW_ROOT); created = true; } else if (hasBinding()) { try { setProperties(parentComponent); } catch (FacesException e) { if (e.getCause() instanceof JspException) { throw ((JspException)e.getCause()); } throw e; } } // this is not the first time this tag instance is trying to // be bound to this UIViewRoot, take no extra action. component = parentComponent; return (component); } // Step 3 -- Calculate the component identifier for this component String newId = createId(context); // Step 4 -- Create or return a facet with the specified name (if any) String facetName = getFacetName(); boolean created = parentTag.getCreated(); if (facetName != null) { component = parentComponent.getFacets().get(facetName); if (component == null) { component = createFacet(context, parentComponent, facetName, newId); } return (component); } else { // Step 5 -- Create or return a child with the specified id component = getChild(parentTag, parentComponent, newId); if (component == null) { component = createChild(context, parentComponent, parentTag, newId); } return (component); } } // // Tag tree navigation // /** *

Locate and return the nearest enclosing {@link UIComponentClassicTagBase} * if any; otherwise, return null.

* * @param context PageContext for the current page * * @return the parent tag */ public static UIComponentClassicTagBase getParentUIComponentClassicTagBase(PageContext context) { return _getParentUIComponentClassicTagBase(getFacesContext(context)); } private static UIComponentClassicTagBase _getParentUIComponentClassicTagBase( FacesContext facesContext) { return _getParentUIComponentClassicTagBase(facesContext.getAttributes()); } private static UIComponentClassicTagBase _getParentUIComponentClassicTagBase(Map cMap) { List list = null; if (cMap != null) { list = (List) cMap.get(COMPONENT_TAG_STACK_ATTR); } if (list != null) { return ((UIComponentClassicTagBase) list.get(list.size() - 1)); } else { return null; } } // // Methods related to the createdComponents and createdFacets lists. // @Override protected int getIndexOfNextChildTag() { if (createdComponents != null) { return (createdComponents.size()); } else { return (0); } } @Override protected void addChild(UIComponent child) { if (createdComponents == null) { createdComponents = new ArrayList<>(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); } @Override protected void addFacet(String name) { if (createdFacets == null) { //noinspection CollectionWithoutInitialCapacity createdFacets = new ArrayList<>(3); } createdFacets.add(name); } /** *

Pop the top {@link UIComponentTag} instance off of our component tag * stack, deleting the stack if this was the last entry.

*/ private void popUIComponentClassicTagBase() { List list = (List) context.getAttributes().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) { context.getAttributes().remove(COMPONENT_TAG_STACK_ATTR); list = null; } } } /** *

Push the specified {@link UIComponentTag} instance onto our component * tag stack, creating a stack if necessary.

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

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.

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

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.

*/ private void removeOldFacets() { Map attributes = component.getAttributes(); List currentComponents = createdFacets; // Get the old list of created component ids and update the current list as a // component attribute Object oldValue; if (currentComponents != null) { oldValue = attributes.put(JSP_CREATED_FACET_NAMES, currentComponents); createdFacets = null; } else { oldValue = attributes.remove(JSP_CREATED_FACET_NAMES); } // Remove old children that are no longer present if (oldValue != null) { List oldList = TypedCollections.dynamicallyCastList((List)oldValue, String.class); int oldCount = oldList.size(); if (oldCount > 0) { if (currentComponents != null) { int currStartIndex = 0; for (int oldIndex = 0; oldIndex < oldCount; oldIndex++) { String oldId = oldList.get(oldIndex); int foundIndex = _indexOfStartingFrom(currentComponents, currStartIndex, oldId); if (foundIndex != -1) { currStartIndex = foundIndex + 1; } else { component.getFacets().remove(oldId); } } } else { Map facets = component.getFacets(); // All old facets need to be removed for (String oldId : oldList) { facets.remove(oldId); } } } } } // // Methods to support content interweaving // /** * *

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.

* * @return the component */ 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(""))) { verbatim = createVerbatimComponent(); verbatim.setValue(bodyContentString); bodyContent.clearBody(); } else { StringBuilder content = new StringBuilder(trimString.length()); int sIdx = 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("", sIdx); } content.append(trimString); String result = content.toString(); if (result.trim().length() > 0) { verbatim = createVerbatimComponent(); verbatim.setValue(content.toString()); } bodyContent.clearBody(); } } return verbatim; } /** *

Use the {@link Application} instance to create a new component * with the following characteristics.

* *

componentType is * javax.faces.HtmlOutputText.

* *

transient is true.

* *

escape is false.

* *

id is * FacesContext.getViewRoot().createUniqueId()

* * * @return the component */ 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; } /** *

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

* * * @param parentTag the parent tag * * @param verbatim the verbatim to add before the component * * @param component the component to be added after the component * */ protected void addVerbatimBeforeComponent( UIComponentClassicTagBase parentTag, UIComponent verbatim, UIComponent component) { UIComponent parent = component.getParent(); if (null == parent) { return; } List 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); } /** *

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

* * @param parentTag the parent tag * * @param verbatim the verbatim to add after the component * * @param component the component to be added before the component */ 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 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 /** * *

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 * before the UIComponent instance for * this tag. The value of this UIOutput * component must include anything covered by CASE 1 or * CASE 2 in the class description.

* *

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 before the * actual component for this tag instance instance by calling * {@link #addVerbatimBeforeComponent}.

* *

Before returning, the component is pushed onto the component * stack for this response to enable the {@link * #getParentUIComponentClassicTagBase} method to work properly.

* *

The flag value to be returned is acquired by calling the * getDoStartValue() method, which tag subclasses may * override if they do not want the default value.

* * @throws JspException if an error occurs */ @Override 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"); } List list = (List) context.getAttributes().get(COMPONENT_TAG_STACK_ATTR); if (list != null) { parentTag = ((UIComponentClassicTagBase) list.get(list.size() - 1)); } else { parentTag = null; } Map componentIds; // If we're not inside of a facet, and if we are inside of a // rendersChildren==true component, stuff any template text or // custom tag output into a transient component. if (null == getFacetName() && null != parentTag) { Tag p = this.getParent(); // If we're not inside a JSP tag or we're not inside // a UIComponentTag flush the buffer if (null == p || !(p instanceof UIComponentTagBase)) { JspWriter out = pageContext.getOut(); if (!(out instanceof BodyContent)) { try { out.flush(); } catch (IOException ioe) { throw new JspException(ioe); } } } verbatim = parentTag.createVerbatimComponentFromBodyContent(); } // Locate the UIComponent associated with this UIComponentTag, // creating one if necessary component = findComponent(context); // if we have a verbatim component, add it after this component. if (null != verbatim) { addVerbatimBeforeComponent(parentTag, verbatim, component); } Object tagInstance = null; String clientId = null; if (component instanceof NamingContainer || (parentTag == null)) { namingContainerChildIds = new HashMap<>(); } if (this.id != null) { clientId = getId(); UIComponentClassicTagBase temp = (UIComponentClassicTagBase) getParentNamingContainerTag().getNamingContainerChildIds().get(clientId); // According to the JavaDocs for JspIdConsumer tag handlers // that implement this interface are not to be pooled, however // due to a bug in Jasper this is not the case. // Because of this, two tags with the same ID within the same // naming container will not be detected as duplicates. So // in order to ensure we detect it, if the instance is the same, // verify the JSP IDs are different. If they are, then continue, // if they aren't, then we're dealing with EVAL_BODY_AGAIN (see // below) //noinspection ObjectEquality if (temp == this && !this.getJspId().equals(temp.getJspId())) { tagInstance = this; } else if (temp != null && temp != this && this.getJspId().equals(temp.getJspId())) { // new instance, same JSP ID - this is the EVAL_BODY_AGAIN case. tagInstance = temp; } } // If we have a tag instance, then, most likely, a tag handler // returned EVAL_BODY_AGAIN somewhere. Make sure the instance // returned is the same as the current instance and if this is the case, // do not perform ID validation as it has already been done. if (tagInstance == null) { // only check for id uniqueness if the author has manually given // us an id. if (null != this.id) { // assert component ID uniqueness if (clientId != null) { if (getParentNamingContainerTag().getNamingContainerChildIds().containsKey(clientId)) { // PENDING i18n StringWriter writer = new StringWriter(128); printTree(context.getViewRoot(), clientId, writer, 0); String msg = "Duplicate component id: '" + clientId + "', first used in tag: '" + getParentNamingContainerTag().getNamingContainerChildIds().get(clientId).getClass().getName() + "'\n" + writer.toString(); throw new JspException(new IllegalStateException(msg)); } else { getParentNamingContainerTag().getNamingContainerChildIds().put(clientId, this); } } } // Add to parent's list of created components or facets if needed if (parentTag != null) { if (getFacetName() == null) { parentTag.addChild(component); } else { parentTag.addFacet(getFacetName()); } } } // Rendering is deferred until after the tree is completely // created // Return the appropriate control value pushUIComponentClassicTagBase(); return (getDoStartValue()); } /** * *

Perform any processing necessary to handle the content * implications of CASE 3 in the class description.

* *

The default implementation, which is intended to be sufficient * for most components, calls {@link * #createVerbatimComponentFromBodyContent} on this instance * and adds it as a child of the component for this tag's component * at the end of the child list. In addition, the following * housekeeping steps are taken.

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

The flag value to be returned is acquired by calling the * getDoEndValue() method, which tag subclasses may * override if they do not want the default value.

* * @throws JspException if an error occurs */ @Override public int doEndTag() throws JspException { // Remove old children and facets as needed popUIComponentClassicTagBase(); removeOldChildren(); removeOldFacets(); //If we are at the end tag of a NamingContainer component, reset the Map of ids // for the NamingContainer tag. if (namingContainerChildIds != null) { namingContainerChildIds = null; } // Render the children (if needed) and end of the component // associated with this tag try { UIComponent verbatim; UIComponentClassicTagBase parentTag = _getParentUIComponentClassicTagBase( context.getAttributes()); 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; } this.release(); return (getDoEndValue()); } /** *

Release any resources allocated during the execution of this * tag handler.

*/ @Override public void release() { this.parent = null; this.id = null; this.facesJspId = null; this.created = false; this.bodyContent = null; this.isNestedInIterator = false; _nextChildIndex = 0; } // -------------------------------------------- Support methods for BodyTag /** *

Return the flag value that should be returned from the * doAfterBody() method when it is called. Subclasses * may override this method to return the appropriate value.

* * @return the value to return from doAfterBody() * * @return JspException if the value cannot be returned * * @throws JspException if an unexpected condition arises while * getting the value */ protected int getDoAfterBodyValue() throws JspException { return (SKIP_BODY); } // -------------------------------------------------------- BodyTag Methods /** *

Set the bodyContent for this tag handler. This method * is invoked by the JSP page implementation object at most once per * action invocation, before doInitiBody(). This method * will not be invoked for empty tags or for non-empty tags whose * doStartTag() method returns SKIP_BODY or * EVAL_BODY_INCLUDE.

* * @param bodyContent The new BodyContent for this tag */ @Override public void setBodyContent(BodyContent bodyContent) { this.bodyContent = bodyContent; } /** *

Get the JspWriter from our BodyContent. *

* * @return the writer */ public JspWriter getPreviousOut() { return (this.bodyContent.getEnclosingWriter()); } public BodyContent getBodyContent() { return (this.bodyContent); } /** *

Prepare for evaluation of the body. This method is invoked by the * JSP page implementation object after setBodyContent() * 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 * doStartTag() method returns SKIP_BODY * or EVAL_BODY_INCLUDE.

* * @throws JspException if an error is encountered */ @Override public void doInitBody() throws JspException { // Default implementation does nothing } /** * *

Perform any processing necessary to handle the content * implications of CASE 4 in the class description.

* *

Return result from {@link #getDoAfterBodyValue}

* @throws JspException if an error is encountered */ @Override public int doAfterBody() throws JspException { UIComponent verbatim; UIComponentClassicTagBase parentTag = _getParentUIComponentClassicTagBase(context.getAttributes()); // 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 /** *

Set the component identifier for our component. If the * argument begins with {@link * UIViewRoot#UNIQUE_ID_PREFIX} throw an * IllegalArgumentException

* * @param id The new component identifier. This may not start with * {@link UIViewRoot#UNIQUE_ID_PREFIX}. * * @throws IllegalArgumentException if the argument is * non-null and starts with {@link * UIViewRoot#UNIQUE_ID_PREFIX}. */ @Override public void setId(String id) { if (null != id && id.startsWith(UIViewRoot.UNIQUE_ID_PREFIX)) { throw new IllegalArgumentException(); } this.id = id; } /** *

Return the id value assigned by the page author.

* * @return the id of this tag */ protected String getId() { return (id); } /** *

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-null, prepend {@link #UNIQUE_ID_PREFIX} * to the jspId and return the result.

* * @return the value as specified above */ protected String getFacesJspId() { if (null == facesJspId) { if (null != jspId) { facesJspId = UNIQUE_ID_PREFIX + jspId; // if this tag happens to be nested within , // 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 * id. This will be the case if this tag is * nested within tag or any other JSTL loop tag * or if the page has components with the same Id. * * @param componentId id to be looked up for possible * duplication. * @return true if this nested with facesJspId is duplicate. */ private boolean isDuplicateId(String componentId) { boolean result = false; if (parentTag != null) { if (parentTag.isNestedInIterator) { return true; } List childComponents = parentTag.createdComponents; // PENDING: Need to analyze the impact of this look up on pages // with several levels of nesting. if (childComponents != null) { result = childComponents.contains(componentId); if (result && (!isNestedInIterator)) { return true; } } } return result; } /* * Appends a counter to the passed in id and stores the * id and counter information in request scope. * * @return String id with a counter appended to it. */ private String generateIncrementedId (String componentId) { Integer serialNum = (Integer) context.getAttributes().get(componentId); if (null == serialNum) { serialNum = 1; } else { serialNum = serialNum.intValue() + 1; } context.getAttributes().put(componentId, serialNum); componentId = componentId + UNIQUE_ID_PREFIX + serialNum.intValue(); return componentId; } /** * Returns the List of {@link UIComponent} ids created or * located by nested {@link UIComponentTag}s while processing the current * request. * * @return the created components */ protected List getCreatedComponents() { return createdComponents; } /** *

Create the component identifier to be used for this component.

*/ private String createId(FacesContext context) throws JspException { if (this.id == null) { return getFacesJspId(); } else { // if this tag happens to be nested within , 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 true if this ID is unique within the closest naming * container, otherwise false */ 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 created = this.parentTag.createdComponents; return !(created != null && created.contains(id)); } else { return false; } } } /** * @return the parent tag that represents the closest NamingContainer * component. */ private UIComponentClassicTagBase getParentNamingContainerTag() { if (this.parentTag == null) { return this; } UIComponentClassicTagBase parent = this.parentTag; while (parent != null) { if (parent.component instanceof NamingContainer || parent.parentTag == null && parent.component instanceof UIViewRoot) { return parent; } parent = parent.parentTag; } return null; } // ------------------------------------------------ JspIdConsumer Methods /** *

Defined on {@link JspIdConsumer}. This method is called by * the container before {@link #doStartTag}. The argument is * guaranteed to be unique within the page.

* *

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.

* * @param id the container generated id for this tag, guaranteed to * be unique within the page. */ @Override public void setJspId(String id) { // reset JSP ID here instead of release as we may need // to check the ID after the tag has been used this.jspId = null; Integer pcId = (Integer) pageContext.getAttribute(JAVAX_FACES_PAGECONTEXT_MARKER, PageContext.PAGE_SCOPE); if (pcId == null) { if (null == context) { context = FacesContext.getCurrentInstance(); } AtomicInteger aInt = (AtomicInteger) context.getAttributes().get(JAVAX_FACES_PAGECONTEXT_COUNTER); if (aInt == null) { aInt = new AtomicInteger(); context.getAttributes().put(JAVAX_FACES_PAGECONTEXT_COUNTER, aInt); } pcId = aInt.incrementAndGet(); pageContext.setAttribute(JAVAX_FACES_PAGECONTEXT_MARKER, pcId); } if (pcId.intValue() > 1) { StringBuilder builder = new StringBuilder(id.length() + 3); builder.append(id).append("pc").append(pcId); jspId = builder.toString(); } else { jspId = id; } facesJspId = null; updatePreviousJspIdAndIteratorStatus(jspId); } /** *

Called from {@link #setJspId} to update the value saved for * the previous call to {@link #setJspId} for this component on * this request. If this method is presented with the same * argument id 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 c:forEach. If so, * we set the isNestedInIterator ivar to * true otherwise, we set it to false.

* *

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 id. * * @param id the id to be compared with the previous id, if any, for * this tag instance on this request. */ private void updatePreviousJspIdAndIteratorStatus(String id) { Set previousJspIdSet = TypedCollections.dynamicallyCastSet((Set) pageContext.getAttribute(PREVIOUS_JSP_ID_SET, PageContext.PAGE_SCOPE), String.class); if (null == previousJspIdSet) { previousJspIdSet = new HashSet<>(); //noinspection CollectionWithoutInitialCapacity pageContext.setAttribute(PREVIOUS_JSP_ID_SET, previousJspIdSet, PageContext.PAGE_SCOPE); } // detect the iterator case, since add will return true if the collection already // contains the id if (previousJspIdSet.add(id)) { // id wasn't in Set, so we aren't nested yet isNestedInIterator = false; } else { // the Set didn't change, so we are nested if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "Id " + id + " is nested within an iterating tag."); } isNestedInIterator = true; } } public String getJspId() { return jspId; } // ------------------------------------------------------- Abstract methods /** *

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 ONLY * if the specified {@link UIComponent} was in fact created during * the execution of this tag handler instance, and this call will occur * BEFORE the {@link UIComponent} is added to * the view.

* *

Tag subclasses that want to support additional set properties * must ensure that the base class setProperties() * method is still called. A typical implementation that supports * extra properties foo and bar would look * something like this:

*
     * protected void setProperties(UIComponent component) {
     *   super.setProperties(component);
     *   if (foo != null) {
     *     component.setAttribute("foo", foo);
     *   }
     *   if (bar != null) {
     *     component.setAttribute("bar", bar);
     *   }
     * }
     * 
* *

The default implementation overrides the following properties:

*
    *
  • rendered - Set if a value for the * rendered property is specified for * this tag handler instance.
  • *
  • rendererType - Set if the getRendererType() * method returns a non-null value.
  • *
* * @param component {@link UIComponent} whose properties are to be * overridden */ protected abstract void setProperties(UIComponent component); /** *

Create and return a new child component of the type returned * by calling getComponentType(). If this {@link * UIComponentTag} has a non-null binding attribute, * this is done by call {@link Application#createComponent} with the * expression created for the binding 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.

* @param context {@link FacesContext} for the current request * @param newId id of the component * * @return the created component * * @throws JspException if the component cannot be created */ protected abstract UIComponent createComponent(FacesContext context, String newId) throws JspException; /** *

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

* * @return whether or not this component has a binding attribute */ protected abstract boolean hasBinding(); // --------------------------------------------------------- Properties /** *

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 doStartTag() and doEndTag() * on this tag instance.

*/ @Override public UIComponent getComponentInstance() { return (this.component); } /** *

Return true 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 doStartTag() and * doEndTag() on this tag instance.

*/ @Override public boolean getCreated() { // NOPMD return (this.created); } private Map getNamingContainerChildIds() { return (this.namingContainerChildIds); } @Override 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); } /** *

Return the facet name that we should be stored under, if any; * otherwise, return null (indicating that we will be a child component). *

* * @return the name of the facet */ 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 } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy