org.richfaces.context.AjaxOutputTracker Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of richfaces-core Show documentation
Show all versions of richfaces-core Show documentation
The RichFaces core framework.
The newest version!
/*
* JBoss, Home of Professional Open Source
* Copyright 2013, Red Hat, Inc. and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.richfaces.context;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.PostAddToViewEvent;
import javax.faces.event.PreRemoveFromViewEvent;
import javax.faces.event.SystemEvent;
import javax.faces.event.SystemEventListener;
import org.ajax4jsf.component.AjaxOutput;
/**
* Tracker that tracks all {@link AjaxOutput} components in the component tree and that can retrieve tracked {@link AjaxOutput}s
* in O(n) where "n" is depth of the tree.
*
* The O(n) is ensured by tracking nested {@link AjaxOutput} in each {@link NamingContainer} component on a way from the
* {@link UIViewRoot} to the given component.
*
* @author Nick Belaevski
*/
public class AjaxOutputTracker implements SystemEventListener {
private static final String ATTRIBUTE_NAME = "org.richfaces.AjaxOutputTracker";
/**
* Tracks additions (resp. removals) of {@link AjaxOutput} to (resp. from) a component tree on {@link PostAddToViewEvent}
* (resp. {@link PreRemoveFromViewEvent})
*/
@Override
public void processEvent(SystemEvent event) throws AbortProcessingException {
if (event instanceof PostAddToViewEvent) {
PostAddToViewEvent addToViewEvent = (PostAddToViewEvent) event;
componentAdded(addToViewEvent.getComponent());
} else if (event instanceof PreRemoveFromViewEvent) {
PreRemoveFromViewEvent removeFromViewEvent = (PreRemoveFromViewEvent) event;
componentRemoved(removeFromViewEvent.getComponent());
} else {
throw new IllegalArgumentException(event.toString());
}
}
/**
* Tracks just {@link AjaxOutput} components
*/
public boolean isListenerForSource(Object source) {
return source instanceof AjaxOutput;
}
/**
* Return a list of all {@link AjaxOutput} components in a tree under a given component.
*/
static Collection getAjaxOutputs(FacesContext facesContext, UIComponent component) {
final Collection childrenIds = getDirectChildrenIds(component);
final Set ajaxOutputs = new HashSet();
if (childrenIds != null) {
for (String childId : childrenIds) {
UIComponent child = component.findComponent(childId);
if (child instanceof AjaxOutput) {
ajaxOutputs.add(child);
} else if (isContainerComponent(child)) {
ajaxOutputs.addAll(getAjaxOutputs(facesContext, child));
}
}
}
return ajaxOutputs;
}
/**
* Returns a list of {@link AjaxOutput} (or IDs of {@link NamingContainer} that contains at least one {@link AjaxOutput})
* tracked in the given component subtree.
*
* Provided component must be Container Component as specified by {@link #isContainerComponent(UIComponent)}.
*
* @throws IllegalArgumentException when the provided component is not Container Component
*/
static Collection getDirectChildrenIds(UIComponent component) {
if (!isContainerComponent(component)) {
throw new IllegalArgumentException(component.toString());
}
return getTrackedChildrenSet(component, false);
}
/**
* Detects whether given component has some {@link AjaxOutput} components tracked in its subtree.
*/
static boolean hasNestedAjaxOutputs(UIComponent component) {
Collection childrenIds = getDirectChildrenIds(component);
return childrenIds != null && !childrenIds.isEmpty();
}
/**
* Returns list of {@link AjaxOutput} IDs tracked in the given component subtree
*/
private static Set getTrackedChildrenSet(UIComponent component, boolean create) {
Map attributes = component.getAttributes();
@SuppressWarnings("unchecked")
Set trackedChildrenSet = (Set) attributes.get(ATTRIBUTE_NAME);
if (trackedChildrenSet == null && create) {
trackedChildrenSet = new HashSet();
attributes.put(ATTRIBUTE_NAME, trackedChildrenSet);
}
return trackedChildrenSet;
}
/**
* Clears a list of tracked {@link AjaxOutput} IDs in a given component
*/
private static void clearTrackedChildrenSet(UIComponent component) {
component.getAttributes().remove(ATTRIBUTE_NAME);
}
private static String getId(UIComponent component) {
String id = component.getId();
if (id == null) {
// TODO force clientId creation?
component.getClientId();
id = component.getId();
}
return id;
}
private static boolean isContainerComponent(UIComponent component) {
return component instanceof NamingContainer || component instanceof UIViewRoot;
}
private UIComponent findParentContainerComponent(UIComponent component) {
UIComponent c = component.getParent();
while (c != null && !isContainerComponent(c)) {
c = c.getParent();
}
return c;
}
private void componentAdded(UIComponent c) {
UIComponent child = c;
UIComponent parent;
while ((parent = findParentContainerComponent(child)) != null) {
Set trackedChildrenSet = getTrackedChildrenSet(parent, true);
boolean updateNextParent = trackedChildrenSet.isEmpty();
trackedChildrenSet.add(getId(child));
if (!updateNextParent) {
break;
}
child = parent;
}
}
private void componentRemoved(UIComponent c) {
UIComponent child = c;
UIComponent parent;
while ((parent = findParentContainerComponent(child)) != null) {
Set trackingSet = getTrackedChildrenSet(parent, false);
if (trackingSet != null) {
trackingSet.remove(getId(child));
if (trackingSet.isEmpty()) {
clearTrackedChildrenSet(parent);
} else {
break;
}
}
child = parent;
}
}
}