| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package javax.faces.webapp; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.Stack; |
| import java.util.concurrent.atomic.AtomicInteger; |
| import java.util.logging.Level; |
| |
| import javax.faces.component.NamingContainer; |
| import javax.faces.component.UIComponent; |
| import javax.faces.component.UIOutput; |
| import javax.faces.component.UIViewRoot; |
| import javax.faces.context.FacesContext; |
| import javax.faces.render.ResponseStateManager; |
| import javax.servlet.jsp.JspException; |
| import javax.servlet.jsp.JspWriter; |
| import javax.servlet.jsp.PageContext; |
| import javax.servlet.jsp.jstl.core.LoopTag; |
| import javax.servlet.jsp.tagext.BodyContent; |
| import javax.servlet.jsp.tagext.BodyTag; |
| import javax.servlet.jsp.tagext.JspIdConsumer; |
| import javax.servlet.jsp.tagext.Tag; |
| |
| /** |
| * @author Bruno Aranda (latest modification by $Author: baranda $) |
| * @author Manfred Geiler |
| * @author Dennis Byrne |
| * @version $Revision$ $Date$ |
| * |
| * @since 1.2 |
| */ |
| |
| public abstract class UIComponentClassicTagBase extends UIComponentTagBase |
| implements BodyTag, JspIdConsumer |
| { |
| |
| // do not change this w/out doing likewise in UIComponentTag |
| private static final String COMPONENT_STACK_ATTR = "org.apache.myfaces.COMPONENT_STACK"; |
| |
| private static final String REQUEST_FACES_CONTEXT = "org.apache.myfaces.REQUEST_FACES_CONTEXT"; |
| |
| private static final String VIEW_IDS = "org.apache.myfaces.VIEW_IDS"; |
| |
| private static final String FORMER_CHILD_IDS_SET_ATTR = "org.apache.myfaces.FORMER_CHILD_IDS"; |
| private static final String FORMER_FACET_NAMES_SET_ATTR = "org.apache.myfaces.FORMER_FACET_NAMES"; |
| |
| private static final String PREVIOUS_JSP_IDS_SET = "org.apache.myfaces.PREVIOUS_JSP_IDS_SET"; |
| |
| private static final String BOUND_VIEW_ROOT = "org.apache.myfaces.BOUND_VIEW_ROOT"; |
| |
| private static final String LOGICAL_PAGE_ID = "org.apache.myfaces.LOGICAL_PAGE_ID"; |
| |
| private static final String LOGICAL_PAGE_COUNTER = "org.apache.myfaces.LOGICAL_PAGE_COUNTER"; |
| |
| protected static final String UNIQUE_ID_PREFIX = UIViewRoot.UNIQUE_ID_PREFIX + "_"; |
| |
| protected PageContext pageContext = null; |
| protected BodyContent bodyContent = null; |
| |
| private boolean _created = false; |
| |
| private String _jspId = null; |
| private String _facesJspId = null; |
| |
| private List<String> _childrenAdded = null; |
| private List<String> _facetsAdded = null; |
| |
| private UIComponent _componentInstance = null; |
| private String _id = null; |
| |
| private boolean isInAnIterator; |
| |
| // the parent tag |
| private Tag _parent = null; |
| |
| // the enclosing "classic" parent tag |
| private UIComponentClassicTagBase _parentClassicTag = null; |
| |
| private FacesContext _facesContext = null; |
| |
| protected abstract void setProperties(UIComponent component); |
| |
| protected abstract UIComponent createComponent(FacesContext context, |
| String newId) throws JspException; |
| |
| public void release() |
| { |
| internalRelease(); |
| |
| //members, that must/need only be reset when there is no more risk, that the container |
| //wants to reuse this tag |
| pageContext = null; |
| _parent = null; |
| _jspId = null; |
| _id = null; |
| _facesJspId = null; |
| bodyContent = null; |
| } |
| |
| |
| /** |
| * Reset any members that apply to the according component instance and |
| * must not be reused if the container wants to reuse this tag instance. |
| * This method is called when rendering for this tag is finished |
| * ( doEndTag() ) or when released by the container. |
| */ |
| private void internalRelease() |
| { |
| _facesContext = null; |
| _componentInstance = null; |
| _created = false; |
| |
| _childrenAdded = null; |
| _facetsAdded = null; |
| } |
| |
| /** |
| * @see http://java.sun.com/javaee/5/docs/api/javax/faces/webapp/UIComponentClassicTagBase.html#getCreated() |
| */ |
| public boolean getCreated() |
| { |
| return _created; |
| } |
| |
| protected List<String> getCreatedComponents() { |
| return _childrenAdded; |
| } |
| |
| /** |
| * @see http://java.sun.com/javaee/5/docs/api/javax/faces/webapp/UIComponentClassicTagBase.html#getParentUIComponentClassicTagBase(javax.servlet.jsp.PageContext) |
| * @param pageContext |
| * @return |
| */ |
| public static UIComponentClassicTagBase getParentUIComponentClassicTagBase( |
| PageContext pageContext) |
| { |
| Stack<UIComponentClassicTagBase> stack = getStack(pageContext); |
| |
| int size = stack.size(); |
| |
| return size > 0 ? stack.get(size - 1) : null; |
| } |
| |
| /** |
| * @see http://java.sun.com/javaee/5/docs/api/javax/faces/webapp/UIComponentClassicTagBase.html#getFacesJspId() |
| * @return |
| */ |
| public String getJspId() |
| { |
| return _jspId; |
| } |
| |
| public void setJspId(String jspId) |
| { |
| // -= Leonardo Uribe =- The javadoc says the following about this method: |
| // |
| // 1. This method is called by the container before doStartTag(). |
| // 2. The argument is guaranteed to be unique within the page. |
| // |
| // Doing some tests it was found that the jspId generated in a |
| // jsp:include are "reset", so if before call it it was id10 |
| // the tags inside jsp:include starts from id1 (really I suppose a |
| // different counter is used), so if we assign this one |
| // directly it is possible to cause duplicate id exceptions later. |
| // |
| // One problem is caused by f:view tag. This one is not included when |
| // we check for duplicate id, so it is possible to assign to a component |
| // in a jsp:include the id of the UIViewRoot instance and cause a |
| // duplicate id exception when the view is saved. |
| // |
| // Checking the javadoc it was found the following note: |
| // |
| // "... 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..." |
| // |
| // So we need to keep a counter per logical page or page context found. |
| // It is assumed the first one should not be suffixed. The others needs to be |
| // suffixed, so all generated ids of those pages are different. The final result |
| // is that jsp:include works correctly. |
| // |
| // Note this implementation detail takes precedence over c:forEach tag. If a |
| // jsp:include is inside a c:forEach, jsp:include takes precedence and the |
| // iteration prefix is ignored. If a custom id is provided for a component, |
| // it will throw duplicate id exception, because this code is "override" |
| // by the custom id, and the iteration suffix only applies on generated ids. |
| Integer logicalPageId = (Integer) pageContext.getAttribute(LOGICAL_PAGE_ID); |
| |
| if (logicalPageId != null) |
| { |
| if (logicalPageId.intValue() == 1) |
| { |
| //Base case, just pass it unchanged |
| _jspId = jspId; |
| } |
| else |
| { |
| // We are on a different page context, suffix it with the logicalPageId |
| _jspId = jspId + "pc" + logicalPageId; |
| } |
| } |
| else |
| { |
| Map<String,Object> requestMap = getFacesContext().getExternalContext().getRequestMap(); |
| AtomicInteger logicalPageCounter = (AtomicInteger) requestMap.get(LOGICAL_PAGE_COUNTER); |
| |
| if (logicalPageCounter == null) |
| { |
| //We are processing the first component tag. |
| logicalPageCounter = new AtomicInteger(1); |
| logicalPageId = 1; |
| requestMap.put(LOGICAL_PAGE_COUNTER, logicalPageCounter); |
| pageContext.setAttribute(LOGICAL_PAGE_ID, logicalPageId); |
| } |
| else |
| { |
| //We are on a different page context, so we need to assign and set. |
| logicalPageId = logicalPageCounter.incrementAndGet(); |
| pageContext.setAttribute(LOGICAL_PAGE_ID, logicalPageId); |
| _jspId = jspId + "pc" + logicalPageId; |
| } |
| } |
| _facesJspId = null; |
| checkIfItIsInAnIterator(_jspId); |
| } |
| |
| |
| /** |
| * @param child |
| * @see http://java.sun.com/javaee/5/docs/api/javax/faces/webapp/UIComponentTagBase.html#addChild(javax.faces._componentInstance.UIComponent) |
| */ |
| |
| protected void addChild(UIComponent child) |
| { |
| if (_childrenAdded == null) |
| { |
| _childrenAdded = new ArrayList<String>(); |
| } |
| |
| _childrenAdded.add(child.getId()); |
| } |
| |
| /** |
| * @param name |
| * @see http://java.sun.com/javaee/5/docs/api/javax/faces/webapp/UIComponentTagBase.html#addFacet(java.lang.String) |
| */ |
| protected void addFacet(String name) |
| { |
| if (_facetsAdded == null) |
| { |
| _facetsAdded = new ArrayList<String>(); |
| } |
| |
| _facetsAdded.add(name); |
| } |
| |
| /** |
| * Return the UIComponent instance associated with this tag. |
| * @return a UIComponent, never null. |
| */ |
| public UIComponent getComponentInstance() |
| { |
| return _componentInstance; |
| } |
| |
| /** |
| * @return |
| * @see http://java.sun.com/javaee/5/docs/api/javax/faces/webapp/UIComponentTagBase.html#getFacesContext() |
| */ |
| |
| protected FacesContext getFacesContext() |
| { |
| if (_facesContext != null) |
| { |
| return _facesContext; |
| } |
| |
| _facesContext = pageContext == null ? null : (FacesContext) pageContext.getAttribute(REQUEST_FACES_CONTEXT); |
| |
| if (_facesContext != null) |
| { |
| return _facesContext; |
| } |
| |
| _facesContext = FacesContext.getCurrentInstance(); |
| |
| if (_facesContext != null) |
| { |
| if(pageContext != null) |
| { |
| pageContext.setAttribute(REQUEST_FACES_CONTEXT, _facesContext); |
| } |
| return _facesContext; |
| } |
| |
| // should never be reached |
| throw new RuntimeException("FacesContext not found"); |
| } |
| |
| /** |
| * @return |
| * @see http://java.sun.com/javaee/5/docs/api/javax/faces/webapp/UIComponentTagBase.html#getIndexOfNextChildTag() |
| */ |
| |
| protected int getIndexOfNextChildTag() |
| { |
| if (_childrenAdded == null) |
| { |
| return 0; |
| } |
| |
| return _childrenAdded.size(); |
| } |
| |
| /** |
| * @param id |
| * @see http://java.sun.com/javaee/5/docs/api/javax/faces/webapp/UIComponentTagBase.html#setId(java.lang.String) |
| */ |
| public void setId(String id) |
| { |
| if (id != null && id.startsWith(UIViewRoot.UNIQUE_ID_PREFIX)) |
| { |
| throw new IllegalArgumentException("Id is non-null and starts with UIViewRoot.UNIQUE_ID_PREFIX: "+id); |
| } |
| |
| _id = id; |
| } |
| |
| /** |
| * Return the id (if any) specified as an xml attribute on this tag. |
| */ |
| protected String getId() |
| { |
| return _id; |
| } |
| |
| protected String getFacesJspId() { |
| if (_facesJspId == null) |
| { |
| if (_jspId != null) |
| { |
| _facesJspId = UNIQUE_ID_PREFIX + _jspId; |
| |
| if (isIdDuplicated(_facesJspId)) { |
| _facesJspId = createNextId(_facesJspId); |
| } |
| } |
| else |
| { |
| _facesJspId = _facesContext.getViewRoot().createUniqueId(); |
| } |
| } |
| |
| return _facesJspId; |
| } |
| |
| public void setBodyContent(BodyContent bodyContent) |
| { |
| this.bodyContent = bodyContent; |
| } |
| |
| public void doInitBody() throws JspException |
| { |
| // nothing by default |
| } |
| |
| public int doAfterBody() throws JspException |
| { |
| UIComponentClassicTagBase parentTag = getParentUIComponentClassicTagBase(pageContext); |
| |
| if (isRootTag(parentTag) || isInRenderedChildrenComponent(parentTag)) |
| { |
| UIComponent verbatimComp = createVerbatimComponentFromBodyContent(); |
| |
| if (verbatimComp != null) |
| { |
| List<String> childrenAddedIds = (List<String>) |
| _componentInstance.getAttributes().get(FORMER_CHILD_IDS_SET_ATTR); |
| |
| if (childrenAddedIds == null) |
| { |
| _componentInstance.getChildren().add(verbatimComp); |
| } |
| else |
| { |
| int index = _componentInstance.getChildCount(); |
| if (childrenAddedIds.size() == index) |
| { |
| // verbatim already present, replace it |
| _componentInstance.getChildren().add(index-1, verbatimComp); |
| } |
| else |
| { |
| _componentInstance.getChildren().add(verbatimComp); |
| } |
| } |
| |
| // also tell the parent-tag about the new component instance |
| if(parentTag!=null) { |
| parentTag.addChild(verbatimComp); |
| } |
| } |
| } |
| |
| return getDoAfterBodyValue(); |
| } |
| |
| /** |
| * Standard method invoked by the JSP framework to inform this tag |
| * of the PageContext associated with the jsp page currently being |
| * processed. |
| */ |
| public void setPageContext(PageContext pageContext) |
| { |
| this.pageContext = pageContext; |
| } |
| |
| /** |
| * Returns the enclosing JSP tag object. Note that this is not |
| * necessarily a JSF tag. |
| */ |
| public Tag getParent() |
| { |
| return _parent; |
| } |
| |
| /** |
| * Standard method invoked by the JSP framework to inform this tag |
| * of the enclosing JSP tag object. |
| */ |
| public void setParent(Tag tag) |
| { |
| this._parent = tag; |
| } |
| |
| public BodyContent getBodyContent() |
| { |
| return bodyContent; |
| } |
| |
| public int doStartTag() throws JspException |
| { |
| this._facesContext = getFacesContext(); |
| |
| if (_facesContext == null) |
| { |
| throw new JspException("FacesContext not found"); |
| } |
| |
| _childrenAdded = null; |
| _facetsAdded = null; |
| |
| _parentClassicTag = getParentUIComponentClassicTagBase(pageContext); |
| |
| UIComponent verbatimComp = null; |
| |
| // create the verbatim component if not inside a facet (facets are rendered |
| // by their parents) and in a component that renders children |
| if (!isFacet()) |
| { |
| Tag parent = getParent(); |
| |
| // flush if in a loop tag and not in a jsp tag |
| if (parent != null && parent instanceof LoopTag) |
| { |
| JspWriter outWriter = pageContext.getOut(); |
| boolean insideJspTag = (outWriter instanceof BodyContent); |
| |
| if (!insideJspTag) |
| { |
| try |
| { |
| outWriter.flush(); |
| } |
| catch (IOException e) |
| { |
| throw new JspException("Exception flushing when creating verbatim _componentInstance", e); |
| } |
| } |
| } |
| |
| // create the transient _componentInstance |
| if (_parentClassicTag != null) |
| { |
| verbatimComp = _parentClassicTag.createVerbatimComponentFromBodyContent(); |
| } |
| } |
| |
| // find the _componentInstance for this tag |
| _componentInstance = findComponent(_facesContext); |
| |
| // add the verbatim component |
| if (verbatimComp != null && _parentClassicTag != null) |
| { |
| addVerbatimBeforeComponent(_parentClassicTag, verbatimComp, _componentInstance); |
| } |
| |
| Map<String,Object> viewComponentIds = getViewComponentIds(); |
| |
| // check that the instance returned by the client ID for the viewComponentIds |
| // is the same like this one, so we do not perform again the check for duplicated ids |
| Object tagInstance = null; |
| String clientId = null; |
| if (_id != null) { |
| clientId = _componentInstance.getClientId(_facesContext); |
| tagInstance = (viewComponentIds.get(clientId) == this) ? this : null; |
| } |
| |
| if (tagInstance == null) |
| { |
| // check for duplicated IDs |
| if (_id != null) |
| { |
| if (clientId != null) |
| { |
| if (viewComponentIds.containsKey(clientId)) |
| { |
| throw new JspException("Duplicated component Id: '"+clientId+"' " + |
| "for component: '"+getPathToComponent(_componentInstance)+"'."); |
| } |
| |
| viewComponentIds.put(clientId, this); |
| } |
| } |
| |
| // add to the component or facet to parent |
| if (_parentClassicTag != null) |
| { |
| if (isFacet()) |
| { |
| _parentClassicTag.addFacet(getFacetName()); |
| } |
| else |
| { |
| _parentClassicTag.addChild(_componentInstance); |
| } |
| } |
| } |
| |
| // push this tag on the stack |
| pushTag(); |
| |
| return getDoStartValue(); |
| } |
| |
| public int doEndTag() throws JspException |
| { |
| popTag(); |
| UIComponent component = getComponentInstance(); |
| |
| removeFormerChildren(component); |
| removeFormerFacets(component); |
| |
| try |
| { |
| UIComponentClassicTagBase parentTag = |
| getParentUIComponentClassicTagBase(pageContext); |
| |
| UIComponent verbatimComp = createVerbatimComponentFromBodyContent(); |
| |
| if (verbatimComp != null) |
| { |
| component.getChildren().add(verbatimComp); |
| |
| if (parentTag != null) |
| { |
| parentTag.addChild(verbatimComp); |
| } |
| } |
| } |
| catch (Throwable e) |
| { |
| throw new JspException(e); |
| } |
| finally |
| { |
| component = null; |
| } |
| |
| int retValue = getDoEndValue(); |
| |
| internalRelease(); |
| |
| return retValue; |
| } |
| |
| protected int getDoAfterBodyValue() throws javax.servlet.jsp.JspException |
| { |
| return SKIP_BODY; |
| } |
| |
| /** |
| * Get the value to be returned by the doStartTag method to the |
| * JSP framework. Subclasses which wish to use the inherited |
| * doStartTag but control whether the tag is permitted to contain |
| * nested tags or not can just override this method to return |
| * Tag.SOME_CONSTANT. |
| * |
| * @return BodyTag.EVAL_BODY_BUFFERED |
| */ |
| protected int getDoStartValue() |
| throws JspException |
| { |
| return BodyTag.EVAL_BODY_BUFFERED; |
| } |
| |
| /** |
| * Get the value to be returned by the doEndTag method to the |
| * JSP framework. Subclasses which wish to use the inherited |
| * doEndTag but control whether the tag is permitted to contain |
| * nested tags or not can just override this method to return |
| * Tag.SOME_CONSTANT. |
| * |
| * @return Tag.EVAL_PAGE |
| */ |
| protected int getDoEndValue() |
| throws JspException |
| { |
| return Tag.EVAL_PAGE; |
| } |
| |
| protected String getFacetName() |
| { |
| return isFacet() ? ((FacetTag)_parent).getName() : null; |
| } |
| |
| |
| /** |
| * Creates a UIComponent from the BodyContent |
| */ |
| protected UIComponent createVerbatimComponentFromBodyContent() |
| { |
| UIOutput verbatimComp = null; |
| |
| if (bodyContent != null) |
| { |
| String strContent = bodyContent.getString(); |
| |
| if (strContent != null) |
| { |
| String trimmedContent = strContent.trim(); |
| if (trimmedContent.length() > 0 && !isComment(strContent)) |
| { |
| verbatimComp = createVerbatimComponent(); |
| verbatimComp.setValue(strContent); |
| } |
| } |
| |
| bodyContent.clearBody(); |
| } |
| |
| return verbatimComp; |
| } |
| |
| private static boolean isComment(String bodyContent) |
| { |
| return (bodyContent.startsWith("<!--") && bodyContent.endsWith("-->")); |
| } |
| |
| /** |
| * <p>Creates a transient UIOutput using the Application, with the following characteristics:</p> |
| * <p/> |
| * <p><code>componentType</code> is |
| * <code>javax.faces.HtmlOutputText</code>.</p> |
| * <p/> |
| * <p><code>transient</code> is <code>true</code>.</p> |
| * <p/> |
| * <p><code>escape</code> is <code>false</code>.</p> |
| * <p/> |
| * <p><code>id</code> is |
| * <code>FacesContext.getViewRoot().createUniqueId()</code></p> |
| */ |
| protected UIOutput createVerbatimComponent() |
| { |
| UIOutput verbatimComp = (UIOutput) getFacesContext().getApplication() |
| .createComponent("javax.faces.HtmlOutputText"); |
| verbatimComp.setTransient(true); |
| verbatimComp.getAttributes().put("escape", Boolean.FALSE); |
| verbatimComp.setId(getFacesContext().getViewRoot().createUniqueId()); |
| |
| return verbatimComp; |
| } |
| |
| protected void addVerbatimBeforeComponent( |
| UIComponentClassicTagBase parentTag, |
| UIComponent verbatimComp, |
| UIComponent component) |
| { |
| UIComponent parent = component.getParent(); |
| |
| if (parent == null) |
| { |
| return; |
| } |
| |
| List<UIComponent> children = parent.getChildren(); |
| // EDGE CASE: |
| // Consider CASE 1 or 2 where the _componentInstance is provided via a |
| // _componentInstance binding in session or application scope. |
| // The automatically created UIOuput instances for the template text |
| // will already be present. Check the JSP_CREATED_COMPONENT_IDS attribute, |
| // if present and the number of created components is the same |
| // as the number of children replace at a -1 offset from the current |
| // value of indexOfComponentInParent, otherwise, call add() |
| |
| List<String> childrenAddedIds = (List<String>) |
| parent.getAttributes().get(FORMER_CHILD_IDS_SET_ATTR); |
| |
| |
| int parentIndex = children.indexOf(component); |
| |
| if (childrenAddedIds != null) |
| { |
| if (parentIndex > 0 && childrenAddedIds.size() == parentIndex) |
| { |
| UIComponent formerVerbatim = children.get(parentIndex - 1); |
| |
| if (formerVerbatim instanceof UIOutput && formerVerbatim.isTransient()) |
| { |
| children.set(parentIndex - 1, verbatimComp); |
| } |
| } |
| } |
| |
| children.add(parentIndex, verbatimComp); |
| |
| parentTag.addChild(verbatimComp); |
| } |
| |
| /** |
| * <p>Add <i>verbatim</i> as a sibling of <i>_componentInstance</i> in |
| * <i>_componentInstance</i> in the parent's child list. <i>verbatim</i> is |
| * added to the list at the position immediatly following |
| * <i>_componentInstance</i>.</p> |
| */ |
| |
| protected void addVerbatimAfterComponent(UIComponentClassicTagBase parentTag, |
| UIComponent verbatim, |
| UIComponent component) |
| { |
| int indexOfComponentInParent = 0; |
| UIComponent parent = component.getParent(); |
| |
| // invert the order of this if and the assignment below. Since this line is |
| // here, it appears an early return is acceptable/desired if parent is null, |
| // and, if it is null, we should probably check for that before we try to |
| // access it. 2006-03-15 jdl |
| if (null == parent) |
| { |
| return; |
| } |
| List<UIComponent> children = parent.getChildren(); |
| indexOfComponentInParent = children.indexOf(component); |
| if (children.size() - 1 == indexOfComponentInParent) |
| { |
| children.add(verbatim); |
| } |
| else |
| { |
| children.add(indexOfComponentInParent + 1, verbatim); |
| } |
| parentTag.addChild(verbatim); |
| } |
| |
| /** |
| * @deprecated the ResponseWriter is now set by {@link |
| * javax.faces.application.ViewHandler#renderView} |
| */ |
| protected void setupResponseWriter() { |
| } |
| |
| /** |
| * Invoke encodeBegin on the associated UIComponent. Subclasses can |
| * override this method to perform custom processing before or after |
| * the UIComponent method invocation. |
| */ |
| protected void encodeBegin() |
| throws IOException |
| { |
| if (log.isLoggable(Level.FINE)) |
| log.fine("Entered encodeBegin for client-Id: " + _componentInstance.getClientId(getFacesContext())); |
| _componentInstance.encodeBegin(getFacesContext()); |
| if (log.isLoggable(Level.FINE)) |
| log.fine("Exited encodeBegin"); |
| } |
| |
| /** |
| * Invoke encodeChildren on the associated UIComponent. Subclasses can |
| * override this method to perform custom processing before or after |
| * the UIComponent method invocation. This is only invoked for components |
| * whose getRendersChildren method returns true. |
| */ |
| protected void encodeChildren() |
| throws IOException |
| { |
| if (log.isLoggable(Level.FINE)) |
| log.fine("Entered encodeChildren for client-Id: " + _componentInstance.getClientId(getFacesContext())); |
| _componentInstance.encodeChildren(getFacesContext()); |
| if (log.isLoggable(Level.FINE)) |
| log.fine("Exited encodeChildren for client-Id: " + _componentInstance.getClientId(getFacesContext())); |
| } |
| |
| /** |
| * Invoke encodeEnd on the associated UIComponent. Subclasses can override this |
| * method to perform custom processing before or after the UIComponent method |
| * invocation. |
| */ |
| protected void encodeEnd() |
| throws IOException |
| { |
| if (log.isLoggable(Level.FINE)) |
| log.fine("Entered encodeEnd for client-Id: " + _componentInstance.getClientId(getFacesContext())); |
| _componentInstance.encodeEnd(getFacesContext()); |
| if (log.isLoggable(Level.FINE)) |
| log.fine("Exited encodeEnd for client-Id: " + _componentInstance.getClientId(getFacesContext())); |
| |
| } |
| |
| |
| |
| private boolean isRootTag(UIComponentClassicTagBase parentTag) |
| { |
| return (parentTag == this); |
| } |
| |
| private boolean isInRenderedChildrenComponent (UIComponentClassicTagBase tag) |
| { |
| return (_parentClassicTag != null && tag.getComponentInstance().getRendersChildren()); |
| } |
| |
| private boolean isFacet() |
| { |
| return _parent != null && _parent instanceof FacetTag; |
| } |
| |
| /** Map of <ID,Tag> in the view */ |
| @SuppressWarnings("unchecked") |
| private Map<String, Object> getViewComponentIds() |
| { |
| Map<String, Object> requestMap = _facesContext.getExternalContext().getRequestMap(); |
| Map<String, Object> viewComponentIds; |
| |
| if (_parent == null) |
| { |
| // top level _componentInstance |
| viewComponentIds = new HashMap<String, Object>(); |
| requestMap.put(VIEW_IDS, viewComponentIds); |
| } |
| else |
| { |
| viewComponentIds = (Map<String, Object>)requestMap.get(VIEW_IDS); |
| |
| // Check if null, this can happen if someone programatically tries to do an include of a |
| // JSP fragment. This code will prevent NullPointerException from happening in such cases. |
| if (viewComponentIds == null) |
| { |
| viewComponentIds = new HashMap<String, Object>(); |
| requestMap.put(VIEW_IDS, viewComponentIds); |
| } |
| } |
| |
| return viewComponentIds; |
| } |
| |
| |
| private static final Stack<UIComponentClassicTagBase> getStack(PageContext pageContext) |
| { |
| Stack<UIComponentClassicTagBase> stack = (Stack<UIComponentClassicTagBase>) pageContext.getAttribute(COMPONENT_STACK_ATTR, |
| PageContext.REQUEST_SCOPE); |
| |
| if (stack == null) |
| { |
| stack = new Stack<UIComponentClassicTagBase>(); |
| pageContext.setAttribute(COMPONENT_STACK_ATTR, |
| stack, PageContext.REQUEST_SCOPE); |
| } |
| |
| return stack; |
| } |
| |
| /** |
| * The pageContext's request scope map is used to hold a stack of |
| * JSP tag objects seen so far, so that a new tag can find the |
| * parent tag that encloses it. Access to the parent tag is used |
| * to find the parent UIComponent for the component associated |
| * with this tag plus some other uses. |
| */ |
| private void popTag() |
| { |
| Stack<UIComponentClassicTagBase> stack = getStack(pageContext); |
| |
| int size = stack.size(); |
| stack.remove(size -1); |
| if (size <= 1) |
| pageContext.removeAttribute(COMPONENT_STACK_ATTR, |
| PageContext.REQUEST_SCOPE); |
| |
| } |
| |
| private void pushTag() |
| { |
| getStack(pageContext).add(this); |
| } |
| |
| //private boolean isIncludedOrForwarded() { |
| // return getFacesContext().getExternalContext().getRequestMap(). |
| // containsKey("javax.servlet.include.request_uri"); |
| //} |
| |
| /** Generate diagnostic output. */ |
| private String getPathToComponent(UIComponent component) |
| { |
| StringBuffer buf = new StringBuffer(); |
| |
| if(component == null) |
| { |
| buf.append("{Component-Path : "); |
| buf.append("[null]}"); |
| return buf.toString(); |
| } |
| |
| getPathToComponent(component,buf); |
| |
| buf.insert(0,"{Component-Path : "); |
| buf.append("}"); |
| |
| return buf.toString(); |
| } |
| |
| /** Generate diagnostic output. */ |
| private static void getPathToComponent(UIComponent component, StringBuffer buf) |
| { |
| if(component == null) |
| return; |
| |
| StringBuffer intBuf = new StringBuffer(); |
| |
| intBuf.append("[Class: "); |
| intBuf.append(component.getClass().getName()); |
| if(component instanceof UIViewRoot) |
| { |
| intBuf.append(",ViewId: "); |
| intBuf.append(((UIViewRoot) component).getViewId()); |
| } |
| else |
| { |
| intBuf.append(",Id: "); |
| intBuf.append(component.getId()); |
| } |
| intBuf.append("]"); |
| |
| buf.insert(0,intBuf); |
| |
| getPathToComponent(component.getParent(),buf); |
| } |
| |
| /** |
| * Remove any child components of the associated components which do not |
| * have corresponding tags as children of this tag. This only happens |
| * when a view is being re-rendered and there are components in the view |
| * tree which don't have corresponding JSP tags. Wrapping JSF tags in |
| * JSTL "c:if" statements is one way this can happen. |
| * <br /> |
| * Attention: programmatically added components are are not affected by this: |
| * they will not be on the old list of created components nor on the new list |
| * of created components, so nothing will happen to them. |
| */ |
| private void removeFormerChildren(UIComponent component) |
| { |
| List<String> formerChildIds = (List<String>)component.getAttributes().get(FORMER_CHILD_IDS_SET_ATTR); |
| if (formerChildIds != null) |
| { |
| for (String childId : formerChildIds) |
| { |
| if (_childrenAdded == null || !_childrenAdded.contains(childId)) |
| { |
| UIComponent childToRemove = component.findComponent(childId); |
| if (childToRemove != null) |
| { |
| component.getChildren().remove(childToRemove); |
| } |
| } |
| } |
| if (_childrenAdded == null) |
| { |
| component.getAttributes().remove(FORMER_CHILD_IDS_SET_ATTR); |
| } |
| else |
| { |
| component.getAttributes().put(FORMER_CHILD_IDS_SET_ATTR, _childrenAdded); |
| } |
| } |
| else |
| { |
| if (_childrenAdded != null) |
| { |
| component.getAttributes().put(FORMER_CHILD_IDS_SET_ATTR, _childrenAdded); |
| } |
| } |
| } |
| |
| /** See removeFormerChildren. */ |
| private void removeFormerFacets(UIComponent component) |
| { |
| List<String> formerFacetNames = (List<String>)component.getAttributes().get(FORMER_FACET_NAMES_SET_ATTR); |
| if (formerFacetNames != null) |
| { |
| for (String facetName : formerFacetNames) |
| { |
| if (_facetsAdded == null || !_facetsAdded.contains(facetName)) { |
| component.getFacets().remove(facetName); |
| } |
| } |
| if (_facetsAdded == null) |
| { |
| component.getAttributes().remove(FORMER_FACET_NAMES_SET_ATTR); |
| } |
| else |
| { |
| component.getAttributes().put(FORMER_FACET_NAMES_SET_ATTR, _facetsAdded); |
| } |
| } |
| else |
| { |
| if (_facetsAdded != null) |
| { |
| component.getAttributes().put(FORMER_FACET_NAMES_SET_ATTR, _facetsAdded); |
| } |
| } |
| } |
| |
| /** |
| * Return the corresponding UIComponent for this tag, creating it |
| * if necessary. |
| * <p> |
| * If this is not the first time this method has been called, then |
| * return the cached _componentInstance instance found last time. |
| * <p> |
| * If this is not the first time this view has been seen, then |
| * locate the existing _componentInstance using the id attribute assigned |
| * to this tag and return it. Note that this is simple for |
| * components with user-assigned ids. For components with |
| * generated ids, the "reattachment" relies on the fact that |
| * UIViewRoot will generate the same id values for tags in |
| * this page as it did when first generating the view. For this |
| * reason all JSF tags within a JSTL "c:if" are required to have |
| * explicitly-assigned ids. |
| * <p> |
| * Otherwise create the _componentInstance, populate its properties from |
| * the xml attributes on this JSP tag and attach it to its parent. |
| * <p> |
| * When a _componentInstance is found or created the parent JSP tag is also |
| * told that the _componentInstance has been "seen". When the parent tag |
| * ends it will delete any components which were in the view |
| * previously but have not been seen this time; see doEndTag for |
| * more details. |
| */ |
| protected UIComponent findComponent(FacesContext context) |
| throws JspException |
| { |
| // 1. If we have previously located this component, return it. |
| if (_componentInstance != null) |
| { |
| return _componentInstance; |
| } |
| |
| // 2. Locate the parent component by looking for a parent UIComponentTag instance, |
| // and ask it for its component. If there is no parent UIComponentTag instance, |
| // this tag represents the root component, so get it from the current Tree and return it. |
| UIComponentClassicTagBase parentTag = getParentUIComponentClassicTagBase(pageContext); |
| |
| if (parentTag == null) |
| { |
| // This is the root |
| _componentInstance = context.getViewRoot(); |
| |
| // check if the view root is already bound to the tag |
| Object alreadyBoundViewRootFlag = _componentInstance.getAttributes().get(BOUND_VIEW_ROOT); |
| |
| if (alreadyBoundViewRootFlag == null) |
| { |
| try |
| { |
| setProperties(_componentInstance); |
| } |
| catch (Throwable e) |
| { |
| throw new JspException(e); |
| } |
| |
| if (_id != null) |
| { |
| _componentInstance.setId(_id); |
| } |
| else |
| { |
| _componentInstance.setId(getFacesJspId()); |
| } |
| _componentInstance.getAttributes().put(BOUND_VIEW_ROOT, true); |
| _created = true; |
| |
| } |
| else if (hasBinding()) |
| { |
| setProperties(_componentInstance); |
| } |
| |
| return _componentInstance; |
| } |
| |
| UIComponent parent = parentTag.getComponentInstance(); |
| |
| if (parent == null) |
| { |
| throw new IllegalStateException("parent is null?"); |
| } |
| |
| String facetName = getFacetName(); |
| if (facetName != null) |
| { |
| //Facet |
| String id = createUniqueId(context, parent); |
| _componentInstance = parent.getFacet(facetName); |
| if (_componentInstance == null) |
| { |
| _componentInstance = createComponent(context, id); |
| _created = true; |
| parent.getFacets().put(facetName, _componentInstance); |
| } |
| else |
| { |
| if (checkFacetNameOnParentExists(parentTag, facetName)) |
| { |
| throw new IllegalStateException("facet '" + facetName + "' already has a child associated. current associated _componentInstance id: " |
| + _componentInstance.getClientId(context) + " class: " + _componentInstance.getClass().getName()); |
| } |
| } |
| |
| addFacetNameToParentTag(parentTag, facetName); |
| return _componentInstance; |
| } |
| |
| //Child |
| // |
| // Note that setProperties is called only when we create the |
| // _componentInstance; on later passes, the attributes defined on the |
| // JSP tag are set on this Tag object, but then completely |
| // ignored. |
| |
| String id = createUniqueId(context, parent); |
| |
| // Warn users that this tag is about to find/steal the UIComponent |
| // that has already been created for a sibling tag with the same id value . |
| // _childrenAdded is a Set, and we will stomp over a past id when calling |
| // addChildIdToParentTag. |
| // |
| // It would also be reasonable to throw an exception here rather than |
| // just issue a warning as this is a pretty serious problem. However the |
| // Sun RI just issues a warning... |
| if(parentTag._childrenAdded != null && parentTag._childrenAdded.contains(id)) |
| { |
| if(log.isLoggable(Level.WARNING)) |
| log.warning("There is more than one JSF tag with an id : " + id); |
| } |
| |
| _componentInstance = findComponent(parent,id); |
| if (_componentInstance == null) |
| { |
| _componentInstance = createComponent(context, id); |
| if (id.equals(_componentInstance.getId()) ) |
| { |
| _created = true; |
| int index = parentTag.getIndexOfNextChildTag(); |
| if (index > parent.getChildCount()) |
| { |
| index = parent.getChildCount(); |
| } |
| |
| List<UIComponent> children = parent.getChildren(); |
| children.add(index, _componentInstance); |
| } |
| // On weblogic portal using faces-adapter, the id set and the retrieved |
| // one for <netuix:namingContainer> is different. The reason is |
| // this custom solution for integrate jsf changes the id of the parent |
| // component to allow the same native portlet to be allocated multiple |
| // times in the same page |
| else if (null == findComponent(parent,_componentInstance.getId())) |
| { |
| _created = true; |
| int index = parentTag.getIndexOfNextChildTag(); |
| if (index > parent.getChildCount()) |
| { |
| index = parent.getChildCount(); |
| } |
| |
| List<UIComponent> children = parent.getChildren(); |
| children.add(index, _componentInstance); |
| } |
| } |
| |
| return _componentInstance; |
| |
| } |
| |
| private UIComponent findComponent(UIComponent parent, String id) |
| { |
| List li = parent.getChildren(); |
| |
| for (int i = 0; i < li.size(); i++) |
| { |
| UIComponent uiComponent = (UIComponent) li.get(i); |
| if(uiComponent.getId()!=null && uiComponent.getId().equals(id)) |
| { |
| return uiComponent; |
| } |
| } |
| |
| return null; |
| } |
| |
| |
| private String createUniqueId(FacesContext context, UIComponent parent) |
| throws JspException |
| { |
| String id = getId(); |
| if (id == null) |
| { |
| id = getFacesJspId(); |
| } |
| else if (isIdDuplicated(id)) |
| { |
| if (isInAnIterator) |
| { |
| setId(createNextId(id)); |
| id = getId(); |
| } |
| else |
| { |
| if (parent != null) |
| { |
| |
| UIComponent namingContainer; |
| |
| if (parent instanceof NamingContainer) |
| { |
| namingContainer = parent; |
| } |
| else |
| { |
| namingContainer = parent.getParent(); |
| } |
| |
| if (namingContainer != null) |
| { |
| UIComponent component = namingContainer.findComponent(id); |
| |
| if (component == null || isPostBack(context)) |
| { |
| return id; |
| } |
| } |
| } |
| |
| throw new JspException("Duplicated Id found in the view: " + id); |
| } |
| } |
| |
| return id; |
| } |
| |
| private String createNextId(String componentId) |
| { |
| Integer currentCounter = (Integer) getFacesContext().getExternalContext().getRequestMap().get(componentId); |
| |
| int iCurrentCounter = 1; |
| |
| if (currentCounter != null) |
| { |
| iCurrentCounter = currentCounter.intValue(); |
| iCurrentCounter++; |
| } |
| |
| getFacesContext().getExternalContext().getRequestMap().put(componentId, new Integer(iCurrentCounter)); |
| |
| //if (isIncludedOrForwarded()) |
| //{ |
| // componentId = componentId + "pc" + iCurrentCounter; |
| //} |
| //else |
| //{ |
| componentId = componentId + UNIQUE_ID_PREFIX + iCurrentCounter; |
| //} |
| |
| return componentId; |
| } |
| |
| private void checkIfItIsInAnIterator(String jspId) |
| { |
| Set<String> previousJspIdsSet = getPreviousJspIdsSet(); |
| |
| if (previousJspIdsSet.contains(jspId)) |
| { |
| isInAnIterator = true; |
| } |
| else |
| { |
| previousJspIdsSet.add(jspId); |
| isInAnIterator = false; |
| } |
| } |
| |
| private Set<String> getPreviousJspIdsSet() |
| { |
| Set<String> previousJspIdsSet = (Set<String>) getFacesContext().getExternalContext() |
| .getRequestMap().get(PREVIOUS_JSP_IDS_SET); |
| |
| if (previousJspIdsSet == null) |
| { |
| previousJspIdsSet = new HashSet<String>(); |
| //Add it to the context! The next time is called |
| //this method it takes the ref from the RequestContext |
| getFacesContext().getExternalContext().getRequestMap() |
| .put(PREVIOUS_JSP_IDS_SET, previousJspIdsSet); |
| } |
| |
| return previousJspIdsSet; |
| } |
| |
| private boolean isIdDuplicated(String componentId) |
| { |
| boolean result = false; |
| if (_parentClassicTag != null) |
| { |
| if (_parentClassicTag.isInAnIterator) |
| { |
| return true; |
| } |
| List childComponents = _parentClassicTag.getCreatedComponents(); |
| |
| if (childComponents != null) |
| { |
| result = childComponents.contains(componentId); |
| if (result && (!isInAnIterator)) |
| { |
| return true; |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| private boolean isPostBack(FacesContext facesContext) |
| { |
| return facesContext.getExternalContext().getRequestParameterMap(). |
| containsKey(ResponseStateManager.VIEW_STATE_PARAM); |
| } |
| |
| |
| /** |
| * check if the facet is already added to the parent |
| */ |
| private boolean checkFacetNameOnParentExists(UIComponentClassicTagBase parentTag, String facetName) |
| { |
| return parentTag._facetsAdded != null && parentTag._facetsAdded.contains(facetName); |
| } |
| |
| /** |
| * Notify the enclosing JSP tag of the id of this facet's id. The parent |
| * tag will later delete any existing view facets that were not seen |
| * during this rendering phase; see doEndTag for details. |
| */ |
| private void addFacetNameToParentTag(UIComponentClassicTagBase parentTag, String facetName) |
| { |
| if (parentTag._facetsAdded == null) |
| { |
| parentTag._facetsAdded = new ArrayList<String>(); |
| } |
| parentTag._facetsAdded.add(facetName); |
| } |
| |
| protected abstract boolean hasBinding(); |
| |
| public JspWriter getPreviousOut() { |
| return bodyContent.getEnclosingWriter(); |
| } |
| } |