org.omnifaces.component.util.ResolveComponent Maven / Gradle / Ivy
/*
* Copyright OmniFaces
*
* Licensed 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
*
* https://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.omnifaces.component.util;
import static java.lang.String.format;
import static org.omnifaces.component.util.ResolveComponent.PropertyKeys.name;
import static org.omnifaces.component.util.ResolveComponent.PropertyKeys.scope;
import static org.omnifaces.util.Components.findComponentRelatively;
import static org.omnifaces.util.Events.subscribeToViewEvent;
import static org.omnifaces.util.Faces.isPostback;
import static org.omnifaces.util.Faces.setRequestAttribute;
import static org.omnifaces.util.Utils.isEmpty;
import javax.faces.component.FacesComponent;
import javax.faces.component.UIComponent;
import javax.faces.event.ComponentSystemEvent;
import javax.faces.event.PostRestoreStateEvent;
import javax.faces.event.PreRenderViewEvent;
import javax.faces.event.SystemEvent;
import javax.faces.event.SystemEventListener;
import javax.faces.view.facelets.FaceletContext;
import org.omnifaces.el.ReadOnlyValueExpression;
import org.omnifaces.taghandler.ComponentExtraHandler;
import org.omnifaces.util.Callback.SerializableReturning;
import org.omnifaces.util.Components;
import org.omnifaces.util.State;
/**
*
* The <o:resolveComponent>
component is a utility component via which a component can be looked up
* by its ID and a reference to it put in either the "facelet scope" (default) or the request scope.
*
* @since 2.0
* @author Arjan Tijms
* @see FaceletContextConsumer
* @see ComponentExtraHandler
* @see UtilFamily
*/
@FacesComponent(ResolveComponent.COMPONENT_TYPE)
public class ResolveComponent extends UtilFamily implements FaceletContextConsumer, SystemEventListener {
public static final String COMPONENT_TYPE = "org.omnifaces.component.util.ResolveComponent";
private static final String ERROR_COMPONENT_NOT_FOUND =
"A component with ID '%s' as specified by the 'for' attribute of the ResolveComponent with Id '%s' could not be found.";
private static final String ERROR_ILLEGAL_SCOPE =
"o:resolveComponent 'scope' attribute only supports 'facelet' (default) or 'request'. Encountered an invalid value of '%s'.";
public static final String DEFAULT_SCOPE = "facelet";
private ReadOnlyValueExpression readOnlyValueExpression;
private final State state = new State(getStateHelper());
enum PropertyKeys {
name, scope, /* for */
}
public ResolveComponent() {
if (!isPostback()) { // For an initial (GET) request, there's no restore
// state event and we use pre-render view
subscribeToViewEvent(PreRenderViewEvent.class, this);
}
}
// Actions --------------------------------------------------------------------------------------------------------
@Override
public void setFaceletContext(FaceletContext faceletContext) {
if (DEFAULT_SCOPE.equals(getScope())) {
readOnlyValueExpression = new ReadOnlyValueExpression(UIComponent.class);
faceletContext.getVariableMapper().setVariable(getName(), readOnlyValueExpression);
}
}
@Override
public boolean isListenerForSource(Object source) {
return true;
}
@Override
public void processEvent(SystemEvent event) {
doProcess();
}
@Override
public void processEvent(ComponentSystemEvent event) {
if (event instanceof PostRestoreStateEvent) { // For a postback we use the post-restore state event.
doProcess();
}
}
public void doProcess() {
String forValue = getFor();
if (!isEmpty(forValue)) {
UIComponent component = findComponentRelatively(this, forValue);
if (component == null) {
component = findComponent(forValue);
}
if (component == null) {
throw new IllegalArgumentException(format(ERROR_COMPONENT_NOT_FOUND, forValue, getId()));
}
String scope = getScope(); // TODO: refactor "scope" to a reusable enum, together with those of a.o. Cache.
if (DEFAULT_SCOPE.equals(scope)) {
// Component will be resolved again dynamically when the value expression is evaluated.
if (readOnlyValueExpression != null) {
readOnlyValueExpression.setCallbackReturning(new ComponentClientIdResolver(component.getClientId()));
}
}
else if ("request".equals(scope)) {
setRequestAttribute(getName(), component);
}
else {
throw new IllegalArgumentException(format(ERROR_ILLEGAL_SCOPE, scope));
}
}
}
public static class ComponentClientIdResolver implements SerializableReturning