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 java.io.IOException;
22  import java.util.ArrayList;
23  import java.util.HashMap;
24  import java.util.HashSet;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Set;
28  import java.util.Stack;
29  import java.util.concurrent.atomic.AtomicInteger;
30  import java.util.logging.Level;
31  
32  import javax.faces.component.NamingContainer;
33  import javax.faces.component.UIComponent;
34  import javax.faces.component.UIOutput;
35  import javax.faces.component.UIViewRoot;
36  import javax.faces.context.FacesContext;
37  import javax.faces.render.ResponseStateManager;
38  import javax.servlet.jsp.JspException;
39  import javax.servlet.jsp.JspWriter;
40  import javax.servlet.jsp.PageContext;
41  import javax.servlet.jsp.jstl.core.LoopTag;
42  import javax.servlet.jsp.tagext.BodyContent;
43  import javax.servlet.jsp.tagext.BodyTag;
44  import javax.servlet.jsp.tagext.JspIdConsumer;
45  import javax.servlet.jsp.tagext.Tag;
46  
47  /**
48   * @author Bruno Aranda (latest modification by $Author: bommel $)
49   * @author Manfred Geiler
50   * @author Dennis Byrne
51   * @version $Revision: 1187700 $ $Date: 2011-10-22 07:19:37 -0500 (Sat, 22 Oct 2011) $
52   * 
53   * @since 1.2
54   */
55  
56  public abstract class UIComponentClassicTagBase extends UIComponentTagBase implements BodyTag, JspIdConsumer
57  {
58  
59      // do not change this w/out doing likewise in UIComponentTag
60      private static final String COMPONENT_STACK_ATTR = "org.apache.myfaces.COMPONENT_STACK";
61  
62      private static final String REQUEST_FACES_CONTEXT = "org.apache.myfaces.REQUEST_FACES_CONTEXT";
63  
64      private static final String VIEW_IDS = "org.apache.myfaces.VIEW_IDS";
65  
66      private static final String FORMER_CHILD_IDS_SET_ATTR = "org.apache.myfaces.FORMER_CHILD_IDS";
67      private static final String FORMER_FACET_NAMES_SET_ATTR = "org.apache.myfaces.FORMER_FACET_NAMES";
68  
69      private static final String PREVIOUS_JSP_IDS_SET = "org.apache.myfaces.PREVIOUS_JSP_IDS_SET";
70  
71      private static final String BOUND_VIEW_ROOT = "org.apache.myfaces.BOUND_VIEW_ROOT";
72      
73      private static final String LOGICAL_PAGE_ID = "org.apache.myfaces.LOGICAL_PAGE_ID";
74      
75      private static final String LOGICAL_PAGE_COUNTER = "org.apache.myfaces.LOGICAL_PAGE_COUNTER";
76  
77      protected static final String UNIQUE_ID_PREFIX = UIViewRoot.UNIQUE_ID_PREFIX + "_";
78  
79      protected PageContext pageContext = null;
80      protected BodyContent bodyContent = null;
81  
82      private boolean _created = false;
83  
84      private String _jspId = null;
85      private String _facesJspId = null;
86  
87      private List<String> _childrenAdded = null;
88      private List<String> _facetsAdded = null;
89  
90      private UIComponent _componentInstance = null;
91      private String _id = null;
92  
93      private boolean isInAnIterator;
94  
95      // the parent tag
96      private Tag _parent = null;
97  
98      // the enclosing "classic" parent tag
99      private UIComponentClassicTagBase _parentClassicTag = null;
100 
101     private FacesContext _facesContext = null;
102 
103     protected abstract void setProperties(UIComponent component);
104 
105     protected abstract UIComponent createComponent(FacesContext context, String newId) throws JspException;
106 
107     public void release()
108     {
109         internalRelease();
110 
111         // members, that must/need only be reset when there is no more risk, that the container
112         // wants to reuse this tag
113         pageContext = null;
114         _parent = null;
115         _jspId = null;
116         _id = null;
117         _facesJspId = null;
118         bodyContent = null;
119     }
120 
121     /**
122      * Reset any members that apply to the according component instance and must not be reused if the container wants to
123      * reuse this tag instance. This method is called when rendering for this tag is finished ( doEndTag() ) or when
124      * released by the container.
125      */
126     private void internalRelease()
127     {
128         _facesContext = null;
129         _componentInstance = null;
130         _created = false;
131 
132         _childrenAdded = null;
133         _facetsAdded = null;
134     }
135 
136     /**
137      * @see http://java.sun.com/javaee/5/docs/api/javax/faces/webapp/UIComponentClassicTagBase.html#getCreated()
138      */
139     @Override
140     public boolean getCreated()
141     {
142         return _created;
143     }
144 
145     protected List<String> getCreatedComponents()
146     {
147         return _childrenAdded;
148     }
149 
150     /**
151      * @see http://java.sun.com/javaee/5/docs/api/javax/faces/webapp/UIComponentClassicTagBase.html#
152      *      getParentUIComponentClassicTagBase(javax.servlet.jsp.PageContext)
153      * @param pageContext
154      * @return
155      */
156     public static UIComponentClassicTagBase getParentUIComponentClassicTagBase(PageContext pageContext)
157     {
158         Stack<UIComponentClassicTagBase> stack = getStack(pageContext);
159 
160         int size = stack.size();
161 
162         return size > 0 ? stack.get(size - 1) : null;
163     }
164 
165     /**
166      * @see http://java.sun.com/javaee/5/docs/api/javax/faces/webapp/UIComponentClassicTagBase.html#getFacesJspId()
167      * @return
168      */
169     public String getJspId()
170     {
171         return _jspId;
172     }
173 
174     public void setJspId(String jspId)
175     {
176         // -= Leonardo Uribe =- The javadoc says the following about this method:
177         //
178         // 1. This method is called by the container before doStartTag(). 
179         // 2. The argument is guaranteed to be unique within the page.
180         //
181         // Doing some tests it was found that the jspId generated in a
182         // jsp:include are "reset", so if before call it it was id10
183         // the tags inside jsp:include starts from id1 (really I suppose a
184         // different counter is used), so if we assign this one
185         // directly it is possible to cause duplicate id exceptions later.
186         //
187         // One problem is caused by f:view tag. This one is not included when
188         // we check for duplicate id, so it is possible to assign to a component
189         // in a jsp:include the id of the UIViewRoot instance and cause a 
190         // duplicate id exception when the view is saved.
191         //
192         // Checking the javadoc it was found the following note:
193         //
194         // "... IMPLEMENTATION NOTE: This method will detect where we are in an 
195         // include and assign a unique ID for each include in a particular 'logical page'. 
196         // This allows us to avoid possible duplicate ID situations for included pages 
197         // that have components without explicit IDs..."
198         //
199         // So we need to keep a counter per logical page or page context found. 
200         // It is assumed the first one should not be suffixed. The others needs to be
201         // suffixed, so all generated ids of those pages are different. The final result
202         // is that jsp:include works correctly.
203         //
204         // Note this implementation detail takes precedence over c:forEach tag. If a
205         // jsp:include is inside a c:forEach, jsp:include takes precedence and the 
206         // iteration prefix is ignored. If a custom id is provided for a component, 
207         // it will throw duplicate id exception, because this code is "override" 
208         // by the custom id, and the iteration suffix only applies on generated ids.
209         Integer logicalPageId = (Integer) pageContext.getAttribute(LOGICAL_PAGE_ID);
210         
211         if (logicalPageId != null)
212         {
213             if (logicalPageId.intValue() == 1)
214             {
215                 //Base case, just pass it unchanged
216                 _jspId = jspId;
217             }
218             else
219             {
220                 // We are on a different page context, suffix it with the logicalPageId
221                 _jspId = jspId + "pc" + logicalPageId;
222             }
223         }
224         else
225         {
226             Map<Object, Object> attributeMap = getFacesContext().getAttributes();
227             AtomicInteger logicalPageCounter = (AtomicInteger) attributeMap.get(LOGICAL_PAGE_COUNTER);
228             
229             if (logicalPageCounter == null)
230             {
231                 //We are processing the first component tag. 
232                 logicalPageCounter = new AtomicInteger(1);
233                 logicalPageId = 1;
234                 attributeMap.put(LOGICAL_PAGE_COUNTER, logicalPageCounter);
235                 pageContext.setAttribute(LOGICAL_PAGE_ID, logicalPageId);
236             }
237             else
238             {
239                 //We are on a different page context, so we need to assign and set.
240                 logicalPageId = logicalPageCounter.incrementAndGet();
241                 pageContext.setAttribute(LOGICAL_PAGE_ID, logicalPageId);
242                 _jspId = jspId + "pc" + logicalPageId;
243             }
244         }
245         _facesJspId = null;
246         checkIfItIsInAnIterator(_jspId);
247     }
248 
249     /**
250      * @param child
251      * @see http://java.sun.com/javaee/5/docs/api/javax/faces/webapp/UIComponentTagBase.html#addChild(javax.faces.
252      *      _componentInstance.UIComponent)
253      */
254 
255     @Override
256     protected void addChild(UIComponent child)
257     {
258         if (_childrenAdded == null)
259         {
260             _childrenAdded = new ArrayList<String>();
261         }
262 
263         _childrenAdded.add(child.getId());
264     }
265 
266     /**
267      * @param name
268      * @see http://java.sun.com/javaee/5/docs/api/javax/faces/webapp/UIComponentTagBase.html#addFacet(java.lang.String)
269      */
270     @Override
271     protected void addFacet(String name)
272     {
273         if (_facetsAdded == null)
274         {
275             _facetsAdded = new ArrayList<String>();
276         }
277 
278         _facetsAdded.add(name);
279     }
280 
281     /**
282      * Return the UIComponent instance associated with this tag.
283      * 
284      * @return a UIComponent, never null.
285      */
286     @Override
287     public UIComponent getComponentInstance()
288     {
289         return _componentInstance;
290     }
291 
292     /**
293      * @return
294      * @see http://java.sun.com/javaee/5/docs/api/javax/faces/webapp/UIComponentTagBase.html#getFacesContext()
295      */
296 
297     @Override
298     protected FacesContext getFacesContext()
299     {
300         if (_facesContext != null)
301         {
302             return _facesContext;
303         }
304 
305         _facesContext = pageContext == null ? null : (FacesContext)pageContext.getAttribute(REQUEST_FACES_CONTEXT);
306 
307         if (_facesContext != null)
308         {
309             return _facesContext;
310         }
311 
312         _facesContext = FacesContext.getCurrentInstance();
313 
314         if (_facesContext != null)
315         {
316             if (pageContext != null)
317             {
318                 pageContext.setAttribute(REQUEST_FACES_CONTEXT, _facesContext);
319             }
320             return _facesContext;
321         }
322 
323         // should never be reached
324         throw new RuntimeException("FacesContext not found");
325     }
326 
327     /**
328      * @return
329      * @see http://java.sun.com/javaee/5/docs/api/javax/faces/webapp/UIComponentTagBase.html#getIndexOfNextChildTag()
330      */
331 
332     @Override
333     protected int getIndexOfNextChildTag()
334     {
335         if (_childrenAdded == null)
336         {
337             return 0;
338         }
339 
340         return _childrenAdded.size();
341     }
342 
343     /**
344      * @param id
345      * @see http://java.sun.com/javaee/5/docs/api/javax/faces/webapp/UIComponentTagBase.html#setId(java.lang.String)
346      */
347     @Override
348     public void setId(String id)
349     {
350         if (id != null && id.startsWith(UIViewRoot.UNIQUE_ID_PREFIX))
351         {
352             throw new IllegalArgumentException("Id is non-null and starts with UIViewRoot.UNIQUE_ID_PREFIX: " + id);
353         }
354 
355         _id = id;
356     }
357 
358     /**
359      * Return the id (if any) specified as an xml attribute on this tag.
360      */
361     protected String getId()
362     {
363         return _id;
364     }
365 
366     protected String getFacesJspId()
367     {
368         if (_facesJspId == null)
369         {
370             if (_jspId != null)
371             {
372                 _facesJspId = UNIQUE_ID_PREFIX + _jspId;
373 
374                 if (isIdDuplicated(_facesJspId))
375                 {
376                     _facesJspId = createNextId(_facesJspId);
377                 }
378             }
379             else
380             {
381                 _facesJspId = _facesContext.getViewRoot().createUniqueId();
382             }
383         }
384 
385         return _facesJspId;
386     }
387 
388     public void setBodyContent(BodyContent bodyContent)
389     {
390         this.bodyContent = bodyContent;
391     }
392 
393     public void doInitBody() throws JspException
394     {
395         // nothing by default
396     }
397 
398     @SuppressWarnings("unchecked")
399     public int doAfterBody() throws JspException
400     {
401         UIComponentClassicTagBase parentTag = getParentUIComponentClassicTagBase(pageContext);
402 
403         if (isRootTag(parentTag) || isInRenderedChildrenComponent(parentTag))
404         {
405             UIComponent verbatimComp = createVerbatimComponentFromBodyContent();
406 
407             if (verbatimComp != null)
408             {
409                 List<String> childrenAddedIds =
410                         (List<String>)_componentInstance.getAttributes().get(FORMER_CHILD_IDS_SET_ATTR);
411 
412                 if (childrenAddedIds == null)
413                 {
414                     _componentInstance.getChildren().add(verbatimComp);
415                 }
416                 else
417                 {
418                     int index = _componentInstance.getChildCount();
419                     if (childrenAddedIds.size() == index)
420                     {
421                         // verbatim already present, replace it
422                         _componentInstance.getChildren().add(index - 1, verbatimComp);
423                     }
424                     else
425                     {
426                         _componentInstance.getChildren().add(verbatimComp);
427                     }
428                 }
429 
430                 // also tell the parent-tag about the new component instance
431                 if (parentTag != null)
432                 {
433                     parentTag.addChild(verbatimComp);
434                 }
435             }
436         }
437 
438         return getDoAfterBodyValue();
439     }
440 
441     /**
442      * Standard method invoked by the JSP framework to inform this tag of the PageContext associated with the jsp page
443      * currently being processed.
444      */
445     public void setPageContext(PageContext pageContext)
446     {
447         this.pageContext = pageContext;
448     }
449 
450     /**
451      * Returns the enclosing JSP tag object. Note that this is not necessarily a JSF tag.
452      */
453     public Tag getParent()
454     {
455         return _parent;
456     }
457 
458     /**
459      * Standard method invoked by the JSP framework to inform this tag of the enclosing JSP tag object.
460      */
461     public void setParent(Tag tag)
462     {
463         this._parent = tag;
464     }
465 
466     public BodyContent getBodyContent()
467     {
468         return bodyContent;
469     }
470 
471     public int doStartTag() throws JspException
472     {
473         this._facesContext = getFacesContext();
474 
475         if (_facesContext == null)
476         {
477             throw new JspException("FacesContext not found");
478         }
479 
480         _childrenAdded = null;
481         _facetsAdded = null;
482 
483         _parentClassicTag = getParentUIComponentClassicTagBase(pageContext);
484 
485         UIComponent verbatimComp = null;
486 
487         // create the verbatim component if not inside a facet (facets are rendered
488         // by their parents) and in a component that renders children
489         if (!isFacet())
490         {
491             Tag parent = getParent();
492 
493             // flush if in a loop tag and not in a jsp tag
494             if (parent != null && parent instanceof LoopTag)
495             {
496                 JspWriter outWriter = pageContext.getOut();
497                 boolean insideJspTag = (outWriter instanceof BodyContent);
498 
499                 if (!insideJspTag)
500                 {
501                     try
502                     {
503                         outWriter.flush();
504                     }
505                     catch (IOException e)
506                     {
507                         throw new JspException("Exception flushing when creating verbatim _componentInstance", e);
508                     }
509                 }
510             }
511 
512             // create the transient _componentInstance
513             if (_parentClassicTag != null)
514             {
515                 verbatimComp = _parentClassicTag.createVerbatimComponentFromBodyContent();
516             }
517         }
518 
519         // find the _componentInstance for this tag
520         _componentInstance = findComponent(_facesContext);
521 
522         // add the verbatim component
523         if (verbatimComp != null && _parentClassicTag != null)
524         {
525             addVerbatimBeforeComponent(_parentClassicTag, verbatimComp, _componentInstance);
526         }
527 
528         Map<String, Object> viewComponentIds = getViewComponentIds();
529 
530         // check that the instance returned by the client ID for the viewComponentIds
531         // is the same like this one, so we do not perform again the check for duplicated ids
532         Object tagInstance = null;
533         String clientId = null;
534         if (_id != null)
535         {
536             clientId = _componentInstance.getClientId(_facesContext);
537             tagInstance = (viewComponentIds.get(clientId) == this) ? this : null;
538         }
539 
540         if (tagInstance == null)
541         {
542             // check for duplicated IDs
543             if (_id != null)
544             {
545                 if (clientId != null)
546                 {
547                     if (viewComponentIds.containsKey(clientId))
548                     {
549                         throw new JspException("Duplicated component Id: '" + clientId + "' " + "for component: '"
550                                 + getPathToComponent(_componentInstance) + "'.");
551                     }
552 
553                     viewComponentIds.put(clientId, this);
554                 }
555             }
556 
557             // add to the component or facet to parent
558             if (_parentClassicTag != null)
559             {
560                 if (isFacet())
561                 {
562                     _parentClassicTag.addFacet(getFacetName());
563                 }
564                 else
565                 {
566                     _parentClassicTag.addChild(_componentInstance);
567                 }
568             }
569         }
570 
571         // push this tag on the stack
572         pushTag();
573 
574         return getDoStartValue();
575     }
576 
577     public int doEndTag() throws JspException
578     {
579         popTag();
580         UIComponent component = getComponentInstance();
581 
582         removeFormerChildren(component);
583         removeFormerFacets(component);
584 
585         try
586         {
587             UIComponentClassicTagBase parentTag = getParentUIComponentClassicTagBase(pageContext);
588 
589             UIComponent verbatimComp = createVerbatimComponentFromBodyContent();
590 
591             if (verbatimComp != null)
592             {
593                 component.getChildren().add(verbatimComp);
594 
595                 if (parentTag != null)
596                 {
597                     parentTag.addChild(verbatimComp);
598                 }
599             }
600         }
601         catch (Throwable e)
602         {
603             throw new JspException(e);
604         }
605         finally
606         {
607             component = null;
608         }
609 
610         int retValue = getDoEndValue();
611 
612         internalRelease();
613 
614         return retValue;
615     }
616 
617     /**
618      * @throws JspException  
619      */
620     protected int getDoAfterBodyValue() throws JspException
621     {
622         return SKIP_BODY;
623     }
624 
625     /**
626      * Get the value to be returned by the doStartTag method to the JSP framework. Subclasses which wish to use the
627      * inherited doStartTag but control whether the tag is permitted to contain nested tags or not can just override
628      * this method to return Tag.SOME_CONSTANT.
629      * 
630      * @return BodyTag.EVAL_BODY_BUFFERED
631      * @throws JspException 
632      */
633     protected int getDoStartValue() throws JspException
634     {
635         return BodyTag.EVAL_BODY_BUFFERED;
636     }
637 
638     /**
639      * Get the value to be returned by the doEndTag method to the JSP framework. Subclasses which wish to use the
640      * inherited doEndTag but control whether the tag is permitted to contain nested tags or not can just override this
641      * method to return Tag.SOME_CONSTANT.
642      * 
643      * @return Tag.EVAL_PAGE
644      * @throws JspException 
645      */
646     protected int getDoEndValue() throws JspException
647     {
648         return Tag.EVAL_PAGE;
649     }
650 
651     protected String getFacetName()
652     {
653         return isFacet() ? ((FacetTag)_parent).getName() : null;
654     }
655 
656     /**
657      * Creates a UIComponent from the BodyContent
658      */
659     protected UIComponent createVerbatimComponentFromBodyContent()
660     {
661         UIOutput verbatimComp = null;
662 
663         if (bodyContent != null)
664         {
665             String strContent = bodyContent.getString();
666 
667             if (strContent != null)
668             {
669                 String trimmedContent = strContent.trim();
670                 if (trimmedContent.length() > 0 && !isComment(strContent))
671                 {
672                     verbatimComp = createVerbatimComponent();
673                     verbatimComp.setValue(strContent);
674                 }
675             }
676 
677             bodyContent.clearBody();
678         }
679 
680         return verbatimComp;
681     }
682 
683     private static boolean isComment(String bodyContent)
684     {
685         return (bodyContent.startsWith("<!--") && bodyContent.endsWith("-->"));
686     }
687 
688     /**
689      * <p>
690      * Creates a transient UIOutput using the Application, with the following characteristics:
691      * </p>
692      * <p/>
693      * <p>
694      * <code>componentType</code> is <code>javax.faces.HtmlOutputText</code>.
695      * </p>
696      * <p/>
697      * <p>
698      * <code>transient</code> is <code>true</code>.
699      * </p>
700      * <p/>
701      * <p>
702      * <code>escape</code> is <code>false</code>.
703      * </p>
704      * <p/>
705      * <p>
706      * <code>id</code> is <code>FacesContext.getViewRoot().createUniqueId()</code>
707      * </p>
708      */
709     protected UIOutput createVerbatimComponent()
710     {
711         UIOutput verbatimComp =
712                 (UIOutput)getFacesContext().getApplication().createComponent("javax.faces.HtmlOutputText");
713         verbatimComp.setTransient(true);
714         verbatimComp.getAttributes().put("escape", Boolean.FALSE);
715         verbatimComp.setId(getFacesContext().getViewRoot().createUniqueId());
716 
717         return verbatimComp;
718     }
719 
720     @SuppressWarnings("unchecked")
721     protected void addVerbatimBeforeComponent(UIComponentClassicTagBase parentTag, UIComponent verbatimComp,
722                                               UIComponent component)
723     {
724         UIComponent parent = component.getParent();
725 
726         if (parent == null)
727         {
728             return;
729         }
730 
731         List<UIComponent> children = parent.getChildren();
732         // EDGE CASE:
733         // Consider CASE 1 or 2 where the _componentInstance is provided via a
734         // _componentInstance binding in session or application scope.
735         // The automatically created UIOuput instances for the template text
736         // will already be present. Check the JSP_CREATED_COMPONENT_IDS attribute,
737         // if present and the number of created components is the same
738         // as the number of children replace at a -1 offset from the current
739         // value of indexOfComponentInParent, otherwise, call add()
740 
741         List<String> childrenAddedIds = (List<String>)parent.getAttributes().get(FORMER_CHILD_IDS_SET_ATTR);
742 
743         int parentIndex = children.indexOf(component);
744 
745         if (childrenAddedIds != null)
746         {
747             if (parentIndex > 0 && childrenAddedIds.size() == parentIndex)
748             {
749                 UIComponent formerVerbatim = children.get(parentIndex - 1);
750 
751                 if (formerVerbatim instanceof UIOutput && formerVerbatim.isTransient())
752                 {
753                     children.set(parentIndex - 1, verbatimComp);
754                 }
755             }
756         }
757 
758         children.add(parentIndex, verbatimComp);
759 
760         parentTag.addChild(verbatimComp);
761     }
762 
763     /**
764      * <p>
765      * Add <i>verbatim</i> as a sibling of <i>_componentInstance</i> in <i>_componentInstance</i> in the parent's child
766      * list. <i>verbatim</i> is added to the list at the position immediatly following <i>_componentInstance</i>.
767      * </p>
768      */
769 
770     protected void addVerbatimAfterComponent(UIComponentClassicTagBase parentTag, UIComponent verbatim,
771                                              UIComponent component)
772     {
773         int indexOfComponentInParent = 0;
774         UIComponent parent = component.getParent();
775 
776         // invert the order of this if and the assignment below. Since this line is
777         // here, it appears an early return is acceptable/desired if parent is null,
778         // and, if it is null, we should probably check for that before we try to
779         // access it. 2006-03-15 jdl
780         if (null == parent)
781         {
782             return;
783         }
784         List<UIComponent> children = parent.getChildren();
785         indexOfComponentInParent = children.indexOf(component);
786         if (children.size() - 1 == indexOfComponentInParent)
787         {
788             children.add(verbatim);
789         }
790         else
791         {
792             children.add(indexOfComponentInParent + 1, verbatim);
793         }
794         parentTag.addChild(verbatim);
795     }
796 
797     /**
798      * @deprecated the ResponseWriter is now set by {@link javax.faces.application.ViewHandler#renderView}
799      */
800     protected void setupResponseWriter()
801     {
802     }
803 
804     /**
805      * Invoke encodeBegin on the associated UIComponent. Subclasses can override this method to perform custom
806      * processing before or after the UIComponent method invocation.
807      */
808     protected void encodeBegin() throws IOException
809     {
810         if (log.isLoggable(Level.FINE))
811             log.fine("Entered encodeBegin for client-Id: " + _componentInstance.getClientId(getFacesContext()));
812         _componentInstance.encodeBegin(getFacesContext());
813         if (log.isLoggable(Level.FINE))
814             log.fine("Exited encodeBegin");
815     }
816 
817     /**
818      * Invoke encodeChildren on the associated UIComponent. Subclasses can override this method to perform custom
819      * processing before or after the UIComponent method invocation. This is only invoked for components whose
820      * getRendersChildren method returns true.
821      */
822     protected void encodeChildren() throws IOException
823     {
824         if (log.isLoggable(Level.FINE))
825             log.fine("Entered encodeChildren for client-Id: " + _componentInstance.getClientId(getFacesContext()));
826         _componentInstance.encodeChildren(getFacesContext());
827         if (log.isLoggable(Level.FINE))
828             log.fine("Exited encodeChildren for client-Id: " + _componentInstance.getClientId(getFacesContext()));
829     }
830 
831     /**
832      * Invoke encodeEnd on the associated UIComponent. Subclasses can override this method to perform custom processing
833      * before or after the UIComponent method invocation.
834      */
835     protected void encodeEnd() throws IOException
836     {
837         if (log.isLoggable(Level.FINE))
838             log.fine("Entered encodeEnd for client-Id: " + _componentInstance.getClientId(getFacesContext()));
839         _componentInstance.encodeEnd(getFacesContext());
840         if (log.isLoggable(Level.FINE))
841             log.fine("Exited encodeEnd for client-Id: " + _componentInstance.getClientId(getFacesContext()));
842 
843     }
844 
845     private boolean isRootTag(UIComponentClassicTagBase parentTag)
846     {
847         return (parentTag == this);
848     }
849 
850     private boolean isInRenderedChildrenComponent(UIComponentClassicTagBase tag)
851     {
852         return (_parentClassicTag != null && tag.getComponentInstance().getRendersChildren());
853     }
854 
855     private boolean isFacet()
856     {
857         return _parent != null && _parent instanceof FacetTag;
858     }
859 
860     /** Map of <ID,Tag> in the view */
861     @SuppressWarnings("unchecked")
862     private Map<String, Object> getViewComponentIds()
863     {
864         Map<Object, Object> attributes = _facesContext.getAttributes();
865         Map<String, Object> viewComponentIds;
866 
867         if (_parent == null)
868         {
869             // top level _componentInstance
870             viewComponentIds = new HashMap<String, Object>();
871             attributes.put(VIEW_IDS, viewComponentIds);
872         }
873         else
874         {
875             viewComponentIds = (Map<String, Object>) attributes.get(VIEW_IDS);
876             
877             // Check if null, this can happen if someone programatically tries to do an include of a 
878             // JSP fragment. This code will prevent NullPointerException from happening in such cases.
879             if (viewComponentIds == null)
880             {
881                 viewComponentIds = new HashMap<String, Object>();
882                 attributes.put(VIEW_IDS, viewComponentIds);
883             }
884         }
885 
886         return viewComponentIds;
887     }
888 
889     @SuppressWarnings("unchecked")
890     private static final Stack<UIComponentClassicTagBase> getStack(PageContext pageContext)
891     {
892         Stack<UIComponentClassicTagBase> stack =
893                 (Stack<UIComponentClassicTagBase>)pageContext.getAttribute(COMPONENT_STACK_ATTR,
894                     PageContext.REQUEST_SCOPE);
895 
896         if (stack == null)
897         {
898             stack = new Stack<UIComponentClassicTagBase>();
899             pageContext.setAttribute(COMPONENT_STACK_ATTR, stack, PageContext.REQUEST_SCOPE);
900         }
901 
902         return stack;
903     }
904 
905     /**
906      * 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
907      * find the parent tag that encloses it. Access to the parent tag is used to find the parent UIComponent for the
908      * component associated with this tag plus some other uses.
909      */
910     private void popTag()
911     {
912         Stack<UIComponentClassicTagBase> stack = getStack(pageContext);
913 
914         int size = stack.size();
915         stack.remove(size - 1);
916         if (size <= 1)
917             pageContext.removeAttribute(COMPONENT_STACK_ATTR, PageContext.REQUEST_SCOPE);
918 
919     }
920 
921     private void pushTag()
922     {
923         getStack(pageContext).add(this);
924     }
925 
926     //private boolean isIncludedOrForwarded() {
927     //    return getFacesContext().getExternalContext().getRequestMap().
928     //            containsKey("javax.servlet.include.request_uri");
929     //}
930 
931     /** Generate diagnostic output. */
932     private String getPathToComponent(UIComponent component)
933     {
934         StringBuffer buf = new StringBuffer();
935 
936         if (component == null)
937         {
938             buf.append("{Component-Path : ");
939             buf.append("[null]}");
940             return buf.toString();
941         }
942 
943         getPathToComponent(component, buf);
944 
945         buf.insert(0, "{Component-Path : ");
946         buf.append("}");
947 
948         return buf.toString();
949     }
950 
951     /** Generate diagnostic output. */
952     private static void getPathToComponent(UIComponent component, StringBuffer buf)
953     {
954         if (component == null)
955             return;
956 
957         StringBuffer intBuf = new StringBuffer();
958 
959         intBuf.append("[Class: ");
960         intBuf.append(component.getClass().getName());
961         if (component instanceof UIViewRoot)
962         {
963             intBuf.append(",ViewId: ");
964             intBuf.append(((UIViewRoot)component).getViewId());
965         }
966         else
967         {
968             intBuf.append(",Id: ");
969             intBuf.append(component.getId());
970         }
971         intBuf.append("]");
972 
973         buf.insert(0, intBuf);
974 
975         getPathToComponent(component.getParent(), buf);
976     }
977 
978     /**
979      * Remove any child components of the associated components which do not have corresponding tags as children of this
980      * tag. This only happens when a view is being re-rendered and there are components in the view tree which don't
981      * have corresponding JSP tags. Wrapping JSF tags in JSTL "c:if" statements is one way this can happen. <br />
982      * Attention: programmatically added components are are not affected by this: they will not be on the old list of
983      * created components nor on the new list of created components, so nothing will happen to them.
984      */
985     @SuppressWarnings("unchecked")
986     private void removeFormerChildren(UIComponent component)
987     {
988         List<String> formerChildIds = (List<String>)component.getAttributes().get(FORMER_CHILD_IDS_SET_ATTR);
989         if (formerChildIds != null)
990         {
991             for (String childId : formerChildIds)
992             {
993                 if (_childrenAdded == null || !_childrenAdded.contains(childId))
994                 {
995                     UIComponent childToRemove = component.findComponent(childId);
996                     if (childToRemove != null)
997                     {
998                         component.getChildren().remove(childToRemove);
999                     }
1000                 }
1001             }
1002             if (_childrenAdded == null)
1003             {
1004                 component.getAttributes().remove(FORMER_CHILD_IDS_SET_ATTR);
1005             }
1006             else
1007             {
1008                 component.getAttributes().put(FORMER_CHILD_IDS_SET_ATTR, _childrenAdded);
1009             }
1010         }
1011         else
1012         {
1013             if (_childrenAdded != null)
1014             {
1015                 component.getAttributes().put(FORMER_CHILD_IDS_SET_ATTR, _childrenAdded);
1016             }
1017         }
1018     }
1019 
1020     /** See removeFormerChildren. */
1021     @SuppressWarnings("unchecked")
1022     private void removeFormerFacets(UIComponent component)
1023     {
1024         List<String> formerFacetNames = (List<String>)component.getAttributes().get(FORMER_FACET_NAMES_SET_ATTR);
1025         if (formerFacetNames != null)
1026         {
1027             for (String facetName : formerFacetNames)
1028             {
1029                 if (_facetsAdded == null || !_facetsAdded.contains(facetName))
1030                 {
1031                     component.getFacets().remove(facetName);
1032                 }
1033             }
1034             if (_facetsAdded == null)
1035             {
1036                 component.getAttributes().remove(FORMER_FACET_NAMES_SET_ATTR);
1037             }
1038             else
1039             {
1040                 component.getAttributes().put(FORMER_FACET_NAMES_SET_ATTR, _facetsAdded);
1041             }
1042         }
1043         else
1044         {
1045             if (_facetsAdded != null)
1046             {
1047                 component.getAttributes().put(FORMER_FACET_NAMES_SET_ATTR, _facetsAdded);
1048             }
1049         }
1050     }
1051 
1052     /**
1053      * Return the corresponding UIComponent for this tag, creating it if necessary.
1054      * <p>
1055      * If this is not the first time this method has been called, then return the cached _componentInstance instance
1056      * found last time.
1057      * <p>
1058      * If this is not the first time this view has been seen, then locate the existing _componentInstance using the id
1059      * attribute assigned to this tag and return it. Note that this is simple for components with user-assigned ids. For
1060      * components with generated ids, the "reattachment" relies on the fact that UIViewRoot will generate the same id
1061      * values for tags in this page as it did when first generating the view. For this reason all JSF tags within a JSTL
1062      * "c:if" are required to have explicitly-assigned ids.
1063      * <p>
1064      * Otherwise create the _componentInstance, populate its properties from the xml attributes on this JSP tag and
1065      * attach it to its parent.
1066      * <p>
1067      * When a _componentInstance is found or created the parent JSP tag is also told that the _componentInstance has
1068      * been "seen". When the parent tag ends it will delete any components which were in the view previously but have
1069      * not been seen this time; see doEndTag for more details.
1070      */
1071     protected UIComponent findComponent(FacesContext context) throws JspException
1072     {
1073         // 1. If we have previously located this component, return it.
1074         if (_componentInstance != null)
1075         {
1076             return _componentInstance;
1077         }
1078 
1079         // 2. Locate the parent component by looking for a parent UIComponentTag instance,
1080         // and ask it for its component. If there is no parent UIComponentTag instance,
1081         // this tag represents the root component, so get it from the current Tree and return it.
1082         UIComponentClassicTagBase parentTag = getParentUIComponentClassicTagBase(pageContext);
1083 
1084         if (parentTag == null)
1085         {
1086             // This is the root
1087             _componentInstance = context.getViewRoot();
1088 
1089             // check if the view root is already bound to the tag
1090             Object alreadyBoundViewRootFlag = _componentInstance.getAttributes().get(BOUND_VIEW_ROOT);
1091 
1092             if (alreadyBoundViewRootFlag == null)
1093             {
1094                 try
1095                 {
1096                     setProperties(_componentInstance);
1097                 }
1098                 catch (Throwable e)
1099                 {
1100                     throw new JspException(e);
1101                 }
1102 
1103                 if (_id != null)
1104                 {
1105                     _componentInstance.setId(_id);
1106                 }
1107                 else
1108                 {
1109                     _componentInstance.setId(getFacesJspId());
1110                 }
1111                 _componentInstance.getAttributes().put(BOUND_VIEW_ROOT, true);
1112                 _created = true;
1113 
1114             }
1115             else if (hasBinding())
1116             {
1117                 setProperties(_componentInstance);
1118             }
1119 
1120             return _componentInstance;
1121         }
1122 
1123         UIComponent parent = parentTag.getComponentInstance();
1124 
1125         if (parent == null)
1126         {
1127             throw new IllegalStateException("parent is null?");
1128         }
1129 
1130         String facetName = getFacetName();
1131         if (facetName != null)
1132         {
1133             // Facet
1134             String id = createUniqueId(context, parent);
1135             _componentInstance = parent.getFacet(facetName);
1136             if (_componentInstance == null)
1137             {
1138                 _componentInstance = createComponent(context, id);
1139                 _created = true;
1140                 parent.getFacets().put(facetName, _componentInstance);
1141             }
1142             else
1143             {
1144                 if (checkFacetNameOnParentExists(parentTag, facetName))
1145                 {
1146                     throw new IllegalStateException("facet '" + facetName
1147                             + "' already has a child associated. current associated _componentInstance id: "
1148                             + _componentInstance.getClientId(context) + " class: "
1149                             + _componentInstance.getClass().getName());
1150                 }
1151             }
1152 
1153             addFacetNameToParentTag(parentTag, facetName);
1154             return _componentInstance;
1155         }
1156 
1157         // Child
1158         //
1159         // Note that setProperties is called only when we create the
1160         // _componentInstance; on later passes, the attributes defined on the
1161         // JSP tag are set on this Tag object, but then completely
1162         // ignored.
1163 
1164         String id = createUniqueId(context, parent);
1165 
1166         // Warn users that this tag is about to find/steal the UIComponent
1167         // that has already been created for a sibling tag with the same id value .
1168         // _childrenAdded is a Set, and we will stomp over a past id when calling
1169         // addChildIdToParentTag.
1170         //
1171         // It would also be reasonable to throw an exception here rather than
1172         // just issue a warning as this is a pretty serious problem. However the
1173         // Sun RI just issues a warning...
1174         if (parentTag._childrenAdded != null && parentTag._childrenAdded.contains(id))
1175         {
1176             if (log.isLoggable(Level.WARNING))
1177                 log.warning("There is more than one JSF tag with an id : " + id);
1178         }
1179 
1180         _componentInstance = findComponent(parent, id);
1181         if (_componentInstance == null)
1182         {
1183             _componentInstance = createComponent(context, id);
1184             if (id.equals(_componentInstance.getId()) )
1185             {
1186             _created = true;
1187             int index = parentTag.getIndexOfNextChildTag();
1188             if (index > parent.getChildCount())
1189             {
1190                 index = parent.getChildCount();
1191             }
1192 
1193             List<UIComponent> children = parent.getChildren();
1194             children.add(index, _componentInstance);
1195         }
1196             // On weblogic portal using faces-adapter, the id set and the retrieved 
1197             // one for <netuix:namingContainer> is different. The reason is 
1198             // this custom solution for integrate jsf changes the id of the parent
1199             // component to allow the same native portlet to be allocated multiple
1200             // times in the same page
1201             else if (null == findComponent(parent,_componentInstance.getId()))
1202             {
1203                 _created = true;
1204                 int index = parentTag.getIndexOfNextChildTag();
1205                 if (index > parent.getChildCount())
1206                 {
1207                     index = parent.getChildCount();
1208                 }
1209 
1210                 List<UIComponent> children = parent.getChildren();
1211                 children.add(index, _componentInstance);
1212             }
1213         }
1214 
1215         return _componentInstance;
1216 
1217     }
1218 
1219     private UIComponent findComponent(UIComponent parent, String id)
1220     {
1221         for (UIComponent child : parent.getChildren())
1222         {
1223             if (child.getId() != null && child.getId().equals(id))
1224             {
1225                 return child;
1226             }
1227         }
1228 
1229         return null;
1230     }
1231 
1232     private String createUniqueId(FacesContext context, UIComponent parent) throws JspException
1233     {
1234         String id = getId();
1235         if (id == null)
1236         {
1237             id = getFacesJspId();
1238         }
1239         else if (isIdDuplicated(id))
1240         {
1241             if (isInAnIterator)
1242             {
1243                 setId(createNextId(id));
1244                 id = getId();
1245             }
1246             else
1247             {
1248                 if (parent != null)
1249                 {
1250 
1251                     UIComponent namingContainer;
1252 
1253                     if (parent instanceof NamingContainer)
1254                     {
1255                         namingContainer = parent;
1256                     }
1257                     else
1258                     {
1259                         namingContainer = parent.getParent();
1260                     }
1261 
1262                     if (namingContainer != null)
1263                     {
1264                         UIComponent component = namingContainer.findComponent(id);
1265 
1266                         if (component == null || isPostBack(context))
1267                         {
1268                             return id;
1269                         }
1270                     }
1271                 }
1272 
1273                 throw new JspException("Duplicated Id found in the view: " + id);
1274             }
1275         }
1276 
1277         return id;
1278     }
1279 
1280     private String createNextId(String componentId)
1281     {
1282         Integer currentCounter = (Integer) getFacesContext().getAttributes().get(componentId);
1283 
1284         int iCurrentCounter = 1;
1285 
1286         if (currentCounter != null)
1287         {
1288             iCurrentCounter = currentCounter;
1289             iCurrentCounter++;
1290         }
1291 
1292         getFacesContext().getAttributes().put(componentId, iCurrentCounter);
1293 
1294         //if (isIncludedOrForwarded())
1295         //{
1296         //    componentId = componentId + "pc" + iCurrentCounter;
1297         //}
1298         //else
1299         //{
1300         componentId = componentId + UNIQUE_ID_PREFIX + iCurrentCounter;            
1301         //}
1302 
1303         return componentId;
1304     }
1305 
1306     private void checkIfItIsInAnIterator(String jspId)
1307     {
1308         Set<String> previousJspIdsSet = getPreviousJspIdsSet();
1309 
1310         if (previousJspIdsSet.contains(jspId))
1311         {
1312             isInAnIterator = true;
1313         }
1314         else
1315         {
1316             previousJspIdsSet.add(jspId);
1317             isInAnIterator = false;
1318         }
1319     }
1320 
1321     @SuppressWarnings("unchecked")
1322     private Set<String> getPreviousJspIdsSet()
1323     {
1324         Set<String> previousJspIdsSet =
1325                 (Set<String>)getFacesContext().getAttributes().get(PREVIOUS_JSP_IDS_SET);
1326 
1327         if (previousJspIdsSet == null)
1328         {
1329             previousJspIdsSet = new HashSet<String>();
1330             // Add it to the context! The next time is called
1331             // this method it takes the ref from the RequestContext
1332             getFacesContext().getAttributes().put(PREVIOUS_JSP_IDS_SET, previousJspIdsSet);
1333         }
1334 
1335         return previousJspIdsSet;
1336     }
1337 
1338     private boolean isIdDuplicated(String componentId)
1339     {
1340         boolean result = false;
1341         if (_parentClassicTag != null)
1342         {
1343             if (_parentClassicTag.isInAnIterator)
1344             {
1345                 return true;
1346             }
1347             List<String> childComponents = _parentClassicTag.getCreatedComponents();
1348 
1349             if (childComponents != null)
1350             {
1351                 result = childComponents.contains(componentId);
1352                 if (result && (!isInAnIterator))
1353                 {
1354                     return true;
1355                 }
1356             }
1357         }
1358 
1359         return result;
1360     }
1361 
1362     private boolean isPostBack(FacesContext facesContext)
1363     {
1364         return facesContext.getExternalContext().getRequestParameterMap().containsKey(
1365             ResponseStateManager.VIEW_STATE_PARAM);
1366     }
1367 
1368     /**
1369      * check if the facet is already added to the parent
1370      */
1371     private boolean checkFacetNameOnParentExists(UIComponentClassicTagBase parentTag, String facetName)
1372     {
1373         return parentTag._facetsAdded != null && parentTag._facetsAdded.contains(facetName);
1374     }
1375 
1376     /**
1377      * Notify the enclosing JSP tag of the id of this facet's id. The parent tag will later delete any existing view
1378      * facets that were not seen during this rendering phase; see doEndTag for details.
1379      */
1380     private void addFacetNameToParentTag(UIComponentClassicTagBase parentTag, String facetName)
1381     {
1382         if (parentTag._facetsAdded == null)
1383         {
1384             parentTag._facetsAdded = new ArrayList<String>();
1385         }
1386         parentTag._facetsAdded.add(facetName);
1387     }
1388 
1389     protected abstract boolean hasBinding();
1390 
1391     public JspWriter getPreviousOut()
1392     {
1393         return bodyContent.getEnclosingWriter();
1394     }
1395 }