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: struberg $)
49   * @author Manfred Geiler
50   * @author Dennis Byrne
51   * @version $Revision: 1188235 $ $Date: 2011-10-24 12:09:33 -0500 (Mon, 24 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         {
812             log.fine("Entered encodeBegin for client-Id: " + _componentInstance.getClientId(getFacesContext()));
813         }
814         _componentInstance.encodeBegin(getFacesContext());
815         if (log.isLoggable(Level.FINE))
816         {
817             log.fine("Exited encodeBegin");
818         }
819     }
820 
821     /**
822      * Invoke encodeChildren on the associated UIComponent. Subclasses can override this method to perform custom
823      * processing before or after the UIComponent method invocation. This is only invoked for components whose
824      * getRendersChildren method returns true.
825      */
826     protected void encodeChildren() throws IOException
827     {
828         if (log.isLoggable(Level.FINE))
829         {
830             log.fine("Entered encodeChildren for client-Id: " + _componentInstance.getClientId(getFacesContext()));
831         }
832         _componentInstance.encodeChildren(getFacesContext());
833         if (log.isLoggable(Level.FINE))
834         {
835             log.fine("Exited encodeChildren for client-Id: " + _componentInstance.getClientId(getFacesContext()));
836         }
837     }
838 
839     /**
840      * Invoke encodeEnd on the associated UIComponent. Subclasses can override this method to perform custom processing
841      * before or after the UIComponent method invocation.
842      */
843     protected void encodeEnd() throws IOException
844     {
845         if (log.isLoggable(Level.FINE))
846         {
847             log.fine("Entered encodeEnd for client-Id: " + _componentInstance.getClientId(getFacesContext()));
848         }
849         _componentInstance.encodeEnd(getFacesContext());
850         if (log.isLoggable(Level.FINE))
851         {
852             log.fine("Exited encodeEnd for client-Id: " + _componentInstance.getClientId(getFacesContext()));
853         }
854 
855     }
856 
857     private boolean isRootTag(UIComponentClassicTagBase parentTag)
858     {
859         return (parentTag == this);
860     }
861 
862     private boolean isInRenderedChildrenComponent(UIComponentClassicTagBase tag)
863     {
864         return (_parentClassicTag != null && tag.getComponentInstance().getRendersChildren());
865     }
866 
867     private boolean isFacet()
868     {
869         return _parent != null && _parent instanceof FacetTag;
870     }
871 
872     /** Map of <ID,Tag> in the view */
873     @SuppressWarnings("unchecked")
874     private Map<String, Object> getViewComponentIds()
875     {
876         Map<Object, Object> attributes = _facesContext.getAttributes();
877         Map<String, Object> viewComponentIds;
878 
879         if (_parent == null)
880         {
881             // top level _componentInstance
882             viewComponentIds = new HashMap<String, Object>();
883             attributes.put(VIEW_IDS, viewComponentIds);
884         }
885         else
886         {
887             viewComponentIds = (Map<String, Object>) attributes.get(VIEW_IDS);
888             
889             // Check if null, this can happen if someone programatically tries to do an include of a 
890             // JSP fragment. This code will prevent NullPointerException from happening in such cases.
891             if (viewComponentIds == null)
892             {
893                 viewComponentIds = new HashMap<String, Object>();
894                 attributes.put(VIEW_IDS, viewComponentIds);
895             }
896         }
897 
898         return viewComponentIds;
899     }
900 
901     @SuppressWarnings("unchecked")
902     private static final Stack<UIComponentClassicTagBase> getStack(PageContext pageContext)
903     {
904         Stack<UIComponentClassicTagBase> stack =
905                 (Stack<UIComponentClassicTagBase>)pageContext.getAttribute(COMPONENT_STACK_ATTR,
906                     PageContext.REQUEST_SCOPE);
907 
908         if (stack == null)
909         {
910             stack = new Stack<UIComponentClassicTagBase>();
911             pageContext.setAttribute(COMPONENT_STACK_ATTR, stack, PageContext.REQUEST_SCOPE);
912         }
913 
914         return stack;
915     }
916 
917     /**
918      * 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
919      * find the parent tag that encloses it. Access to the parent tag is used to find the parent UIComponent for the
920      * component associated with this tag plus some other uses.
921      */
922     private void popTag()
923     {
924         Stack<UIComponentClassicTagBase> stack = getStack(pageContext);
925 
926         int size = stack.size();
927         stack.remove(size - 1);
928         if (size <= 1)
929         {
930             pageContext.removeAttribute(COMPONENT_STACK_ATTR, PageContext.REQUEST_SCOPE);
931         }
932 
933     }
934 
935     private void pushTag()
936     {
937         getStack(pageContext).add(this);
938     }
939 
940     //private boolean isIncludedOrForwarded() {
941     //    return getFacesContext().getExternalContext().getRequestMap().
942     //            containsKey("javax.servlet.include.request_uri");
943     //}
944 
945     /** Generate diagnostic output. */
946     private String getPathToComponent(UIComponent component)
947     {
948         StringBuffer buf = new StringBuffer();
949 
950         if (component == null)
951         {
952             buf.append("{Component-Path : ");
953             buf.append("[null]}");
954             return buf.toString();
955         }
956 
957         getPathToComponent(component, buf);
958 
959         buf.insert(0, "{Component-Path : ");
960         buf.append("}");
961 
962         return buf.toString();
963     }
964 
965     /** Generate diagnostic output. */
966     private static void getPathToComponent(UIComponent component, StringBuffer buf)
967     {
968         if (component == null)
969         {
970             return;
971         }
972 
973         StringBuffer intBuf = new StringBuffer();
974 
975         intBuf.append("[Class: ");
976         intBuf.append(component.getClass().getName());
977         if (component instanceof UIViewRoot)
978         {
979             intBuf.append(",ViewId: ");
980             intBuf.append(((UIViewRoot)component).getViewId());
981         }
982         else
983         {
984             intBuf.append(",Id: ");
985             intBuf.append(component.getId());
986         }
987         intBuf.append("]");
988 
989         buf.insert(0, intBuf);
990 
991         getPathToComponent(component.getParent(), buf);
992     }
993 
994     /**
995      * Remove any child components of the associated components which do not have corresponding tags as children of this
996      * tag. This only happens when a view is being re-rendered and there are components in the view tree which don't
997      * have corresponding JSP tags. Wrapping JSF tags in JSTL "c:if" statements is one way this can happen. <br />
998      * Attention: programmatically added components are are not affected by this: they will not be on the old list of
999      * created components nor on the new list of created components, so nothing will happen to them.
1000      */
1001     @SuppressWarnings("unchecked")
1002     private void removeFormerChildren(UIComponent component)
1003     {
1004         List<String> formerChildIds = (List<String>)component.getAttributes().get(FORMER_CHILD_IDS_SET_ATTR);
1005         if (formerChildIds != null)
1006         {
1007             for (String childId : formerChildIds)
1008             {
1009                 if (_childrenAdded == null || !_childrenAdded.contains(childId))
1010                 {
1011                     UIComponent childToRemove = component.findComponent(childId);
1012                     if (childToRemove != null)
1013                     {
1014                         component.getChildren().remove(childToRemove);
1015                     }
1016                 }
1017             }
1018             if (_childrenAdded == null)
1019             {
1020                 component.getAttributes().remove(FORMER_CHILD_IDS_SET_ATTR);
1021             }
1022             else
1023             {
1024                 component.getAttributes().put(FORMER_CHILD_IDS_SET_ATTR, _childrenAdded);
1025             }
1026         }
1027         else
1028         {
1029             if (_childrenAdded != null)
1030             {
1031                 component.getAttributes().put(FORMER_CHILD_IDS_SET_ATTR, _childrenAdded);
1032             }
1033         }
1034     }
1035 
1036     /** See removeFormerChildren. */
1037     @SuppressWarnings("unchecked")
1038     private void removeFormerFacets(UIComponent component)
1039     {
1040         List<String> formerFacetNames = (List<String>)component.getAttributes().get(FORMER_FACET_NAMES_SET_ATTR);
1041         if (formerFacetNames != null)
1042         {
1043             for (String facetName : formerFacetNames)
1044             {
1045                 if (_facetsAdded == null || !_facetsAdded.contains(facetName))
1046                 {
1047                     component.getFacets().remove(facetName);
1048                 }
1049             }
1050             if (_facetsAdded == null)
1051             {
1052                 component.getAttributes().remove(FORMER_FACET_NAMES_SET_ATTR);
1053             }
1054             else
1055             {
1056                 component.getAttributes().put(FORMER_FACET_NAMES_SET_ATTR, _facetsAdded);
1057             }
1058         }
1059         else
1060         {
1061             if (_facetsAdded != null)
1062             {
1063                 component.getAttributes().put(FORMER_FACET_NAMES_SET_ATTR, _facetsAdded);
1064             }
1065         }
1066     }
1067 
1068     /**
1069      * Return the corresponding UIComponent for this tag, creating it if necessary.
1070      * <p>
1071      * If this is not the first time this method has been called, then return the cached _componentInstance instance
1072      * found last time.
1073      * <p>
1074      * If this is not the first time this view has been seen, then locate the existing _componentInstance using the id
1075      * attribute assigned to this tag and return it. Note that this is simple for components with user-assigned ids. For
1076      * components with generated ids, the "reattachment" relies on the fact that UIViewRoot will generate the same id
1077      * values for tags in this page as it did when first generating the view. For this reason all JSF tags within a JSTL
1078      * "c:if" are required to have explicitly-assigned ids.
1079      * <p>
1080      * Otherwise create the _componentInstance, populate its properties from the xml attributes on this JSP tag and
1081      * attach it to its parent.
1082      * <p>
1083      * When a _componentInstance is found or created the parent JSP tag is also told that the _componentInstance has
1084      * been "seen". When the parent tag ends it will delete any components which were in the view previously but have
1085      * not been seen this time; see doEndTag for more details.
1086      */
1087     protected UIComponent findComponent(FacesContext context) throws JspException
1088     {
1089         // 1. If we have previously located this component, return it.
1090         if (_componentInstance != null)
1091         {
1092             return _componentInstance;
1093         }
1094 
1095         // 2. Locate the parent component by looking for a parent UIComponentTag instance,
1096         // and ask it for its component. If there is no parent UIComponentTag instance,
1097         // this tag represents the root component, so get it from the current Tree and return it.
1098         UIComponentClassicTagBase parentTag = getParentUIComponentClassicTagBase(pageContext);
1099 
1100         if (parentTag == null)
1101         {
1102             // This is the root
1103             _componentInstance = context.getViewRoot();
1104 
1105             // check if the view root is already bound to the tag
1106             Object alreadyBoundViewRootFlag = _componentInstance.getAttributes().get(BOUND_VIEW_ROOT);
1107 
1108             if (alreadyBoundViewRootFlag == null)
1109             {
1110                 try
1111                 {
1112                     setProperties(_componentInstance);
1113                 }
1114                 catch (Throwable e)
1115                 {
1116                     throw new JspException(e);
1117                 }
1118 
1119                 if (_id != null)
1120                 {
1121                     _componentInstance.setId(_id);
1122                 }
1123                 else
1124                 {
1125                     _componentInstance.setId(getFacesJspId());
1126                 }
1127                 _componentInstance.getAttributes().put(BOUND_VIEW_ROOT, true);
1128                 _created = true;
1129 
1130             }
1131             else if (hasBinding())
1132             {
1133                 setProperties(_componentInstance);
1134             }
1135 
1136             return _componentInstance;
1137         }
1138 
1139         UIComponent parent = parentTag.getComponentInstance();
1140 
1141         if (parent == null)
1142         {
1143             throw new IllegalStateException("parent is null?");
1144         }
1145 
1146         String facetName = getFacetName();
1147         if (facetName != null)
1148         {
1149             // Facet
1150             String id = createUniqueId(context, parent);
1151             _componentInstance = parent.getFacet(facetName);
1152             if (_componentInstance == null)
1153             {
1154                 _componentInstance = createComponent(context, id);
1155                 _created = true;
1156                 parent.getFacets().put(facetName, _componentInstance);
1157             }
1158             else
1159             {
1160                 if (checkFacetNameOnParentExists(parentTag, facetName))
1161                 {
1162                     throw new IllegalStateException("facet '" + facetName
1163                             + "' already has a child associated. current associated _componentInstance id: "
1164                             + _componentInstance.getClientId(context) + " class: "
1165                             + _componentInstance.getClass().getName());
1166                 }
1167             }
1168 
1169             addFacetNameToParentTag(parentTag, facetName);
1170             return _componentInstance;
1171         }
1172 
1173         // Child
1174         //
1175         // Note that setProperties is called only when we create the
1176         // _componentInstance; on later passes, the attributes defined on the
1177         // JSP tag are set on this Tag object, but then completely
1178         // ignored.
1179 
1180         String id = createUniqueId(context, parent);
1181 
1182         // Warn users that this tag is about to find/steal the UIComponent
1183         // that has already been created for a sibling tag with the same id value .
1184         // _childrenAdded is a Set, and we will stomp over a past id when calling
1185         // addChildIdToParentTag.
1186         //
1187         // It would also be reasonable to throw an exception here rather than
1188         // just issue a warning as this is a pretty serious problem. However the
1189         // Sun RI just issues a warning...
1190         if (parentTag._childrenAdded != null && parentTag._childrenAdded.contains(id))
1191         {
1192             if (log.isLoggable(Level.WARNING))
1193             {
1194                 log.warning("There is more than one JSF tag with an id : " + id);
1195             }
1196         }
1197 
1198         _componentInstance = findComponent(parent, id);
1199         if (_componentInstance == null)
1200         {
1201             _componentInstance = createComponent(context, id);
1202             if (id.equals(_componentInstance.getId()) )
1203             {
1204             _created = true;
1205             int index = parentTag.getIndexOfNextChildTag();
1206             if (index > parent.getChildCount())
1207             {
1208                 index = parent.getChildCount();
1209             }
1210 
1211             List<UIComponent> children = parent.getChildren();
1212             children.add(index, _componentInstance);
1213         }
1214             // On weblogic portal using faces-adapter, the id set and the retrieved 
1215             // one for <netuix:namingContainer> is different. The reason is 
1216             // this custom solution for integrate jsf changes the id of the parent
1217             // component to allow the same native portlet to be allocated multiple
1218             // times in the same page
1219             else if (null == findComponent(parent,_componentInstance.getId()))
1220             {
1221                 _created = true;
1222                 int index = parentTag.getIndexOfNextChildTag();
1223                 if (index > parent.getChildCount())
1224                 {
1225                     index = parent.getChildCount();
1226                 }
1227 
1228                 List<UIComponent> children = parent.getChildren();
1229                 children.add(index, _componentInstance);
1230             }
1231         }
1232 
1233         return _componentInstance;
1234 
1235     }
1236 
1237     private UIComponent findComponent(UIComponent parent, String id)
1238     {
1239         for (UIComponent child : parent.getChildren())
1240         {
1241             if (child.getId() != null && child.getId().equals(id))
1242             {
1243                 return child;
1244             }
1245         }
1246 
1247         return null;
1248     }
1249 
1250     private String createUniqueId(FacesContext context, UIComponent parent) throws JspException
1251     {
1252         String id = getId();
1253         if (id == null)
1254         {
1255             id = getFacesJspId();
1256         }
1257         else if (isIdDuplicated(id))
1258         {
1259             if (isInAnIterator)
1260             {
1261                 setId(createNextId(id));
1262                 id = getId();
1263             }
1264             else
1265             {
1266                 if (parent != null)
1267                 {
1268 
1269                     UIComponent namingContainer;
1270 
1271                     if (parent instanceof NamingContainer)
1272                     {
1273                         namingContainer = parent;
1274                     }
1275                     else
1276                     {
1277                         namingContainer = parent.getParent();
1278                     }
1279 
1280                     if (namingContainer != null)
1281                     {
1282                         UIComponent component = namingContainer.findComponent(id);
1283 
1284                         if (component == null || isPostBack(context))
1285                         {
1286                             return id;
1287                         }
1288                     }
1289                 }
1290 
1291                 throw new JspException("Duplicated Id found in the view: " + id);
1292             }
1293         }
1294 
1295         return id;
1296     }
1297 
1298     private String createNextId(String componentId)
1299     {
1300         Integer currentCounter = (Integer) getFacesContext().getAttributes().get(componentId);
1301 
1302         int iCurrentCounter = 1;
1303 
1304         if (currentCounter != null)
1305         {
1306             iCurrentCounter = currentCounter;
1307             iCurrentCounter++;
1308         }
1309 
1310         getFacesContext().getAttributes().put(componentId, iCurrentCounter);
1311 
1312         //if (isIncludedOrForwarded())
1313         //{
1314         //    componentId = componentId + "pc" + iCurrentCounter;
1315         //}
1316         //else
1317         //{
1318         componentId = componentId + UNIQUE_ID_PREFIX + iCurrentCounter;            
1319         //}
1320 
1321         return componentId;
1322     }
1323 
1324     private void checkIfItIsInAnIterator(String jspId)
1325     {
1326         Set<String> previousJspIdsSet = getPreviousJspIdsSet();
1327 
1328         if (previousJspIdsSet.contains(jspId))
1329         {
1330             isInAnIterator = true;
1331         }
1332         else
1333         {
1334             previousJspIdsSet.add(jspId);
1335             isInAnIterator = false;
1336         }
1337     }
1338 
1339     @SuppressWarnings("unchecked")
1340     private Set<String> getPreviousJspIdsSet()
1341     {
1342         Set<String> previousJspIdsSet =
1343                 (Set<String>)getFacesContext().getAttributes().get(PREVIOUS_JSP_IDS_SET);
1344 
1345         if (previousJspIdsSet == null)
1346         {
1347             previousJspIdsSet = new HashSet<String>();
1348             // Add it to the context! The next time is called
1349             // this method it takes the ref from the RequestContext
1350             getFacesContext().getAttributes().put(PREVIOUS_JSP_IDS_SET, previousJspIdsSet);
1351         }
1352 
1353         return previousJspIdsSet;
1354     }
1355 
1356     private boolean isIdDuplicated(String componentId)
1357     {
1358         boolean result = false;
1359         if (_parentClassicTag != null)
1360         {
1361             if (_parentClassicTag.isInAnIterator)
1362             {
1363                 return true;
1364             }
1365             List<String> childComponents = _parentClassicTag.getCreatedComponents();
1366 
1367             if (childComponents != null)
1368             {
1369                 result = childComponents.contains(componentId);
1370                 if (result && (!isInAnIterator))
1371                 {
1372                     return true;
1373                 }
1374             }
1375         }
1376 
1377         return result;
1378     }
1379 
1380     private boolean isPostBack(FacesContext facesContext)
1381     {
1382         return facesContext.getExternalContext().getRequestParameterMap().containsKey(
1383             ResponseStateManager.VIEW_STATE_PARAM);
1384     }
1385 
1386     /**
1387      * check if the facet is already added to the parent
1388      */
1389     private boolean checkFacetNameOnParentExists(UIComponentClassicTagBase parentTag, String facetName)
1390     {
1391         return parentTag._facetsAdded != null && parentTag._facetsAdded.contains(facetName);
1392     }
1393 
1394     /**
1395      * Notify the enclosing JSP tag of the id of this facet's id. The parent tag will later delete any existing view
1396      * facets that were not seen during this rendering phase; see doEndTag for details.
1397      */
1398     private void addFacetNameToParentTag(UIComponentClassicTagBase parentTag, String facetName)
1399     {
1400         if (parentTag._facetsAdded == null)
1401         {
1402             parentTag._facetsAdded = new ArrayList<String>();
1403         }
1404         parentTag._facetsAdded.add(facetName);
1405     }
1406 
1407     protected abstract boolean hasBinding();
1408 
1409     public JspWriter getPreviousOut()
1410     {
1411         return bodyContent.getEnclosingWriter();
1412     }
1413 }