blob: bc86c13e31db5ca3d89309201370e1af27970294 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package javax.faces.webapp;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.application.Application;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.component.UIOutput;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.context.ExternalContext;
import javax.faces.el.ValueBinding;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.Tag;
import javax.servlet.jsp.tagext.BodyContent;
import java.io.IOException;
import java.io.Reader;
import java.util.*;
/**
* Base class for all JSP tags that represent a JSF UIComponent.
* <p>
* <i>Disclaimer</i>: The official definition for the behaviour of
* this class is the JSF specification but for legal reasons the
* specification cannot be replicated here. Any javadoc present on this
* class therefore describes the current implementation rather than the
* officially required behaviour, though it is believed that this class
* does comply with the specification.
*
* see Javadoc of <a href="http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/index.html">JSF Specification</a> for more.
*
* @author Manfred Geiler (latest modification by $Author$)
* @version $Revision$ $Date$
*/
public abstract class UIComponentTag
implements Tag
{
private static final String FORMER_CHILD_IDS_SET_ATTR = UIComponentTag.class.getName() + ".FORMER_CHILD_IDS";
private static final String FORMER_FACET_NAMES_SET_ATTR = UIComponentTag.class.getName() + ".FORMER_FACET_NAMES";
private static final String COMPONENT_STACK_ATTR = UIComponentTag.class.getName() + ".COMPONENT_STACK";
private static final String UNIQUE_ID_COUNTER_ATTR = UIComponentTag.class.getName() + ".UNIQUE_ID_COUNTER";
private static final String PARTIAL_STATE_SAVING_METHOD_PARAM_NAME = "javax.faces.PARTIAL_STATE_SAVING_METHOD";
private static final String PARTIAL_STATE_SAVING_METHOD_ON = "true";
private static final String PARTIAL_STATE_SAVING_METHOD_OFF = "false";
private static final String BEFORE_VIEW_CONTEXT = "org.apache.myfaces.BEFORE_VIEW_CONTEXT";
protected PageContext pageContext = null;
private Tag _parent = null;
//tag attributes
private String _binding = null;
private String _id = null;
private String _rendered = null;
private FacesContext _facesContext = null;
private UIComponent _componentInstance = null;
private boolean _created = false;
private Boolean _suppressed = null;
private ResponseWriter _writer = null;
private Set _childrenAdded = null;
private Set _facetsAdded = null;
private Boolean _partialStateSaving = null;
private static Log log = LogFactory.getLog(UIComponentTag.class);
private static final int READ_LENGTH = 8000;
private boolean isPartialStateSavingOn(javax.faces.context.FacesContext context)
{
if(context == null) throw new NullPointerException("context");
if (_partialStateSaving != null) return _partialStateSaving.booleanValue();
String stateSavingMethod = context.getExternalContext().getInitParameter(PARTIAL_STATE_SAVING_METHOD_PARAM_NAME);
if (stateSavingMethod == null)
{
_partialStateSaving = Boolean.FALSE; //Specs 10.1.3: default server saving
context.getExternalContext().log("No context init parameter '"+PARTIAL_STATE_SAVING_METHOD_PARAM_NAME+"' found; no partial state saving method defined, assuming default partial state saving method off.");
}
else if (stateSavingMethod.equals(PARTIAL_STATE_SAVING_METHOD_ON))
{
_partialStateSaving = Boolean.TRUE;
}
else if (stateSavingMethod.equals(PARTIAL_STATE_SAVING_METHOD_OFF))
{
_partialStateSaving = Boolean.FALSE;
}
else
{
_partialStateSaving = Boolean.FALSE; //Specs 10.1.3: default server saving
context.getExternalContext().log("Illegal context init parameter '"+PARTIAL_STATE_SAVING_METHOD_PARAM_NAME+"' found; illegal partial state saving method '" + stateSavingMethod + "', default partial state saving will be used (partial state saving off).");
}
return _partialStateSaving.booleanValue();
}
public UIComponentTag()
{
}
public void release()
{
internalRelease();
//members, that must/need only be reset when there is no more risk, that the container
//wants to reuse this tag
pageContext = null;
_parent = null;
// Reset tag attribute members. These are reset here rather than in
// internalRelease because of some Resin-related issue. See commit
// r166747.
_binding = null;
_id = null;
_rendered = null;
}
/**
* Reset any members that apply to the according component instance and
* must not be reused if the container wants to reuse this tag instance.
* This method is called when rendering for this tag is finished
* ( doEndTag() ) or when released by the container.
*/
private void internalRelease()
{
_facesContext = null;
_componentInstance = null;
_created = false;
_suppressed = null;
_writer = null;
_childrenAdded = null;
_facetsAdded = null;
}
/** Setter for common JSF xml attribute "binding". */
public void setBinding(String binding)
throws JspException
{
if (!isValueReference(binding))
{
throw new IllegalArgumentException("not a valid binding: " + binding);
}
_binding = binding;
}
/** Setter for common JSF xml attribute "id". */
public void setId(String id)
{
_id = id;
}
/**
* Return the id (if any) specified as an xml attribute on this tag.
*/
protected String getId()
{
return _id;
}
/** Setter for common JSF xml attribute "rendered". */
public void setRendered(String rendered)
{
_rendered = rendered;
}
/**
* Specify the "component type name" used together with the component's
* family and the Application object to create a UIComponent instance for
* this tag. This method is called by other methods in this class, and is
* intended to be overridden in subclasses to specify the actual component
* type to be created.
*
* @return a registered component type name, never null.
*/
public abstract String getComponentType();
/**
* Return the UIComponent instance associated with this tag.
* @return a UIComponent, never null.
*/
public UIComponent getComponentInstance()
{
return _componentInstance;
}
/**
* Return true if this tag created the associated UIComponent (rather
* than locating an existing instance of the UIComponent in the view).
*/
public boolean getCreated()
{
return _created;
}
/**
* Return the nearest JSF tag that encloses this tag.
*/
public static UIComponentTag getParentUIComponentTag(PageContext pageContext)
{
// Question: why not just walk up the _parent chain testing for
// instanceof UIComponentTag rather than maintaining a separate
// stack with the pushTag and popTag methods?
List list = (List)pageContext.getAttribute(COMPONENT_STACK_ATTR,
PageContext.REQUEST_SCOPE);
if (list != null)
{
return (UIComponentTag)list.get(list.size() - 1);
}
return null;
}
/** See documentation for pushTag. */
private void popTag()
{
List list = (List)pageContext.getAttribute(COMPONENT_STACK_ATTR,
PageContext.REQUEST_SCOPE);
if (list != null)
{
int size = list.size();
list.remove(size -1);
if (size <= 1)
{
pageContext.removeAttribute(COMPONENT_STACK_ATTR,
PageContext.REQUEST_SCOPE);
}
}
}
/**
* Push this tag onto the stack of JSP tags seen.
* <p>
* The pageContext's request scope map is used to hold a stack of
* JSP tag objects seen so far, so that a new tag can find the
* parent tag that encloses it. Access to the parent tag is used
* to find the parent UIComponent for the component associated
* with this tag plus some other uses.
*/
private void pushTag()
{
List list = (List)pageContext.getAttribute(COMPONENT_STACK_ATTR,
PageContext.REQUEST_SCOPE);
if (list == null)
{
list = new ArrayList();
pageContext.setAttribute(COMPONENT_STACK_ATTR,
list,
PageContext.REQUEST_SCOPE);
}
list.add(this);
}
/**
* Specify the "renderer type name" used together with the current
* renderKit to get a Renderer instance for the corresponding UIComponent.
* <p>
* A JSP tag can return null here to use the default renderer type string.
* If non-null is returned, then the UIComponent's setRendererType method
* will be called passing this value, and this will later affect the
* type of renderer object returned by UIComponent.getRenderer().
*/
public abstract String getRendererType();
/**
* Return true if the specified string contains an EL expression.
* <p>
* UIComponent properties are often required to be value-binding
* expressions; this method allows code to check whether that is
* the case or not.
*/
public static boolean isValueReference(String value)
{
if (value == null) throw new NullPointerException("value");
int start = value.indexOf("#{");
if (start < 0) return false;
int end = value.lastIndexOf('}');
return (end >=0 && start < end);
}
/**
* Standard method invoked by the JSP framework to inform this tag
* of the PageContext associated with the jsp page currently being
* processed.
*/
public void setPageContext(PageContext pageContext)
{
this.pageContext = pageContext;
}
/**
* Returns the enclosing JSP tag object. Note that this is not
* necessarily a JSF tag.
*/
public Tag getParent()
{
return _parent;
}
/**
* Standard method invoked by the JSP framework to inform this tag
* of the enclosing JSP tag object.
*/
public void setParent(Tag parent)
{
_parent = parent;
}
/**
* Flushes the Writer and adds the content of the Writer ( which is only the direct output
* of the JSP if the component uses a dummyWriter wich does not render the Component itself) as
* UIOutput component after the current component to the myfaces component tree.
* @param writerToFlush
* @throws JspException
* @throws IOException
*/
private void flushWriter( JspWriter writerToFlush ) throws JspException,IOException {
if (getFacesContext() == null)
return;
BodyContent tempwriter;
_componentInstance = findComponent(_facesContext);
if (writerToFlush instanceof BodyContent)
{
int count = 0;
char [] readChars = new char[READ_LENGTH];
tempwriter = (BodyContent) writerToFlush;
Reader read =tempwriter.getReader();
count = read.read(readChars,0,READ_LENGTH);
String readString = new String(readChars,0,count);
if(!readString.trim().equals(""))
{
//_componentInstance = findComponent(_facesContext);
UIComponentTag parentTag = getParentUIComponentTag(pageContext);
addOutputComponentAfterComponent(parentTag,createUIOutputComponentFromString(readString),_componentInstance);
tempwriter.clearBody();
}
}
else
{
if (_componentInstance.getParent() == null) {
writerToFlush.flush();
String beforeViewContent = _facesContext.getExternalContext().getResponse().toString();
if((beforeViewContent != null)&&(!beforeViewContent.trim().equals("")))
{ // BEFORE BODY CONTENT
_facesContext.getExternalContext().getRequestMap().put(BEFORE_VIEW_CONTEXT,beforeViewContent);
}
}
}
}
/**
* Adds an Output Component afert a given component.
* @param parentTag the parent tag of the component.
* @param outputComponent the component which should be added after this component.
* @param component the component after witch the outputComponent should be added.
*/
private void addOutputComponentAfterComponent(UIComponentTag parentTag,
UIComponent outputComponent,
UIComponent component) {
int indexOfComponentInParent = 0;
UIComponent parent = component.getParent();
if (null == parent) {
return;
}
List children = parent.getChildren();
indexOfComponentInParent = children.indexOf(component);
if (children.size() - 1 == indexOfComponentInParent) {
children.add(outputComponent);
}
else {
children.add(indexOfComponentInParent + 1, outputComponent);
}
parentTag.addChildIdToParentTag(parentTag,outputComponent.getId());
}
/**
* Creates a UIOutput component and fill in the content. The attribute
* escape will be set to false. The content will be renderd as it is.
* @param content the content of the UIOutput component.
* @return A UIOutput component with the content an the attribute escape
* set to false.
*/
private UIComponent createUIOutputComponentFromString( String content) {
UIOutput outputComponent = null;
outputComponent = createUIOutputComponent(getFacesContext());
outputComponent.setValue(content);
return outputComponent;
}
/**
* Creates a UIOutput component with the attribute escape = false. The Value
* of the Component will be rendert as it is, which is useful if there are html tags
* which should be injectet into the component tree.
* @param context the Myfaces Context.
* @return A UIOutput Component with escape = false. The String of this Component
* will be rendert as it is.
*/
private UIOutput createUIOutputComponent(FacesContext context) {
if (context == null) return null;
UIOutput outputComponent = null;
Application application = context.getApplication();
outputComponent = (UIOutput) application.createComponent("javax.faces.HtmlOutputText");
outputComponent.setTransient(true);
outputComponent.getAttributes().put("escape", Boolean.FALSE);
outputComponent.setId(context.getViewRoot().createUniqueId());
return outputComponent;
}
/**
* Invoked by the standard jsp processing mechanism when the opening
* tag of a JSF component element is found.
* <p>
* The UIComponent associated with this tag is created (if the view
* doesn't exist) or located if the view is being re-rendered. If
* the component is not "suppressed" then its encodeBegin method is
* called (see method isSuppressed). Note also that method
* encodeBegin is <i>not</i> called for components for which
* getRendersChildren returns true; that occurs only in doEndTag.
*/
public int doStartTag()
throws JspException
{
setupResponseWriter();
FacesContext facesContext = getFacesContext();
if ( isPartialStateSavingOn(facesContext) )
{
JspWriter writer = pageContext.getOut();
try {
flushWriter(writer);
} catch (IOException e) {
log.error(e.toString());
}
}
UIComponent component = findComponent(facesContext);
if (!component.getRendersChildren() && !isSuppressed())
{
try
{
encodeBegin();
_writer.flush();
}
catch (IOException e)
{
throw new JspException(e.getMessage(), e);
}
}
pushTag();
return getDoStartValue();
}
/**
* Invoked by the standard jsp processing mechanism when the closing
* tag of a JSF component element is found.
* <p>
* When the view is being re-rendered, any former children of this tag's
* corresponding component which do not have corresponding tags
* as children of this tag are removed from the view. This isn't likely
* to be a common occurrence: wrapping JSF tags in JSTL tag "c:if" is
* one possible cause. Programmatically created components are not affected
* by this.
* <p>
* If the corresponding component returns true from getRendersChildren
* then its encodeBegin and encodeChildren methods are called here.
* <p>
* The component's encodeEnd method is called provided the component
* is not "suppressed".
*/
public int doEndTag()
throws JspException
{
if (isPartialStateSavingOn(getFacesContext()))
{
JspWriter writerToFlush = pageContext.getOut();
if (_componentInstance == null) {
findComponent(getFacesContext());
}
if ((!(writerToFlush instanceof BodyContent)) && _componentInstance.getParent() == null) {
//add the before view content to the UIViewRoot
_componentInstance.getChildren().add(0,
createUIOutputComponentFromString((String)
_facesContext.getExternalContext().getRequestMap().get(BEFORE_VIEW_CONTEXT)));
_facesContext.getExternalContext().getRequestMap().remove(BEFORE_VIEW_CONTEXT);
}
}
popTag();
UIComponent component = getComponentInstance();
removeFormerChildren(component);
removeFormerFacets(component);
try
{
if (!isSuppressed())
{
// Note that encodeBegin is inside this if-clause because for components that do
// NOT render their children, begin was already called during the doStartTag method.
// However for components that do render their children, we want those children to
// exist before the begin method gets called, so delay until here. Of course that
// causes nasty problems when this tag has non-JSF stuff inside it, eg plain text.
// That plain text will be output before encodeBegin is invoked. The spec authors
// presumably decided this was an acceptable tradeoff to allow encodeBegin to have
// access to the children.
if (component.getRendersChildren())
{
encodeBegin();
encodeChildren();
}
encodeEnd();
}
}
catch (IOException e)
{
throw new JspException(e.getMessage(), e);
}
int retValue = getDoEndValue();
internalRelease();
return retValue;
}
/**
* Remove any child components of the associated components which do not
* have corresponding tags as children of this tag. This only happens
* when a view is being re-rendered and there are components in the view
* tree which don't have corresponding JSP tags. Wrapping JSF tags in
* JSTL "c:if" statements is one way this can happen.
* <br />
* Attention: programmatically added components are are not affected by this:
* they will not be on the old list of created components nor on the new list
* of created components, so nothing will happen to them.
*/
private void removeFormerChildren(UIComponent component)
{
Set formerChildIdsSet = (Set)component.getAttributes().get(FORMER_CHILD_IDS_SET_ATTR);
if (formerChildIdsSet != null)
{
for (Iterator iterator = formerChildIdsSet.iterator(); iterator.hasNext();)
{
String childId = (String)iterator.next();
if (_childrenAdded == null || !_childrenAdded.contains(childId))
{
UIComponent childToRemove = component.findComponent(childId);
if (childToRemove != null)
{
component.getChildren().remove(childToRemove);
}
}
}
if (_childrenAdded == null)
{
component.getAttributes().remove(FORMER_CHILD_IDS_SET_ATTR);
}
else
{
component.getAttributes().put(FORMER_CHILD_IDS_SET_ATTR, _childrenAdded);
}
}
else
{
if (_childrenAdded != null)
{
component.getAttributes().put(FORMER_CHILD_IDS_SET_ATTR, _childrenAdded);
}
}
}
/** See removeFormerChildren. */
private void removeFormerFacets(UIComponent component)
{
Set formerFacetNamesSet = (Set)component.getAttributes().get(FORMER_FACET_NAMES_SET_ATTR);
if (formerFacetNamesSet != null)
{
for (Iterator iterator = formerFacetNamesSet.iterator(); iterator.hasNext();)
{
String facetName = (String)iterator.next();
if (_facetsAdded == null || !_facetsAdded.contains(facetName))
{
component.getFacets().remove(facetName);
}
}
if (_facetsAdded == null)
{
component.getAttributes().remove(FORMER_FACET_NAMES_SET_ATTR);
}
else
{
component.getAttributes().put(FORMER_FACET_NAMES_SET_ATTR, _facetsAdded);
}
}
else
{
if (_facetsAdded != null)
{
component.getAttributes().put(FORMER_FACET_NAMES_SET_ATTR, _facetsAdded);
}
}
}
/**
* Invoke encodeBegin on the associated UIComponent. Subclasses can
* override this method to perform custom processing before or after
* the UIComponent method invocation.
*/
protected void encodeBegin()
throws IOException
{
if(log.isDebugEnabled())
log.debug("Entered encodeBegin for client-Id: "+_componentInstance.getClientId(getFacesContext()));
_componentInstance.encodeBegin(getFacesContext());
if(log.isDebugEnabled())
log.debug("Exited encodeBegin");
}
/**
* Invoke encodeChildren on the associated UIComponent. Subclasses can
* override this method to perform custom processing before or after
* the UIComponent method invocation. This is only invoked for components
* whose getRendersChildren method returns true.
*/
protected void encodeChildren()
throws IOException
{
if(log.isDebugEnabled())
log.debug("Entered encodeChildren for client-Id: "+_componentInstance.getClientId(getFacesContext()));
_componentInstance.encodeChildren(getFacesContext());
if(log.isDebugEnabled())
log.debug("Exited encodeChildren for client-Id: "+_componentInstance.getClientId(getFacesContext()));
}
/**
* Invoke encodeEnd on the associated UIComponent. Subclasses can override this
* method to perform custom processing before or after the UIComponent method
* invocation.
*/
protected void encodeEnd()
throws IOException
{
if(log.isDebugEnabled())
log.debug("Entered encodeEnd for client-Id: "+_componentInstance.getClientId(getFacesContext()));
_componentInstance.encodeEnd(getFacesContext());
if(log.isDebugEnabled())
log.debug("Exited encodeEnd for client-Id: "+_componentInstance.getClientId(getFacesContext()));
}
/**
* Return the corresponding UIComponent for this tag, creating it
* if necessary.
* <p>
* If this is not the first time this method has been called, then
* return the cached component instance found last time.
* <p>
* If this is not the first time this view has been seen, then
* locate the existing component using the id attribute assigned
* to this tag and return it. Note that this is simple for
* components with user-assigned ids. For components with
* generated ids, the "reattachment" relies on the fact that
* UIViewRoot will generate the same id values for tags in
* this page as it did when first generating the view. For this
* reason all JSF tags within a JSTL "c:if" are required to have
* explicitly-assigned ids.
* <p>
* Otherwise create the component, populate its properties from
* the xml attributes on this JSP tag and attach it to its parent.
* <p>
* When a component is found or created the parent JSP tag is also
* told that the component has been "seen". When the parent tag
* ends it will delete any components which were in the view
* previously but have not been seen this time; see doEndTag for
* more details.
*/
protected UIComponent findComponent(FacesContext context)
throws JspException
{
if (_componentInstance != null) return _componentInstance;
UIComponentTag parentTag = getParentUIComponentTag(pageContext);
if (parentTag == null)
{
//This is the root
_componentInstance = context.getViewRoot();
setProperties(_componentInstance);
return _componentInstance;
}
UIComponent parent = parentTag.getComponentInstance();
//TODO: what if parent == null?
if (parent == null) throw new IllegalStateException("parent is null?");
String facetName = getFacetName();
if (facetName != null)
{
//Facet
String id = getOrCreateUniqueId(context);
_componentInstance = parent.getFacet(facetName);
if (_componentInstance == null)
{
_componentInstance = createComponentInstance(context, id);
setProperties(_componentInstance);
parent.getFacets().put(facetName, _componentInstance);
}
else
{
if (checkFacetNameOnParentExists(parentTag, facetName))
{
throw new IllegalStateException("facet '" + facetName + "' already has a child associated. current associated component id: "
+ _componentInstance.getClientId(context) + " class: " + _componentInstance.getClass().getName());
}
}
addFacetNameToParentTag(parentTag, facetName);
return _componentInstance;
}
else
{
//Child
//
// Note that setProperties is called only when we create the
// component; on later passes, the attributes defined on the
// JSP tag are set on this Tag object, but then completely
// ignored.
String id = getOrCreateUniqueId(context);
// Warn users that this tag is about to find/steal the UIComponent
// that has already been created for a sibling tag with the same id value .
// _childrenAdded is a Set, and we will stomp over a past id when calling
// addChildIdToParentTag.
//
// we throw an exception here - RI issues a warning.
if(parentTag._childrenAdded != null && parentTag._childrenAdded.contains(id))
{
throw new FacesException("There is more than one JSF tag with id : " + id+" for parent component with id : '"+parent.getId()+"'");
}
_componentInstance = findComponent(parent,id);
if (_componentInstance == null)
{
_componentInstance = createComponentInstance(context, id);
if (id.equals(_componentInstance.getId()) )
{
setProperties(_componentInstance);
int index = getAddedChildrenCount(parentTag);
List children = parent.getChildren();
if (index <= children.size())
{
children.add(index, _componentInstance);
}
else
{
throw new FacesException("cannot add component with id '" +
_componentInstance.getId() + " to its parent component with id : '"+parent.getId()+"' and path '"+
getPathToComponent(parent)+"'at position :"+index+" in list of children. "+
"This might be a problem due to a duplicate id in a previously added component,"+
"if this is the case, the problematic id might be one of : "+printSet(parentTag._childrenAdded));
}
}
// 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()))
{
setProperties(_componentInstance);
int index = getAddedChildrenCount(parentTag);
List children = parent.getChildren();
if (index <= children.size())
{
children.add(index, _componentInstance);
}
else
{
throw new FacesException("cannot add component with id '" +
_componentInstance.getId() + " to its parent component with id : '"+parent.getId()+"' and path '"+
getPathToComponent(parent)+"'at position :"+index+" in list of children. "+
"This might be a problem due to a duplicate id in a previously added component,"+
"if this is the case, the problematic id might be one of : "+printSet(parentTag._childrenAdded));
}
}
}
addChildIdToParentTag(parentTag, id);
return _componentInstance;
}
}
private UIComponent findComponent(UIComponent parent, String id)
{
List li = parent.getChildren();
for (int i = 0; i < li.size(); i++)
{
UIComponent uiComponent = (UIComponent) li.get(i);
if(uiComponent.getId()!=null && uiComponent.getId().equals(id))
{
return uiComponent;
}
}
return null;
}
/**
* Utility method for creating diagnostic output.
*/
private String printSet(Set childrenAdded)
{
StringBuffer buf = new StringBuffer();
if(childrenAdded!=null)
{
Iterator it = childrenAdded.iterator();
while (it.hasNext())
{
Object obj = it.next();
buf.append(obj);
if(it.hasNext())
buf.append(",");
}
}
return buf.toString();
}
private String getOrCreateUniqueId(FacesContext context)
{
String id = getId();
if (id != null)
{
return id;
}
else
{
//we've been calling
//return context.getViewRoot().createUniqueId(); - don't want that anymore
Long currentCounter = (Long) context.getExternalContext().getRequestMap().get(UNIQUE_ID_COUNTER_ATTR);
long lCurrentCounter = 0;
if(currentCounter!=null)
{
lCurrentCounter = currentCounter.longValue();
}
StringBuffer retValue = new StringBuffer(UIViewRoot.UNIQUE_ID_PREFIX);
retValue.append("Jsp");
retValue.append(lCurrentCounter);
lCurrentCounter++;
context.getExternalContext().getRequestMap().put(UNIQUE_ID_COUNTER_ATTR,new Long(lCurrentCounter));
ExternalContext extCtx = FacesContext.getCurrentInstance().getExternalContext();
return extCtx.encodeNamespace(retValue.toString());
}
}
/**
* Create a UIComponent. Abstract method getComponentType is invoked to
* determine the actual type name for the component to be created.
*
* If this tag has a "binding" attribute, then that is immediately
* evaluated to store the created component in the specified property.
*/
private UIComponent createComponentInstance(FacesContext context, String id)
{
String componentType = getComponentType();
if (componentType == null)
{
throw new NullPointerException("componentType");
}
if (_binding != null)
{
Application application = context.getApplication();
ValueBinding componentBinding = application.createValueBinding(_binding);
UIComponent component = application.createComponent(componentBinding,
context,
componentType);
component.setId(id);
component.setValueBinding("binding", componentBinding);
recurseFacetsAndChildrenForId(component.getFacetsAndChildren(), id + "_", 0);
_created = true;
return component;
}
else
{
UIComponent component = context.getApplication().createComponent(componentType);
component.setId(id);
_created = true;
return component;
}
}
/**
* Recurse all facets and children and assign them an unique ID if
* necessary. We must *not* use UIViewRoot#createUniqueId here,
* because this would affect the order of the created ids upon
* rerendering the page!
*/
private int recurseFacetsAndChildrenForId(
Iterator facetsAndChildren,
String idPrefix,
int cnt)
{
while (facetsAndChildren.hasNext())
{
UIComponent comp = (UIComponent)facetsAndChildren.next();
if (comp.getId() == null)
{
++cnt;
comp.setId(idPrefix + cnt);
}
cnt = recurseFacetsAndChildrenForId(comp.getFacetsAndChildren(), idPrefix, cnt);
}
return cnt;
}
/**
* Notify the enclosing JSP tag of the id of this component's id. The
* parent tag will later delete any existing view components that were
* not seen during this rendering phase; see doEndTag for details.
*/
private void addChildIdToParentTag(UIComponentTag parentTag, String id)
{
if (parentTag._childrenAdded == null)
{
parentTag._childrenAdded = new HashSet();
}
parentTag._childrenAdded.add(id);
}
/**
* check if the facet is already added to the parent
*/
private boolean checkFacetNameOnParentExists(UIComponentTag parentTag, String facetName)
{
return parentTag._facetsAdded != null && parentTag._facetsAdded.contains(facetName);
}
/**
* Notify the enclosing JSP tag of the id of this facet's id. The parent
* tag will later delete any existing view facets that were not seen
* during this rendering phase; see doEndTag for details.
*/
private void addFacetNameToParentTag(UIComponentTag parentTag, String facetName)
{
if (parentTag._facetsAdded == null)
{
parentTag._facetsAdded = new HashSet();
}
parentTag._facetsAdded.add(facetName);
}
private int getAddedChildrenCount(UIComponentTag parentTag)
{
return parentTag._childrenAdded != null ?
parentTag._childrenAdded.size() :
0;
}
/**
* Get the value to be returned by the doStartTag method to the
* JSP framework. Subclasses which wish to use the inherited
* doStartTag but control whether the tag is permitted to contain
* nested tags or not can just override this method to return
* Tag.SOME_CONSTANT.
*
* @return Tag.EVAL_BODY_INCLUDE
*/
protected int getDoStartValue()
throws JspException
{
return Tag.EVAL_BODY_INCLUDE;
}
/**
* Get the value to be returned by the doEndTag method to the
* JSP framework. Subclasses which wish to use the inherited
* doEndTag but control whether the tag is permitted to contain
* nested tags or not can just override this method to return
* Tag.SOME_CONSTANT.
*
* @return Tag.EVAL_PAGE
*/
protected int getDoEndValue()
throws JspException
{
return Tag.EVAL_PAGE;
}
protected FacesContext getFacesContext()
{
if (_facesContext == null)
{
_facesContext = FacesContext.getCurrentInstance();
}
return _facesContext;
}
private boolean isFacet()
{
return _parent != null && _parent instanceof FacetTag;
}
protected String getFacetName()
{
return isFacet() ? ((FacetTag)_parent).getName() : null;
}
/**
* Determine whether this component renders itself. A component
* is "suppressed" when it is either not rendered, or when it is
* rendered by its parent component at a time of the parent's choosing.
*/
protected boolean isSuppressed()
{
if (_suppressed == null)
{
// we haven't called this method before, so determine the suppressed
// value and cache it for later calls to this method.
if (isFacet())
{
// facets are always rendered by their parents --> suppressed
_suppressed = Boolean.TRUE;
return true;
}
UIComponent component = getComponentInstance();
// Does any parent render its children?
// (We must determine this first, before calling any isRendered method
// because rendered properties might reference a data var of a nesting UIData,
// which is not set at this time, and would cause a VariableResolver error!)
UIComponent parent = component.getParent();
while (parent != null)
{
if (parent.getRendersChildren())
{
//Yes, parent found, that renders children --> suppressed
_suppressed = Boolean.TRUE;
return true;
}
parent = parent.getParent();
}
// does component or any parent has a false rendered attribute?
while (component != null)
{
if (!component.isRendered())
{
//Yes, component or any parent must not be rendered --> suppressed
_suppressed = Boolean.TRUE;
return true;
}
component = component.getParent();
}
// else --> not suppressed
_suppressed = Boolean.FALSE;
}
return _suppressed.booleanValue();
}
protected void setProperties(UIComponent component)
{
if (getRendererType() != null)
{
_componentInstance.setRendererType(getRendererType());
}
if (_rendered != null)
{
if (isValueReference(_rendered))
{
ValueBinding vb = getFacesContext().getApplication().createValueBinding(_rendered);
component.setValueBinding("rendered", vb);
} else
{
boolean b = Boolean.valueOf(_rendered).booleanValue();
component.setRendered(b);
}
}
}
protected void setupResponseWriter()
{
FacesContext facesContext = getFacesContext();
if(facesContext == null)
{
throw new FacesException("Faces context not found. getResponseWriter will fail. "+
"Check if the FacesServlet has been initialized at all in your web.xml configuration file"+
"and if you are accessing your jsf-pages through the correct mapping. E.g.: if your FacesServlet is mapped to "+
" *.jsf (with the <servlet-mapping>-element), you need to access your pages as 'sample.jsf'. If you tried to access 'sample.jsp', you'd get this error-message."
);
}
_writer = facesContext.getResponseWriter();
if (_writer == null)
{
RenderKitFactory renderFactory = (RenderKitFactory)FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
RenderKit renderKit = renderFactory.getRenderKit(facesContext,
facesContext.getViewRoot().getRenderKitId());
if (isPartialStateSavingOn(facesContext)) {
_writer = renderKit.createResponseWriter(new _DummyPageContextOutWriter(pageContext),
null /*Default: get the allowed content-types from the accept-header*/,
pageContext.getRequest().getCharacterEncoding());
} else {
_writer = renderKit.createResponseWriter(new _PageContextOutWriter(pageContext),
null /*Default: get the allowed content-types from the accept-header*/,
pageContext.getRequest().getCharacterEncoding());
}
facesContext.setResponseWriter(_writer);
}
}
/** Generate diagnostic output. */
private String getPathToComponent(UIComponent component)
{
StringBuffer buf = new StringBuffer();
if(component == null)
{
buf.append("{Component-Path : ");
buf.append("[null]}");
return buf.toString();
}
getPathToComponent(component,buf);
buf.insert(0,"{Component-Path : ");
buf.append("}");
return buf.toString();
}
/** Generate diagnostic output. */
private static void getPathToComponent(UIComponent component, StringBuffer buf)
{
if(component == null)
return;
StringBuffer intBuf = new StringBuffer();
intBuf.append("[Class: ");
intBuf.append(component.getClass().getName());
if(component instanceof UIViewRoot)
{
intBuf.append(",ViewId: ");
intBuf.append(((UIViewRoot) component).getViewId());
}
else
{
intBuf.append(",Id: ");
intBuf.append(component.getId());
}
intBuf.append("]");
buf.insert(0,(Object)intBuf);
getPathToComponent(component.getParent(),buf);
}
}