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 org.apache.myfaces.view.facelets.impl;
20  
21  import java.util.ArrayList;
22  import java.util.HashMap;
23  import java.util.Iterator;
24  import java.util.LinkedList;
25  import java.util.List;
26  import java.util.Map;
27  
28  import javax.faces.component.UIComponent;
29  import javax.faces.component.UIViewRoot;
30  import javax.faces.component.UniqueIdVendor;
31  import javax.faces.context.FacesContext;
32  import javax.faces.view.AttachedObjectHandler;
33  import javax.faces.view.EditableValueHolderAttachedObjectHandler;
34  
35  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
36  import org.apache.myfaces.shared.config.MyfacesConfig;
37  import org.apache.myfaces.shared.util.WebConfigParamUtils;
38  import org.apache.myfaces.view.facelets.ELExpressionCacheMode;
39  import org.apache.myfaces.view.facelets.FaceletCompositionContext;
40  import org.apache.myfaces.view.facelets.FaceletFactory;
41  import org.apache.myfaces.view.facelets.FaceletViewDeclarationLanguage;
42  import org.apache.myfaces.view.facelets.tag.jsf.ComponentSupport;
43  
44  /**
45   * @since 2.0.1
46   * @author Leonardo Uribe (latest modification by $Author: lu4242 $)
47   * @version $Revision: 1424975 $ $Date: 2012-12-21 10:41:27 -0500 (Fri, 21 Dec 2012) $
48   */
49  public class FaceletCompositionContextImpl extends FaceletCompositionContext
50  {
51      /**
52       * Indicates if expressions generated by facelets should be cached or not.
53       * Default is noCache. There there are four modes:
54       * 
55       * <ul>
56       * <li>always: Only does not cache when expressions are inside user tags or the e
57       * xpression contains a variable resolved using VariableMapper</li>
58       * <li>allowCset: Like always, but does not allow cache when ui:param
59       * was used on the current template context</li>
60       * <li>strict: Like allowCset, but does not allow cache when c:set with
61       * var and value properties only is used on the current page context</li>
62       * <li>noCache: All expression are created each time the view is built</li>
63       * </ul>
64       * 
65       */
66      @JSFWebConfigParam(since="2.0.8", defaultValue="noCache", expectedValues="noCache, strict, allowCset, always",
67                         group="EL", tags="performance")
68      public static final String INIT_PARAM_CACHE_EL_EXPRESSIONS = "org.apache.myfaces.CACHE_EL_EXPRESSIONS";
69      
70      /**
71       * Wrap exception caused by calls to EL expressions, so information like
72       * the location, expression string and tag name can be retrieved by
73       * the ExceptionHandler implementation and used to output meaningful information about itself.
74       * 
75       * <p>Note in some cases this will wrap the original javax.el.ELException,
76       * so the information will not be on the stack trace unless ExceptionHandler
77       * retrieve checking if the exception implements ContextAware interface and calling getWrapped() method.
78       * </p>
79       * 
80       */
81      @JSFWebConfigParam(since="2.0.9, 2.1.3" , defaultValue="true", expectedValues="true, false")
82      public static final String INIT_PARAM_WRAP_TAG_EXCEPTIONS_AS_CONTEXT_AWARE
83              = "org.apache.myfaces.WRAP_TAG_EXCEPTIONS_AS_CONTEXT_AWARE";
84      
85      private static final String JAVAX_FACES_LOCATION_PREFIX = "javax_faces_location_";
86      
87      private FacesContext _facesContext;
88      
89      private FaceletFactory _factory;
90  
91      private LinkedList<UIComponent> _compositeComponentStack;
92      
93      private LinkedList<UniqueIdVendor> _uniqueIdVendorStack;
94      
95      private LinkedList<String> _validationGroupsStack; 
96      
97      private LinkedList<String> _excludedValidatorIdsStack;
98      
99      private LinkedList<Map.Entry<String, EditableValueHolderAttachedObjectHandler>> _enclosingValidatorIdsStack;
100     
101     private Boolean _isRefreshingTransientBuild;
102     
103     private Boolean _isMarkInitialState;
104     
105     private Boolean _isBuildingViewMetadata;
106     
107     private Boolean _refreshTransientBuildOnPSS;
108     
109     private Boolean _refreshTransientBuildOnPSSPreserveState;
110     
111     private Boolean _usingPSSOnThisView;
112     
113     private ELExpressionCacheMode _elExpressionCacheMode;
114     
115     private Boolean _isWrapTagExceptionsAsContextAware;
116 
117     private List<Map<String, UIComponent>> _componentsMarkedForDeletion;
118     
119     private Map<String, UIComponent> _relocatableResourceForDeletion;
120     
121     private int _deletionLevel;
122     
123     private Map<UIComponent, List<AttachedObjectHandler>> _attachedObjectHandlers;
124     
125     private Map<UIComponent, Map<String, Object> > _methodExpressionsTargeted;
126     
127     private Map<UIComponent, Map<String, Boolean> > _compositeComponentAttributesMarked;
128 
129     private static final String VIEWROOT_FACELET_ID = "oam.VIEW_ROOT";
130     
131     private SectionUniqueIdCounter _sectionUniqueIdCounter;
132     
133     private SectionUniqueIdCounter _sectionUniqueComponentIdCounter;
134     
135     private List<String> _uniqueIdsList;
136     private Iterator<String> _uniqueIdsIterator;
137     private int _level;
138     
139     private int _isInMetadataSection;
140     private SectionUniqueIdCounter _sectionUniqueMetadataIdCounter;
141     private SectionUniqueIdCounter _sectionUniqueNormalIdCounter;
142     private SectionUniqueIdCounter _sectionUniqueComponentMetadataIdCounter;
143     private SectionUniqueIdCounter _sectionUniqueComponentNormalIdCounter;
144     
145     private StringBuilder _sharedStringBuilder;
146     
147     private int _ccLevel;
148     
149     public FaceletCompositionContextImpl(FaceletFactory factory, FacesContext facesContext)
150     {
151         super();
152         _factory = factory;
153         _facesContext = facesContext;
154         _componentsMarkedForDeletion = new ArrayList<Map<String,UIComponent>>();
155         _relocatableResourceForDeletion = new HashMap<String, UIComponent>();
156         _deletionLevel = -1;
157         _sectionUniqueIdCounter = new SectionUniqueIdCounter();
158         //Cached at facelet view
159         MyfacesConfig myfacesConfig = MyfacesConfig.getCurrentInstance(
160                 facesContext.getExternalContext());
161         if (myfacesConfig.getComponentUniqueIdsCacheSize() > 0)
162         {
163             String[] componentIdsCache = (String [])facesContext.getExternalContext().
164                     getApplicationMap().get(FaceletViewDeclarationLanguage.CACHED_COMPONENT_IDS);
165             if (componentIdsCache != null)
166             {
167                 _sectionUniqueComponentIdCounter = new SectionUniqueIdCounter("_", 
168                         componentIdsCache);
169             }
170             else
171             {
172                 _sectionUniqueComponentIdCounter = new SectionUniqueIdCounter("_");
173             }
174         }
175         else
176         {
177             _sectionUniqueComponentIdCounter = new SectionUniqueIdCounter("_");
178         }
179         _sectionUniqueNormalIdCounter = _sectionUniqueIdCounter;
180         _sectionUniqueComponentNormalIdCounter = _sectionUniqueComponentIdCounter;
181         _uniqueIdsList = null;
182         _uniqueIdsIterator = null;
183         _level = 0;
184         _isInMetadataSection = 0;
185         _sharedStringBuilder = null;
186         _ccLevel = 0;
187     }
188     
189     @Override
190     public void setUniqueIdsIterator(Iterator<String> uniqueIdsIterator)
191     {
192         _uniqueIdsList = null;
193         _uniqueIdsIterator = uniqueIdsIterator;
194     }
195     
196     @Override
197     public void initUniqueIdRecording()
198     {
199         _uniqueIdsList = new LinkedList<String>();
200         _uniqueIdsIterator = null;
201     }
202     
203     @Override
204     public void addUniqueId(String uniqueId)
205     {
206         if (_uniqueIdsList != null && _level == 0 && !(_isInMetadataSection > 0))
207         {
208             _uniqueIdsList.add(uniqueId);
209         }
210     }
211     
212     @Override
213     public String getUniqueIdFromIterator()
214     {
215         if (_uniqueIdsIterator != null && _uniqueIdsIterator.hasNext() && 
216                 _level == 0 && !(_isInMetadataSection > 0))
217         {
218             return _uniqueIdsIterator.next();
219         }
220         return null;
221     }
222     
223     @Override
224     public List<String> getUniqueIdList()
225     {
226         return _uniqueIdsList;
227     }
228 
229     public FaceletFactory getFaceletFactory()
230     {
231         return _factory;
232     }
233     
234     @Override
235     public void release(FacesContext facesContext)
236     {
237         super.release(facesContext);
238         _factory = null;
239         _facesContext = null;
240         _compositeComponentStack = null;
241         _enclosingValidatorIdsStack = null;
242         _excludedValidatorIdsStack = null;
243         _uniqueIdVendorStack = null;
244         _validationGroupsStack = null;
245         _componentsMarkedForDeletion = null;
246         _relocatableResourceForDeletion = null;
247         _sectionUniqueIdCounter = null;
248         _sectionUniqueNormalIdCounter = null;
249         _sectionUniqueMetadataIdCounter = null;
250         _sectionUniqueComponentIdCounter = null;
251         _sectionUniqueComponentNormalIdCounter = null;
252         _sectionUniqueComponentMetadataIdCounter = null;
253         _sharedStringBuilder = null;
254     }
255    
256     @Override
257     public UIComponent getCompositeComponentFromStack()
258     {
259         if (_compositeComponentStack != null && !_compositeComponentStack.isEmpty())
260         {
261             return _compositeComponentStack.peek();
262         }
263         return null;
264     }
265 
266     @Override
267     public void pushCompositeComponentToStack(UIComponent parent)
268     {
269         if (_compositeComponentStack == null)
270         {
271             _compositeComponentStack = new LinkedList<UIComponent>();
272         }
273         _compositeComponentStack.addFirst(parent);
274         _ccLevel++;
275     }
276 
277     @Override
278     public void popCompositeComponentToStack()
279     {
280         if (_compositeComponentStack != null && !_compositeComponentStack.isEmpty())
281         {
282             _compositeComponentStack.removeFirst();
283         }
284         _ccLevel--;
285     }
286     
287     @Override
288     public int getCompositeComponentLevel()
289     {
290         return _ccLevel;
291     }
292 
293     @Override
294     public UniqueIdVendor getUniqueIdVendorFromStack()
295     {
296         if (_uniqueIdVendorStack != null && !_uniqueIdVendorStack.isEmpty())
297         {
298             return _uniqueIdVendorStack.peek();
299         }
300         return null;
301     }
302 
303     @Override
304     public void popUniqueIdVendorToStack()
305     {
306         if (_uniqueIdVendorStack != null && !_uniqueIdVendorStack.isEmpty())
307         {
308             _uniqueIdVendorStack.removeFirst();
309         }
310     }
311 
312     @Override
313     public void pushUniqueIdVendorToStack(UniqueIdVendor parent)
314     {
315         if (_uniqueIdVendorStack == null)
316         {
317             _uniqueIdVendorStack = new LinkedList<UniqueIdVendor>();
318         }
319         _uniqueIdVendorStack.addFirst(parent);
320     }
321     
322     /**
323      * Gets the top of the validationGroups stack.
324      * @return
325      * @since 2.0
326      */
327     @Override
328     public String getFirstValidationGroupFromStack()
329     {
330         if (_validationGroupsStack != null && !_validationGroupsStack.isEmpty())
331         {
332             return _validationGroupsStack.getFirst(); // top-of-stack
333         }
334         return null;
335     }
336     
337     /**
338      * Removes top of stack.
339      * @since 2.0
340      */
341     @Override
342     public void popValidationGroupsToStack()
343     {
344         if (_validationGroupsStack != null && !_validationGroupsStack.isEmpty())
345         {
346             _validationGroupsStack.removeFirst();
347         }
348     }
349     
350     /**
351      * Pushes validationGroups to the stack.
352      * @param validationGroups
353      * @since 2.0
354      */
355     @Override
356     public void pushValidationGroupsToStack(String validationGroups)
357     {
358         if (_validationGroupsStack == null)
359         {
360             _validationGroupsStack = new LinkedList<String>();
361         }
362 
363         _validationGroupsStack.addFirst(validationGroups);
364     }
365     
366     /**
367      * Gets all validationIds on the stack.
368      * @return
369      * @since 2.0
370      */
371     @Override
372     public Iterator<String> getExcludedValidatorIds()
373     {
374         if (_excludedValidatorIdsStack != null && !_excludedValidatorIdsStack.isEmpty())
375         {
376             return _excludedValidatorIdsStack.iterator();
377         }
378         return null;
379     }
380     
381     /**
382      * Removes top of stack.
383      * @since 2.0
384      */
385     @Override
386     public void popExcludedValidatorIdToStack()
387     {
388         if (_excludedValidatorIdsStack != null && !_excludedValidatorIdsStack.isEmpty())
389         {
390             _excludedValidatorIdsStack.removeFirst();
391         }
392     }
393     
394     /**
395      * Pushes validatorId to the stack of excluded validatorIds.
396      * @param validatorId
397      * @since 2.0
398      */
399     @Override
400     public void pushExcludedValidatorIdToStack(String validatorId)
401     {
402         if (_excludedValidatorIdsStack == null)
403         {
404             _excludedValidatorIdsStack = new LinkedList<String>();
405         }
406 
407         _excludedValidatorIdsStack.addFirst(validatorId);
408     }
409     
410     /**
411      * Gets all validationIds on the stack.
412      * @return
413      * @since 2.0
414      */
415     @Override
416     public Iterator<String> getEnclosingValidatorIds()
417     {
418         if (_enclosingValidatorIdsStack != null && !_enclosingValidatorIdsStack.isEmpty())
419         {
420             return new KeyEntryIterator<String, EditableValueHolderAttachedObjectHandler>
421                 (_enclosingValidatorIdsStack.iterator()); 
422         }
423         return null;
424     }
425     
426     /**
427      * Removes top of stack.
428      * @since 2.0
429      */
430     @Override
431     public void popEnclosingValidatorIdToStack()
432     {
433         if (_enclosingValidatorIdsStack != null && !_enclosingValidatorIdsStack.isEmpty())
434         {
435             _enclosingValidatorIdsStack.removeFirst();
436         }
437     }
438     
439     /**
440      * Pushes validatorId to the stack of all enclosing validatorIds.
441      * @param validatorId
442      * @since 2.0
443      */
444     @Override
445     public void pushEnclosingValidatorIdToStack(String validatorId)
446     {
447         pushEnclosingValidatorIdToStack(validatorId, null);
448     }
449     
450     @Override
451     public void pushEnclosingValidatorIdToStack(String validatorId, 
452             EditableValueHolderAttachedObjectHandler attachedObjectHandler)
453     {
454         if (_enclosingValidatorIdsStack == null)
455         {
456             _enclosingValidatorIdsStack = 
457                 new LinkedList<Map.Entry<String, EditableValueHolderAttachedObjectHandler>>();
458         }
459 
460         _enclosingValidatorIdsStack.addFirst(
461                 new SimpleEntry<String, EditableValueHolderAttachedObjectHandler>
462                     (validatorId, attachedObjectHandler));
463     }
464 
465     public Iterator<Map.Entry<String, EditableValueHolderAttachedObjectHandler>> getEnclosingValidatorIdsAndHandlers()
466     {
467         if (_enclosingValidatorIdsStack != null && !_enclosingValidatorIdsStack.isEmpty())
468         {
469             return _enclosingValidatorIdsStack.iterator(); 
470         }
471         return null;
472     }
473     
474     public boolean containsEnclosingValidatorId(String id)
475     {
476         if (_enclosingValidatorIdsStack != null && !_enclosingValidatorIdsStack.isEmpty())
477         {
478             for (Map.Entry<String, EditableValueHolderAttachedObjectHandler> entry : _enclosingValidatorIdsStack)
479             {
480                 if (entry.getKey().equals(id))
481                 {
482                     return true;
483                 }
484             }
485         }
486         return false;
487     }
488 
489     @Override
490     public boolean isRefreshingTransientBuild()
491     {
492         if (_isRefreshingTransientBuild == null)
493         {
494             _isRefreshingTransientBuild = FaceletViewDeclarationLanguage.
495                 isRefreshingTransientBuild(_facesContext);
496         }
497         return _isRefreshingTransientBuild;
498     }
499 
500     @Override
501     public boolean isMarkInitialState()
502     {
503         if (_isMarkInitialState == null)
504         {
505             _isMarkInitialState = FaceletViewDeclarationLanguage.
506                 isMarkInitialState(_facesContext);
507         }
508         return _isMarkInitialState;
509     }
510 
511     @Override
512     public void setMarkInitialState(boolean value)
513     {
514         _isMarkInitialState = value;
515     }
516 
517     @Override
518     public boolean isRefreshTransientBuildOnPSS()
519     {
520         if (_refreshTransientBuildOnPSS == null)
521         {
522             _refreshTransientBuildOnPSS = FaceletViewDeclarationLanguage.
523                 isRefreshTransientBuildOnPSS(_facesContext);
524         }
525         return _refreshTransientBuildOnPSS;
526     }
527     
528     public boolean isRefreshTransientBuildOnPSSPreserveState()
529     {
530         if (_refreshTransientBuildOnPSSPreserveState == null)
531         {
532             _refreshTransientBuildOnPSSPreserveState = MyfacesConfig.getCurrentInstance(
533                     _facesContext.getExternalContext()).isRefreshTransientBuildOnPSSPreserveState();
534         }
535         return _refreshTransientBuildOnPSSPreserveState;
536     }
537     
538     @Override
539     public boolean isBuildingViewMetadata()
540     {
541         if (_isBuildingViewMetadata == null)
542         {
543             _isBuildingViewMetadata = FaceletViewDeclarationLanguage.
544                     isBuildingViewMetadata(_facesContext);
545         }
546         return _isBuildingViewMetadata;
547     }
548 
549     @Override
550     public boolean isUsingPSSOnThisView()
551     {
552         if (_usingPSSOnThisView == null)
553         {
554             _usingPSSOnThisView = FaceletViewDeclarationLanguage.
555                 isUsingPSSOnThisView(_facesContext);
556         }
557         return _usingPSSOnThisView;
558     }
559     
560     public boolean isMarkInitialStateAndIsRefreshTransientBuildOnPSS()
561     {
562         return isMarkInitialState() && isRefreshTransientBuildOnPSS();
563     }
564 
565     @Override
566     public ELExpressionCacheMode getELExpressionCacheMode()
567     {
568         if (_elExpressionCacheMode == null)
569         {
570             String value = WebConfigParamUtils.getStringInitParameter(
571                     _facesContext.getExternalContext(),
572                     INIT_PARAM_CACHE_EL_EXPRESSIONS, ELExpressionCacheMode.noCache.name());
573             
574             _elExpressionCacheMode = Enum.valueOf(ELExpressionCacheMode.class, value); 
575         }
576         return _elExpressionCacheMode;
577     }
578 
579     @Override
580     public boolean isWrapTagExceptionsAsContextAware()
581     {
582         if (_isWrapTagExceptionsAsContextAware == null)
583         {
584             _isWrapTagExceptionsAsContextAware
585                     = WebConfigParamUtils.getBooleanInitParameter(_facesContext.getExternalContext(),
586                     INIT_PARAM_WRAP_TAG_EXCEPTIONS_AS_CONTEXT_AWARE, true);
587         }
588         return _isWrapTagExceptionsAsContextAware;
589     }
590 
591     @Override
592     public void addAttachedObjectHandler(UIComponent compositeComponentParent, AttachedObjectHandler handler)
593     {
594         List<AttachedObjectHandler> list = null;
595         if (_attachedObjectHandlers == null)
596         {
597             _attachedObjectHandlers = new HashMap<UIComponent, List<AttachedObjectHandler>>();
598         }
599         else
600         {
601             list = _attachedObjectHandlers.get(compositeComponentParent);
602         }
603 
604         if (list == null)
605         {
606             list = new ArrayList<AttachedObjectHandler>();
607             _attachedObjectHandlers.put(compositeComponentParent, list);
608         }
609 
610         list.add(handler);
611     }
612 
613     @Override
614     public void removeAttachedObjectHandlers(UIComponent compositeComponentParent)
615     {
616         if (_attachedObjectHandlers == null)
617         {
618             return;
619         }
620         _attachedObjectHandlers.remove(compositeComponentParent);
621     }
622 
623     @Override
624     public List<AttachedObjectHandler> getAttachedObjectHandlers(UIComponent compositeComponentParent)
625     {
626         if (_attachedObjectHandlers == null)
627         {
628             return null;
629         }
630         return _attachedObjectHandlers.get(compositeComponentParent);
631     }
632     
633     @Override
634     public void addMethodExpressionTargeted(UIComponent targetedComponent, String attributeName, Object backingValue)
635     {
636         Map<String, Object> map = null;
637         if (_methodExpressionsTargeted == null)
638         {
639             _methodExpressionsTargeted = new HashMap<UIComponent, Map<String, Object>>();
640         }
641         else
642         {
643             map = _methodExpressionsTargeted.get(targetedComponent);
644         }
645 
646         if (map == null)
647         {
648             map = new HashMap<String, Object>(8);
649             _methodExpressionsTargeted.put(targetedComponent, map);
650         }
651 
652         map.put(attributeName, backingValue);
653     }
654 
655     public boolean isMethodExpressionAttributeApplied(UIComponent compositeComponentParent, String attributeName)
656     {
657         if (_compositeComponentAttributesMarked == null)
658         {
659             return false;
660         }
661         Map<String, Boolean> map = _compositeComponentAttributesMarked.get(compositeComponentParent);
662         if (map == null)
663         {
664             return false;
665         }
666         Boolean v = map.get(attributeName);
667         return v == null ? false : v.booleanValue();
668     }
669     
670     public void markMethodExpressionAttribute(UIComponent compositeComponentParent, String attributeName)
671     {
672         Map<String, Boolean> map = null;
673         if (_compositeComponentAttributesMarked == null)
674         {
675             _compositeComponentAttributesMarked = new HashMap<UIComponent, Map<String, Boolean>>(); 
676         }
677         else
678         {
679             map = _compositeComponentAttributesMarked.get(compositeComponentParent);
680         }
681         
682         if (map == null)
683         {
684             map = new HashMap<String, Boolean>(8);
685             _compositeComponentAttributesMarked.put(compositeComponentParent, map);
686         }
687         map.put(attributeName, Boolean.TRUE);
688         
689     }
690     
691     public void clearMethodExpressionAttribute(UIComponent compositeComponentParent, String attributeName)
692     {
693         if (_compositeComponentAttributesMarked == null)
694         {
695             return;
696         }
697         Map<String, Boolean> map = _compositeComponentAttributesMarked.get(compositeComponentParent);
698         if (map == null)
699         {
700             //No map, so just return
701             return;
702         }
703         map.put(attributeName, Boolean.FALSE);
704     }
705     
706     
707     @Override
708     public Object removeMethodExpressionTargeted(UIComponent targetedComponent, String attributeName)
709     {
710         if (_methodExpressionsTargeted == null)
711         {
712             return null;
713         }
714         Map<String, Object> map = _methodExpressionsTargeted.get(targetedComponent);
715         if (map != null)
716         {
717             return map.remove(attributeName);
718         }
719         return null;
720     }
721 
722     /**
723      * Add a level of components marked for deletion.
724      */
725     private void increaseComponentLevelMarkedForDeletion()
726     {
727         _deletionLevel++;
728         if (_componentsMarkedForDeletion.size() <= _deletionLevel)
729         {
730             _componentsMarkedForDeletion.add(new HashMap<String, UIComponent>());
731             
732         }
733     }
734 
735     /**
736      * Remove the last component level from the components marked to be deleted. The components are removed
737      * from this list because they are deleted from the tree. This is done in ComponentSupport.finalizeForDeletion.
738      *
739      * @return the array of components that are removed.
740      */
741     private void decreaseComponentLevelMarkedForDeletion()
742     {
743         //The common case is this co
744         if (!_componentsMarkedForDeletion.get(_deletionLevel).isEmpty())
745         {
746             _componentsMarkedForDeletion.get(_deletionLevel).clear();
747         }
748         _deletionLevel--;
749     }
750 
751     /** Mark a component to be deleted from the tree. The component to be deleted is addded on the
752      * current level. This is done from ComponentSupport.markForDeletion
753      *
754      * @param id
755      * @param component the component marked for deletion.
756      */
757     private void markComponentForDeletion(String id , UIComponent component)
758     {
759         _componentsMarkedForDeletion.get(_deletionLevel).put(id, component);
760     }
761 
762     /**
763      * Remove a component from the last level of components marked to be deleted.
764      *
765      * @param id
766      */
767     private UIComponent removeComponentForDeletion(String id)
768     {
769         UIComponent removedComponent = _componentsMarkedForDeletion.get(_deletionLevel).remove(id); 
770         if (removedComponent != null && _deletionLevel > 0)
771         {
772             _componentsMarkedForDeletion.get(_deletionLevel-1).remove(id);
773         }
774         return removedComponent;
775     }
776     
777     public void markForDeletion(UIComponent component)
778     {
779         increaseComponentLevelMarkedForDeletion();
780         
781         String id = (String) component.getAttributes().get(ComponentSupport.MARK_CREATED);
782         id = (id == null) ? VIEWROOT_FACELET_ID : id;
783         markComponentForDeletion(id, component);
784         
785         
786         if (component.getFacetCount() > 0)
787         {
788             for (UIComponent fc: component.getFacets().values())
789             {
790                 id = (String) fc.getAttributes().get(ComponentSupport.MARK_CREATED);
791                 if (id != null)
792                 {
793                     markComponentForDeletion(id, fc);
794                 }
795                 else if (Boolean.TRUE.equals(fc.getAttributes().get(ComponentSupport.FACET_CREATED_UIPANEL_MARKER)))
796                 {
797                     //Mark its children, but do not mark itself.
798                     int childCount = fc.getChildCount();
799                     if (childCount > 0)
800                     {
801                         for (int i = 0; i < childCount; i++)
802                         {
803                             UIComponent child = fc.getChildren().get(i);
804                             id = (String) child.getAttributes().get(ComponentSupport.MARK_CREATED);
805                             if (id != null)
806                             {
807                                 markComponentForDeletion(id, child);
808                             }
809                         }
810                     }
811                 }
812             }
813         }
814                 
815         int childCount = component.getChildCount();
816         if (childCount > 0)
817         {
818             for (int i = 0; i < childCount; i++)
819             {
820                 UIComponent child = component.getChildren().get(i);
821                 id = (String) child.getAttributes().get(ComponentSupport.MARK_CREATED);
822                 if (id != null)
823                 {
824                     markComponentForDeletion(id, child);
825                 }
826             }
827         }
828     }
829     
830     @Override
831     public void removeComponentForDeletion(UIComponent component)
832     {
833         String id = (String) component.getAttributes().get(ComponentSupport.MARK_CREATED);
834         if (id != null)
835         {
836             removeComponentForDeletion(id);
837         }
838         else if (id == null
839                  && Boolean.TRUE.equals(component.getAttributes().get(ComponentSupport.FACET_CREATED_UIPANEL_MARKER)))
840         {
841             if (component.getChildCount() > 0)
842             {
843                 for (int i = 0, size = component.getChildCount(); i < size; i++)
844                 {
845                     UIComponent child = component.getChildren().get(i);
846                     id = (String) child.getAttributes().get(ComponentSupport.MARK_CREATED);
847                     if (id != null)
848                     {
849                         removeComponentForDeletion(id);
850                     }
851                 }
852             }
853         }
854     }
855     
856     public void finalizeForDeletion(UIComponent component)
857     {
858         String id = (String) component.getAttributes().get(ComponentSupport.MARK_CREATED);
859         id = (id == null) ? VIEWROOT_FACELET_ID : id;
860         // remove any existing marks of deletion
861         removeComponentForDeletion(id);
862         
863         // finally remove any children marked as deleted
864         int childCount = component.getChildCount();
865         if (childCount > 0)
866         {
867             for (int i = 0; i < childCount; i ++)
868             {
869                 UIComponent child = component.getChildren().get(i);
870                 id = (String) child.getAttributes().get(ComponentSupport.MARK_CREATED); 
871                 if (id != null && removeComponentForDeletion(id) != null)
872                 {
873                     component.getChildren().remove(i);
874                     i--;
875                     childCount--;
876                 }
877             }
878         }
879 
880         // remove any facets marked as deleted
881         
882         if (component.getFacetCount() > 0)
883         {
884             Map<String, UIComponent> facets = component.getFacets();
885             for (Iterator<UIComponent> itr = facets.values().iterator(); itr.hasNext();)
886             {
887                 UIComponent fc = itr.next();
888                 id = (String) fc.getAttributes().get(ComponentSupport.MARK_CREATED);
889                 if (id != null && removeComponentForDeletion(id) != null)
890                 {
891                     itr.remove();
892                 }
893                 else if (id == null
894                          && Boolean.TRUE.equals(fc.getAttributes().get(ComponentSupport.FACET_CREATED_UIPANEL_MARKER)))
895                 {
896                     if (fc.getChildCount() > 0)
897                     {
898                         for (int i = 0, size = fc.getChildCount(); i < size; i++)
899                         {
900                             UIComponent child = fc.getChildren().get(i);
901                             id = (String) child.getAttributes().get(ComponentSupport.MARK_CREATED);
902                             if (id != null && removeComponentForDeletion(id) != null)
903                             {
904                                 fc.getChildren().remove(i);
905                                 i--;
906                                 size--;
907                             }
908                         }
909                     }
910                     if (fc.getChildCount() == 0)
911                     {
912                         itr.remove();
913                     }
914                 }
915             }
916         }
917         
918         decreaseComponentLevelMarkedForDeletion();
919     }
920     
921     @Override
922     public void markRelocatableResourceForDeletion(UIComponent component)
923     {
924         // The idea is keep track of the component resources that can be relocated
925         // to later check which resources were not refreshed and delete them.
926         String id = (String) component.getAttributes().get(ComponentSupport.MARK_CREATED);
927         if (id != null)
928         {
929             _relocatableResourceForDeletion.put(id, component);
930         }
931     }
932 
933     @Override
934     public void finalizeRelocatableResourcesForDeletion(UIViewRoot root)
935     {
936         String id = null;
937         //Check facets 
938         if (root.getFacetCount() > 0)
939         {
940             Map<String, UIComponent> facets = root.getFacets();
941             for (Iterator<UIComponent> itr = facets.values().iterator(); itr.hasNext();)
942             {
943                 UIComponent fc = itr.next();
944                 // It is necessary to check only the facets that are used as holder for
945                 // component resources. To do that, the best way is check the ones that
946                 // has id starting with "javax_faces_location_"
947                 if (fc.getId() != null && fc.getId().startsWith(JAVAX_FACES_LOCATION_PREFIX))
948                 {
949                     // Check all children with MARK_CREATED and if one is found, check if it was
950                     // refreshed by the algorithm.
951                     int childCount = fc.getChildCount();
952                     if (childCount > 0)
953                     {
954                         for (int i = 0; i < childCount; i ++)
955                         {
956                             UIComponent child = fc.getChildren().get(i);
957                             id = (String) child.getAttributes().get(ComponentSupport.MARK_CREATED); 
958                             if (id != null && finalizeRelocatableResourcesForDeletion(id) == null)
959                             {
960                                 fc.getChildren().remove(i);
961                                 i--;
962                                 childCount--;
963                             }
964                         }
965                     }
966                 }
967             }
968         }
969     }
970     
971     private UIComponent finalizeRelocatableResourcesForDeletion(String id)
972     {
973         return _relocatableResourceForDeletion.remove(id); 
974     }
975     
976     public String startComponentUniqueIdSection()
977     {
978         _level++;
979         _sectionUniqueComponentIdCounter.startUniqueIdSection();
980         return _sectionUniqueIdCounter.startUniqueIdSection();
981     }
982 
983     @Override
984     public void incrementUniqueId()
985     {
986         _sectionUniqueIdCounter.incrementUniqueId();
987     }
988     
989     @Override
990     public String generateUniqueId()
991     {
992         return _sectionUniqueIdCounter.generateUniqueId();
993     }
994     
995     @Override
996     public void generateUniqueId(StringBuilder builderToAdd)
997     {
998         _sectionUniqueIdCounter.generateUniqueId(builderToAdd);
999     }
1000 
1001     @Override
1002     public String generateUniqueComponentId()
1003     {
1004         return _sectionUniqueComponentIdCounter.generateUniqueId();
1005     }
1006     
1007     @Override
1008     public void incrementUniqueComponentId()
1009     {
1010         _sectionUniqueComponentIdCounter.incrementUniqueId();
1011     }
1012     
1013     public void endComponentUniqueIdSection()
1014     {
1015         _level--;
1016         _sectionUniqueIdCounter.endUniqueIdSection();
1017         _sectionUniqueComponentIdCounter.endUniqueIdSection();
1018     }
1019     
1020     @Override
1021     public void startMetadataSection()
1022     {
1023         if (_isInMetadataSection == 0)
1024         {
1025             if (_sectionUniqueMetadataIdCounter == null)
1026             {
1027                 _sectionUniqueMetadataIdCounter = new SectionUniqueIdCounter("__md_");
1028             }
1029             if (_sectionUniqueComponentMetadataIdCounter == null)
1030             {
1031                 _sectionUniqueComponentMetadataIdCounter = new SectionUniqueIdCounter("__md_");
1032             }
1033             //Replace the counter with metadata counter
1034             _sectionUniqueIdCounter = _sectionUniqueMetadataIdCounter;
1035             _sectionUniqueComponentIdCounter = _sectionUniqueComponentMetadataIdCounter;
1036         }
1037         _isInMetadataSection++;
1038     }
1039     
1040     @Override
1041     public void endMetadataSection()
1042     {
1043         _isInMetadataSection--;
1044         if (_isInMetadataSection == 0)
1045         {
1046             //Use normal id counter again
1047             _sectionUniqueIdCounter = _sectionUniqueNormalIdCounter;
1048             _sectionUniqueComponentIdCounter = _sectionUniqueComponentNormalIdCounter;
1049         }
1050     }
1051     
1052     @Override
1053     public boolean isInMetadataSection()
1054     {
1055        return _isInMetadataSection > 0;
1056     }
1057     
1058     @Override
1059     public boolean isRefreshingSection()
1060     {
1061        return isRefreshingTransientBuild() ||  (!isBuildingViewMetadata() && isInMetadataSection());
1062     }
1063     
1064     @Override
1065     public StringBuilder getSharedStringBuilder()
1066     {
1067         if (_sharedStringBuilder == null)
1068         {
1069             _sharedStringBuilder = new StringBuilder();
1070         }
1071         else
1072         {
1073             _sharedStringBuilder.setLength(0);
1074         }
1075         return _sharedStringBuilder;
1076     }
1077     
1078     private static class KeyEntryIterator<K, V> implements Iterator<K>
1079     {
1080         private Iterator<Map.Entry<K, V>> _delegateIterator;
1081         
1082         public KeyEntryIterator(Iterator<Map.Entry<K, V>> delegate)
1083         {
1084             _delegateIterator = delegate;
1085         }
1086         
1087         public boolean hasNext()
1088         {
1089             if (_delegateIterator != null)
1090             {
1091                 return _delegateIterator.hasNext();
1092             }
1093             return false;
1094         }
1095 
1096         public K next()
1097         {
1098             if (_delegateIterator != null)
1099             {
1100                 return _delegateIterator.next().getKey();
1101             }
1102             return null;
1103         }
1104 
1105         public void remove()
1106         {
1107             if (_delegateIterator != null)
1108             {
1109                 _delegateIterator.remove();
1110             }
1111         }
1112         
1113     }
1114     
1115     private static class SimpleEntry<K, V> implements Map.Entry<K, V>
1116     {
1117         private final K _key;
1118         private final V _value;
1119 
1120         public SimpleEntry(K key, V value)
1121         {
1122             _key = key;
1123             _value = value;
1124         }
1125         
1126         public K getKey()
1127         {
1128             return _key;
1129         }
1130 
1131         public V getValue()
1132         {
1133             return _value;
1134         }
1135 
1136         @Override
1137         public int hashCode()
1138         {
1139             final int prime = 31;
1140             int result = 1;
1141             result = prime * result + ((_key == null) ? 0 : _key.hashCode());
1142             result = prime * result + ((_value == null) ? 0 : _value.hashCode());
1143             return result;
1144         }
1145 
1146         @SuppressWarnings("unchecked")
1147         @Override
1148         public boolean equals(Object obj)
1149         {
1150             if (this == obj)
1151             {
1152                 return true;
1153             }
1154             if (obj == null)
1155             {
1156                 return false;
1157             }
1158             if (getClass() != obj.getClass())
1159             {
1160                 return false;
1161             }
1162             SimpleEntry other = (SimpleEntry) obj;
1163             if (_key == null)
1164             {
1165                 if (other._key != null)
1166                 {
1167                     return false;
1168                 }
1169             }
1170             else if (!_key.equals(other._key))
1171             {
1172                 return false;
1173             }
1174             
1175             if (_value == null)
1176             {
1177                 if (other._value != null)
1178                 {
1179                     return false;
1180                 }
1181             }
1182             else if (!_value.equals(other._value))
1183             {
1184                 return false;
1185             }
1186             return true;
1187         }
1188 
1189         public V setValue(V value)
1190         {
1191             throw new UnsupportedOperationException();
1192         }
1193     }
1194 }