Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add back Performance-optimized version of ComponentSupport.findChildByTagId() #4934

Merged
merged 2 commits into from
May 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 117 additions & 69 deletions impl/src/main/java/com/sun/faces/facelets/tag/jsf/ComponentSupport.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,35 +16,35 @@

package com.sun.faces.facelets.tag.jsf;

import com.sun.faces.RIConstants;
import com.sun.faces.context.StateContext;
import com.sun.faces.facelets.tag.jsf.core.FacetHandler;
import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.PartialStateSaving;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.sun.faces.util.Util;

import javax.faces.FacesException;
import javax.faces.component.UIComponent;
import javax.faces.component.UIPanel;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseId;
import javax.faces.view.facelets.ComponentConfig;
import javax.faces.view.facelets.ComponentHandler;
import javax.faces.view.facelets.FaceletContext;
import javax.faces.view.facelets.Tag;
import javax.faces.view.facelets.TagAttribute;
import javax.faces.view.facelets.TagAttributeException;
import javax.faces.view.facelets.Tag;
import javax.faces.event.PhaseId;

import com.sun.faces.RIConstants;
import com.sun.faces.context.StateContext;
import com.sun.faces.facelets.tag.jsf.core.FacetHandler;
import com.sun.faces.util.Util;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
*
Expand All @@ -55,6 +55,7 @@ public final class ComponentSupport {

private final static String MARK_DELETED = "com.sun.faces.facelets.MARK_DELETED";
public final static String MARK_CREATED = "com.sun.faces.facelets.MARK_ID";
private final static String MARK_ID_CACHE = "com.sun.faces.facelets.MARK_ID_CACHE";

// Expando boolean attribute used to identify parent components that have had
// a dynamic child addition or removal.
Expand Down Expand Up @@ -237,65 +238,112 @@ public static UIComponent findUIInstructionChildByTagId(FacesContext context, UI
* @return the UI component
*/
public static UIComponent findChildByTagId(FacesContext context, UIComponent parent, String id) {
UIComponent c = null;
UIViewRoot root = context.getViewRoot();
boolean hasDynamicComponents = (null != root &&
root.getAttributes().containsKey(RIConstants.TREE_HAS_DYNAMIC_COMPONENTS));
String cid = null;
List<UIComponent> components;
String facetName = getFacetName(parent);
if (null != facetName) {
c = parent.getFacet(facetName);
// We will have a facet name, but no corresponding facet in the
// case of facets with composite components. In this case,
// we must do the brute force search.
if (null != c) {
cid = (String) c.getAttributes().get(MARK_CREATED);
if (id.equals(cid)) {
return c;
}
}
// fast path - get the child from the descendant mark id cache
return getDescendantMarkIdCache(parent).get(id);
}

@SuppressWarnings("unchecked")
private static Map<String, UIComponent> getDescendantMarkIdCache(UIComponent component) {
Map<String, UIComponent> descendantMarkIdCache = (Map<String, UIComponent>) component.getTransientStateHelper().getTransient(MARK_ID_CACHE);

if (descendantMarkIdCache == null) {
descendantMarkIdCache = new HashMap<String, UIComponent>();
component.getTransientStateHelper().putTransient(MARK_ID_CACHE, descendantMarkIdCache);
}
if (0 < parent.getFacetCount()) {
components = new ArrayList<>();
components.addAll(parent.getFacets().values());
components.addAll(parent.getChildren());
} else {
components = parent.getChildren();

return descendantMarkIdCache;
}

/**
* Adds the mark id of the specified {@link UIComponent} <code>otherComponent</code> to the mark id cache of this component,
* including all its descendant mark ids. Changes are propagated up the component tree.
*/
public static void addToDescendantMarkIdCache(UIComponent component, UIComponent otherComponent) {
String markId = (String) otherComponent.getAttributes().get(MARK_CREATED);
if (markId != null) {
addSingleDescendantMarkId(component, markId, otherComponent);
}
Map<String, UIComponent> otherMarkIds = getDescendantMarkIdCache(otherComponent);
if (!otherMarkIds.isEmpty()) {
addAllDescendantMarkIds(component, otherMarkIds);
}
}

int len = components.size();
for (int i = 0; i < len; i++) {
c = components.get(i);
cid = (String) c.getAttributes().get(MARK_CREATED);
if (id.equals(cid)) {
return c;
}
if (c instanceof UIPanel && c.getAttributes().containsKey(IMPLICIT_PANEL)) {
for (UIComponent c2 : c.getChildren()) {
cid = (String) c2.getAttributes().get(MARK_CREATED);
if (id.equals(cid)) {
return c2;
}
}
}
if (hasDynamicComponents) {
/*
* Make sure we look for the child recursively it might have moved
* into a different parent in the parent hierarchy. Note currently
* we are only looking down the tree. Maybe it would be better
* to use the VisitTree API instead.
*/
UIComponent foundChild = findChildByTagId(context, c, id);
if (foundChild != null) {
return foundChild;
}
}
/**
* Adds the specified <code>markId</code> and its corresponding {@link UIComponent} <code>otherComponent</code>
* to the mark id cache of this component. Changes are propagated up the component tree.
*/
private static void addSingleDescendantMarkId(UIComponent component, String markId, UIComponent otherComponent) {
getDescendantMarkIdCache(component).put(markId, otherComponent);
UIComponent parent = component.getParent();
if (parent != null) {
addSingleDescendantMarkId(parent, markId, otherComponent);
}
}

return null;
/**
* Adds all specified <code>otherMarkIds</code> to the mark id cache of this component.
* Changes are propagated up the component tree.
*/
private static void addAllDescendantMarkIds(UIComponent component, Map<String, UIComponent> otherMarkIds) {
getDescendantMarkIdCache(component).putAll(otherMarkIds);
UIComponent parent = component.getParent();
if (parent != null) {
addAllDescendantMarkIds(parent, otherMarkIds);
}
}


/**
* Removes the mark id of the specified {@link UIComponent} <code>otherComponent</code> from the mark id cache of this component,
* including all its descendant mark ids. Changes are propagated up the component tree.
*/
public static void removeFromDescendantMarkIdCache(UIComponent component, UIComponent otherComponent) {
String markId = (String) otherComponent.getAttributes().get(MARK_CREATED);
if (markId != null) {
removeSingleDescendantMarkId(component, markId);
}
Map<String, UIComponent> otherMarkIds = getDescendantMarkIdCache(otherComponent);
if (!otherMarkIds.isEmpty()) {
removeAllDescendantMarkIds(component, otherMarkIds);
}
}

/**
* Removes the specified <code>markId</code> from the mark id cache of this component.
* Changes are propagated up the component tree.
*/
private static void removeSingleDescendantMarkId(UIComponent component, String markId) {
getDescendantMarkIdCache(component).remove(markId);
UIComponent parent = component.getParent();
if (parent != null) {
removeSingleDescendantMarkId(parent, markId);
}
}

/**
* Removes all specified <code>otherMarkIds</code> from the mark id cache of this component.
* Changes are propagated up the component tree.
*/
private static void removeAllDescendantMarkIds(UIComponent component, Map<String, UIComponent> otherMarkIds) {
Map<String, UIComponent> descendantMarkIdCache = getDescendantMarkIdCache(component);
Iterator<String> iterator = otherMarkIds.keySet().iterator();
while (iterator.hasNext()) {
descendantMarkIdCache.remove(iterator.next());
}
UIComponent parent = component.getParent();
if (parent != null) {
removeAllDescendantMarkIds(parent, otherMarkIds);
}
}

/**
* Returns <code>true</code> if the given faces context is <strong>not</strong> {@link FacesContext#isReleased()},
* and its current phase ID is <strong>not</strong> {@link PhaseId#RENDER_RESPONSE}.
*/
public static boolean isNotRenderingResponse(FacesContext context) {
return !context.isReleased() && context.getCurrentPhaseId() != PhaseId.RENDER_RESPONSE;
}

/**
* According to JSF 1.2 tag specs, this helper method will use the
* TagAttribute passed in determining the Locale intended.
Expand Down
15 changes: 15 additions & 0 deletions impl/src/main/java/javax/faces/component/ComponentStateHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@

package javax.faces.component;

import static com.sun.faces.facelets.tag.jsf.ComponentSupport.MARK_CREATED;
import static com.sun.faces.facelets.tag.jsf.ComponentSupport.addToDescendantMarkIdCache;
import static com.sun.faces.util.Util.coalesce;
import static com.sun.faces.util.Util.isEmpty;
import static javax.faces.component.UIComponent.PropertyKeys;
import static javax.faces.component.UIComponentBase.restoreAttachedState;
import static javax.faces.component.UIComponentBase.saveAttachedState;

Expand All @@ -31,6 +34,8 @@
import javax.el.ValueExpression;
import javax.faces.context.FacesContext;

import com.sun.faces.facelets.tag.jsf.ComponentSupport;

/**
* A base implementation for maps which implement the PartialStateHolder and TransientStateHolder
* interfaces.
Expand Down Expand Up @@ -114,6 +119,16 @@ public Object remove(Serializable key) {
public Object put(Serializable key, String mapKey, Object value) {
initMap(key);

if (MARK_CREATED.equals(mapKey)) {
if (PropertyKeys.attributes.equals(key)) {
UIComponent parent = component.getParent();
if (parent != null) {
// remember this component by its mark id
addToDescendantMarkIdCache(parent, component);
}
}
}

Object ret = null;
if (component.initialStateMarked()) {
Map<String, Object> dMap = (Map<String, Object>) deltaMap.get(key);
Expand Down
1 change: 1 addition & 0 deletions impl/src/main/java/javax/faces/component/UIComponent.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package javax.faces.component;

import static com.sun.faces.facelets.tag.jsf.ComponentSupport.MARK_CREATED;
import static com.sun.faces.util.Util.isAnyNull;
import static com.sun.faces.util.Util.isOneOf;
import static java.util.Collections.emptyMap;
Expand Down
Loading