com.sun.webui.jsf.component.DropDown Maven / Gradle / Ivy
/*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the license at
* https://woodstock.dev.java.net/public/CDDLv1.0.html.
* See the License for the specific language governing
* permissions and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at https://woodstock.dev.java.net/public/CDDLv1.0.html.
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* you own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
*/
package com.sun.webui.jsf.component;
import com.sun.faces.annotation.Component;
import com.sun.faces.annotation.Property;
import com.sun.webui.jsf.el.DropDownMethodExpression;
import com.sun.webui.jsf.event.MethodExprActionListener;
import com.sun.webui.jsf.util.MethodBindingMethodExpressionAdapter;
import com.sun.webui.jsf.util.MethodExpressionMethodBindingAdapter;
import java.util.Iterator;
import java.util.Map;
import javax.el.ValueExpression;
import javax.faces.component.ActionSource2;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.faces.event.ActionListener;
import javax.faces.application.Application;
import javax.faces.event.FacesEvent;
import javax.faces.event.PhaseId;
import javax.faces.el.MethodBinding;
import javax.el.MethodExpression;
/**
* The DropDown component is used to display a drop down menu to allow
* users to select one or more items from a list.
*/
@Component(type = "com.sun.webui.jsf.DropDown", family = "com.sun.webui.jsf.DropDown",
displayName = "Drop Down List", tagName = "dropDown",
helpKey = "projrave_ui_elements_palette_wdstk-jsf1.2_dropdown_list",
propertiesHelpKey = "projrave_ui_elements_palette_wdstk-jsf1.2_propsheets_drop_down_props")
public class DropDown extends ListSelector implements ActionSource2 {
public final static String SUBMIT = "_submitter";
private boolean fireAction = false;
private static final boolean DEBUG = false;
private MethodBinding methodBindingActionListener;
private MethodExpression actionExpression;
/**
* Default constructor.
*/
public DropDown() {
super();
setRendererType("com.sun.webui.jsf.DropDown");
}
/**
* Return the family for this component.
*/
@Override
public String getFamily() {
return "com.sun.webui.jsf.DropDown";
}
/**
* Getter for property Rows.
* @return Value of property Rows.
*/
private int _getRows() {
return 1;
}
/**
* Setter for property Rows.
* @param DisplayRows New value of property DisplayRows.
*/
@Override
public void setRows(int DisplayRows) {
setRows(1);
}
/**
* Getter for property multiple
* @return Value of property multiple
*/
public boolean getMultiple() {
return false;
}
/**
* Setter for property multiple
* @param multiple New value of property multiple
*/
@Override
public void setMultiple(boolean multiple) {
super.setMultiple(false);
}
/**
* Add a new {@link ActionListener} to the set of listeners interested
* in being notified when {@link ActionEvent}s occur.
*
* @param listener The {@link ActionListener} to be added
*
* @exception NullPointerException if listener
* is null
*/
public void addActionListener(ActionListener listener) {
// add the specified action listener
addFacesListener(listener);
}
/**
* Return the set of registered {@link ActionListener}s for this
* {@link ActionSource2} instance. If there are no registered listeners,
* a zero-length array is returned.
*/
public ActionListener[] getActionListeners() {
// return all ActionListener instances associated with this component
ActionListener listeners[] =
(ActionListener[]) getFacesListeners(ActionListener.class);
return listeners;
}
/**
* Remove an existing {@link ActionListener} (if any) from the set of
* listeners interested in being notified when {@link ActionEvent}s
* occur.
*
* @param listener The {@link ActionListener} to be removed
*
* @exception NullPointerException if listener
* is null
*/
public void removeActionListener(ActionListener listener) {
// remove the specified ActionListener from the list of listeners
removeFacesListener(listener);
}
/**
* The DropDown needs to override the standard decoding
* behaviour since it may also be an action source. We
* decode the component w.r.t. the value first, and
* validate it if the component is immediate. Then we
* fire an action event.
* @exception NullPointerException
*/
@Override
public void processDecodes(FacesContext context) {
if (DEBUG) {
log("processDecodes()");
}
// Skip processing if our rendered flag is false
if (!isRendered()) {
return;
}
// Decode the component and any children
// Do we really want to decode any children?
// Process all facets and children of this component
Iterator childComponents = getFacetsAndChildren();
while (childComponents.hasNext()) {
UIComponent comp = (UIComponent) childComponents.next();
comp.processDecodes(context);
}
// Get the value of this component
try {
decode(context);
} catch (RuntimeException e) {
context.renderResponse();
throw e;
}
// Testing isSubmitter() alone is deceiving here.
// The component may have been the submitter but its
// submittedValue may still be null.
// This true in the case of an OptionTitle list option
// It submits but is treated as if there was no submit
// by not setting the submittedValue. It doesn't explicitly
// set it to null, in case the caller of the renderer decode
// method, set it to something special, for example if the
// DropDown is used as a sub component.
//
// However because this component did submit the form
// we still need to call setLastClientElement
// so after calling is submitter, still check submittedValue.
// and return if it is null.
//
// Also not that the submittedValue check has been added to
// validate, as in UIInput's validate method.
//
boolean isSubmitter = isSubmitter(context);
// Should we fire an action?
//
// Check submittedValue. Let the code fall through to
// validate for an immediate action and it will just return.
//
fireAction = isSubmitter && isSubmitForm() &&
getSubmittedValue() != null;
// If we are supposed to fire an action and navigate to the value
// of the component, we get the submitted value now and pass
// it to the DropDownMethodBinding.
if (fireAction && isNavigateToValue()) {
if (DEBUG) {
log("\tHanding navigateToValue...");
}
MethodExpression mb = getActionExpression();
if (DEBUG) {
if (mb != null) {
log("\tMethod binding is " + mb.toString());
} else {
log("\tMethod binding is null");
}
}
if (mb instanceof DropDownMethodExpression) {
String outcome = null;
Object values = getSubmittedValue();
if (values instanceof String[]) {
String[] stringValues = (String[]) values;
if (stringValues.length > 0) {
outcome = stringValues[0];
if (DEBUG) {
log("Outcome is " + outcome);
}
}
}
((DropDownMethodExpression) mb).setValue(outcome);
if (DEBUG) {
log("\tNavigate to " + outcome);
}
}
}
// Next, if the component is immediate, we validate the component
if (isImmediate()) {
try {
validate(context);
} catch (RuntimeException e) {
context.renderResponse();
throw e;
}
if (!isValid()) {
context.renderResponse();
}
}
}
/**
* In addition to to the default {@link UIComponent#broadcast}
* processing, pass the {@link ActionEvent} being broadcast to the method
* referenced by actionListener
(if any), and to the default
* {@link ActionListener} registered on the {@link javax.faces.application.Application}.
*
* @param event {@link FacesEvent} to be broadcast
*
* @exception AbortProcessingException Signal the JavaServer Faces
* implementation that no further processing on the current event should be
* performed @exception IllegalArgumentException if the implementation class
* of this {@link FacesEvent} is not supported by this component
* @exception NullPointerException if event
is
* null
*/
@Override
public void broadcast(FacesEvent event) throws AbortProcessingException {
// Perform standard superclass processing
super.broadcast(event);
if (isSubmitForm() && (event instanceof ActionEvent)) {
FacesContext context = getFacesContext();
// Notify the specified action expression method (if any)
/* FIXME : Temporary hack to prevent to "actionListenerExpression"
events.
MethodExpression mb= getActionListenerExpression();
if (mb != null) {
mb.invoke(context.getELContext(), new Object[] { event });
}
*/
// Invoke the default ActionListener
ActionListener listener =
context.getApplication().getActionListener();
if (listener != null) {
listener.processAction((ActionEvent) event);
}
}
}
/**
* Intercept queueEvent
and, for {@link ActionEvent}s, mark
* the phaseId for the event to be PhaseId.APPLY_REQUEST_VALUES
* if the immediate
flag is true,
* PhaseId.INVOKE_APPLICATION
otherwise.
*/
@Override
public void queueEvent(FacesEvent e) {
// If this is an action event, we set the phase according to whether
// the component is immediate or not.
if (isSubmitForm()) {
if (e instanceof ActionEvent) {
if (isImmediate()) {
e.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
} else {
e.setPhaseId(PhaseId.INVOKE_APPLICATION);
}
}
}
super.queueEvent(e);
}
private boolean isSubmitter(FacesContext context) {
if (DEBUG) {
log("isSubmitter()");
}
String compID = getClientId(context).concat(SUBMIT);
Map requestParameters =
context.getExternalContext().getRequestParameterMap();
String submitter = (String) requestParameters.get(compID);
if (DEBUG) {
log("\tValue of submitter field " + submitter);
}
return (submitter != null) ? submitter.equals("true") : false;
}
@Override
public void validate(FacesContext context) {
// From UIInput
//
// Submitted value == null means "the component was not submitted
// at all"; validation should not continue
//
Object submittedValue = getSubmittedValue();
if (submittedValue == null) {
return;
}
super.validate(context);
if (isValid() && fireAction) {
if (DEBUG) {
log("\tQueue the component event");
}
// queue an event
queueEvent(new ActionEvent(this));
}
}
// ----------------------------------------------------- StateHolder Methods
@Override
public Object saveState(FacesContext context) {
Object values[] = new Object[4];
values[0] = _saveState(context);
values[1] = saveAttachedState(context, methodBindingActionListener);
values[2] = saveAttachedState(context, actionExpression);
values[3] = saveAttachedState(context, actionListenerExpression);
return (values);
}
@Override
public void restoreState(FacesContext context, Object state) {
Object values[] = (Object[]) state;
_restoreState(context, values[0]);
methodBindingActionListener = (MethodBinding) restoreAttachedState(context, values[1]);
actionExpression = (MethodExpression) restoreAttachedState(context, values[2]);
actionListenerExpression = (MethodExpression) restoreAttachedState(context, values[3]);
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Tag attribute methods
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Hide onSelect
@Property(name = "onSelect", isHidden = true, isAttribute = false)
@Override
public String getOnSelect() {
return super.getOnSelect();
}
// Hide rows
@Property(name = "rows", isHidden = true, isAttribute = false)
@Override
public int getRows() {
return _getRows();
}
// Hide value
@Property(name = "value", isHidden = true, isAttribute = false)
@Override
public Object getValue() {
return super.getValue();
}
/**
* The actionExpression attribute is used to specify the action to take when this
* component is activated by the user. The value of the actionExpression attribute
* must be one of the following:
*
* - an outcome string, used to indicate which page to display next,
* as defined by a navigation rule in the application configuration
* resource file
(faces-config.xml)
.
* - a JavaServer Faces EL expression that resolves
* to a backing bean method. The method must take no parameters
* and return an outcome string. The class that defines the method
* must implement the
java.io.Serializable
interface or
* javax.faces.component.StateHolder
interface.
* When you use the actionExpression attribute in the DropDown component, you must also
* set the submitForm attribute to true.
*/
@Property(name = "actionExpression", isHidden = true, displayName = "Action Expression")
@Property.Method(signature = "java.lang.String action()")
public MethodExpression getActionExpression() {
if (this.actionExpression == null && isNavigateToValue()) {
setActionExpression(new DropDownMethodExpression());
}
return this.actionExpression;
}
/**
*
The actionExpression attribute is used to specify the action to take when this
* component is activated by the user. The value of the actionExpression attribute
* must be one of the following:
*
* - an outcome string, used to indicate which page to display next,
* as defined by a navigation rule in the application configuration
* resource file
(faces-config.xml)
.
* - a JavaServer Faces EL expression that resolves
* to a backing bean method. The method must take no parameters
* and return an outcome string. The class that defines the method
* must implement the
java.io.Serializable
interface or
* javax.faces.component.StateHolder
interface.
* When you use the actionExpression attribute in the DropDown component, you must also
* set the submitForm attribute to true.
*/
public void setActionExpression(MethodExpression me) {
this.actionExpression = me;
}
/**@deprecated*/
//emulating UICommand
public javax.faces.el.MethodBinding getAction() {
MethodBinding result = null;
MethodExpression me = null;
if (null != (me = getActionExpression())) {
// if the MethodExpression is an instance of our private
// wrapper class.
if (me.getClass() == MethodExpressionMethodBindingAdapter.class) {
result = ((MethodExpressionMethodBindingAdapter) me).getWrapped();
} else {
// otherwise, this is a real MethodExpression. Wrap it
// in a MethodBinding.
result = new MethodBindingMethodExpressionAdapter(me);
}
}
return result;
}
/**@deprecated*/
//emulating UICommand
public void setAction(javax.faces.el.MethodBinding action) {
MethodExpressionMethodBindingAdapter adapter = null;
if (null != action) {
adapter = new MethodExpressionMethodBindingAdapter(action);
setActionExpression(adapter);
} else {
setActionExpression(null);
}
}
/**@deprecated*/
//emulating UICommand
public javax.faces.el.MethodBinding getActionListener() {
return this.methodBindingActionListener;
}
/**@deprecated*/
//emulating UICommand
public void setActionListener(javax.faces.el.MethodBinding actionListener) {
this.methodBindingActionListener = actionListener;
}
/**
*
The actionListenerExpression attribute is used to specify a method to handle
* an action event that is triggered when this
* component is activated by the user. The actionListenerExpression attribute
* value must be a JavaServer Faces EL expression that resolves
* to a method in a backing bean. The method must take a single parameter
* that is an ActionEvent, and its return type must be void
.
* The class that defines the method must implement the java.io.Serializable
* interface or javax.faces.component.StateHolder
interface.
*
* The actionListenerExpression method is only invoked when the submitForm attribute
* is true.
*/
@Property(name = "actionListenerExpression", isHidden = true, isAttribute = true,
displayName = "Action Listener Expression", category = "Advanced")
@Property.Method(signature = "void processAction(javax.faces.event.ActionEvent)")
private MethodExpression actionListenerExpression;
/**
*
The actionListenerExpression attribute is used to specify a method to handle
* an action event that is triggered when this
* component is activated by the user. The actionListenerExpression attribute
* value must be a JavaServer Faces EL expression that resolves
* to a method in a backing bean. The method must take a single parameter
* that is an ActionEvent, and its return type must be void
.
* The class that defines the method must implement the java.io.Serializable
* interface or javax.faces.component.StateHolder
interface.
*
* The actionListenerExpression method is only invoked when the submitForm attribute
* is true.
*/
public MethodExpression getActionListenerExpression() {
return this.actionListenerExpression;
}
/**
*
The actionListenerExpression attribute is used to specify a method to handle
* an action event that is triggered when this
* component is activated by the user. The actionListenerExpression attribute
* value must be a JavaServer Faces EL expression that resolves
* to a method in a backing bean. The method must take a single parameter
* that is an ActionEvent, and its return type must be void
.
* The class that defines the method must implement the java.io.Serializable
* interface or javax.faces.component.StateHolder
interface.
*
* The actionListenerExpression method is only invoked when the submitForm attribute
* is true.
*/
public void setActionListenerExpression(MethodExpression me) {
//call thru
ActionListener[] curActionListeners = getActionListeners();
// see if we need to remove existing actionListener.
// only need to if this.actionListenerExpression != null (since curMethodExpression won't be null)
if (null != curActionListeners && this.actionListenerExpression != null) {
for (int i = 0; i < curActionListeners.length; i++) {
if (curActionListeners[i] instanceof MethodExprActionListener) {
MethodExprActionListener curActionListener = (MethodExprActionListener) curActionListeners[i];
MethodExpression curMethodExpression = curActionListener.getMethodExpression();
if (this.actionListenerExpression.equals(curMethodExpression)) {
removeActionListener(curActionListener);
break;
}
}
}
}
if (me == null) {
this.actionListenerExpression = null;
} else {
this.actionListenerExpression = me;
addActionListener(new MethodExprActionListener(this.actionListenerExpression));
}
}
/**
*
If this flag is set to true, then the component is always
* rendered with no initial selection. By default, the component displays
* the selection that was made in the last submit of the page. This value
* should be set to true when the drop down is used for navigation.
*/
@Property(name = "forgetValue", displayName = "Do not display selected value", category = "Advanced", isHidden = true)
private boolean forgetValue = false;
private boolean forgetValue_set = false;
/**
* If this flag is set to true, then the component is always
* rendered with no initial selection. By default, the component displays
* the selection that was made in the last submit of the page. This value
* should be set to true when the drop down is used for navigation.
*/
public boolean isForgetValue() {
if (this.forgetValue_set) {
return this.forgetValue;
}
ValueExpression _vb = getValueExpression("forgetValue");
if (_vb != null) {
Object _result = _vb.getValue(getFacesContext().getELContext());
if (_result == null) {
return false;
} else {
return ((Boolean) _result).booleanValue();
}
}
return false;
}
/**
* If this flag is set to true, then the component is always
* rendered with no initial selection. By default, the component displays
* the selection that was made in the last submit of the page. This value
* should be set to true when the drop down is used for navigation.
* @see #isForgetValue()
*/
public void setForgetValue(boolean forgetValue) {
this.forgetValue = forgetValue;
this.forgetValue_set = true;
}
/**
* When this attribute is set to true, the value of the menu selection
* is used as the action, to determine which page is displayed next
* according to the registered navigation rules. Use this attribute
* instead of the action attribute when the drop down is used for
* navigation.
* When you set navigateToValue to true, you must also set submitForm to true.
*/
@Property(name = "navigateToValue", displayName = "Navigate to Component Value",
category = "Advanced", isHidden = true)
private boolean navigateToValue = false;
private boolean navigateToValue_set = false;
/**
* When this attribute is set to true, the value of the menu selection
* is used as the action, to determine which page is displayed next
* according to the registered navigation rules. Use this attribute
* instead of the action attribute when the drop down is used for
* navigation.
* When you set navigateToValue to true, you must also set submitForm to true.
*/
public boolean isNavigateToValue() {
if (this.navigateToValue_set) {
return this.navigateToValue;
}
ValueExpression _vb = getValueExpression("navigateToValue");
if (_vb != null) {
Object _result = _vb.getValue(getFacesContext().getELContext());
if (_result == null) {
return false;
} else {
return ((Boolean) _result).booleanValue();
}
}
return false;
}
/**
* When this attribute is set to true, the value of the menu selection
* is used as the action, to determine which page is displayed next
* according to the registered navigation rules. Use this attribute
* instead of the action attribute when the drop down is used for
* navigation.
* When you set navigateToValue to true, you must also set submitForm to true.
* @see #isNavigateToValue()
*/
public void setNavigateToValue(boolean navigateToValue) {
this.navigateToValue = navigateToValue;
this.navigateToValue_set = true;
}
/**
* When the submitForm attribute is set to true,
* the form is immediately submitted when the user changes the
* selection in the drop down list.
*/
@Property(name = "submitForm", displayName = "Submit the Page on Change", isHidden = true)
private boolean submitForm = false;
private boolean submitForm_set = false;
/**
* When the submitForm attribute is set to true,
* the form is immediately submitted when the user changes the
* selection in the drop down list.
*/
public boolean isSubmitForm() {
if (this.submitForm_set) {
return this.submitForm;
}
ValueExpression _vb = getValueExpression("submitForm");
if (_vb != null) {
Object _result = _vb.getValue(getFacesContext().getELContext());
if (_result == null) {
return false;
} else {
return ((Boolean) _result).booleanValue();
}
}
return false;
}
/**
* When the submitForm attribute is set to true,
* the form is immediately submitted when the user changes the
* selection in the drop down list.
* @see #isSubmitForm()
*/
public void setSubmitForm(boolean submitForm) {
this.submitForm = submitForm;
this.submitForm_set = true;
}
/**
* Sets the value of the title attribute for the HTML element.
* The specified text will display as a tooltip if the mouse cursor hovers
* over the HTML element.
*/
@Property(name = "toolTip", displayName = "Tool Tip", category = "Behavior")
private String toolTip = null;
/**
* Sets the value of the title attribute for the HTML element.
* The specified text will display as a tooltip if the mouse cursor hovers
* over the HTML element.
*/
@Override
public String getToolTip() {
if (this.toolTip != null) {
return this.toolTip;
}
ValueExpression _vb = getValueExpression("toolTip");
if (_vb != null) {
return (String) _vb.getValue(getFacesContext().getELContext());
}
return null;
}
/**
* Sets the value of the title attribute for the HTML element.
* The specified text will display as a tooltip if the mouse cursor hovers
* over the HTML element.
* @see #getToolTip()
*/
@Override
public void setToolTip(String toolTip) {
this.toolTip = toolTip;
}
/**
* Restore the state of this component.
*/
private void _restoreState(FacesContext _context, Object _state) {
Object _values[] = (Object[]) _state;
super.restoreState(_context, _values[0]);
this.forgetValue = ((Boolean) _values[1]).booleanValue();
this.forgetValue_set = ((Boolean) _values[2]).booleanValue();
this.navigateToValue = ((Boolean) _values[3]).booleanValue();
this.navigateToValue_set = ((Boolean) _values[4]).booleanValue();
this.submitForm = ((Boolean) _values[5]).booleanValue();
this.submitForm_set = ((Boolean) _values[6]).booleanValue();
this.toolTip = (String) _values[7];
}
/**
* Save the state of this component.
*/
private Object _saveState(FacesContext _context) {
Object _values[] = new Object[8];
_values[0] = super.saveState(_context);
_values[1] = this.forgetValue ? Boolean.TRUE : Boolean.FALSE;
_values[2] = this.forgetValue_set ? Boolean.TRUE : Boolean.FALSE;
_values[3] = this.navigateToValue ? Boolean.TRUE : Boolean.FALSE;
_values[4] = this.navigateToValue_set ? Boolean.TRUE : Boolean.FALSE;
_values[5] = this.submitForm ? Boolean.TRUE : Boolean.FALSE;
_values[6] = this.submitForm_set ? Boolean.TRUE : Boolean.FALSE;
_values[7] = this.toolTip;
return _values;
}
}