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