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

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

Go to download

This is the master POM file for Sun's Implementation of the JSF 1.2 Specification.

There is a newer version: 2.1
Show newest version
/*
 */

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

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

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


/**
 * 

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 request scope attribute under which a component tag stack * for the current request 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 requestScope */ 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 request 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; // --------------------------------------------- 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 */ 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 */ 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 */ public void setPageContext(PageContext pageContext) { this.pageContext = pageContext; } /** *

Return the Tag that is the parent of this instance.

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

Set the Tag that is the parent of this instance.

* * @param parent The new parent Tag */ 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, String componentId) throws JspException { UIComponent component = createComponent(context, componentId); UIComponentTagBase parentTag = getParentUIComponentClassicTagBase(pageContext); int indexOfNextChildTag = parentTag.getIndexOfNextChildTag(); if (indexOfNextChildTag > parent.getChildCount()) { indexOfNextChildTag = parent.getChildCount(); } parent.getChildren().add(indexOfNextChildTag, component); created = true; return (component); } /** *

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(UIComponent component, String componentId) { Iterator kids = component.getChildren().iterator(); while (kids.hasNext()) { UIComponent kid = kids.next(); if (componentId.equals(kid.getId())) { return (kid); } } 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. *
*/ protected UIComponent findComponent(FacesContext context) throws JspException { // Step 1 -- Have we already found the relevant component? if (component != null) { return (component); } // Step 2 -- Identify the component that is, or will be, our parent UIComponentClassicTagBase parentTag = getParentUIComponentClassicTagBase(pageContext); UIComponent parentComponent; if (parentTag != null) { parentComponent = parentTag.getComponentInstance(); } else { // Special case. The component to be found is the // UIViewRoot. // see if this is the first time this tag instance is trying // to be bound to the UIViewRoot parentComponent = context.getViewRoot(); // Has this UIViewRoot instance had a tag bound to it // before? if (null == parentComponent.getAttributes().get(CURRENT_VIEW_ROOT)) { // No it hasn't. // make sure setProperties() and setId() are called // once per UIViewRoot instance. try { setProperties(parentComponent); } catch (FacesException e) { if (e.getCause() instanceof JspException) { throw ((JspException)e.getCause()); } throw e; } if (null != this.id) { parentComponent.setId(this.id); } else { assert(null != getFacesJspId()); parentComponent.setId(getFacesJspId()); } parentComponent.getAttributes().put(CURRENT_VIEW_ROOT, CURRENT_VIEW_ROOT); created = true; } else if (hasBinding()) { try { setProperties(parentComponent); } catch (FacesException e) { if (e.getCause() instanceof JspException) { throw ((JspException)e.getCause()); } throw e; } } // this is not the first time this tag instance is trying to // be bound to this UIViewRoot, take no extra action. component = parentComponent; return (component); } // Step 3 -- Calculate the component identifier for this component String newId = createId(context); // Step 4 -- Create or return a facet with the specified name (if any) String facetName = getFacetName(); if (facetName != null) { component = parentComponent.getFacets().get(facetName); if (component == null) { component = createFacet(context, parentComponent, facetName, newId); } return (component); } // Step 5 -- Create or return a child with the specified id component = getChild(parentComponent, newId); if (component == null) { component = createChild(context, parentComponent, newId); } return (component); } // // Tag tree navigation // /** *

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

* * @param context PageContext for the current page */ public static UIComponentClassicTagBase getParentUIComponentClassicTagBase(PageContext context) { FacesContext facesContext = getFacesContext(context); List list = (List) facesContext.getExternalContext().getRequestMap() .get(COMPONENT_TAG_STACK_ATTR); if (list != null) { return ((UIComponentClassicTagBase) list.get(list.size() - 1)); } else { return (null); } } // // Methods related to the createdComponents and createdFacets lists. // protected int getIndexOfNextChildTag() { if (createdComponents != null) { return (createdComponents.size()); } else { return (0); } } protected void addChild(UIComponent child) { if (createdComponents == null) { createdComponents = new ArrayList(6); } createdComponents.add(child.getId()); } /* * Adds argument child to component tree as a child of this component. */ void addChildToComponentAndTag(UIComponent child) { UIComponent myComponent = this.getComponentInstance(); int indexOfNextChildTag = this.getIndexOfNextChildTag(); if (indexOfNextChildTag > myComponent.getChildCount()) { indexOfNextChildTag = myComponent.getChildCount(); } myComponent.getChildren().add(indexOfNextChildTag, child); this.addChild(child); } protected void addFacet(String name) { if (createdFacets == null) { //noinspection CollectionWithoutInitialCapacity createdFacets = new ArrayList(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() { Map requestMap = context.getExternalContext().getRequestMap(); List list = (List) requestMap.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) { requestMap.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() { Map requestMap = context.getExternalContext().getRequestMap(); List list = TypedCollections.dynamicallyCastList((List) requestMap.get(COMPONENT_TAG_STACK_ATTR), UIComponentClassicTagBase.class); if (list == null) { //noinspection CollectionWithoutInitialCapacity list = new ArrayList(); requestMap.put(COMPONENT_TAG_STACK_ATTR, list); } list.add(this); } /** *

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() { // Remove old children that are no longer present List oldList = TypedCollections.dynamicallyCastList( (List) component.getAttributes().get(JSP_CREATED_COMPONENT_IDS), String.class); if (oldList != null && !oldList.isEmpty()) { if (createdComponents != null) { // Components not in the new list need to be removed Iterator olds = oldList.iterator(); while (olds.hasNext()) { String old = olds.next(); if (!createdComponents.contains(old)) { UIComponent child = component.findComponent(old); // if a component is marked transient, it would have // been already removed from the child list, but the // oldList would still have it. In addition, the component // might have manually been removed. So, if findComponent // isn't successful, don't call remove child (it will NPE) if ( child != null) { component.getChildren().remove(child); } } } } else { // All old components need to be removed Iterator olds = oldList.iterator(); while (olds.hasNext()) { String old = olds.next(); UIComponent child = component.findComponent(old); if (child != null) { component.getChildren().remove(child); } } } } // Save the current list as a component attribute if (createdComponents != null) { component.getAttributes().put(JSP_CREATED_COMPONENT_IDS, createdComponents); } else { component.getAttributes().remove(JSP_CREATED_COMPONENT_IDS); } createdComponents = null; } /** *

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() { // Remove old facets that are no longer present List oldList = TypedCollections.dynamicallyCastList( (List) component.getAttributes().get(JSP_CREATED_FACET_NAMES), String.class); if (oldList != null) { if (createdFacets != null) { // Facets not in the new list need to be removed Iterator olds = oldList.iterator(); while (olds.hasNext()) { String old = olds.next(); if (!createdFacets.contains(old)) { component.getFacets().remove(old); } } } else { // All old facets need to be removed Iterator olds = oldList.iterator(); while (olds.hasNext()) { String old = olds.next(); component.getFacets().remove(old); } } } // Save the current list as a component attribute if (createdFacets != null) { component.getAttributes().put(JSP_CREATED_FACET_NAMES, createdFacets); } else { component.getAttributes().remove(JSP_CREATED_FACET_NAMES); } createdFacets = null; } // // Methods to support content interweaving // /** * *

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.

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

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

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

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

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 */ public int doEndTag() throws JspException { // Remove old children and facets as needed popUIComponentClassicTagBase(); removeOldChildren(); removeOldFacets(); // Render the children (if needed) and end of the component // associated with this tag try { UIComponent verbatim; UIComponentClassicTagBase parentTag = getParentUIComponentClassicTagBase(pageContext); if (null != (verbatim = this.createVerbatimComponentFromBodyContent())) { component.getChildren().add(verbatim); if (null != parentTag) { parentTag.addChild(verbatim); } } // else, we don't render rendersChildren==true // components here } catch (Throwable e) { throw new JspException(e); } finally { component = null; context = null; } // Return the appropriate control value created = false; this.release(); return (getDoEndValue()); } /** *

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

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

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.

*/ 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 */ public void setBodyContent(BodyContent bodyContent) { this.bodyContent = bodyContent; } /** *

Get the JspWriter from our BodyContent. *

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

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

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

*/ 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) { Map requestMap = getFacesContext().getExternalContext().getRequestMap(); Integer serialNum = (Integer) requestMap.get(componentId); if (null == serialNum) { serialNum = 1; } else { serialNum = serialNum.intValue() + 1; } requestMap.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.

*/ 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. */ public void setJspId(String id) { // reset JSP ID here instead of release as we may need // to check the ID after the tag has been used this.jspId = null; Map reqMap = getFacesContext().getExternalContext().getRequestMap(); AtomicInteger aInt = (AtomicInteger) reqMap.get(JAVAX_FACES_PAGECONTEXT_COUNTER); if (aInt == null) { aInt = new AtomicInteger(); reqMap.put(JAVAX_FACES_PAGECONTEXT_COUNTER, aInt); } Integer pcId = (Integer) pageContext.getAttribute(JAVAX_FACES_PAGECONTEXT_MARKER, PageContext.PAGE_SCOPE); if (pcId == null) { pcId = aInt.incrementAndGet(); pageContext.setAttribute(JAVAX_FACES_PAGECONTEXT_MARKER, pcId); } if (pcId.intValue() > 1) { StringBuilder builder = new StringBuilder(id.length() + 3); builder.append(id).append("pc").append(pcId); jspId = builder.toString(); } else { jspId = id; } facesJspId = null; updatePreviousJspIdAndIteratorStatus(jspId); } /** *

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; if (null == (previousJspIdSet = TypedCollections.dynamicallyCastSet((Set) pageContext.getAttribute(PREVIOUS_JSP_ID_SET, PageContext.PAGE_SCOPE), String.class))) { //noinspection CollectionWithoutInitialCapacity pageContext.setAttribute(PREVIOUS_JSP_ID_SET, previousJspIdSet = new HashSet(), PageContext.PAGE_SCOPE); } // detect the iterator case if (previousJspIdSet.contains(id)) { if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "Id " + id + " is nested within an iterating tag."); } isNestedInIterator = true; } else { isNestedInIterator = false; previousJspIdSet.add(id); } } public String getJspId() { return jspId; } // ------------------------------------------------------- Abstract methods /** *

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

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

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

*/ public boolean getCreated() { return (this.created); } 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). *

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