View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package javax.faces.webapp;
20  
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  
24  import javax.faces.FacesException;
25  import javax.faces.FactoryFinder;
26  import javax.faces.application.Application;
27  import javax.faces.component.UIComponent;
28  import javax.faces.component.UIViewRoot;
29  import javax.faces.component.UIOutput;
30  import javax.faces.context.FacesContext;
31  import javax.faces.context.ResponseWriter;
32  import javax.faces.context.ExternalContext;
33  import javax.faces.el.ValueBinding;
34  import javax.faces.render.RenderKit;
35  import javax.faces.render.RenderKitFactory;
36  
37  import javax.servlet.jsp.JspException;
38  import javax.servlet.jsp.PageContext;
39  import javax.servlet.jsp.JspWriter;
40  import javax.servlet.jsp.tagext.Tag;
41  import javax.servlet.jsp.tagext.BodyContent;
42  import java.io.IOException;
43  import java.io.Reader;
44  import java.util.*;
45  
46  /**
47   * Base class for all JSP tags that represent a JSF UIComponent.
48   * <p>
49   * <i>Disclaimer</i>: The official definition for the behaviour of
50   * this class is the JSF specification but for legal reasons the
51   * specification cannot be replicated here. Any javadoc present on this
52   * class therefore describes the current implementation rather than the
53   * officially required behaviour, though it is believed that this class
54   * does comply with the specification.
55   * 
56   * see Javadoc of <a href="http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/index.html">JSF Specification</a> for more.
57   * 
58   * @author Manfred Geiler (latest modification by $Author: lu4242 $)
59   * @version $Revision: 826571 $ $Date: 2009-10-18 20:23:46 -0500 (Sun, 18 Oct 2009) $
60   */
61  public abstract class UIComponentTag
62          implements Tag
63  {
64      private static final String FORMER_CHILD_IDS_SET_ATTR = UIComponentTag.class.getName() + ".FORMER_CHILD_IDS";
65      private static final String FORMER_FACET_NAMES_SET_ATTR = UIComponentTag.class.getName() + ".FORMER_FACET_NAMES";
66      private static final String COMPONENT_STACK_ATTR =  UIComponentTag.class.getName() + ".COMPONENT_STACK";
67  
68      private static final String UNIQUE_ID_COUNTER_ATTR = UIComponentTag.class.getName() + ".UNIQUE_ID_COUNTER";
69  
70      private static final String PARTIAL_STATE_SAVING_METHOD_PARAM_NAME = "javax.faces.PARTIAL_STATE_SAVING_METHOD";
71      private static final String PARTIAL_STATE_SAVING_METHOD_ON = "true";
72      private static final String PARTIAL_STATE_SAVING_METHOD_OFF = "false";
73  
74      private static final String BEFORE_VIEW_CONTEXT = "org.apache.myfaces.BEFORE_VIEW_CONTEXT";
75  
76      protected PageContext pageContext = null;
77      private Tag _parent = null;
78  
79      //tag attributes
80      private String _binding = null;
81      private String _id = null;
82      private String _rendered = null;
83  
84      private FacesContext _facesContext = null;
85      private UIComponent _componentInstance = null;
86      private boolean _created = false;
87      private Boolean _suppressed = null;
88      private ResponseWriter _writer = null;
89      private Set _childrenAdded = null;
90      private Set _facetsAdded = null;
91      private Boolean _partialStateSaving = null;
92  
93      private static Log log = LogFactory.getLog(UIComponentTag.class);
94      private static final int READ_LENGTH = 8000;
95  
96      private boolean isPartialStateSavingOn(javax.faces.context.FacesContext context)
97      {
98          if(context == null) throw new NullPointerException("context");
99          if (_partialStateSaving != null) return _partialStateSaving.booleanValue();
100         String stateSavingMethod = context.getExternalContext().getInitParameter(PARTIAL_STATE_SAVING_METHOD_PARAM_NAME);
101         if (stateSavingMethod == null)
102         {
103             _partialStateSaving = Boolean.FALSE; //Specs 10.1.3: default server saving
104             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.");
105         }
106         else if (stateSavingMethod.equals(PARTIAL_STATE_SAVING_METHOD_ON))
107         {
108             _partialStateSaving = Boolean.TRUE;
109         }
110         else if (stateSavingMethod.equals(PARTIAL_STATE_SAVING_METHOD_OFF))
111         {
112             _partialStateSaving = Boolean.FALSE;
113         }
114         else
115         {
116             _partialStateSaving = Boolean.FALSE; //Specs 10.1.3: default server saving
117             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).");
118         }
119         return _partialStateSaving.booleanValue();
120     }
121 
122 
123     public UIComponentTag()
124     {
125 
126     }
127 
128     public void release()
129     {
130         internalRelease();
131 
132         //members, that must/need only be reset when there is no more risk, that the container
133         //wants to reuse this tag
134         pageContext = null;
135         _parent = null;
136 
137         // Reset tag attribute members. These are reset here rather than in
138         // internalRelease because of some Resin-related issue. See commit
139         // r166747.
140         _binding = null;
141         _id = null;
142         _rendered = null;
143     }
144 
145 
146     /**
147      * Reset any members that apply to the according component instance and
148      * must not be reused if the container wants to reuse this tag instance.
149      * This method is called when rendering for this tag is finished 
150      * ( doEndTag() ) or when released by the container.
151      */
152     private void internalRelease()
153     {
154         _facesContext = null;
155         _componentInstance = null;
156         _created = false;
157         _suppressed = null;
158         _writer = null;
159         _childrenAdded = null;
160         _facetsAdded = null;
161     }
162 
163     /** Setter for common JSF xml attribute "binding". */
164     public void setBinding(String binding)
165             throws JspException
166     {
167         if (!isValueReference(binding))
168         {
169             throw new IllegalArgumentException("not a valid binding: " + binding);
170         }
171         _binding = binding;
172     }
173 
174     /** Setter for common JSF xml attribute "id". */
175     public void setId(String id)
176     {
177         _id = id;
178     }
179 
180     /**
181      * Return the id (if any) specified as an xml attribute on this tag.
182      */
183     protected String getId()
184     {
185         return _id;
186     }
187 
188     /** Setter for common JSF xml attribute "rendered". */
189     public void setRendered(String rendered)
190     {
191         _rendered = rendered;
192     }
193 
194     /**
195      * Specify the "component type name" used together with the component's
196      * family and the Application object to create a UIComponent instance for
197      * this tag. This method is called by other methods in this class, and is
198      * intended to be overridden in subclasses to specify the actual component
199      * type to be created.
200      * 
201      * @return a registered component type name, never null.
202      */
203     public abstract String getComponentType();
204 
205     /**
206      * Return the UIComponent instance associated with this tag.
207      * @return a UIComponent, never null.
208      */
209     public UIComponent getComponentInstance()
210     {
211         return _componentInstance;
212     }
213 
214     /**
215      * Return true if this tag created the associated UIComponent (rather
216      * than locating an existing instance of the UIComponent in the view).
217      */
218     public boolean getCreated()
219     {
220         return _created;
221     }
222 
223     /**
224      * Return the nearest JSF tag that encloses this tag.
225      */
226     public static UIComponentTag getParentUIComponentTag(PageContext pageContext)
227     {
228         // Question: why not just walk up the _parent chain testing for
229         // instanceof UIComponentTag rather than maintaining a separate
230         // stack with the pushTag and popTag methods?
231         List list = (List)pageContext.getAttribute(COMPONENT_STACK_ATTR,
232                                                    PageContext.REQUEST_SCOPE);
233         if (list != null)
234         {
235             return (UIComponentTag)list.get(list.size() - 1);
236         }
237         return null;
238     }
239 
240     /** See documentation for pushTag. */
241     private void popTag()
242     {
243         List list = (List)pageContext.getAttribute(COMPONENT_STACK_ATTR,
244                                                     PageContext.REQUEST_SCOPE);
245         if (list != null)
246         {
247             int size = list.size();
248             list.remove(size -1);
249             if (size <= 1)
250             {
251                 pageContext.removeAttribute(COMPONENT_STACK_ATTR,
252                                              PageContext.REQUEST_SCOPE);
253             }
254         }
255     }
256 
257     /**
258      * Push this tag onto the stack of JSP tags seen.
259      * <p>
260      * The pageContext's request scope map is used to hold a stack of
261      * JSP tag objects seen so far, so that a new tag can find the
262      * parent tag that encloses it. Access to the parent tag is used
263      * to find the parent UIComponent for the component associated
264      * with this tag plus some other uses. 
265      */
266     private void pushTag()
267     {
268         List list = (List)pageContext.getAttribute(COMPONENT_STACK_ATTR,
269                                                     PageContext.REQUEST_SCOPE);
270         if (list == null)
271         {
272             list = new ArrayList();
273             pageContext.setAttribute(COMPONENT_STACK_ATTR,
274                                       list,
275                                       PageContext.REQUEST_SCOPE);
276         }
277         list.add(this);
278     }
279 
280     /**
281      * Specify the "renderer type name" used together with the current
282      * renderKit to get a Renderer instance for the corresponding UIComponent.
283      * <p>
284      * A JSP tag can return null here to use the default renderer type string.
285      * If non-null is returned, then the UIComponent's setRendererType method
286      * will be called passing this value, and this will later affect the
287      * type of renderer object returned by UIComponent.getRenderer().
288      */
289     public abstract String getRendererType();
290 
291     /**
292      * Return true if the specified string contains an EL expression.
293      * <p>
294      * UIComponent properties are often required to be value-binding
295      * expressions; this method allows code to check whether that is
296      * the case or not.
297      */
298     public static boolean isValueReference(String value)
299     {
300         if (value == null) throw new NullPointerException("value");
301 
302         int start = value.indexOf("#{");
303         if (start < 0) return false;
304 
305         int end = value.lastIndexOf('}');
306         return (end >=0 && start < end);
307     }
308 
309     /**
310      * Standard method invoked by the JSP framework to inform this tag
311      * of the PageContext associated with the jsp page currently being
312      * processed. 
313      */
314     public void setPageContext(PageContext pageContext)
315     {
316         this.pageContext = pageContext;
317     }
318 
319     /**
320      * Returns the enclosing JSP tag object. Note that this is not
321      * necessarily a JSF tag.
322      */
323     public Tag getParent()
324     {
325         return _parent;
326     }
327 
328     /**
329      * Standard method invoked by the JSP framework to inform this tag
330      * of the enclosing JSP tag object.
331      */
332     public void setParent(Tag parent)
333     {
334         _parent = parent;
335     }
336 
337     /**
338      * Flushes the Writer and adds the content of the Writer ( which is only the direct output
339      * of the JSP if the component uses a dummyWriter wich does not render the Component itself) as
340      * UIOutput component after the current component to the myfaces component tree.
341      * @param writerToFlush
342      * @throws JspException
343      * @throws IOException
344      */
345 
346     private void flushWriter( JspWriter writerToFlush ) throws JspException,IOException {
347 
348       if (getFacesContext() == null)
349             return;
350       BodyContent tempwriter;
351       _componentInstance = findComponent(_facesContext);
352       if (writerToFlush instanceof BodyContent)
353         {
354             int count = 0;
355             char [] readChars = new char[READ_LENGTH];
356             tempwriter = (BodyContent) writerToFlush;
357             Reader read =tempwriter.getReader();
358             count = read.read(readChars,0,READ_LENGTH);
359             String readString = new String(readChars,0,count);
360             if(!readString.trim().equals(""))
361             {
362                 //_componentInstance = findComponent(_facesContext);
363                 UIComponentTag parentTag = getParentUIComponentTag(pageContext);
364                 addOutputComponentAfterComponent(parentTag,createUIOutputComponentFromString(readString),_componentInstance);
365                 tempwriter.clearBody();
366             }
367 
368         }
369         else
370       {
371           if (_componentInstance.getParent() == null) {
372             writerToFlush.flush();
373             String beforeViewContent = _facesContext.getExternalContext().getResponse().toString();
374             if((beforeViewContent != null)&&(!beforeViewContent.trim().equals("")))
375             {   // BEFORE BODY CONTENT
376                 _facesContext.getExternalContext().getRequestMap().put(BEFORE_VIEW_CONTEXT,beforeViewContent);
377             }
378           }
379       }
380 
381     }
382 
383     /**
384      * Adds an Output Component afert a given component.
385      * @param parentTag the parent tag of the component.
386      * @param outputComponent the component which should be added after this component.
387      * @param component the component after witch the outputComponent should be added.
388      */
389     private void addOutputComponentAfterComponent(UIComponentTag parentTag,
390                                                   UIComponent outputComponent,
391                                                   UIComponent component) {
392     int indexOfComponentInParent = 0;
393     UIComponent parent = component.getParent();
394 
395     if (null == parent) {
396         return;
397     }
398     List children = parent.getChildren();
399     indexOfComponentInParent = children.indexOf(component);
400     if (children.size() - 1 == indexOfComponentInParent) {
401         children.add(outputComponent);
402     }
403     else {
404         children.add(indexOfComponentInParent + 1, outputComponent);
405     }
406     parentTag.addChildIdToParentTag(parentTag,outputComponent.getId());
407 
408     }
409 
410 
411     /**
412      * Creates a UIOutput component and fill in the content. The attribute
413      * escape will be set to false. The content will be renderd as it is.
414      * @param content the content of the UIOutput component.
415      * @return A UIOutput component with the content an the attribute escape
416      *         set to false.
417      */
418      private UIComponent createUIOutputComponentFromString( String content) {
419          UIOutput outputComponent = null;
420 
421          outputComponent = createUIOutputComponent(getFacesContext());
422          outputComponent.setValue(content);
423 
424          return outputComponent;
425      }
426 
427 
428     /**
429      * Creates a UIOutput component with the attribute escape = false. The Value
430      * of the Component will be rendert as it is, which is useful if there are html tags
431      * which should be injectet into the component tree.
432      * @param context the Myfaces Context.
433      * @return A UIOutput Component with escape = false. The String of this Component
434      *         will be rendert as it is.
435      */
436      private UIOutput createUIOutputComponent(FacesContext context) {
437          if (context == null) return null;
438          UIOutput outputComponent = null;
439          Application application = context.getApplication();
440          outputComponent = (UIOutput) application.createComponent("javax.faces.HtmlOutputText");
441          outputComponent.setTransient(true);
442          outputComponent.getAttributes().put("escape", Boolean.FALSE);
443          outputComponent.setId(context.getViewRoot().createUniqueId());
444          return outputComponent;
445      }
446 
447     /**
448      * Invoked by the standard jsp processing mechanism when the opening
449      * tag of a JSF component element is found.
450      * <p>
451      * The UIComponent associated with this tag is created (if the view
452      * doesn't exist) or located if the view is being re-rendered. If
453      * the component is not "suppressed" then its encodeBegin method is
454      * called (see method isSuppressed). Note also that method
455      * encodeBegin is <i>not</i> called for components for which
456      * getRendersChildren returns true; that occurs only in doEndTag.
457      */
458     public int doStartTag()
459             throws JspException
460     {
461         setupResponseWriter();
462         FacesContext facesContext = getFacesContext();
463         if ( isPartialStateSavingOn(facesContext) )
464         {
465             JspWriter writer = pageContext.getOut();
466             try {
467                 flushWriter(writer);
468             } catch (IOException e) {
469                 log.error(e.toString());
470             }
471         }
472         UIComponent component = findComponent(facesContext);
473         if (!component.getRendersChildren() && !isSuppressed())
474         {
475             try
476             {
477                 encodeBegin();
478                 _writer.flush();
479             }
480             catch (IOException e)
481             {
482                 throw new JspException(e.getMessage(), e);
483             }
484         }
485         pushTag();
486         return getDoStartValue();
487     }
488 
489     /**
490      * Invoked by the standard jsp processing mechanism when the closing
491      * tag of a JSF component element is found.
492      * <p>
493      * When the view is being re-rendered, any former children of this tag's
494      * corresponding component which do not have corresponding tags
495      * as children of this tag are removed from the view. This isn't likely
496      * to be a common occurrence: wrapping JSF tags in JSTL tag "c:if" is
497      * one possible cause. Programmatically created components are not affected
498      * by this.
499      * <p>
500      * If the corresponding component returns true from getRendersChildren
501      * then its encodeBegin and encodeChildren methods are called here.
502      * <p>
503      * The component's encodeEnd method is called provided the component
504      * is not "suppressed".
505      */
506     public int doEndTag()
507             throws JspException
508     {
509         if (isPartialStateSavingOn(getFacesContext()))
510         {
511             JspWriter writerToFlush =   pageContext.getOut();
512             if (_componentInstance == null) {
513                 findComponent(getFacesContext());
514             }
515             if ((!(writerToFlush instanceof BodyContent)) && _componentInstance.getParent() == null) {
516                 //add the before view content to the UIViewRoot
517                 _componentInstance.getChildren().add(0,
518                         createUIOutputComponentFromString((String)
519                                 _facesContext.getExternalContext().getRequestMap().get(BEFORE_VIEW_CONTEXT)));
520                 _facesContext.getExternalContext().getRequestMap().remove(BEFORE_VIEW_CONTEXT);
521             }
522         }
523 
524         popTag();
525         UIComponent component = getComponentInstance();
526         removeFormerChildren(component);
527         removeFormerFacets(component);
528 
529         try
530         {
531             if (!isSuppressed())
532             {
533                 // Note that encodeBegin is inside this if-clause because for components that do
534                 // NOT render their children, begin was already called during the doStartTag method.
535                 // However for components that do render their children, we want those children to
536                 // exist before the begin method gets called, so delay until here. Of course that
537                 // causes nasty problems when this tag has non-JSF stuff inside it, eg plain text.
538                 // That plain text will be output before encodeBegin is invoked. The spec authors
539                 // presumably decided this was an acceptable tradeoff to allow encodeBegin to have
540                 // access to the children.
541                 if (component.getRendersChildren())
542                 {
543                     encodeBegin();
544                     encodeChildren();
545                 }
546                 encodeEnd();
547             }
548         }
549         catch (IOException e)
550         {
551             throw new JspException(e.getMessage(), e);
552         }
553 
554         int retValue = getDoEndValue();
555         internalRelease();
556         return retValue;
557     }
558 
559     /**
560      * Remove any child components of the associated components which do not
561      * have corresponding tags as children of this tag. This only happens
562      * when a view is being re-rendered and there are components in the view
563      * tree which don't have corresponding JSP tags. Wrapping JSF tags in
564      * JSTL "c:if" statements is one way this can happen.
565      * <br />
566      * Attention: programmatically added components are are not affected by this:
567      * they will not be on the old list of created components nor on the new list
568      * of created components, so nothing will happen to them.
569      */
570     private void removeFormerChildren(UIComponent component)
571     {
572         Set formerChildIdsSet = (Set)component.getAttributes().get(FORMER_CHILD_IDS_SET_ATTR);
573         if (formerChildIdsSet != null)
574         {
575             for (Iterator iterator = formerChildIdsSet.iterator(); iterator.hasNext();)
576             {
577                 String childId = (String)iterator.next();
578                 if (_childrenAdded == null || !_childrenAdded.contains(childId))
579                 {
580                     UIComponent childToRemove = component.findComponent(childId);
581                     if (childToRemove != null)
582                     {
583                         component.getChildren().remove(childToRemove);
584                     }
585                 }
586             }
587             if (_childrenAdded == null)
588             {
589                 component.getAttributes().remove(FORMER_CHILD_IDS_SET_ATTR);
590             }
591             else
592             {
593                 component.getAttributes().put(FORMER_CHILD_IDS_SET_ATTR, _childrenAdded);
594             }
595         }
596         else
597         {
598             if (_childrenAdded != null)
599             {
600                 component.getAttributes().put(FORMER_CHILD_IDS_SET_ATTR, _childrenAdded);
601             }
602         }
603     }
604 
605     /** See removeFormerChildren. */
606     private void removeFormerFacets(UIComponent component)
607     {
608         Set formerFacetNamesSet = (Set)component.getAttributes().get(FORMER_FACET_NAMES_SET_ATTR);
609         if (formerFacetNamesSet != null)
610         {
611             for (Iterator iterator = formerFacetNamesSet.iterator(); iterator.hasNext();)
612             {
613                 String facetName = (String)iterator.next();
614                 if (_facetsAdded == null || !_facetsAdded.contains(facetName))
615                 {
616                     component.getFacets().remove(facetName);
617                 }
618             }
619             if (_facetsAdded == null)
620             {
621                 component.getAttributes().remove(FORMER_FACET_NAMES_SET_ATTR);
622             }
623             else
624             {
625                 component.getAttributes().put(FORMER_FACET_NAMES_SET_ATTR, _facetsAdded);
626             }
627         }
628         else
629         {
630             if (_facetsAdded != null)
631             {
632                 component.getAttributes().put(FORMER_FACET_NAMES_SET_ATTR, _facetsAdded);
633             }
634         }
635     }
636 
637     /**
638      * Invoke encodeBegin on the associated UIComponent. Subclasses can
639      * override this method to perform custom processing before or after
640      * the UIComponent method invocation.
641      */
642     protected void encodeBegin()
643             throws IOException
644     {
645         if(log.isDebugEnabled())
646             log.debug("Entered encodeBegin for client-Id: "+_componentInstance.getClientId(getFacesContext()));
647         _componentInstance.encodeBegin(getFacesContext());
648         if(log.isDebugEnabled())
649             log.debug("Exited encodeBegin");
650     }
651 
652     /**
653      * Invoke encodeChildren on the associated UIComponent. Subclasses can
654      * override this method to perform custom processing before or after
655      * the UIComponent method invocation. This is only invoked for components
656      * whose getRendersChildren method returns true.
657      */
658     protected void encodeChildren()
659             throws IOException
660     {
661         if(log.isDebugEnabled())
662             log.debug("Entered encodeChildren for client-Id: "+_componentInstance.getClientId(getFacesContext()));
663         _componentInstance.encodeChildren(getFacesContext());
664         if(log.isDebugEnabled())
665             log.debug("Exited encodeChildren for client-Id: "+_componentInstance.getClientId(getFacesContext()));
666     }
667 
668     /**
669      * Invoke encodeEnd on the associated UIComponent. Subclasses can override this
670      * method to perform custom processing before or after the UIComponent method
671      * invocation.
672      */
673     protected void encodeEnd()
674             throws IOException
675     {
676         if(log.isDebugEnabled())
677             log.debug("Entered encodeEnd for client-Id: "+_componentInstance.getClientId(getFacesContext()));
678         _componentInstance.encodeEnd(getFacesContext());
679         if(log.isDebugEnabled())
680             log.debug("Exited encodeEnd for client-Id: "+_componentInstance.getClientId(getFacesContext()));
681 
682     }
683 
684     /**
685      * Return the corresponding UIComponent for this tag, creating it
686      * if necessary.
687      * <p>
688      * If this is not the first time this method has been called, then
689      * return the cached component instance found last time.
690      * <p>
691      * If this is not the first time this view has been seen, then
692      * locate the existing component using the id attribute assigned
693      * to this tag and return it. Note that this is simple for
694      * components with user-assigned ids. For components with
695      * generated ids, the "reattachment" relies on the fact that
696      * UIViewRoot will generate the same id values for tags in
697      * this page as it did when first generating the view. For this
698      * reason all JSF tags within a JSTL "c:if" are required to have
699      * explicitly-assigned ids. 
700      * <p>
701      * Otherwise create the component, populate its properties from
702      * the xml attributes on this JSP tag and attach it to its parent.
703      * <p>
704      * When a component is found or created the parent JSP tag is also
705      * told that the component has been "seen". When the parent tag
706      * ends it will delete any components which were in the view
707      * previously but have not been seen this time; see doEndTag for
708      * more details.
709      */
710     protected UIComponent findComponent(FacesContext context)
711             throws JspException
712     {
713         if (_componentInstance != null) return _componentInstance;
714         UIComponentTag parentTag = getParentUIComponentTag(pageContext);
715         if (parentTag == null)
716         {
717             //This is the root
718             _componentInstance = context.getViewRoot();
719             setProperties(_componentInstance);
720             return _componentInstance;
721         }
722 
723         UIComponent parent = parentTag.getComponentInstance();
724         //TODO: what if parent == null?
725         if (parent == null) throw new IllegalStateException("parent is null?");
726 
727         String facetName = getFacetName();
728         if (facetName != null)
729         {
730             //Facet
731             String id = getOrCreateUniqueId(context);
732             _componentInstance = parent.getFacet(facetName);
733             if (_componentInstance == null)
734             {
735                 _componentInstance = createComponentInstance(context, id);
736                 setProperties(_componentInstance);
737                 parent.getFacets().put(facetName, _componentInstance);
738             }
739             else
740             {
741                 if (checkFacetNameOnParentExists(parentTag, facetName))
742                 {
743                     throw new IllegalStateException("facet '" + facetName + "' already has a child associated. current associated component id: "
744                         + _componentInstance.getClientId(context) + " class: " + _componentInstance.getClass().getName());
745                 }
746             }
747             
748             addFacetNameToParentTag(parentTag, facetName);
749             return _componentInstance;
750         }
751         else
752         {
753             //Child
754             //
755             // Note that setProperties is called only when we create the
756             // component; on later passes, the attributes defined on the
757             // JSP tag are set on this Tag object, but then completely
758             // ignored.
759 
760             String id = getOrCreateUniqueId(context);
761             
762             // Warn users that this tag is about to find/steal the UIComponent
763             // that has already been created for a sibling tag with the same id value .
764             // _childrenAdded is a Set, and we will stomp over a past id when calling 
765             // addChildIdToParentTag.
766             //
767             // we throw an exception here - RI issues a warning.
768             if(parentTag._childrenAdded != null && parentTag._childrenAdded.contains(id))
769             {
770                 throw new FacesException("There is more than one JSF tag with id : " + id+" for parent component with id : '"+parent.getId()+"'");
771             }
772             
773             _componentInstance = findComponent(parent,id);
774             if (_componentInstance == null)
775             {
776                 _componentInstance = createComponentInstance(context, id);
777                 if (id.equals(_componentInstance.getId()) )
778                 {
779                     setProperties(_componentInstance);
780                     int index = getAddedChildrenCount(parentTag);
781                     List children = parent.getChildren();
782                     if (index <= children.size())
783                     {
784                         children.add(index, _componentInstance);
785                     }
786                     else
787                     {
788                         throw new FacesException("cannot add component with id '" +
789                                 _componentInstance.getId() + " to its parent component with id : '"+parent.getId()+"' and path '"+
790                                 getPathToComponent(parent)+"'at position :"+index+" in list of children. "+
791                                 "This might be a problem due to a duplicate id in a previously added component,"+
792                                 "if this is the case, the problematic id might be one of : "+printSet(parentTag._childrenAdded));
793                     }
794                 }
795                 // On weblogic portal using faces-adapter, the id set and the retrieved 
796                 // one for <netuix:namingContainer> is different. The reason is 
797                 // this custom solution for integrate jsf changes the id of the parent
798                 // component to allow the same native portlet to be allocated multiple
799                 // times in the same page
800                 else if (null == findComponent(parent,_componentInstance.getId()))
801                 {
802                     setProperties(_componentInstance);
803                     int index = getAddedChildrenCount(parentTag);
804                     List children = parent.getChildren();
805                     if (index <= children.size())
806                     {
807                         children.add(index, _componentInstance);
808                     }
809                     else
810                     {
811                         throw new FacesException("cannot add component with id '" +
812                                 _componentInstance.getId() + " to its parent component with id : '"+parent.getId()+"' and path '"+
813                                 getPathToComponent(parent)+"'at position :"+index+" in list of children. "+
814                                 "This might be a problem due to a duplicate id in a previously added component,"+
815                                 "if this is the case, the problematic id might be one of : "+printSet(parentTag._childrenAdded));
816                     }
817                 }
818             }
819             addChildIdToParentTag(parentTag, id);
820             return _componentInstance;
821         }
822     }
823 
824     private UIComponent findComponent(UIComponent parent, String id)
825     {
826         List li = parent.getChildren();
827 
828         for (int i = 0; i < li.size(); i++)
829         {
830             UIComponent uiComponent = (UIComponent) li.get(i);
831             if(uiComponent.getId()!=null && uiComponent.getId().equals(id))
832             {
833                 return uiComponent;
834             }
835         }
836 
837         return null;
838     }
839 
840     /**
841      * Utility method for creating diagnostic output.
842      */
843     private String printSet(Set childrenAdded)
844     {
845         StringBuffer buf = new StringBuffer();
846 
847         if(childrenAdded!=null)
848         {
849             Iterator it = childrenAdded.iterator();
850 
851             while (it.hasNext())
852             {
853                 Object obj =  it.next();
854                 buf.append(obj);
855 
856                 if(it.hasNext())
857                     buf.append(",");
858             }
859         }
860         return buf.toString();
861     }
862 
863 
864     private String getOrCreateUniqueId(FacesContext context)
865     {
866         String id = getId();
867         if (id != null)
868         {
869             return id;
870         }
871         else
872         {
873             //we've been calling
874             //return context.getViewRoot().createUniqueId(); - don't want that anymore
875             Long currentCounter = (Long) context.getExternalContext().getRequestMap().get(UNIQUE_ID_COUNTER_ATTR);
876             long lCurrentCounter = 0;
877 
878             if(currentCounter!=null)
879             {
880                 lCurrentCounter = currentCounter.longValue();
881             }
882 
883             StringBuffer retValue = new StringBuffer(UIViewRoot.UNIQUE_ID_PREFIX);
884             retValue.append("Jsp");
885             retValue.append(lCurrentCounter);
886 
887             lCurrentCounter++;
888 
889             context.getExternalContext().getRequestMap().put(UNIQUE_ID_COUNTER_ATTR,new Long(lCurrentCounter));
890 
891             ExternalContext extCtx = FacesContext.getCurrentInstance().getExternalContext();
892             return extCtx.encodeNamespace(retValue.toString());
893         }
894     }
895 
896     /**
897      * Create a UIComponent. Abstract method getComponentType is invoked to
898      * determine the actual type name for the component to be created.
899      * 
900      * If this tag has a "binding" attribute, then that is immediately
901      * evaluated to store the created component in the specified property.
902      */
903     private UIComponent createComponentInstance(FacesContext context, String id)
904     {
905         String componentType = getComponentType();
906         if (componentType == null)
907         {
908             throw new NullPointerException("componentType");
909         }
910 
911         if (_binding != null)
912         {
913             Application application = context.getApplication();
914             ValueBinding componentBinding = application.createValueBinding(_binding);
915             UIComponent component = application.createComponent(componentBinding,
916                                                                 context,
917                                                                 componentType);
918             component.setId(id);
919             component.setValueBinding("binding", componentBinding);
920             recurseFacetsAndChildrenForId(component.getFacetsAndChildren(), id + "_", 0);
921             _created = true;
922             return component;
923         }
924         else
925         {
926             UIComponent component = context.getApplication().createComponent(componentType);
927             component.setId(id);
928             _created = true;
929             return component;
930         }
931     }
932 
933     /**
934      * Recurse all facets and children and assign them an unique ID if
935      * necessary. We must *not* use UIViewRoot#createUniqueId here,
936      * because this would affect the order of the created ids upon
937      * rerendering the page!
938      */
939     private int recurseFacetsAndChildrenForId(
940             Iterator facetsAndChildren,
941             String idPrefix,
942             int cnt)
943     {
944         while (facetsAndChildren.hasNext())
945         {
946             UIComponent comp = (UIComponent)facetsAndChildren.next();
947             if (comp.getId() == null)
948             {
949                 ++cnt;
950                 comp.setId(idPrefix + cnt);
951             }
952             cnt = recurseFacetsAndChildrenForId(comp.getFacetsAndChildren(), idPrefix, cnt);
953         }
954         return cnt;
955     }
956 
957     /**
958      * Notify the enclosing JSP tag of the id of this component's id. The
959      * parent tag will later delete any existing view components that were
960      * not seen during this rendering phase; see doEndTag for details.
961      */
962     private void addChildIdToParentTag(UIComponentTag parentTag, String id)
963     {
964         if (parentTag._childrenAdded == null)
965         {
966             parentTag._childrenAdded = new HashSet();
967         }
968         parentTag._childrenAdded.add(id);
969     }
970 
971     /**
972      * check if the facet is already added to the parent
973      */
974     private boolean checkFacetNameOnParentExists(UIComponentTag parentTag, String facetName)
975     {
976         return parentTag._facetsAdded != null && parentTag._facetsAdded.contains(facetName); 
977     }
978     
979     /**
980      * Notify the enclosing JSP tag of the id of this facet's id. The parent
981      * tag will later delete any existing view facets that were not seen
982      * during this rendering phase; see doEndTag for details.
983      */
984     private void addFacetNameToParentTag(UIComponentTag parentTag, String facetName)
985     {
986         if (parentTag._facetsAdded == null)
987         {
988             parentTag._facetsAdded = new HashSet();
989         }
990         parentTag._facetsAdded.add(facetName);
991     }
992 
993     private int getAddedChildrenCount(UIComponentTag parentTag)
994     {
995         return parentTag._childrenAdded != null ?
996                parentTag._childrenAdded.size() :
997                0;
998     }
999 
1000     /**
1001      * Get the value to be returned by the doStartTag method to the
1002      * JSP framework. Subclasses which wish to use the inherited
1003      * doStartTag but control whether the tag is permitted to contain
1004      * nested tags or not can just override this method to return
1005      * Tag.SOME_CONSTANT.
1006      * 
1007      * @return Tag.EVAL_BODY_INCLUDE
1008      */
1009     protected int getDoStartValue()
1010             throws JspException
1011     {
1012         return Tag.EVAL_BODY_INCLUDE;
1013     }
1014 
1015     /**
1016      * Get the value to be returned by the doEndTag method to the
1017      * JSP framework. Subclasses which wish to use the inherited
1018      * doEndTag but control whether the tag is permitted to contain
1019      * nested tags or not can just override this method to return
1020      * Tag.SOME_CONSTANT.
1021      * 
1022      * @return Tag.EVAL_PAGE
1023      */
1024     protected int getDoEndValue()
1025             throws JspException
1026     {
1027         return Tag.EVAL_PAGE;
1028     }
1029 
1030     protected FacesContext getFacesContext()
1031     {
1032         if (_facesContext == null)
1033         {
1034             _facesContext = FacesContext.getCurrentInstance();
1035         }
1036         return _facesContext;
1037     }
1038 
1039 
1040     private boolean isFacet()
1041     {
1042         return _parent != null && _parent instanceof FacetTag;
1043     }
1044 
1045     protected String getFacetName()
1046     {
1047         return isFacet() ? ((FacetTag)_parent).getName() : null;
1048     }
1049 
1050     /**
1051      * Determine whether this component renders itself. A component
1052      * is "suppressed" when it is either not rendered, or when it is
1053      * rendered by its parent component at a time of the parent's choosing.
1054      */
1055     protected boolean isSuppressed()
1056     {
1057         if (_suppressed == null)
1058         {
1059             // we haven't called this method before, so determine the suppressed
1060             // value and cache it for later calls to this method.
1061 
1062             if (isFacet())
1063             {
1064                 // facets are always rendered by their parents --> suppressed
1065                 _suppressed = Boolean.TRUE;
1066                 return true;
1067             }
1068 
1069             UIComponent component = getComponentInstance();
1070 
1071             // Does any parent render its children?
1072             // (We must determine this first, before calling any isRendered method
1073             //  because rendered properties might reference a data var of a nesting UIData,
1074             //  which is not set at this time, and would cause a VariableResolver error!)
1075             UIComponent parent = component.getParent();
1076             while (parent != null)
1077             {
1078                 if (parent.getRendersChildren())
1079                 {
1080                     //Yes, parent found, that renders children --> suppressed
1081                     _suppressed = Boolean.TRUE;
1082                     return true;
1083                 }
1084                 parent = parent.getParent();
1085             }
1086 
1087             // does component or any parent has a false rendered attribute?
1088             while (component != null)
1089             {
1090                 if (!component.isRendered())
1091                 {
1092                     //Yes, component or any parent must not be rendered --> suppressed
1093                     _suppressed = Boolean.TRUE;
1094                     return true;
1095                 }
1096                 component = component.getParent();
1097             }
1098 
1099             // else --> not suppressed
1100             _suppressed = Boolean.FALSE;
1101         }
1102         return _suppressed.booleanValue();
1103     }
1104 
1105     protected void setProperties(UIComponent component)
1106     {
1107         if (getRendererType() != null)
1108         {
1109             _componentInstance.setRendererType(getRendererType());
1110         }
1111 
1112         if (_rendered != null)
1113         {
1114             if (isValueReference(_rendered))
1115             {
1116                 ValueBinding vb = getFacesContext().getApplication().createValueBinding(_rendered);
1117                 component.setValueBinding("rendered", vb);
1118             } else
1119             {
1120                 boolean b = Boolean.valueOf(_rendered).booleanValue();
1121                 component.setRendered(b);
1122             }
1123         }
1124     }
1125 
1126     protected void setupResponseWriter()
1127     {
1128         FacesContext facesContext = getFacesContext();
1129 
1130         if(facesContext == null)
1131         {
1132             throw new FacesException("Faces context not found. getResponseWriter will fail. "+
1133                     "Check if the FacesServlet has been initialized at all in your web.xml configuration file"+
1134                     "and if you are accessing your jsf-pages through the correct mapping. E.g.: if your FacesServlet is mapped to "+
1135                     " *.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."                    
1136                     );
1137         }
1138 
1139         _writer = facesContext.getResponseWriter();
1140         if (_writer == null)
1141         {
1142             RenderKitFactory renderFactory = (RenderKitFactory)FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
1143             RenderKit renderKit = renderFactory.getRenderKit(facesContext,
1144                                                              facesContext.getViewRoot().getRenderKitId());
1145 
1146             if (isPartialStateSavingOn(facesContext)) {
1147                     _writer = renderKit.createResponseWriter(new _DummyPageContextOutWriter(pageContext),
1148                                                              null /*Default: get the allowed content-types from the accept-header*/,
1149                                                              pageContext.getRequest().getCharacterEncoding());
1150             } else {
1151                     _writer = renderKit.createResponseWriter(new _PageContextOutWriter(pageContext),
1152                                                              null /*Default: get the allowed content-types from the accept-header*/,
1153                                                              pageContext.getRequest().getCharacterEncoding());
1154 
1155             }
1156             facesContext.setResponseWriter(_writer);
1157         }
1158     }
1159 
1160     /** Generate diagnostic output. */
1161     private String getPathToComponent(UIComponent component)
1162     {
1163         StringBuffer buf = new StringBuffer();
1164 
1165         if(component == null)
1166         {
1167             buf.append("{Component-Path : ");
1168             buf.append("[null]}");
1169             return buf.toString();
1170         }
1171 
1172         getPathToComponent(component,buf);
1173 
1174         buf.insert(0,"{Component-Path : ");
1175         buf.append("}");
1176 
1177         return buf.toString();
1178     }
1179 
1180     /** Generate diagnostic output. */
1181     private static void getPathToComponent(UIComponent component, StringBuffer buf)
1182     {
1183         if(component == null)
1184             return;
1185 
1186         StringBuffer intBuf = new StringBuffer();
1187 
1188         intBuf.append("[Class: ");
1189         intBuf.append(component.getClass().getName());
1190         if(component instanceof UIViewRoot)
1191         {
1192             intBuf.append(",ViewId: ");
1193             intBuf.append(((UIViewRoot) component).getViewId());
1194         }
1195         else
1196         {
1197             intBuf.append(",Id: ");
1198             intBuf.append(component.getId());
1199         }
1200         intBuf.append("]");
1201 
1202         buf.insert(0,(Object)intBuf);
1203 
1204         getPathToComponent(component.getParent(),buf);
1205     }
1206 
1207 }