All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javax.faces.component.behavior.AjaxBehavior Maven / Gradle / Ivy

There is a newer version: 8.0.1
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package javax.faces.component.behavior;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import javax.el.ELContext;
import javax.el.ELException;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.component.UIComponentBase;
import javax.faces.context.FacesContext;
import javax.faces.event.AjaxBehaviorListener;


/**
 * 

An instance of this class * is added as a {@link ClientBehavior} to a component using the {@link * javax.faces.component.behavior.ClientBehaviorHolder#addClientBehavior} * contract that components implement. The presence of this {@link * ClientBehavior} will cause the rendering of JavaScript that produces * an Ajax request using the specification public * JavaScript API when the component is rendered.

*

If the component is an instance * of {@link javax.faces.component.EditableValueHolder}, Where at all * possible, the component must have the UI register the ajax event when * the initial value is changed, not when focus is lost on the * component.

* * * @since 2.0 */ public class AjaxBehavior extends ClientBehaviorBase { /** *

The standard id for this behavior.

*/ public static final String BEHAVIOR_ID = "javax.faces.behavior.Ajax"; private static final Set HINTS = Collections.unmodifiableSet(EnumSet.of(ClientBehaviorHint.SUBMITTING)); private String onerror; private String onevent; private String delay; private List execute; private List render; private Boolean disabled; private Boolean immediate; private Boolean resetValues; private Map bindings; public AjaxBehavior() { } // ---------------------------------------------------------- Public Methods @Override public String getRendererType() { // We use the same sring for both the behavior id and the renderer // type. return BEHAVIOR_ID; } /** *

* This method returns an unmodifiable Set containing * the {@link ClientBehaviorHint} SUBMITTING.

* * @return unmodifiable set containing the hint {@link ClientBehaviorHint} * SUBMITTING. * * @since 2.0 */ @Override public Set getHints() { return HINTS; } /** *

Return the String of * JavaScript function name that will be used to identify * the client callback function that should be run in the event of * an error. * * @return the JavaScript function name of ONERROR. * * @since 2.0 */ public String getOnerror() { return (String) eval(ONERROR, onerror); } /** *

Sets the JavaScript function name * that will be used to identify the client callback function that * should be run in the event of an error. * * @param onerror the error handling function name * * @since 2.0 */ public void setOnerror(String onerror) { this.onerror = onerror; clearInitialState(); } /** *

Return the String of * JavaScript function name that will be used to identify the * client callback function that should be run on the occurance * of a client-side event. * * @return the JavaScript function name of ONEVENT. * * @since 2.0 */ public String getOnevent() { return (String) eval(ONEVENT, onevent); } /** *

Sets the JavaScript function name * that will be used to identify the client callback function that * should be run in response to event activity. * * @param onevent the event handling function name * * @since 2.0 */ public void setOnevent(String onevent) { this.onevent = onevent; clearInitialState(); } /** *

Return a non-empty * Collection<String> of component * identifiers that will be used to identify components that should be * processed during the execute phase of the request * processing lifecycle.

*

Note that the returned collection may be unmodifiable. Modifications * should be performed by calling {@link #setExecute}.

* * @return the JavaScript function name of EXECUTE. * * @since 2.0 */ public Collection getExecute() { return getCollectionValue(EXECUTE, execute); } /** *

Sets the component identifiers that * will be used to identify components that should be * processed during the execute phase of the request * processing lifecycle.

* * @param execute the ids of components to execute * * @since 2.0 */ public void setExecute(Collection execute) { this.execute = copyToList(execute); clearInitialState(); } /** *

Returns the delay value, or null * if no value was set.

* * @return the delay value. * * @since 2.2 */ public String getDelay() { return (String) eval(DELAY, delay); } /** *

If less than * delay milliseconds elapses between calls to * request() only the most recent one is sent and all other * requests are discarded. The default value of this option is * 300. If the value of delay is the literal string * 'none' without the quotes, no delay is used.

* * @param delay the ajax delay value * * @since 2.2 */ public void setDelay(String delay) { this.delay = delay; clearInitialState(); } /** *

Return a non-empty * Collection<String> of component * identifiers that will be used to identify components that should be * processed during the render phase of the request * processing lifecycle.

*

Note that the returned collection may be unmodifiable. Modifications * should be performed by calling {@link #setRender}.

* * @return the ids of components to render. * * @since 2.0 */ public Collection getRender() { return getCollectionValue(RENDER, render); } /** *

Sets the component identifiers that * will be used to identify components that should be * processed during the render phase of the request * processing lifecycle.

* * @param render the ids of components to render * * @since 2.0 */ public void setRender(Collection render) { this.render = copyToList(render); clearInitialState(); } /** *

* Return the resetValues status of this behavior.

* * @return the resetValues status. * * @since 2.2 */ public boolean isResetValues() { Boolean result = (Boolean) eval(RESET_VALUES, resetValues); return ((result != null) ? result : false); } /** *

* Set the resetValues status of this behavior.

* * @param resetValues the resetValues status. * * @since 2.2 */ public void setResetValues(boolean resetValues) { this.resetValues = resetValues; clearInitialState(); } /** *

Return the disabled status of this behavior.

* * @return the disabled status of this behavior. * * @since 2.0 */ public boolean isDisabled() { Boolean result = (Boolean) eval(DISABLED, disabled); return ((result != null) ? result : false); } /** *

Sets the disabled status of this * behavior.

* * @param disabled the flag to be set. * * @since 2.0 */ public void setDisabled(boolean disabled) { this.disabled = disabled; clearInitialState(); } /** *

Return the immediate status of this * behavior.

* * @return the immediate status. * * @since 2.0 */ public boolean isImmediate() { Boolean result = (Boolean) eval(IMMEDIATE, immediate); return ((result != null) ? result : false); } /** *

Sets the immediate status of this * behavior.

* * @param immediate the flag to be set. * * @since 2.0 */ public void setImmediate(boolean immediate) { this.immediate = immediate; clearInitialState(); } /** *

Tests whether the immediate attribute * is specified. Returns true if the immediate attribute is specified, * either as a locally set property or as a value expression. This * information allows an associated client behavior renderer to fall back * on the parent component's immediate status when immediate is not * explicitly specified on the AjaxBehavior. *

* * @return the flag whether the immediate attribute is specified. * * @since 2.0 */ public boolean isImmediateSet() { return ((immediate != null) || (getValueExpression(IMMEDIATE) != null)); } /** *

Tests whether the resetValues attribute * is specified. Returns true if the resetValues attribute is specified, * either as a locally set property or as a value expression. *

* * @return the flag whether the resetValues attribute is specified. * * @since 2.2 */ public boolean isResetValuesSet() { return ((resetValues != null) || (getValueExpression(RESET_VALUES) != null)); } /** *

Returns the {@link ValueExpression} * used to calculate the value for the specified property name, if any. *

* * @param name Name of the property for which to retrieve a * {@link ValueExpression} * * @return the {@link ValueExpression}. * * @throws NullPointerException if name * is null */ public ValueExpression getValueExpression(String name) { if (name == null) { throw new NullPointerException(); } return ((bindings == null) ? null : bindings.get(name)); } /** *

Sets the {@link ValueExpression} * used to calculate the value for the specified property name.

* * @param name Name of the property for which to set a * {@link ValueExpression} * @param binding The {@link ValueExpression} to set, or null * to remove any currently set {@link ValueExpression} * * @throws NullPointerException if name * is null */ public void setValueExpression(String name, ValueExpression binding) { if (name == null) { throw new NullPointerException(); } if (binding != null) { if (binding.isLiteralText()) { setLiteralValue(name, binding); } else { if (bindings == null) { // We use a very small initial capacity on this HashMap. // The goal is not to reduce collisions, but to keep the // memory footprint small. It is very unlikely that an // an AjaxBehavior would have more than 1 or 2 bound // properties - and even if more are present, it's okay // if we have some collisions - will still be fast. bindings = new HashMap<>(6,1.0f); } bindings.put(name, binding); } } else { if (bindings != null) { bindings.remove(name); if (bindings.isEmpty()) { bindings = null; } } } clearInitialState(); } /** *

Add the specified {@link AjaxBehaviorListener} * to the set of listeners registered to receive event notifications * from this {@link AjaxBehavior}.

* * @param listener The {@link AjaxBehaviorListener} to be registered * * @throws NullPointerException if listener * is null * * @since 2.0 */ public void addAjaxBehaviorListener(AjaxBehaviorListener listener) { addBehaviorListener(listener); } /** *

Remove the specified {@link AjaxBehaviorListener} * from the set of listeners registered to receive event notifications * from this {@link AjaxBehavior}.

* * @param listener The {@link AjaxBehaviorListener} to be removed * * @throws NullPointerException if listener * is null * * @since 2.0 */ public void removeAjaxBehaviorListener(AjaxBehaviorListener listener) { removeBehaviorListener(listener); } @Override public Object saveState(FacesContext context) { if (context == null) { throw new NullPointerException(); } Object[] values; Object superState = super.saveState(context); if (initialStateMarked()) { if (superState == null) { values = null; } else { values = new Object[] { superState }; } } else { values = new Object[10]; values[0] = superState; values[1] = onerror; values[2] = onevent; values[3] = disabled; values[4] = immediate; values[5] = resetValues; values[6] = delay; values[7] = saveList(execute); values[8] = saveList(render); values[9] = saveBindings(context, bindings); } return values; } @Override public void restoreState(FacesContext context, Object state) { if (context == null) { throw new NullPointerException(); } if (state != null) { Object[] values = (Object[]) state; super.restoreState(context, values[0]); if (values.length != 1) { onerror = (String)values[1]; onevent = (String)values[2]; disabled = (Boolean)values[3]; immediate = (Boolean)values[4]; resetValues = (Boolean)values[5]; delay = (String)values[6]; execute = restoreList(EXECUTE, values[7]); render = restoreList(RENDER, values[8]); bindings = restoreBindings(context, values[9]); // If we saved state last time, save state again next time. clearInitialState(); } } } // --------------------------------------------------------- Private Methods // Utility for saving bindings state private static Object saveBindings(FacesContext context, Map bindings) { // Note: This code is copied from UIComponentBase. In a future // version of the JSF spec, it would be useful to define a // attribute/property/bindings/state helper object that can be // shared across components/behaviors/validaters/converters. if (bindings == null) { return (null); } Object values[] = new Object[2]; values[0] = bindings.keySet().toArray(new String[bindings.size()]); Object[] bindingValues = bindings.values().toArray(); for (int i = 0; i < bindingValues.length; i++) { bindingValues[i] = UIComponentBase.saveAttachedState(context, bindingValues[i]); } values[1] = bindingValues; return (values); } // Utility for restoring bindings from state private static Map restoreBindings(FacesContext context, Object state) { // Note: This code is copied from UIComponentBase. See note above // in saveBindings(). if (state == null) { return (null); } Object values[] = (Object[]) state; String names[] = (String[]) values[0]; Object states[] = (Object[]) values[1]; Map bindings = new HashMap<>(names.length); for (int i = 0; i < names.length; i++) { bindings.put(names[i], (ValueExpression) UIComponentBase.restoreAttachedState(context, states[i])); } return (bindings); } // Save the List, either as a String (single element) or as // a String[] (multiple elements. private static Object saveList(List list) { if ((list == null) || list.isEmpty()) { return null; } int size = list.size(); if (size == 1) { return list.get(0); } return list.toArray(new String[size]); } // Restore the list from a String (single element) or a String[] // (multiple elements) private static List restoreList(String propertyName, Object state) { if (state == null) { return null; } List list = null; if (state instanceof String) { list = toSingletonList(propertyName, (String)state); } else if (state instanceof String[]) { list = Collections.unmodifiableList(Arrays.asList((String[])state)); } return list; } private Object eval(String propertyName, Object value) { if (value != null) { return value; } ValueExpression expression = getValueExpression(propertyName); if (expression != null) { FacesContext ctx = FacesContext.getCurrentInstance(); return expression.getValue(ctx.getELContext()); } return null; } @SuppressWarnings("unchecked") private Collection getCollectionValue(String propertyName, Collection collection) { if (collection!= null) { return collection; } Collection result = null; ValueExpression expression = getValueExpression(propertyName); if (expression != null) { FacesContext ctx = FacesContext.getCurrentInstance(); Object value = expression.getValue(ctx.getELContext()); if (value != null) { if (value instanceof Collection) { // Unchecked cast to Collection return (Collection)value; } result = toList(propertyName, expression, value); } } return result == null ? Collections.emptyList() : result; } // Sets a property, converting it from a literal private void setLiteralValue(String propertyName, ValueExpression expression) { assert(expression.isLiteralText()); Object value; ELContext context = FacesContext.getCurrentInstance().getELContext(); try { value = expression.getValue(context); } catch (ELException ele) { throw new FacesException(ele); } if (null != propertyName) { switch (propertyName) { case ONEVENT: onevent = (String)value; break; case DELAY: delay = (String)value; break; case ONERROR: onerror = (String)value; break; case IMMEDIATE: immediate = (Boolean)value; break; case RESET_VALUES: resetValues = (Boolean)value; break; case DISABLED: disabled = (Boolean)value; break; case EXECUTE: execute = toList(propertyName, expression, value); break; case RENDER: render = toList(propertyName, expression, value); break; } } } // Converts the specified object to a List private static List toList(String propertyName, ValueExpression expression, Object value) { if (value instanceof String) { String strValue = (String)value; // If the value contains no spaces, we can optimize. // This is worthwhile, since the execute/render lists // will often only contain a single value. if (strValue.indexOf(' ') == -1) { return toSingletonList(propertyName, strValue); } // We're stuck splitting up the string. String[] values = SPLIT_PATTERN.split(strValue); if ((values == null) || (values.length == 0)) { return null; } // Note that we could create a Set out of the values if // we care about removing duplicates. However, the // presence of duplicates does not real harm. They will // be consolidated during the partial view traversal. So, // just create an list - garbage in, garbage out. return Collections.unmodifiableList(Arrays.asList(values)); } // RELEASE_PENDING i18n ; throw new FacesException(expression.toString() + " : '" + propertyName + "' attribute value must be either a String or a Collection"); } // Converts a String with no spaces to a singleton list private static List toSingletonList(String propertyName, String value) { if ((null == value) || (value.length() == 0)) { return null; } if (value.charAt(0) == '@') { // These are very common, so we use shared copies // of these collections instead of re-creating. List list; if (ALL.equals(value)) { list = ALL_LIST; } else if (FORM.equals(value)){ list = FORM_LIST; } else if (THIS.equals(value)) { list = THIS_LIST; } else if (NONE.equals(value)) { list = NONE_LIST; } else { // RELEASE_PENDING i18n ; throw new FacesException(value + " : Invalid id keyword specified for '" + propertyName + "' attribute"); } return list; } return Collections.singletonList(value); } // Makes a defensive copy of the collection, converting to a List // (to make state saving a bit easier). private List copyToList(Collection collection) { if ((collection == null) || collection.isEmpty()) { return null; } return Collections.unmodifiableList(new ArrayList(collection)); } // Property name constants private static final String ONEVENT = "onevent"; private static final String ONERROR = "onerror"; private static final String IMMEDIATE = "immediate"; private static final String RESET_VALUES = "resetValues"; private static final String DISABLED = "disabled"; private static final String EXECUTE = "execute"; private static final String RENDER = "render"; private static final String DELAY = "delay"; // Id keyword constants private static String ALL = "@all"; private static String FORM = "@form"; private static String THIS = "@this"; private static String NONE = "@none"; // Shared execute/render collections private static List ALL_LIST = Collections.singletonList("@all"); private static List FORM_LIST = Collections.singletonList("@form"); private static List THIS_LIST = Collections.singletonList("@this"); private static List NONE_LIST = Collections.singletonList("@none"); // Pattern used for execute/render string splitting private static Pattern SPLIT_PATTERN = Pattern.compile(" "); }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy