Source code: javax/faces/webapp/UIComponentTag.java
1 /*
2 * Copyright 2004 The Apache Software Foundation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package javax.faces.webapp;
17
18 import org.apache.commons.logging.Log;
19 import org.apache.commons.logging.LogFactory;
20
21 import javax.faces.FacesException;
22 import javax.faces.FactoryFinder;
23 import javax.faces.application.Application;
24 import javax.faces.component.UIComponent;
25 import javax.faces.component.UIViewRoot;
26 import javax.faces.context.FacesContext;
27 import javax.faces.context.ResponseWriter;
28 import javax.faces.el.ValueBinding;
29 import javax.faces.render.RenderKit;
30 import javax.faces.render.RenderKitFactory;
31
32 import javax.servlet.jsp.JspException;
33 import javax.servlet.jsp.PageContext;
34 import javax.servlet.jsp.tagext.Tag;
35 import java.io.IOException;
36 import java.util.*;
37
38 /**
39 * @author Manfred Geiler (latest modification by $Author: oros $)
40 * @version $Revision: 265611 $ $Date: 2005-08-31 21:05:16 -0400 (Wed, 31 Aug 2005) $
41 */
42 public abstract class UIComponentTag
43 implements Tag
44 {
45 private static final String FORMER_CHILD_IDS_SET_ATTR = UIComponentTag.class.getName() + ".FORMER_CHILD_IDS";
46 private static final String FORMER_FACET_NAMES_SET_ATTR = UIComponentTag.class.getName() + ".FORMER_FACET_NAMES";
47 private static final String COMPONENT_STACK_ATTR = UIComponentTag.class.getName() + ".COMPONENT_STACK";
48
49 protected PageContext pageContext = null;
50 private Tag _parent = null;
51
52 //tag attributes
53 private String _binding = null;
54 private String _id = null;
55 private String _rendered = null;
56
57 private FacesContext _facesContext = null;
58 private UIComponent _componentInstance = null;
59 private boolean _created = false;
60 private Boolean _suppressed = null;
61 private ResponseWriter _writer = null;
62 private Set _childrenAdded = null;
63 private Set _facetsAdded = null;
64
65 private static Log log = LogFactory.getLog(UIComponentTag.class);
66
67
68 public UIComponentTag()
69 {
70
71 }
72
73 public void release()
74 {
75 internalRelease();
76
77 //members, that must/need only be reset when there is no more risk, that the container
78 //wants to reuse this tag
79 pageContext = null;
80 _parent = null;
81
82 //reset tag attribute members
83 _binding = null;
84 _id = null;
85 _rendered = null;
86 }
87
88
89 /**
90 * Reset any members that apply to the according component instance and
91 * must not be reused if the container wants to reuse this tag instance.
92 * This method is called when rendering for this tag is finished ( doEndTag() )
93 * or when released by the container.
94 */
95 private void internalRelease()
96 {
97 _facesContext = null;
98 _componentInstance = null;
99 _created = false;
100 _suppressed = null;
101 _writer = null;
102 _childrenAdded = null;
103 _facetsAdded = null;
104 }
105
106
107 public void setBinding(String binding)
108 throws JspException
109 {
110 if (!isValueReference(binding))
111 {
112 throw new IllegalArgumentException("not a valid binding: " + binding);
113 }
114 _binding = binding;
115 }
116
117 public void setId(String id)
118 {
119 _id = id;
120 }
121
122 protected String getId()
123 {
124 return _id;
125 }
126
127 public void setRendered(String rendered)
128 {
129 _rendered = rendered;
130 }
131
132 public abstract String getComponentType();
133
134 public UIComponent getComponentInstance()
135 {
136 return _componentInstance;
137 }
138
139 public boolean getCreated()
140 {
141 return _created;
142 }
143
144 public static UIComponentTag getParentUIComponentTag(PageContext pageContext)
145 {
146 List list = (List)pageContext.getAttribute(COMPONENT_STACK_ATTR,
147 PageContext.REQUEST_SCOPE);
148 if (list != null)
149 {
150 return (UIComponentTag)list.get(list.size() - 1);
151 }
152 return null;
153 }
154
155 private void popTag()
156 {
157 List list = (List)pageContext.getAttribute(COMPONENT_STACK_ATTR,
158 PageContext.REQUEST_SCOPE);
159 if (list != null)
160 {
161 int size = list.size();
162 list.remove(size -1);
163 if (size <= 1)
164 {
165 pageContext.removeAttribute(COMPONENT_STACK_ATTR,
166 PageContext.REQUEST_SCOPE);
167 }
168 }
169 }
170
171 private void pushTag()
172 {
173 List list = (List)pageContext.getAttribute(COMPONENT_STACK_ATTR,
174 PageContext.REQUEST_SCOPE);
175 if (list == null)
176 {
177 list = new ArrayList();
178 pageContext.setAttribute(COMPONENT_STACK_ATTR,
179 list,
180 PageContext.REQUEST_SCOPE);
181 }
182 list.add(this);
183 }
184
185
186 public abstract String getRendererType();
187
188 public static boolean isValueReference(String value)
189 {
190 if (value == null) throw new NullPointerException("value");
191
192 int start = value.indexOf("#{");
193 if (start < 0) return false;
194
195 int end = value.lastIndexOf('}');
196 return (end >=0 && start < end);
197 }
198
199 public void setPageContext(PageContext pageContext)
200 {
201 this.pageContext = pageContext;
202 }
203
204 public Tag getParent()
205 {
206 return _parent;
207 }
208
209 public void setParent(Tag parent)
210 {
211 _parent = parent;
212 }
213
214 public int doStartTag()
215 throws JspException
216 {
217 setupResponseWriter();
218 FacesContext facesContext = getFacesContext();
219 UIComponent component = findComponent(facesContext);
220 if (!component.getRendersChildren() && !isSuppressed())
221 {
222 try
223 {
224 encodeBegin();
225 _writer.flush();
226 }
227 catch (IOException e)
228 {
229 throw new JspException(e.getMessage(), e);
230 }
231 }
232 pushTag();
233 return getDoStartValue();
234 }
235
236 public int doEndTag()
237 throws JspException
238 {
239 popTag();
240 UIComponent component = getComponentInstance();
241 removeFormerChildren(component);
242 removeFormerFacets(component);
243
244 try
245 {
246 if (!isSuppressed())
247 {
248 if (component.getRendersChildren())
249 {
250 encodeBegin();
251 encodeChildren();
252 }
253 encodeEnd();
254 }
255 }
256 catch (IOException e)
257 {
258 throw new JspException(e.getMessage(), e);
259 }
260
261 int retValue = getDoEndValue();
262 internalRelease();
263 return retValue;
264 }
265
266 private void removeFormerChildren(UIComponent component)
267 {
268 Set formerChildIdsSet = (Set)component.getAttributes().get(FORMER_CHILD_IDS_SET_ATTR);
269 if (formerChildIdsSet != null)
270 {
271 for (Iterator iterator = formerChildIdsSet.iterator(); iterator.hasNext();)
272 {
273 String childId = (String)iterator.next();
274 if (_childrenAdded == null || !_childrenAdded.contains(childId))
275 {
276 UIComponent childToRemove = component.findComponent(childId);
277 if (childToRemove != null)
278 {
279 component.getChildren().remove(childToRemove);
280 }
281 }
282 }
283 if (_childrenAdded == null)
284 {
285 component.getAttributes().remove(FORMER_CHILD_IDS_SET_ATTR);
286 }
287 else
288 {
289 component.getAttributes().put(FORMER_CHILD_IDS_SET_ATTR, _childrenAdded);
290 }
291 }
292 else
293 {
294 if (_childrenAdded != null)
295 {
296 component.getAttributes().put(FORMER_CHILD_IDS_SET_ATTR, _childrenAdded);
297 }
298 }
299 }
300
301 private void removeFormerFacets(UIComponent component)
302 {
303 Set formerFacetNamesSet = (Set)component.getAttributes().get(FORMER_FACET_NAMES_SET_ATTR);
304 if (formerFacetNamesSet != null)
305 {
306 for (Iterator iterator = formerFacetNamesSet.iterator(); iterator.hasNext();)
307 {
308 String facetName = (String)iterator.next();
309 if (_facetsAdded == null || !_facetsAdded.contains(facetName))
310 {
311 component.getFacets().remove(facetName);
312 }
313 }
314 if (_facetsAdded == null)
315 {
316 component.getAttributes().remove(FORMER_FACET_NAMES_SET_ATTR);
317 }
318 else
319 {
320 component.getAttributes().put(FORMER_FACET_NAMES_SET_ATTR, _facetsAdded);
321 }
322 }
323 else
324 {
325 if (_facetsAdded != null)
326 {
327 component.getAttributes().put(FORMER_FACET_NAMES_SET_ATTR, _facetsAdded);
328 }
329 }
330 }
331
332
333
334 protected void encodeBegin()
335 throws IOException
336 {
337 _componentInstance.encodeBegin(getFacesContext());
338 }
339
340 protected void encodeChildren()
341 throws IOException
342 {
343 _componentInstance.encodeChildren(getFacesContext());
344 }
345
346 protected void encodeEnd()
347 throws IOException
348 {
349 _componentInstance.encodeEnd(getFacesContext());
350 }
351
352 protected UIComponent findComponent(FacesContext context)
353 throws JspException
354 {
355 if (_componentInstance != null) return _componentInstance;
356 UIComponentTag parentTag = getParentUIComponentTag(pageContext);
357 if (parentTag == null)
358 {
359 //This is the root
360 _componentInstance = context.getViewRoot();
361 setProperties(_componentInstance);
362 return _componentInstance;
363 }
364
365 UIComponent parent = parentTag.getComponentInstance();
366 //TODO: what if parent == null?
367 if (parent == null) throw new IllegalStateException("parent is null?");
368
369 String facetName = getFacetName();
370 if (facetName != null)
371 {
372 //Facet
373 String id = getOrCreateUniqueId(context);
374 _componentInstance = parent.getFacet(facetName);
375 if (_componentInstance == null)
376 {
377 _componentInstance = createComponentInstance(context, id);
378 setProperties(_componentInstance);
379 parent.getFacets().put(facetName, _componentInstance);
380 }
381 addFacetNameToParentTag(parentTag, facetName);
382 return _componentInstance;
383 }
384 else
385 {
386 //Child
387 String id = getOrCreateUniqueId(context);
388 _componentInstance = parent.findComponent(id);
389 if (_componentInstance == null)
390 {
391 _componentInstance = createComponentInstance(context, id);
392 setProperties(_componentInstance);
393 int index = getAddedChildrenCount(parentTag);
394 List children = parent.getChildren();
395 if (index <= children.size())
396 {
397 children.add(index, _componentInstance);
398 }
399 else
400 {
401 throw new FacesException("cannot add component with id '" +
402 _componentInstance.getId() + "' and path : "
403 +getPathToComponent(_componentInstance)+" to its parent component. This might be a problem due to duplicate ids.");
404 }
405 }
406 addChildIdToParentTag(parentTag, id);
407 return _componentInstance;
408 }
409 }
410
411
412 private String getOrCreateUniqueId(FacesContext context)
413 {
414 String id = getId();
415 if (id != null)
416 {
417 return id;
418 }
419 else
420 {
421 return context.getViewRoot().createUniqueId();
422 }
423 }
424
425 private UIComponent createComponentInstance(FacesContext context, String id)
426 {
427 String componentType = getComponentType();
428 if (componentType == null)
429 {
430 throw new NullPointerException("componentType");
431 }
432
433 if (_binding != null)
434 {
435 Application application = context.getApplication();
436 ValueBinding componentBinding = application.createValueBinding(_binding);
437 UIComponent component = application.createComponent(componentBinding,
438 context,
439 componentType);
440 component.setId(id);
441 component.setValueBinding("binding", componentBinding);
442 recurseFacetsAndChildrenForId(context, component.getFacetsAndChildren(), id + "_", 0);
443 _created = true;
444 return component;
445 }
446 else
447 {
448 UIComponent component = context.getApplication().createComponent(componentType);
449 component.setId(id);
450 _created = true;
451 return component;
452 }
453 }
454
455 /**
456 * Recurse all facets and children and assign them an unique ID if necessary.
457 * We must *not* use UIViewRoot#createUniqueId here, because this would affect the
458 * order of the created ids upon rerendering the page!
459 */
460 private int recurseFacetsAndChildrenForId(FacesContext context,
461 Iterator facetsAndChildren,
462 String idPrefix,
463 int cnt)
464 {
465 while (facetsAndChildren.hasNext())
466 {
467 UIComponent comp = (UIComponent)facetsAndChildren.next();
468 if (comp.getId() == null)
469 {
470 ++cnt;
471 comp.setId(idPrefix + cnt);
472 }
473 cnt = recurseFacetsAndChildrenForId(context, comp.getFacetsAndChildren(), idPrefix, cnt);
474 }
475 return cnt;
476 }
477
478
479
480 private void addChildIdToParentTag(UIComponentTag parentTag, String id)
481 {
482 if (parentTag._childrenAdded == null)
483 {
484 parentTag._childrenAdded = new HashSet();
485 }
486 parentTag._childrenAdded.add(id);
487 }
488
489 private void addFacetNameToParentTag(UIComponentTag parentTag, String facetName)
490 {
491 if (parentTag._facetsAdded == null)
492 {
493 parentTag._facetsAdded = new HashSet();
494 }
495 parentTag._facetsAdded.add(facetName);
496 }
497
498 private int getAddedChildrenCount(UIComponentTag parentTag)
499 {
500 return parentTag._childrenAdded != null ?
501 parentTag._childrenAdded.size() :
502 0;
503 }
504
505
506
507
508 protected int getDoStartValue()
509 throws JspException
510 {
511 return Tag.EVAL_BODY_INCLUDE;
512 }
513
514 protected int getDoEndValue()
515 throws JspException
516 {
517 return Tag.EVAL_PAGE;
518 }
519
520 protected FacesContext getFacesContext()
521 {
522 if (_facesContext == null)
523 {
524 _facesContext = FacesContext.getCurrentInstance();
525 }
526 return _facesContext;
527 }
528
529
530 private boolean isFacet()
531 {
532 return _parent != null && _parent instanceof FacetTag;
533 }
534
535 protected String getFacetName()
536 {
537 return isFacet() ? ((FacetTag)_parent).getName() : null;
538 }
539
540
541 protected boolean isSuppressed()
542 {
543 if (_suppressed == null)
544 {
545 if (isFacet())
546 {
547 // facets are always rendered by their parents --> suppressed
548 return (_suppressed = Boolean.TRUE).booleanValue();
549 }
550
551 UIComponent component = getComponentInstance();
552
553 // Does any parent render its children?
554 // (We must determine this first, before calling any isRendered method
555 // because rendered properties might reference a data var of a nesting UIData,
556 // which is not set at this time, and would cause a VariableResolver error!)
557 UIComponent parent = component.getParent();
558 while (parent != null)
559 {
560 if (parent.getRendersChildren())
561 {
562 //Yes, parent found, that renders children --> suppressed
563 return (_suppressed = Boolean.TRUE).booleanValue();
564 }
565 parent = parent.getParent();
566 }
567
568 // does component or any parent has a false rendered attribute?
569 while (component != null)
570 {
571 if (!component.isRendered())
572 {
573 //Yes, component or any parent must not be rendered --> suppressed
574 return (_suppressed = Boolean.TRUE).booleanValue();
575 }
576 component = component.getParent();
577 }
578
579 // else --> not suppressed
580 _suppressed = Boolean.FALSE;
581 }
582 return _suppressed.booleanValue();
583 }
584
585 protected void setProperties(UIComponent component)
586 {
587 if (getRendererType() != null)
588 {
589 _componentInstance.setRendererType(getRendererType());
590 }
591
592 if (_rendered != null)
593 {
594 if (isValueReference(_rendered))
595 {
596 ValueBinding vb = getFacesContext().getApplication().createValueBinding(_rendered);
597 component.setValueBinding("rendered", vb);
598 } else
599 {
600 boolean b = Boolean.valueOf(_rendered).booleanValue();
601 component.setRendered(b);
602 }
603 }
604 }
605
606 protected void setupResponseWriter()
607 {
608 FacesContext facesContext = getFacesContext();
609
610 if(facesContext == null)
611 {
612 log.error("Faces context not found. getResponseWriter will fail. Check if the FacesServlet has been initialized at all in your web.xml.");
613 }
614
615 _writer = facesContext.getResponseWriter();
616 if (_writer == null)
617 {
618 RenderKitFactory renderFactory = (RenderKitFactory)FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
619 RenderKit renderKit = renderFactory.getRenderKit(facesContext,
620 facesContext.getViewRoot().getRenderKitId());
621
622 _writer = renderKit.createResponseWriter(new _PageContextOutWriter(pageContext),
623 null /*Default: get the allowed content-types from the accept-header*/,
624 pageContext.getRequest().getCharacterEncoding());
625 facesContext.setResponseWriter(_writer);
626 }
627 }
628
629 private String getPathToComponent(UIComponent component)
630 {
631 StringBuffer buf = new StringBuffer();
632
633 if(component == null)
634 {
635 buf.append("{Component-Path : ");
636 buf.append("[null]}");
637 return buf.toString();
638 }
639
640 getPathToComponent(component,buf);
641
642 buf.insert(0,"{Component-Path : ");
643 buf.append("}");
644
645 return buf.toString();
646 }
647
648 private static void getPathToComponent(UIComponent component, StringBuffer buf)
649 {
650 if(component == null)
651 return;
652
653 StringBuffer intBuf = new StringBuffer();
654
655 intBuf.append("[Class: ");
656 intBuf.append(component.getClass().getName());
657 if(component instanceof UIViewRoot)
658 {
659 intBuf.append(",ViewId: ");
660 intBuf.append(((UIViewRoot) component).getViewId());
661 }
662 else
663 {
664 intBuf.append(",Id: ");
665 intBuf.append(component.getId());
666 }
667 intBuf.append("]");
668
669 buf.insert(0,intBuf);
670
671 if(component!=null)
672 {
673 getPathToComponent(component.getParent(),buf);
674 }
675 }
676
677 }