blob: cece699963832523b52d47ed7d96aa465d673595 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.myfaces.view.facelets.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.faces.FactoryFinder;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.component.UniqueIdVendor;
import javax.faces.component.visit.VisitContextFactory;
import javax.faces.context.FacesContext;
import javax.faces.view.AttachedObjectHandler;
import javax.faces.view.EditableValueHolderAttachedObjectHandler;
import org.apache.myfaces.config.MyfacesConfig;
import org.apache.myfaces.util.lang.Lazy;
import org.apache.myfaces.view.facelets.ELExpressionCacheMode;
import org.apache.myfaces.view.facelets.FaceletCompositionContext;
import org.apache.myfaces.view.facelets.FaceletFactory;
import org.apache.myfaces.view.facelets.FaceletViewDeclarationLanguage;
import org.apache.myfaces.view.facelets.tag.jsf.ComponentSupport;
/**
* @since 2.0.1
* @author Leonardo Uribe (latest modification by $Author$)
* @version $Revision$ $Date$
*/
public class FaceletCompositionContextImpl extends FaceletCompositionContext
{
public static final String JAVAX_FACES_LOCATION_PREFIX = "javax_faces_location_";
private static final String VISIT_CONTEXT_FACTORY = "oam.vf.VisitContextFactory";
private FacesContext _facesContext;
private FaceletFactory _factory;
private LinkedList<UIComponent> _compositeComponentStack;
private LinkedList<UniqueIdVendor> _uniqueIdVendorStack;
private LinkedList<Map.Entry<String, EditableValueHolderAttachedObjectHandler>> _enclosingValidatorIdsStack;
private Boolean _isRefreshingTransientBuild;
private Boolean _isMarkInitialState;
private Boolean _isBuildingViewMetadata;
private Boolean _refreshTransientBuildOnPSS;
private Boolean _usingPSSOnThisView;
private List<Map<String, UIComponent>> _componentsMarkedForDeletion;
private Map<String, UIComponent> _relocatableResourceForDeletion;
private int _deletionLevel;
private Map<UIComponent, List<AttachedObjectHandler>> _attachedObjectHandlers;
private Map<UIComponent, Map<String, Object>> _methodExpressionsTargeted;
private Map<UIComponent, Map<String, Boolean>> _compositeComponentAttributesMarked;
private static final String VIEWROOT_FACELET_ID = "oam.VIEW_ROOT";
private Lazy<SectionUniqueIdCounter> _sectionUniqueIdCounter;
private Lazy<SectionUniqueIdCounter> _sectionUniqueComponentIdCounter;
private List<String> _uniqueIdsList;
private Iterator<String> _uniqueIdsIterator;
private int _level;
private int _isInMetadataSection;
private Lazy<SectionUniqueIdCounter> _sectionUniqueMetadataIdCounter;
private Lazy<SectionUniqueIdCounter> _sectionUniqueNormalIdCounter;
private Lazy<SectionUniqueIdCounter> _sectionUniqueComponentMetadataIdCounter;
private Lazy<SectionUniqueIdCounter> _sectionUniqueComponentNormalIdCounter;
private List<SectionUniqueIdCounter> _sectionUniqueIdCounterStack;
private List<SectionUniqueIdCounter> _sectionUniqueComponentIdCounterStack;
private StringBuilder _sharedStringBuilder;
private int _ccLevel;
private boolean _dynamicComponentHandler;
private boolean _oldRefreshingTransientBuild;
private boolean _dynamicComponentTopLevel;
private int _dynamicComponentSection = 0;
private List<Integer> _dynamicOldDeletionLevel;
private VisitContextFactory _visitContextFactory = null;
private UIViewRoot _viewRoot = null;
private MyfacesConfig myfacesConfig;
public FaceletCompositionContextImpl(FaceletFactory factory, FacesContext facesContext)
{
super();
_factory = factory;
_facesContext = facesContext;
_componentsMarkedForDeletion = new ArrayList<>();
_relocatableResourceForDeletion = new HashMap<>();
_deletionLevel = -1;
_sectionUniqueIdCounter = new Lazy(() -> new SectionUniqueIdCounter());
//Cached at facelet view
myfacesConfig = MyfacesConfig.getCurrentInstance(facesContext);
if (myfacesConfig.getComponentUniqueIdsCacheSize() > 0)
{
String[] componentIdsCache = (String[]) facesContext.getExternalContext().
getApplicationMap().get(FaceletViewDeclarationLanguage.CACHED_COMPONENT_IDS);
if (componentIdsCache != null)
{
_sectionUniqueComponentIdCounter = new Lazy(() -> new SectionUniqueIdCounter("_", componentIdsCache));
}
else
{
_sectionUniqueComponentIdCounter = new Lazy(() -> new SectionUniqueIdCounter("_"));
}
}
else
{
_sectionUniqueComponentIdCounter = new Lazy(() -> new SectionUniqueIdCounter("_"));
}
_sectionUniqueNormalIdCounter = _sectionUniqueIdCounter;
_sectionUniqueComponentNormalIdCounter = _sectionUniqueComponentIdCounter;
_uniqueIdsList = null;
_uniqueIdsIterator = null;
_level = 0;
_isInMetadataSection = 0;
_sharedStringBuilder = null;
_ccLevel = 0;
_viewRoot = null;
}
/**
* This constructor is intended for places where the id generation strategy needs to be changed
* adding a unique base id, like for example on a dynamic component creation.
*
* @param factory
* @param facesContext
* @param base
*/
public FaceletCompositionContextImpl(FaceletFactory factory, FacesContext facesContext, String base)
{
this(factory, facesContext);
_sectionUniqueIdCounter = new Lazy(() -> new SectionUniqueIdCounter(base+ '_'));
_sectionUniqueComponentIdCounter = new Lazy(() -> new SectionUniqueIdCounter('_' + base + '_'));
_sectionUniqueNormalIdCounter = _sectionUniqueIdCounter;
_sectionUniqueComponentNormalIdCounter = _sectionUniqueComponentIdCounter;
_dynamicComponentTopLevel = true;
_dynamicComponentSection = 1;
}
@Override
public void setUniqueIdsIterator(Iterator<String> uniqueIdsIterator)
{
_uniqueIdsList = null;
_uniqueIdsIterator = uniqueIdsIterator;
}
@Override
public void initUniqueIdRecording()
{
_uniqueIdsList = new LinkedList<>();
_uniqueIdsIterator = null;
}
@Override
public void addUniqueId(String uniqueId)
{
if (_uniqueIdsList != null && _level == 0 && !(_isInMetadataSection > 0)
&& !(_dynamicComponentSection > 0))
{
_uniqueIdsList.add(uniqueId);
}
}
@Override
public String getUniqueIdFromIterator()
{
if (_uniqueIdsIterator != null && _uniqueIdsIterator.hasNext() &&
_level == 0 && !(_isInMetadataSection > 0) && !(_dynamicComponentSection > 0))
{
return _uniqueIdsIterator.next();
}
return null;
}
@Override
public List<String> getUniqueIdList()
{
return _uniqueIdsList;
}
@Override
public FaceletFactory getFaceletFactory()
{
return _factory;
}
@Override
public void release(FacesContext facesContext)
{
super.release(facesContext);
_factory = null;
_facesContext = null;
_compositeComponentStack = null;
_enclosingValidatorIdsStack = null;
_uniqueIdVendorStack = null;
_componentsMarkedForDeletion = null;
_relocatableResourceForDeletion = null;
_sectionUniqueIdCounter = null;
_sectionUniqueNormalIdCounter = null;
_sectionUniqueMetadataIdCounter = null;
_sectionUniqueComponentIdCounter = null;
_sectionUniqueComponentNormalIdCounter = null;
_sectionUniqueComponentMetadataIdCounter = null;
_sharedStringBuilder = null;
_visitContextFactory = null;
_dynamicOldDeletionLevel = null;
_viewRoot = null;
}
@Override
public UIComponent getCompositeComponentFromStack()
{
if (_compositeComponentStack != null && !_compositeComponentStack.isEmpty())
{
return _compositeComponentStack.peek();
}
return null;
}
@Override
public void pushCompositeComponentToStack(UIComponent parent)
{
if (_compositeComponentStack == null)
{
_compositeComponentStack = new LinkedList<>();
}
_compositeComponentStack.addFirst(parent);
_ccLevel++;
}
@Override
public void popCompositeComponentToStack()
{
if (_compositeComponentStack != null && !_compositeComponentStack.isEmpty())
{
_compositeComponentStack.removeFirst();
}
_ccLevel--;
}
@Override
public int getCompositeComponentLevel()
{
return _ccLevel;
}
@Override
public UniqueIdVendor getUniqueIdVendorFromStack()
{
if (_uniqueIdVendorStack != null && !_uniqueIdVendorStack.isEmpty())
{
return _uniqueIdVendorStack.peek();
}
return null;
}
@Override
public void popUniqueIdVendorToStack()
{
if (_uniqueIdVendorStack != null && !_uniqueIdVendorStack.isEmpty())
{
_uniqueIdVendorStack.removeFirst();
}
}
@Override
public void pushUniqueIdVendorToStack(UniqueIdVendor parent)
{
if (_uniqueIdVendorStack == null)
{
_uniqueIdVendorStack = new LinkedList<>();
}
_uniqueIdVendorStack.addFirst(parent);
}
/**
* Removes top of stack.
* @since 2.0
*/
@Override
public void popEnclosingValidatorIdToStack()
{
if (_enclosingValidatorIdsStack != null && !_enclosingValidatorIdsStack.isEmpty())
{
_enclosingValidatorIdsStack.removeFirst();
}
}
@Override
public void pushEnclosingValidatorIdToStack(String validatorId,
EditableValueHolderAttachedObjectHandler attachedObjectHandler)
{
if (_enclosingValidatorIdsStack == null)
{
_enclosingValidatorIdsStack = new LinkedList<>();
}
_enclosingValidatorIdsStack.addFirst(new SimpleEntry<>(validatorId, attachedObjectHandler));
}
public Iterator<Map.Entry<String, EditableValueHolderAttachedObjectHandler>> getEnclosingValidatorIdsAndHandlers()
{
if (_enclosingValidatorIdsStack != null && !_enclosingValidatorIdsStack.isEmpty())
{
return _enclosingValidatorIdsStack.iterator();
}
return null;
}
@Override
public boolean containsEnclosingValidatorId(String id)
{
if (_enclosingValidatorIdsStack != null && !_enclosingValidatorIdsStack.isEmpty())
{
for (Map.Entry<String, EditableValueHolderAttachedObjectHandler> entry : _enclosingValidatorIdsStack)
{
if (entry.getKey().equals(id))
{
return true;
}
}
}
return false;
}
@Override
public boolean isRefreshingTransientBuild()
{
if (_isRefreshingTransientBuild == null)
{
_isRefreshingTransientBuild = FaceletViewDeclarationLanguage.isRefreshingTransientBuild(_facesContext);
}
return _isRefreshingTransientBuild;
}
@Override
public boolean isMarkInitialState()
{
if (_isMarkInitialState == null)
{
_isMarkInitialState = FaceletViewDeclarationLanguage.isMarkInitialState(_facesContext);
}
return _isMarkInitialState;
}
@Override
public void setMarkInitialState(boolean value)
{
_isMarkInitialState = value;
}
@Override
public boolean isRefreshTransientBuildOnPSS()
{
if (_refreshTransientBuildOnPSS == null)
{
_refreshTransientBuildOnPSS = FaceletViewDeclarationLanguage.
isRefreshTransientBuildOnPSS(_facesContext);
}
return _refreshTransientBuildOnPSS;
}
@Override
public boolean isRefreshTransientBuildOnPSSPreserveState()
{
return myfacesConfig.isRefreshTransientBuildOnPSSPreserveState();
}
@Override
public boolean isBuildingViewMetadata()
{
if (_isBuildingViewMetadata == null)
{
_isBuildingViewMetadata = FaceletViewDeclarationLanguage.isBuildingViewMetadata(_facesContext);
}
return _isBuildingViewMetadata;
}
@Override
public boolean isUsingPSSOnThisView()
{
if (_usingPSSOnThisView == null)
{
_usingPSSOnThisView = FaceletViewDeclarationLanguage.isUsingPSSOnThisView(_facesContext);
}
return _usingPSSOnThisView;
}
@Override
public boolean isMarkInitialStateAndIsRefreshTransientBuildOnPSS()
{
return isMarkInitialState() && isRefreshTransientBuildOnPSS();
}
@Override
public ELExpressionCacheMode getELExpressionCacheMode()
{
return myfacesConfig.getELExpressionCacheMode();
}
@Override
public boolean isWrapTagExceptionsAsContextAware()
{
return myfacesConfig.isWrapTagExceptionsAsContextAware();
}
@Override
public void addAttachedObjectHandler(UIComponent compositeComponentParent, AttachedObjectHandler handler)
{
List<AttachedObjectHandler> list = null;
if (_attachedObjectHandlers == null)
{
_attachedObjectHandlers = new HashMap<>();
}
else
{
list = _attachedObjectHandlers.get(compositeComponentParent);
}
if (list == null)
{
list = new ArrayList<>();
_attachedObjectHandlers.put(compositeComponentParent, list);
}
list.add(handler);
}
@Override
public void removeAttachedObjectHandlers(UIComponent compositeComponentParent)
{
if (_attachedObjectHandlers == null)
{
return;
}
_attachedObjectHandlers.remove(compositeComponentParent);
}
@Override
public List<AttachedObjectHandler> getAttachedObjectHandlers(UIComponent compositeComponentParent)
{
if (_attachedObjectHandlers == null)
{
return null;
}
return _attachedObjectHandlers.get(compositeComponentParent);
}
@Override
public void addMethodExpressionTargeted(UIComponent targetedComponent, String attributeName, Object backingValue)
{
Map<String, Object> map = null;
if (_methodExpressionsTargeted == null)
{
_methodExpressionsTargeted = new HashMap<>();
}
else
{
map = _methodExpressionsTargeted.get(targetedComponent);
}
if (map == null)
{
map = new HashMap<>(8);
_methodExpressionsTargeted.put(targetedComponent, map);
}
map.put(attributeName, backingValue);
}
@Override
public boolean isMethodExpressionAttributeApplied(UIComponent compositeComponentParent, String attributeName)
{
if (_compositeComponentAttributesMarked == null)
{
return false;
}
Map<String, Boolean> map = _compositeComponentAttributesMarked.get(compositeComponentParent);
if (map == null)
{
return false;
}
Boolean v = map.get(attributeName);
return v == null ? false : v;
}
@Override
public void markMethodExpressionAttribute(UIComponent compositeComponentParent, String attributeName)
{
Map<String, Boolean> map = null;
if (_compositeComponentAttributesMarked == null)
{
_compositeComponentAttributesMarked = new HashMap<>();
}
else
{
map = _compositeComponentAttributesMarked.get(compositeComponentParent);
}
if (map == null)
{
map = new HashMap<>(8);
_compositeComponentAttributesMarked.put(compositeComponentParent, map);
}
map.put(attributeName, Boolean.TRUE);
}
@Override
public void clearMethodExpressionAttribute(UIComponent compositeComponentParent, String attributeName)
{
if (_compositeComponentAttributesMarked == null)
{
return;
}
Map<String, Boolean> map = _compositeComponentAttributesMarked.get(compositeComponentParent);
if (map == null)
{
//No map, so just return
return;
}
map.put(attributeName, Boolean.FALSE);
}
@Override
public Object removeMethodExpressionTargeted(UIComponent targetedComponent, String attributeName)
{
if (_methodExpressionsTargeted == null)
{
return null;
}
Map<String, Object> map = _methodExpressionsTargeted.get(targetedComponent);
if (map != null)
{
return map.remove(attributeName);
}
return null;
}
/**
* Add a level of components marked for deletion.
*/
private void increaseComponentLevelMarkedForDeletion()
{
_deletionLevel++;
if (_componentsMarkedForDeletion.size() <= _deletionLevel)
{
_componentsMarkedForDeletion.add(new HashMap<String, UIComponent>());
}
}
/**
* Remove the last component level from the components marked to be deleted. The components are removed
* from this list because they are deleted from the tree. This is done in ComponentSupport.finalizeForDeletion.
*
* @return the array of components that are removed.
*/
private void decreaseComponentLevelMarkedForDeletion()
{
//The common case is this co
if (!_componentsMarkedForDeletion.get(_deletionLevel).isEmpty())
{
_componentsMarkedForDeletion.get(_deletionLevel).clear();
}
_deletionLevel--;
}
/** Mark a component to be deleted from the tree. The component to be deleted is addded on the
* current level. This is done from ComponentSupport.markForDeletion
*
* @param id
* @param component the component marked for deletion.
*/
private void markComponentForDeletion(String id , UIComponent component)
{
_componentsMarkedForDeletion.get(_deletionLevel).put(id, component);
}
/**
* Remove a component from the last level of components marked to be deleted.
*
* @param id
*/
private UIComponent removeComponentForDeletion(String id)
{
UIComponent removedComponent = _componentsMarkedForDeletion.get(_deletionLevel).remove(id);
if (removedComponent != null && _deletionLevel > 0)
{
_componentsMarkedForDeletion.get(_deletionLevel-1).remove(id);
}
return removedComponent;
}
@Override
public void markForDeletion(UIComponent component)
{
increaseComponentLevelMarkedForDeletion();
String id = (String) component.getAttributes().get(ComponentSupport.MARK_CREATED);
id = (id == null) ? VIEWROOT_FACELET_ID : id;
markComponentForDeletion(id, component);
if (component.getFacetCount() > 0)
{
for (UIComponent fc: component.getFacets().values())
{
id = (String) fc.getAttributes().get(ComponentSupport.MARK_CREATED);
if (id != null)
{
markComponentForDeletion(id, fc);
}
else if (Boolean.TRUE.equals(fc.getAttributes().get(ComponentSupport.FACET_CREATED_UIPANEL_MARKER)))
{
//Mark its children, but do not mark itself.
int childCount = fc.getChildCount();
if (childCount > 0)
{
for (int i = 0; i < childCount; i++)
{
UIComponent child = fc.getChildren().get(i);
id = (String) child.getAttributes().get(ComponentSupport.MARK_CREATED);
if (id != null)
{
markComponentForDeletion(id, child);
}
}
}
}
}
}
int childCount = component.getChildCount();
if (childCount > 0)
{
for (int i = 0; i < childCount; i++)
{
UIComponent child = component.getChildren().get(i);
id = (String) child.getAttributes().get(ComponentSupport.MARK_CREATED);
if (id != null)
{
markComponentForDeletion(id, child);
}
}
}
}
@Override
public void removeComponentForDeletion(UIComponent component)
{
String id = (String) component.getAttributes().get(ComponentSupport.MARK_CREATED);
if (id != null)
{
removeComponentForDeletion(id);
}
else if (id == null
&& Boolean.TRUE.equals(component.getAttributes().get(ComponentSupport.FACET_CREATED_UIPANEL_MARKER)))
{
int childCount = component.getChildCount();
if (childCount > 0)
{
for (int i = 0, size = childCount; i < size; i++)
{
UIComponent child = component.getChildren().get(i);
id = (String) child.getAttributes().get(ComponentSupport.MARK_CREATED);
if (id != null)
{
removeComponentForDeletion(id);
}
}
}
}
}
@Override
public void finalizeForDeletion(UIComponent component)
{
String id = (String) component.getAttributes().get(ComponentSupport.MARK_CREATED);
id = (id == null) ? VIEWROOT_FACELET_ID : id;
// remove any existing marks of deletion
removeComponentForDeletion(id);
// finally remove any children marked as deleted
int childCount = component.getChildCount();
if (childCount > 0)
{
for (int i = 0; i < childCount; i ++)
{
UIComponent child = component.getChildren().get(i);
id = (String) child.getAttributes().get(ComponentSupport.MARK_CREATED);
if (id != null && removeComponentForDeletion(id) != null)
{
component.getChildren().remove(i);
i--;
childCount--;
}
}
}
// remove any facets marked as deleted
if (component.getFacetCount() > 0)
{
Map<String, UIComponent> facets = component.getFacets();
for (Iterator<UIComponent> itr = facets.values().iterator(); itr.hasNext();)
{
UIComponent fc = itr.next();
id = (String) fc.getAttributes().get(ComponentSupport.MARK_CREATED);
if (id != null && removeComponentForDeletion(id) != null)
{
itr.remove();
}
else if (id == null
&& Boolean.TRUE.equals(fc.getAttributes().get(ComponentSupport.FACET_CREATED_UIPANEL_MARKER)))
{
if (fc.getChildCount() > 0)
{
for (int i = 0, size = fc.getChildCount(); i < size; i++)
{
UIComponent child = fc.getChildren().get(i);
id = (String) child.getAttributes().get(ComponentSupport.MARK_CREATED);
if (id != null && removeComponentForDeletion(id) != null)
{
fc.getChildren().remove(i);
i--;
size--;
}
}
}
if (fc.getChildCount() == 0)
{
itr.remove();
}
}
}
}
decreaseComponentLevelMarkedForDeletion();
}
@Override
public void markRelocatableResourceForDeletion(UIComponent component)
{
// The idea is keep track of the component resources that can be relocated
// to later check which resources were not refreshed and delete them.
String id = (String) component.getAttributes().get(ComponentSupport.MARK_CREATED);
if (id != null)
{
_relocatableResourceForDeletion.put(id, component);
}
}
@Override
public void finalizeRelocatableResourcesForDeletion(UIViewRoot root)
{
String id = null;
//Check facets
if (root.getFacetCount() > 0)
{
Map<String, UIComponent> facets = root.getFacets();
for (Iterator<UIComponent> itr = facets.values().iterator(); itr.hasNext();)
{
UIComponent fc = itr.next();
// It is necessary to check only the facets that are used as holder for
// component resources. To do that, the best way is check the ones that
// has id starting with "javax_faces_location_"
if (fc.getId() != null && fc.getId().startsWith(JAVAX_FACES_LOCATION_PREFIX))
{
// Check all children with MARK_CREATED and if one is found, check if it was
// refreshed by the algorithm.
int childCount = fc.getChildCount();
if (childCount > 0)
{
for (int i = 0; i < childCount; i ++)
{
UIComponent child = fc.getChildren().get(i);
id = (String) child.getAttributes().get(ComponentSupport.MARK_CREATED);
if (id != null && finalizeRelocatableResourcesForDeletion(id) == null)
{
fc.getChildren().remove(i);
i--;
childCount--;
}
}
}
}
}
}
}
private UIComponent finalizeRelocatableResourcesForDeletion(String id)
{
return _relocatableResourceForDeletion.remove(id);
}
@Override
public String startComponentUniqueIdSection()
{
_level++;
_sectionUniqueComponentIdCounter.get().startUniqueIdSection();
return _sectionUniqueIdCounter.get().startUniqueIdSection();
}
@Override
public String startComponentUniqueIdSection(String base)
{
_level++;
_sectionUniqueComponentIdCounter.get().startUniqueIdSection(base);
return _sectionUniqueIdCounter.get().startUniqueIdSection(base);
}
@Override
public void incrementUniqueId()
{
_sectionUniqueIdCounter.get().incrementUniqueId();
}
@Override
public String generateUniqueId()
{
return _sectionUniqueIdCounter.get().generateUniqueId();
}
@Override
public void generateUniqueId(StringBuilder builderToAdd)
{
_sectionUniqueIdCounter.get().generateUniqueId(builderToAdd);
}
@Override
public String generateUniqueComponentId()
{
return _sectionUniqueComponentIdCounter.get().generateUniqueId();
}
@Override
public void incrementUniqueComponentId()
{
_sectionUniqueComponentIdCounter.get().incrementUniqueId();
}
@Override
public void endComponentUniqueIdSection()
{
_level--;
_sectionUniqueIdCounter.get().endUniqueIdSection();
_sectionUniqueComponentIdCounter.get().endUniqueIdSection();
}
@Override
public void endComponentUniqueIdSection(String base)
{
_level--;
_sectionUniqueIdCounter.get().endUniqueIdSection(base);
_sectionUniqueComponentIdCounter.get().endUniqueIdSection(base);
}
@Override
public void startMetadataSection()
{
if (_isInMetadataSection == 0)
{
if (_sectionUniqueMetadataIdCounter == null)
{
_sectionUniqueMetadataIdCounter = new Lazy(() -> new SectionUniqueIdCounter("__md_"));
}
if (_sectionUniqueComponentMetadataIdCounter == null)
{
_sectionUniqueComponentMetadataIdCounter = new Lazy(() -> new SectionUniqueIdCounter("__md_"));
}
//Replace the counter with metadata counter
_sectionUniqueIdCounter = _sectionUniqueMetadataIdCounter;
_sectionUniqueComponentIdCounter = _sectionUniqueComponentMetadataIdCounter;
}
_isInMetadataSection++;
}
@Override
public void endMetadataSection()
{
_isInMetadataSection--;
if (_isInMetadataSection == 0)
{
//Use normal id counter again
_sectionUniqueIdCounter = _sectionUniqueNormalIdCounter;
_sectionUniqueComponentIdCounter = _sectionUniqueComponentNormalIdCounter;
}
}
@Override
public boolean isInMetadataSection()
{
return _isInMetadataSection > 0;
}
@Override
public boolean isRefreshingSection()
{
return isRefreshingTransientBuild() || (!isBuildingViewMetadata() && isInMetadataSection());
}
@Override
public StringBuilder getSharedStringBuilder()
{
if (_sharedStringBuilder == null)
{
_sharedStringBuilder = new StringBuilder();
}
else
{
_sharedStringBuilder.setLength(0);
}
return _sharedStringBuilder;
}
@Override
public boolean isDynamicCompositeComponentHandler()
{
return this._dynamicComponentHandler;
}
@Override
public void setDynamicCompositeComponentHandler(boolean value)
{
this._dynamicComponentHandler = value;
}
@Override
public void pushDynamicComponentSection(String base)
{
if (_sectionUniqueIdCounterStack == null)
{
_sectionUniqueIdCounterStack = new ArrayList<SectionUniqueIdCounter>();
}
if (_sectionUniqueComponentIdCounterStack == null)
{
_sectionUniqueComponentIdCounterStack = new ArrayList<SectionUniqueIdCounter>();
}
// Activate refresh transient build over dynamic component section.
if (_sectionUniqueComponentIdCounterStack.isEmpty())
{
_oldRefreshingTransientBuild = _isRefreshingTransientBuild;
}
_isRefreshingTransientBuild = true;
_sectionUniqueIdCounterStack.add(_sectionUniqueIdCounter.get());
_sectionUniqueComponentIdCounterStack.add(_sectionUniqueComponentIdCounter.get());
_sectionUniqueIdCounter = new Lazy(() -> new SectionUniqueIdCounter(base + '_'));
_sectionUniqueComponentIdCounter = new Lazy(() -> new SectionUniqueIdCounter('_' + base + '_'));
_sectionUniqueNormalIdCounter = _sectionUniqueIdCounter;
_sectionUniqueComponentNormalIdCounter = _sectionUniqueComponentIdCounter;
_dynamicComponentTopLevel = true;
_dynamicComponentSection++;
if (_dynamicOldDeletionLevel == null)
{
_dynamicOldDeletionLevel = new ArrayList<Integer>(4);
}
_dynamicOldDeletionLevel.add(_deletionLevel);
// Increase one level in the mark/delete algorithm to avoid any interference in the previous code.
increaseComponentLevelMarkedForDeletion();
}
@Override
public void popDynamicComponentSection()
{
decreaseComponentLevelMarkedForDeletion();
int oldDeletionLevel = _dynamicOldDeletionLevel.remove(_dynamicOldDeletionLevel.size()-1);
if (_deletionLevel != oldDeletionLevel)
{
// This happens because in a dynamic component section, the dynamic top component level does not take
// part in the algorithm. The easiest solution so far is just decrease one level to let it as it was
// before enter the algorithm.
decreaseComponentLevelMarkedForDeletion();
}
_sectionUniqueIdCounter.reset(
_sectionUniqueIdCounterStack.remove(_sectionUniqueIdCounterStack.size() - 1));
_sectionUniqueComponentIdCounter.reset(
_sectionUniqueComponentIdCounterStack.remove(_sectionUniqueComponentIdCounterStack.size() - 1));
//Restore refresh section
if (_sectionUniqueComponentIdCounterStack.isEmpty())
{
_isRefreshingTransientBuild = _oldRefreshingTransientBuild;
}
_sectionUniqueNormalIdCounter = _sectionUniqueIdCounter;
_sectionUniqueComponentNormalIdCounter = _sectionUniqueComponentIdCounter;
_dynamicComponentTopLevel = false;
_dynamicComponentSection--;
}
@Override
public boolean isDynamicComponentTopLevel()
{
return _dynamicComponentTopLevel;
}
@Override
public void setDynamicComponentTopLevel(boolean value)
{
_dynamicComponentTopLevel = value;
}
@Override
public boolean isDynamicComponentSection()
{
return _dynamicComponentSection > 0;
}
@Override
public void setViewRoot(UIViewRoot root)
{
this._viewRoot = root;
}
@Override
public UIViewRoot getViewRoot(FacesContext facesContext)
{
if (_viewRoot == null)
{
return facesContext.getViewRoot();
}
return _viewRoot;
}
@Override
public VisitContextFactory getVisitContextFactory()
{
if (_visitContextFactory == null)
{
// Store it in application map improve performance because it avoids FactoryFinde.getFactory(...) call
// which has synchronized blocks.
_visitContextFactory = (VisitContextFactory) _facesContext.getExternalContext().getApplicationMap()
.computeIfAbsent(
VISIT_CONTEXT_FACTORY,
k -> FactoryFinder.getFactory(FactoryFinder.VISIT_CONTEXT_FACTORY));
}
return _visitContextFactory;
}
private static class SimpleEntry<K, V> implements Map.Entry<K, V>
{
private final K _key;
private final V _value;
public SimpleEntry(K key, V value)
{
_key = key;
_value = value;
}
@Override
public K getKey()
{
return _key;
}
@Override
public V getValue()
{
return _value;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((_key == null) ? 0 : _key.hashCode());
result = prime * result + ((_value == null) ? 0 : _value.hashCode());
return result;
}
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (getClass() != obj.getClass())
{
return false;
}
SimpleEntry other = (SimpleEntry) obj;
if (_key == null)
{
if (other._key != null)
{
return false;
}
}
else if (!_key.equals(other._key))
{
return false;
}
if (_value == null)
{
if (other._value != null)
{
return false;
}
}
else if (!_value.equals(other._value))
{
return false;
}
return true;
}
@Override
public V setValue(V value)
{
throw new UnsupportedOperationException();
}
}
}