com.sun.webui.jsf.component.EditableList Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2007-2018 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://oss.oracle.com/licenses/CDDL+GPL-1.1
* or 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 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 com.sun.webui.jsf.component;
import com.sun.faces.annotation.Component;
import com.sun.faces.annotation.Property;
import com.sun.webui.jsf.model.list.ListItem;
import com.sun.webui.theme.Theme;
import com.sun.webui.jsf.util.ComponentUtilities;
import com.sun.webui.jsf.util.MessageUtil;
import com.sun.webui.jsf.util.ThemeUtilities;
import com.sun.webui.jsf.util.ConversionUtilities;
import com.sun.webui.jsf.validator.StringLengthValidator;
import com.sun.webui.jsf.util.JavaScriptUtilities;
import java.lang.reflect.Array;
import java.io.Serializable;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
import javax.el.MethodExpression;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.application.FacesMessage;
import javax.faces.component.EditableValueHolder;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.event.ActionEvent;
import javax.faces.event.ActionListener;
/**
* The EditableList component allows users to create and modify a list of
* strings.
*
* Use this component when web application users need to create and modify a
* list of strings. The application user can add new strings by typing them into
* the textfield and clicking the "Add" button, and remove them by selecting one
* or more items from the list and clicking the "Remove" button.
*
* Configuring the listbox tag
*
* Use the list
attribute to bind the component
* to a model. The value must be an EL expression that corresponds to
* a managed bean or a property of a managed bean, and it must
* evaluate to an array of java.lang.String
.
*
*
* To set the label of the textfield, use the
* fieldLabel
attribute. To set the label of the
* textfield, use the listLabel
attribute. To validate
* new items, use the fieldValidator
attribute; to
* validate the contents of the list once the user has finished
* adding and removing items, specify a labelValidator
.
*
* Facets
*
*
* fieldLabel
: use this facet to specify a custom
* component for the textfield label.
* listLabel
: use this facet to specify a custom
* component for the textfield label.
* field
: use this facet to specify a custom
* component for the textfield.
* addButton
: use this facet to specify a custom
* component for the add button.
* removeButton
: use this facet to specify a custom
* component for the remove button.
* search
: use this facet to specify a custom
* component for the search button.
* readOnly
: use this facet to specify a custom
* component for display a readonly version of the component.
* header
: use this facet to specify a header,
* rendered in a table row above the component.
* footer
: use this facet to specify a header,
* rendered in a table row below the component.
*
*
* Client-side JavaScript functions
*
*
* - NONE yet
*
*/
@Component(type = "com.sun.webui.jsf.EditableList", family = "com.sun.webui.jsf.EditableList",
displayName = "Editable List", tagName = "editableList",
helpKey = "projrave_ui_elements_palette_wdstk-jsf1.2_editable_list",
propertiesHelpKey = "projrave_ui_elements_palette_wdstk-jsf1.2_propsheets_editable_list_props")
public class EditableList extends WebuiInput implements ListManager,
NamingContainer {
/**
* The component id for the ADD button
*/
public static final String ADD_BUTTON_ID = "_addButton"; //NOI18N
/**
* The add button facet name
*/
public static final String ADD_BUTTON_FACET = "addButton"; //NOI18N
/**
* The add button text key.
*/
public static final String ADD_BUTTON_TEXT_KEY = "EditableList.add"; //NOI18N
/**
* The component ID for the remove button
*/
public static final String REMOVE_BUTTON_ID = "_removeButton"; //NOI18N
/**
* The remove button facet name
*/
public static final String REMOVE_BUTTON_FACET = "removeButton"; //NOI18N
/**
* The remove button text key.
*/
public static final String REMOVE_BUTTON_TEXT_KEY = "EditableList.remove"; //NOI18N
/**
* The component ID for the textfield
*/
public static final String FIELD_ID = "_field"; //NOI18N
/**
* The input field facet name.
*/
public static final String FIELD_FACET = "field"; //NOI18N
/**
* The text key for the StringLengthValidator "too long" message.
*/
public static final String SLV_TOOLONG_KEY =
"EditableList.itemTooLong"; //NOI18N
/**
* The text key for the StringLengthValidator "too short" message.
*/
public static final String SLV_TOOSHORT_KEY =
"EditableList.fieldEmpty"; // NOI18N
/**
* The component ID for the textfield
*/
public static final String LIST_LABEL_ID = "_listLabel"; //NOI18N
/**
* The list label facet name.
*/
public static final String LIST_LABEL_FACET = "listLabel"; //NOI18N
/**
* The list default label text key.
*/
public static final String LIST_LABEL_TEXT_KEY =
"EditableList.defaultListLabel"; //NOI18N
/**
* The component ID for the textfield
*/
public static final String FIELD_LABEL_ID = "_fieldLabel"; //NOI18N
/**
* The input field label facet name.
*/
public static final String FIELD_LABEL_FACET = "fieldLabel"; //NOI18N
/**
* The default field label text key.
*/
public static final String FIELD_LABEL_TEXT_KEY =
"EditableList.defaultFieldLabel"; //NOI18N
/**
* The component ID for the textfield
*/
public static final String READ_ONLY_ID = "_readOnly"; //NOI18N
/**
* The read only facet name.
*/
public static final String READ_ONLY_FACET = "readOnly"; //NOI18N
/**
* Facet name for the header facet
*/
public static final String HEADER_FACET = "header"; //NOI18N
/**
* Facet name for the footer facet
*/
public static final String FOOTER_FACET = "footer"; //NOI18N
/**
* Name of the JavaScript function which is responsible for adding elements from the availble list to the selected list
*/
public static final String ADD_FUNCTION = ".add(); ";
/**
* Name of the JavaScript function which is responsible for
* enabling/disabling the add button
*/
public static final String ENABLE_ADD_FUNCTION = ".enableAdd(); ";
/**
* Name of the JavaScript function which is responsible for
* enabling/disabling the add button
*/
public static final String SET_ADD_DISABLED_FUNCTION = ".setAddDisabled(false);";
/**
* Name of the JavaScript function which is responsible for
* enabling/disabling the remove button
*/
public static final String ENABLE_REMOVE_FUNCTION = ".enableRemove(); ";
/**
* Name of the JavaScript function that updates the buttons
*/
public static final String UPDATE_BUTTONS_FUNCTION = ".updateButtons(); ";
// FIXME: This should be part of the theme.
//
/**
* Read only separator string
*/
private static final String READ_ONLY_SEPARATOR = ", "; //NOI18N
/**
* Facet name for the search facet
*/
public static final String SEARCH_FACET = "search"; //NOI18N
// FIXME: This should be part of the theme.
//
public static final String SPACER_STRING = "_"; //NOI18N
private static final String KEY_STRING = "a"; //NOI18N
private static final String DUP_STRING = "\t"; //NOI18N
// FIXME: This should be part of the theme.
//
private static final int MIN_LENGTH = 20;
private static final boolean DEBUG = false;
private TreeMap listItems = null;
private Collator collator = null;
private transient Theme theme = null;
private String selectedValue = null;
private String[] valuesToRemove = null;
/**
* Default constructor.
*/
public EditableList() {
super();
setRendererType("com.sun.webui.jsf.EditableList");
}
/**
* Return the family for this component.
*/
@Override
public String getFamily() {
return "com.sun.webui.jsf.EditableList";
}
/**
* Get the maximum length of the strings on the list.
* If the length is less than 1, the default value of 15 is returned.
*
* @return An integer value for the maximum number of characters on the list
*/
public int getMaxLength() {
int length = _getMaxLength();
if (length < 1) {
// FIXME: Should be part of Theme.
length = 25;
// Shouldn't reset the length, it clobbers a
// developers value. Just return the default.
//
//super.setMaxLength(length);
}
return length;
}
// Buttons
/**
* Return a component that implements the add button.
* If a facet named addButton
is found
* that component is returned. Otherwise a Button
component
* is returned. It is assigned the id
* getId() + "_addButton"
*
* If the facet is not defined then the returned Button
* component is re-intialized every time this method is called.
*
*
* @return an add button component
*/
public UIComponent getAddButtonComponent() {
if (DEBUG) {
log("getAddButtonComponent()"); //NOI18N
}
return getButtonFacet(ADD_BUTTON_FACET, false,
getTheme().getMessage(ADD_BUTTON_TEXT_KEY), new AddListener());
}
/**
* Return a component that implements the remove button.
* If a facet named removeButton
is found
* that component is returned. Otherwise a Button
component
* is returned. It is assigned the id
* getId() + "_removeButton"
*
* If the facet is not defined then the returned Button
* component is re-intialized every time this method is called.
*
*
* @return a remove button component
*/
public UIComponent getRemoveButtonComponent() {
if (DEBUG) {
log("getRemoveButtonComponent()"); //NOI18N
}
return getButtonFacet(REMOVE_BUTTON_FACET, false,
getTheme().getMessage(REMOVE_BUTTON_TEXT_KEY),
new RemoveListener());
}
/**
* Return a component that implements a button facet.
* If a facet named facetName
is found
* that component is returned. Otherwise a Button
component
* is returned. It is assigned the id
* getId() + "_"
*
* If the facet is not defined then the returned Button
* component is re-intialized every time this method is called.
*
*
* @param facetName the name of the facet to return or create
* @param primary if false the button is not a primary button
* @param text the button text
* @param actionListener the button's actionListener
*
* @return a button facet component
*/
private UIComponent getButtonFacet(String facetName, boolean primary,
String text, ActionListener actionListener) {
if (DEBUG) {
log("getButtonFacet() " + facetName); //NOI18N
}
// Check if the page author has defined the facet
//
UIComponent buttonComponent = getFacet(facetName);
if (buttonComponent != null) {
if (DEBUG) {
log("\tFound facet"); //NOI18N
}
return buttonComponent;
}
// Return the private facet or create one, but initialize
// it every time
//
// We know it's a Button
//
Button button = (Button) ComponentUtilities.getPrivateFacet(this,
facetName, true);
if (button == null) {
if (DEBUG) {
log("create Button");
}
button = new Button();
button.setId(ComponentUtilities.createPrivateFacetId(this,
facetName));
button.addActionListener(actionListener);
ComponentUtilities.putPrivateFacet(this, facetName, button);
}
initButtonFacet(button, primary, text);
return button;
}
/**
* Initialize a Button
facet.
*
* @param button the Button instance
* @param text the button text
*/
private void initButtonFacet(Button button, boolean primary, String text) {
if (DEBUG) {
log("initButtonFacet()"); //NOI18N
}
button.setText(text);
int tindex = getTabIndex();
if (tindex > 0) {
button.setTabIndex(tindex);
}
button.setImmediate(true);
button.setPrimary(primary);
button.setDisabled(isDisabled());
}
// Labels
//
/**
* Return a component that implements a label for the list.
* If a facet named listLabel
is found
* that component is returned. Otherwise a Label
component
* is returned. It is assigned the id
* getId() + "_listLabel"
*
* If the facet is not defined then the returned Label
* component is re-intialized every time this method is called.
*
*
* @return a list label component
*/
public UIComponent getListLabelComponent() {
if (DEBUG) {
log("getListLabelComponent()"); //NOI18N
}
String labelString = getListLabel();
if (labelString == null || labelString.length() == 0) {
labelString = getTheme().getMessage(LIST_LABEL_TEXT_KEY);
}
return getLabelFacet(LIST_LABEL_FACET, labelString, this);
}
/**
* Return a component that implements a label for the input field.
* If a facet named fieldLabel
is found
* that component is returned. Otherwise a Label
component
* is returned. It is assigned the id
* getId() + "_fieldLabel"
*
* If the facet is not defined then the returned Label
* component is re-intialized every time this method is called.
*
*
* @return a field label component
*/
public UIComponent getFieldLabelComponent() {
if (DEBUG) {
log("getFieldLabelComponent()"); //NOI18N
}
String labelString = getFieldLabel();
if (labelString == null || labelString.length() == 0) {
labelString = getTheme().getMessage(FIELD_LABEL_TEXT_KEY);
}
// This will cause two initializations of the private field
// facet, in succession. The renderer is calling
// getFieldLabelComponent()
// getFieldComponent()
//
return getLabelFacet(FIELD_LABEL_FACET, labelString,
getFieldComponent());
}
/**
* Return a component that implements a label facet.
* If a facet named facetName
is found
* that component is returned. Otherwise a Label
component
* is returned. It is assigned the id
* getId() + "_"
*
* If the facet is not defined then the returned Label
* component is re-intialized every time this method is called.
*
*
* @param facetName the name of the facet to return or create
* @param text the label text
* @param forComponent the component instance this label is for
*
* @return a label facet component
*/
private UIComponent getLabelFacet(String facetName, String text,
UIComponent forComponent) {
if (DEBUG) {
log("getLabelFacet() " + facetName); //NOI18N
}
// Check if the page author has defined the facet
//
UIComponent labelComponent = getFacet(facetName);
if (labelComponent != null) {
if (DEBUG) {
log("\tFound facet"); //NOI18N
}
return labelComponent;
}
// Return the private facet or create one, but initialize
// it every time
//
// We know it's a Label
//
Label label = (Label) ComponentUtilities.getPrivateFacet(this,
facetName, true);
if (label == null) {
if (DEBUG) {
log("create Label");
}
label = new Label();
label.setId(ComponentUtilities.createPrivateFacetId(this,
facetName));
ComponentUtilities.putPrivateFacet(this, facetName, label);
}
initLabelFacet(label, text, forComponent.getClientId(getFacesContext()));
return label;
}
/**
* Initialize a label facet.
*
* @param label the Label instance
* @param labelString the label text.
* @param forComponentId the client id of the component instance this label is for
*/
private void initLabelFacet(Label label, String labelString,
String forComponentId) {
if (DEBUG) {
log("initLabelFacet()"); //NOI18N
}
if (labelString == null || labelString.length() < 1) {
// TODO - maybe print a default?
// A Theme default value.
labelString = new String();
}
label.setText(labelString);
label.setLabelLevel(getLabelLevel());
if (!isReadOnly()) {
label.setFor(forComponentId);
}
}
// Other
/**
* Return the actual facet or private facet without
* reinitializing it, if it exists. We sometimes want the component
* in the state that it was last rendered with and not
* a newly initialized state, like during validation processing
* or decoding. For example, you don't want to render with
* a 30 character StringLengthValidator and then validate
* against a 25 character validator on the odd chance that
* getMaxLength() returns a different value during decode.
*/
private UIComponent getRenderedFieldComponent() {
// Check if the page author has defined the facet
//
UIComponent fieldComponent = getFacet(FIELD_FACET);
if (fieldComponent != null) {
if (DEBUG) {
log("\tFound facet"); //NOI18N
}
return fieldComponent;
}
// If the private facet does't exist
// just call the normal "getFieldComponent", but
// it should exist, since getRenderedFieldComponent is designed
// to be used for lifecycle phases before rendereding, during
// request processing.
//
fieldComponent = ComponentUtilities.getPrivateFacet(this,
FIELD_FACET, false);
return fieldComponent == null ? getFieldComponent() : fieldComponent;
}
/**
* Return a component that implements an input field facet.
* If a facet named field
is found
* that component is returned. Otherwise a TextField
component
* is returned. It is assigned the id
* getId() + "_field"
*
* If the facet is not defined then the returned TextField
* component is re-intialized every time this method is called.
*
*
* @return an input field facet component
*/
public UIComponent getFieldComponent() {
if (DEBUG) {
log("getFieldComponent()"); //NOI18N
}
// Check if the page author has defined the facet
//
UIComponent fieldComponent = getFacet(FIELD_FACET);
if (fieldComponent != null) {
if (DEBUG) {
log("\tFound facet"); //NOI18N
}
return fieldComponent;
}
// Return the private facet or create one, but initialize
// it every time
//
// We know it's a TextField
//
TextField field = (TextField) ComponentUtilities.getPrivateFacet(this,
FIELD_FACET, true);
if (field == null) {
if (DEBUG) {
log("create Field"); //NOI18N
}
field = new TextField();
field.setId(ComponentUtilities.createPrivateFacetId(this,
FIELD_FACET));
field.setTrim(true);
ComponentUtilities.putPrivateFacet(this, FIELD_FACET, field);
// Add the validator ONCE !
//
StringLengthValidator strl =
new StringLengthValidator(getMaxLength(), 1);
Theme theme = getTheme();
strl.setTooLongMessage(theme.getMessage(SLV_TOOLONG_KEY));
strl.setTooShortMessage(theme.getMessage(SLV_TOOSHORT_KEY));
field.addValidator(strl);
}
initFieldFacet(field);
return field;
}
private void initFieldFacet(TextField field) {
if (DEBUG) {
log("initFieldFacet()"); //NOI18N
}
// FIXME should use formElements.js function for return key
//
String jsObjectName = getJavaScriptObjectName();
StringBuffer onkeypressBuffer = new StringBuffer(128);
onkeypressBuffer.append("if(event.keyCode == 13) { "); //NOI18N
onkeypressBuffer.append(jsObjectName);
onkeypressBuffer.append(ADD_FUNCTION);
onkeypressBuffer.append("return false; } "); //NOI18N
field.setOnKeyPress(onkeypressBuffer.toString());
StringBuffer onfocusBuffer = new StringBuffer(128);
onfocusBuffer.append(jsObjectName);
onfocusBuffer.append(SET_ADD_DISABLED_FUNCTION);
onfocusBuffer.append("return false;"); //NOI18N
field.setOnFocus(onfocusBuffer.toString());
StringBuffer onfocuslostBuffer = new StringBuffer(128);
onfocuslostBuffer.append(jsObjectName);
onfocuslostBuffer.append(ENABLE_ADD_FUNCTION);
onfocuslostBuffer.append("return false;"); //NOI18N
field.setOnBlur(onfocuslostBuffer.toString());
// FIXME: MIN_LENGTH should be part of Theme
//
int columns = getMaxLength();
if (columns < MIN_LENGTH) {
columns = MIN_LENGTH;
}
field.setColumns(columns);
int tindex = getTabIndex();
if (tindex > 0) {
field.setTabIndex(tindex);
}
field.setDisabled(isDisabled());
// Now add an application field validator expression
// Do this every time in case it was changed on the
// EditableList.
//
field.setValidatorExpression(getFieldValidatorExpression());
}
/**
* Return a component that implements the read only value of this
* EditableList.
* If a facet named readOnly
is found
* that component is returned. Otherwise a StaticText
component
* is returned. It is assigned the id
* getId() + "_readOnly"
*
* If the facet is not defined then the returned StaticText
* component is re-intialized every time this method is called.
*
*
* @return a component that represents the read only value of this EditableList
*/
public UIComponent getReadOnlyValueComponent() {
if (DEBUG) {
log("getReadOnlyValueComponent()"); //NOI18N
}
// Check if the page author has defined the facet
//
UIComponent textComponent = getFacet(READ_ONLY_FACET);
if (textComponent != null) {
if (DEBUG) {
log("\tFound facet"); //NOI18N
}
return textComponent;
}
// Just create it every time.
//
if (DEBUG) {
log("create StaticText"); //NOI18N
}
StaticText text = new StaticText();
text.setId(ComponentUtilities.createPrivateFacetId(this,
READ_ONLY_FACET));
text.setParent(this);
FacesContext context = FacesContext.getCurrentInstance();
String readOnlyString = getValueAsReadOnly(context);
if (readOnlyString == null || readOnlyString.length() < 1) {
// TODO - maybe print a default?
readOnlyString = new String();
}
text.setText(readOnlyString);
return text;
}
// Readonly value
/**
* Return a string suitable for displaying the value in read only mode.
* The default is to separate the list values with a comma.
*
* @param context The FacesContext
* @throws javax.faces.FacesException If the list items cannot be processed
*/
protected String getValueAsReadOnly(FacesContext context)
throws FacesException {
// The comma format READ_ONLY_SEPARATOR should be part of the theme
// and/or configurable by the application
//
StringBuffer valueBuffer = new StringBuffer(200);
Iterator iterator = getListItems(context, false);
while (iterator.hasNext()) {
String string = ((ListItem) (iterator.next())).getLabel();
// Do this with a boolean on getListItems instead
if (string.indexOf("nbsp") > -1) { //NOI18N
continue;
}
valueBuffer.append(string);
if (iterator.hasNext()) {
valueBuffer.append(READ_ONLY_SEPARATOR);
}
}
return valueBuffer.toString();
}
// The following methods overrides default behaviour that does not
// make sense for this component
/**
*
* @param converter
*/
@Override
public void setConverter(javax.faces.convert.Converter converter) {
String msg = getTheme().getMessage("EditableList.noConversion"); //NOI18N
throw new RuntimeException(msg);
}
public String getJavaScriptObjectName() {
return JavaScriptUtilities.getDomNode(getFacesContext(), this);
}
private Theme getTheme() {
return ThemeUtilities.getTheme(FacesContext.getCurrentInstance());
}
public String getOnChange() {
StringBuffer onchangeBuffer = new StringBuffer(128);
onchangeBuffer.append(getJavaScriptObjectName());
onchangeBuffer.append(ENABLE_REMOVE_FUNCTION);
return onchangeBuffer.toString();
}
/**
* Returns the absolute ID of an HTML element suitable for use as
* the value of an HTML LABEL element's for
attribute.
* If the ComplexComponent
has sub-compoents, and one of
* the sub-components is the target of a label, if that sub-component
* is a ComplexComponent
, then
* getLabeledElementId
must called on the sub-component and
* the value returned. The value returned by this
* method call may or may not resolve to a component instance.
*
* If isReadOnly
returns true, null
is returned.
*
*
* @param context The FacesContext used for the request
* @return An abolute id suitable for the value of an HTML LABEL element's
* for
attribute.
*/
public String getLabeledElementId(FacesContext context) {
// If this component is marked read only then it is
// not appropriate for a label's for attribute.
//
if (isReadOnly()) {
return null;
} else {
return getClientId(context).concat(ListSelector.LIST_ID);
}
}
/**
* Returns the id of an HTML element suitable to
* receive the focus.
* If the ComplexComponent
has sub-compoents, and one of
* the sub-components is to reveive the focus, if that sub-component
* is a ComplexComponent
, then
* getFocusElementId
must called on the sub-component and
* the value returned. The value returned by this
* method call may or may not resolve to a component instance.
*
* This implementations returns the value of
* getLabeledElementId
.
*
*
* @param context The FacesContext used for the request
*/
public String getFocusElementId(FacesContext context) {
// This is a little sketchy, because in this case we'd actually prefer
// to return different values for focus and for the labelled component.
// We should always label the list (that's the one that should have the
// invalid icon if the list is empty, for example. But we should
// probably also set the focus to the top input component which could
// be either the field or the label. Ah well. I can get around this
// if I implement some extra bits on the label.
// TODO
return getLabeledElementId(context);
}
/**
* Implement this method so that it returns the DOM ID of the
* HTML element which should receive focus when the component
* receives focus, and to which a component label should apply.
* Usually, this is the first element that accepts input.
*
* @param context The FacesContext for the request
* @return The client id, also the JavaScript element id
*
* @deprecated
* @see #getLabeledElementId
* @see #getFocusElementId
*/
public String getPrimaryElementID(FacesContext context) {
// In case callers can't handle null do not
// return getLabeledElementId here.
//
return getClientId(context).concat(ListSelector.LIST_ID);
}
/**
* Getter for property valuesToRemove.
* @return Value of property valuesToRemove.
*/
public String[] getValuesToRemove() {
if (valuesToRemove == null) {
return new String[0];
}
return this.valuesToRemove;
}
/**
* Setter for property valuesToRemove.
* @param valuesToRemove New value of property valuesToRemove.
*/
public void setValuesToRemove(String[] valuesToRemove) {
this.valuesToRemove = valuesToRemove;
}
/**
* Retrieve an Iterator of ListSelector.ListItem, to be used by the
* renderer.
* @return an Iterator over {@link ListItem}.
* @throws javax.faces.FacesException
*/
public Iterator getListItems(FacesContext context, boolean rulerAtEnd) throws FacesException {
if (DEBUG) {
log("getListItems()");
}
Locale locale = context.getViewRoot().getLocale();
if (DEBUG) {
log("\tLocale is " + locale.toString());
}
collator = Collator.getInstance(locale);
listItems = new TreeMap(collator);
// Are we sorting ?
//
boolean sorted = isSorted();
// Keep a list of keys that have been seen
// to support duplicates when sorting.
//
Map keysSeen = sorted ? new HashMap() : null;
// We have to make sure that the long empty list item (whose
// purpose is to guarantee that the size of the list stays
// constant) is alwasy at the bottom of the list. (=has the
// highest key in the map). We do this by identifying the
// longest key in the map, as long as the collator is
// concerned and appending something at the end. (It's not
// possible to use a constant for this, since an o with an
// umlaut comes after z in Swedish, but before it in German,
// for example).
String lastKey = ""; //NOI18N
String[] currentValues = getCurrentValueAsStringArray();
if (DEBUG) {
log("\tValues are:");
for (int i = 0; i < currentValues.length; ++i) {
log(currentValues[i]);
}
}
// The string currently being evaluated
String currentString = null;
// Two cases:
// First case: the page author requested a sorted map (by
// character), in which case we sort by the strings
// themselves. The last key is set to the string that the
// collator deems to be the last.
// Second case: the list is sorted by the order they were
// added to the map. We deal with that by generating a
// successively longer key for each entry (maps do not
// conserve the order the items were added). The last key
// is set to the last key generated.
//
ListItem listItem = null;
// Keep track of the last ListItem that matches the
// selectedValue. This is necessary so that the last
// seen duplicate of the selectedValue is selected.
// And so that only one match is selected.
//
ListItem selectedItem = null;
// If the page author does not want the list items to be
// sorted (alphabetically by locale), then they're
// supposed to be sorted by the order they were added.
// Maps are not guaranteed to return items in the order
// they were added, so we have to create this order
// artificially. We do that by creating a successively
// longer key for each element. (a, aa, aaa...).
//
// Only need this when not sorting
//
StringBuffer unsortedKeyBuffer = sorted ? null : new StringBuffer(KEY_STRING);
for (int counter = 0; counter < currentValues.length; ++counter) {
currentString = currentValues[counter];
if (DEBUG) {
log("Current string is " + currentString); //NOI18N
log("SelectedValue is " +
String.valueOf(selectedValue)); //NOI18N
}
if (currentString == null) {
String msg = MessageUtil.getMessage("com.sun.webui.jsf.resources.LogMessages",
"EditableList.badValue",
new Object[]{getClientId(context)});
throw new FacesException(msg);
}
listItem = new ListItem(currentString);
listItem.setValue(currentString);
// The selectedValue will be the last entry in the
// submittedValues array (getCurrentValueAsStringArray)
// if it is not null. It will also appear as the "last"
// duplicate.
//
// Keep track of last ListItem that matches the selectedValue
// and at the end mark the last one found as selected.
//
if (currentString.equals(selectedValue)) {
if (DEBUG) {
log("Selected value seen");
}
selectedItem = listItem;
}
if (sorted) {
// Since duplicates are allowed, if a duplicate occurs
// the key must be unique but must sort next
// to the duplicate. Add an increasing string of
// spaces to the duplicate key which should sort the keys
// next to each other.
//
String key = currentString;
if (keysSeen.containsKey(key)) {
String dup_string = (String) keysSeen.get(key);
dup_string = dup_string.concat(DUP_STRING);
key = key.concat(dup_string);
keysSeen.put(currentString, dup_string);
} else {
keysSeen.put(key, DUP_STRING);
}
if (collator.compare(key, lastKey) > 0) {
lastKey = key;
}
listItems.put(key, listItem);
} else {
listItems.put(unsortedKeyBuffer.toString(), listItem);
unsortedKeyBuffer.append(KEY_STRING);
}
}
// selectedItem was the last seen entry that matched the
// selectedValue
//
if (selectedItem != null) {
selectedItem.setSelected(true);
}
if (!sorted) {
lastKey = unsortedKeyBuffer.toString();
}
// rulerAtEnd will be true if the invoker needs a blank
// disabled list option at the end. Typically this is
// needed by the renderer, to guarantee that the widget
// stays the same in size when items are added and removed.
if (rulerAtEnd) {
int length = getMaxLength();
if (length < MIN_LENGTH) {
length = MIN_LENGTH;
}
StringBuffer labelBuffer = new StringBuffer(length);
for (int counter = 0; counter < length; ++counter) {
labelBuffer.append(SPACER_STRING);
}
ListItem item = new ListItem(labelBuffer.toString());
item.setDisabled(true);
listItems.put(lastKey.concat(KEY_STRING), item);
}
return listItems.values().iterator();
}
private String[] getCurrentValueAsStringArray() {
if (DEBUG) {
log("getCurrentValueAsStringArray()");
}
Object value = getSubmittedValue();
if (value == null) {
if (DEBUG) {
log("\tUsing regular value");
}
value = getValue();
} else if (DEBUG) {
log("\tUsing submitted value");
}
if (value == null) {
return new String[0];
}
if (value instanceof String[]) {
return (String[]) value;
}
String msg = MessageUtil.getMessage("com.sun.webui.jsf.resources.LogMessages",
"EditableList.badValue",
new Object[]{getClientId(FacesContext.getCurrentInstance())});
throw new FacesException(msg);
}
private void log(String s) {
System.out.println(this.getClass().getName() + "::" + s); //NOI18N
}
/**
* Retrieve the value of this component (the "selected" property) as an
* object. This method is invoked by the JSF engine during the validation
* phase. The JSF default behaviour is for components to defer the
* conversion and validation to the renderer, but for the Selector based
* components, the renderers do not share as much functionality as the
* components do, so it is more efficient to do it here.
* @param context The FacesContext of the request
* @param submittedValue The submitted value of the component
*/
@Override
public Object getConvertedValue(FacesContext context,
Object submittedValue)
throws ConverterException {
if (DEBUG) {
log("getConvertedValue()");
}
if (!(submittedValue instanceof String[])) {
throw new ConverterException(
"Submitted value must be a String array");
}
String[] rawValues = (String[]) submittedValue;
// If there are no elements in rawValues nothing was submitted.
// If null was rendered, return null
//
if (rawValues.length == 0) {
if (ConversionUtilities.renderedNull(this)) {
return null;
}
}
return submittedValue;
}
/**
* @exception NullPointerException
*/
@Override
public void processValidators(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
// Skip processing if our rendered flag is false
if (!isRendered()) {
return;
}
// This component may be a developer defined facet.
// It is explicitly validated during an Add action.
// It must not be validated during a submit. The assumption
// is that processValidators is being called during
// a submit and not in an immediate context.
// Compare the id of this component with the children
// and facets and if it matches don't call its
// processValidators method.
//
// Get the last rendered field component instead of
// a newly intialized one.
//
UIComponent field = getRenderedFieldComponent();
String fieldId = field.getId();
// Process all the facets and children of this component
Iterator kids = getFacetsAndChildren();
while (kids.hasNext()) {
UIComponent kid = (UIComponent) kids.next();
// We probably should ensure that fieldId is not
// null, during getFieldComponent() even if
// it is a developer defined facet.
//
if (fieldId != null && fieldId.equals(kid.getId())) {
continue;
}
kid.processValidators(context);
}
// Validate the EditableList
//
checkValid(context);
}
public void processAddAction() {
if (DEBUG) {
log("processAddAction()");
}
// If we are rendering prematurely don't do anything
//
if (FacesContext.getCurrentInstance().getRenderResponse()) {
return;
}
selectedValue = null;
String[] values = getCurrentValueAsStringArray();
Object value = getAddedObject();
if (value == null) {
return;
}
//TODO - fix this when implementing conversion for this component
selectedValue = value.toString();
int numValues = values.length;
String[] newValues = new String[numValues + 1];
int counter;
for (counter = 0; counter < numValues; ++counter) {
newValues[counter] = values[counter];
if (DEBUG) {
log("\tAdding " + newValues[counter]);
}
}
newValues[counter] = selectedValue;
if (DEBUG) {
log("\tAdding " + newValues[counter]);
}
setSubmittedValue(newValues);
}
public void processRemoveAction() {
if (DEBUG) {
log("processRemoveAction()");
}
// If we are rendering prematurely don't do anything
//
if (FacesContext.getCurrentInstance().getRenderResponse()) {
return;
}
// Reset the selected value
selectedValue = null;
ArrayList items = new ArrayList();
int counter;
if (getValue() != null) {
if (DEBUG) {
log("\tList was not empty");
}
String[] strings = getCurrentValueAsStringArray();
int length = strings.length;
for (counter = 0; counter < length; ++counter) {
items.add(strings[counter]);
if (DEBUG) {
log("Added " + strings[counter]);
}
}
}
String[] valuesToRemove = getValuesToRemove();
for (counter = 0; counter < valuesToRemove.length; ++counter) {
items.remove(valuesToRemove[counter]);
if (DEBUG) {
log("remove " + valuesToRemove[counter]);
}
}
String[] newValues = new String[items.size()];
for (counter = 0; counter < items.size(); ++counter) {
newValues[counter] = (String) (items.get(counter));
if (DEBUG) {
log("\tAdding back " + newValues[counter]);
}
}
setValuesToRemove(null);
setSubmittedValue(newValues);
}
private void checkValid(FacesContext context) {
if (DEBUG) {
log("checkValid()");
}
try {
validate(context);
} catch (RuntimeException e) {
if (DEBUG) {
log("Error during validation");
}
context.renderResponse();
throw e;
}
if (!isValid()) {
if (DEBUG) {
log("Component is not valid");
}
context.renderResponse();
}
}
private Object getAddedObject() {
FacesContext context = FacesContext.getCurrentInstance();
if (DEBUG) {
log("\tAdd a new item");
}
// Need to get the field's value validated first
// The field can't be immediate because we don't want
// to validate it if the value is not going to be added.
// For example in a real submit request.
//
// Get the last rendered Field component, not necessarily
// a newly initialized one.
//
EditableValueHolder field = (EditableValueHolder) getRenderedFieldComponent();
// This is ok to do here.
// We are currently after the APPLY_REQUEST_VALUES phase
// and before the PROCESS_VALIDATIONS phase.
// If the field were marked immediate, then the validation
// would have occured before we get here. If not done
// here it will be done in the next phase. But it needs
// to be done here, sort a simulation of immediate
// henavior. But we don't want the side effect of immediate
// behavior from external immediate components.
//
((UIComponent) field).processValidators(context);
if (!field.isValid()) {
return null;
}
// Get the value from the field.
//
Object value = field.getValue();
// This is a policy of the EditableList.
// An emptyString or null value cannot be added to the list.
//
if (value == null ||
(value instanceof String && value.toString().length() == 0)) {
field.setValid(false);
context.renderResponse();
if (DEBUG) {
log("No value from the field");
}
String message = ThemeUtilities.getTheme(context).
getMessage("EditableList.fieldEmpty");
context.addMessage(getClientId(context), new FacesMessage(message));
return null;
}
// The new value was added so clear the value.
// This set is questionable, if the field is a developer
// defined facet. This will cause an update to the model
// before the value change event.
//
if (DEBUG) {
log("\tFound new value: " + value);
}
field.setValue(null);
return value;
}
/* Don't need this. Only needed for debugging.
public void setValue(Object value) {
if(DEBUG) log("setValue()...");
super.setValue(value);
if(DEBUG) log("\tLocal value set: " +
String.valueOf(isLocalValueSet()));
}
*/
/** Always returns false for EditableList **/
private boolean _isImmediate() {
return false;
}
/**
* Return true
if the new value is different from the
* previous value.
*
* This only implements a compareValues for value if it is an Array.
* If value is not an Array, defer to super.compareValues.
* The assumption is that the ordering of the elements
* between the previous value and the new value is determined
* in the same manner.
*
* Another assumption is that the two object arguments
* are of the same type, both arrays of both not arrays.
*
* @param previous old value of this component (if any)
* @param value new value of this component (if any)
*/
@Override
protected boolean compareValues(Object previous, Object value) {
// Let super take care of null cases
//
if (previous == null || value == null) {
return super.compareValues(previous, value);
}
if (value instanceof Object[]) {
// If the lengths aren't equal return true
//
int length = Array.getLength(value);
if (Array.getLength(previous) != length) {
return true;
}
// Each element at index "i" in previous must be equal to the
// elementa at index "i" in value.
//
for (int i = 0; i < length; ++i) {
Object newValue = Array.get(value, i);
Object prevValue = Array.get(previous, i);
// This is probably not necessary since
// an Option's value cannot be null
//
if (newValue == null) {
if (prevValue == null) {
continue;
} else {
return true;
}
}
if (prevValue == null) {
return true;
}
if (!prevValue.equals(newValue)) {
return true;
}
}
return false;
}
return super.compareValues(previous, value);
}
public String[] getValueAsStringArray(FacesContext context) {
if (DEBUG) {
log("getValueAsStringArray)");
}
Iterator iterator = getListItems(context, false);
int numItems = listItems.size();
String[] values = new String[numItems];
int counter = 0;
while (counter < numItems) {
values[counter] = ((ListItem) (iterator.next())).getValue();
++counter;
}
return values;
}
public boolean mainListSubmits() {
return true;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Tag attribute methods
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Return the ValueExpression
stored for the
* specified name (if any), respecting any property aliases.
*
* @param name Name of value binding expression to retrieve
*/
@Override
public ValueExpression getValueExpression(String name) {
if (name.equals("list")) {
return super.getValueExpression("value");
}
return super.getValueExpression(name);
}
/**
* Set the ValueExpression
stored for the
* specified name (if any), respecting any property
* aliases.
*
* @param name Name of value binding to set
* @param binding ValueExpression to set, or null to remove
*/
@Override
public void setValueExpression(String name, ValueExpression binding) {
if (name.equals("list")) {
super.setValueExpression("value", binding);
return;
}
super.setValueExpression(name, binding);
}
// Hide converter
@Property(name = "converter", isHidden = true, isAttribute = false)
@Override
public Converter getConverter() {
return super.getConverter();
}
// Hide immediate
@Property(name = "immediate", isHidden = true, isAttribute = false)
@Override
public boolean isImmediate() {
return _isImmediate();
}
// Hide value
@Property(name = "value", isHidden = true, isAttribute = false)
@Override
public Object getValue() {
return super.getValue();
}
/**
* Flag indicating that the user is not permitted to activate this
* component, and that the component's value will not be submitted with the
* form.
*/
@Property(name = "disabled", displayName = "Disabled", category = "Behavior")
private boolean disabled = false;
private boolean disabled_set = false;
/**
* Flag indicating that the user is not permitted to activate this
* component, and that the component's value will not be submitted with the
* form.
*/
public boolean isDisabled() {
if (this.disabled_set) {
return this.disabled;
}
ValueExpression _vb = getValueExpression("disabled");
if (_vb != null) {
Object _result = _vb.getValue(getFacesContext().getELContext());
if (_result == null) {
return false;
} else {
return ((Boolean) _result).booleanValue();
}
}
return false;
}
/**
* Flag indicating that the user is not permitted to activate this
* component, and that the component's value will not be submitted with the
* form.
* @see #isDisabled()
*/
public void setDisabled(boolean disabled) {
this.disabled = disabled;
this.disabled_set = true;
}
/**
* Text to be used as the label next to the input text field.
*/
@Property(name = "fieldLabel", displayName = "Textfield Label", category = "Appearance", editorClassName = "com.sun.rave.propertyeditors.StringPropertyEditor")
private String fieldLabel = null;
/**
* Text to be used as the label next to the input text field.
*/
public String getFieldLabel() {
if (this.fieldLabel != null) {
return this.fieldLabel;
}
ValueExpression _vb = getValueExpression("fieldLabel");
if (_vb != null) {
return (String) _vb.getValue(getFacesContext().getELContext());
}
return null;
}
/**
* Text to be used as the label next to the input text field.
* @see #getFieldLabel()
*/
public void setFieldLabel(String fieldLabel) {
this.fieldLabel = fieldLabel;
}
/**
* Sets the style level for the generated labels. Valid values
* are 1 (largest), 2 and 3 (smallest). The default value is 2.
*/
@Property(name = "labelLevel", displayName = "Label Level", category = "Appearance", editorClassName = "com.sun.webui.jsf.component.propertyeditors.LabelLevelsEditor")
private int labelLevel = Integer.MIN_VALUE;
private boolean labelLevel_set = false;
/**
* Sets the style level for the generated labels. Valid values
* are 1 (largest), 2 and 3 (smallest). The default value is 2.
*/
public int getLabelLevel() {
if (this.labelLevel_set) {
return this.labelLevel;
}
ValueExpression _vb = getValueExpression("labelLevel");
if (_vb != null) {
Object _result = _vb.getValue(getFacesContext().getELContext());
if (_result == null) {
return Integer.MIN_VALUE;
} else {
return ((Integer) _result).intValue();
}
}
return 2;
}
/**
* Sets the style level for the generated labels. Valid values
* are 1 (largest), 2 and 3 (smallest). The default value is 2.
* @see #getLabelLevel()
*/
public void setLabelLevel(int labelLevel) {
this.labelLevel = labelLevel;
this.labelLevel_set = true;
}
/**
*
* The object that represents the list. The list attribute must be an EL
* expression that evaluates to an object of typejava.lang.String[]
.
*/
@Property(name = "list", displayName = "List", category = "Data",
editorClassName = "com.sun.rave.propertyeditors.binding.ValueBindingPropertyEditor")
public Object getList() {
return getValue();
}
/**
*
* The object that represents the list. The list attribute must be an EL
* expression that evaluates to an object of typejava.lang.String[]
.
* @see #getList()
*/
public void setList(Object list) {
setValue(list);
}
/**
* Text to be used as the label next to the list box.
*/
@Property(name = "listLabel", displayName = "List Label", category = "Appearance",
editorClassName = "com.sun.rave.propertyeditors.StringPropertyEditor")
private String listLabel = null;
/**
* Text to be used as the label next to the list box.
*/
public String getListLabel() {
if (this.listLabel != null) {
return this.listLabel;
}
ValueExpression _vb = getValueExpression("listLabel");
if (_vb != null) {
return (String) _vb.getValue(getFacesContext().getELContext());
}
return null;
}
/**
* Text to be used as the label next to the list box.
* @see #getListLabel()
*/
public void setListLabel(String listLabel) {
this.listLabel = listLabel;
}
/**
* Specifies the display order of the parts of this component. When set to
* true, the listOnTop attribute causes the list box to be displayed above
* the text input field. By default, the list box is displayed below the input field.
*/
@Property(name = "listOnTop", displayName = "Show List On Top", category = "Advanced")
private boolean listOnTop = false;
private boolean listOnTop_set = false;
/**
* Specifies the display order of the parts of this component. When set to
* true, the listOnTop attribute causes the list box to be displayed above
* the text input field. By default, the list box is displayed below the input field.
*/
public boolean isListOnTop() {
if (this.listOnTop_set) {
return this.listOnTop;
}
ValueExpression _vb = getValueExpression("listOnTop");
if (_vb != null) {
Object _result = _vb.getValue(getFacesContext().getELContext());
if (_result == null) {
return false;
} else {
return ((Boolean) _result).booleanValue();
}
}
return false;
}
/**
* Specifies the display order of the parts of this component. When set to
* true, the listOnTop attribute causes the list box to be displayed above
* the text input field. By default, the list box is displayed below the input field.
* @see #isListOnTop()
*/
public void setListOnTop(boolean listOnTop) {
this.listOnTop = listOnTop;
this.listOnTop_set = true;
}
/**
* The maximum number of characters allowed for each string in the list.
*/
@Property(name = "maxLength", displayName = "Maximum String Length",
category = "Appearance", editorClassName = "com.sun.rave.propertyeditors.IntegerPropertyEditor")
private int maxLength = Integer.MIN_VALUE;
private boolean maxLength_set = false;
/**
* The maximum number of characters allowed for each string in the list.
*/
private int _getMaxLength() {
if (this.maxLength_set) {
return this.maxLength;
}
ValueExpression _vb = getValueExpression("maxLength");
if (_vb != null) {
Object _result = _vb.getValue(getFacesContext().getELContext());
if (_result == null) {
return Integer.MIN_VALUE;
} else {
return ((Integer) _result).intValue();
}
}
return 25;
}
/**
* The maximum number of characters allowed for each string in the list.
* @see #getMaxLength()
*/
public void setMaxLength(int maxLength) {
this.maxLength = maxLength;
this.maxLength_set = true;
}
/**
* Flag indicating that the application user can select
* more than one option at a time in the listbox.
*/
@Property(name = "multiple", displayName = "Multiple", category = "Data")
private boolean multiple = false;
private boolean multiple_set = false;
/**
* Flag indicating that the application user can select
* more than one option at a time in the listbox.
*/
public boolean isMultiple() {
if (this.multiple_set) {
return this.multiple;
}
ValueExpression _vb = getValueExpression("multiple");
if (_vb != null) {
Object _result = _vb.getValue(getFacesContext().getELContext());
if (_result == null) {
return false;
} else {
return ((Boolean) _result).booleanValue();
}
}
return false;
}
/**
* Flag indicating that the application user can select
* more than one option at a time in the listbox.
* @see #isMultiple()
*/
public void setMultiple(boolean multiple) {
this.multiple = multiple;
this.multiple_set = true;
}
/**
* If this attribute is set to true, the value of the component is
* rendered as text, preceded by the label if one was defined.
*/
@Property(name = "readOnly", displayName = "Read-only", category = "Behavior")
private boolean readOnly = false;
private boolean readOnly_set = false;
/**
* If this attribute is set to true, the value of the component is
* rendered as text, preceded by the label if one was defined.
*/
public boolean isReadOnly() {
if (this.readOnly_set) {
return this.readOnly;
}
ValueExpression _vb = getValueExpression("readOnly");
if (_vb != null) {
Object _result = _vb.getValue(getFacesContext().getELContext());
if (_result == null) {
return false;
} else {
return ((Boolean) _result).booleanValue();
}
}
return false;
}
/**
* If this attribute is set to true, the value of the component is
* rendered as text, preceded by the label if one was defined.
* @see #isReadOnly()
*/
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
this.readOnly_set = true;
}
/**
* The number of items to display, which determines the length of the
* rendered listbox. The default value is 6.
*/
@Property(name = "rows", displayName = "Number of Items to Display",
category = "Appearance", editorClassName = "com.sun.rave.propertyeditors.IntegerPropertyEditor")
private int rows = Integer.MIN_VALUE;
private boolean rows_set = false;
/**
* The number of items to display, which determines the length of the
* rendered listbox. The default value is 6.
*/
public int getRows() {
if (this.rows_set) {
return this.rows;
}
ValueExpression _vb = getValueExpression("rows");
if (_vb != null) {
Object _result = _vb.getValue(getFacesContext().getELContext());
if (_result == null) {
return Integer.MIN_VALUE;
} else {
return ((Integer) _result).intValue();
}
}
return 6;
}
/**
* The number of items to display, which determines the length of the
* rendered listbox. The default value is 6.
* @see #getRows()
*/
public void setRows(int rows) {
this.rows = rows;
this.rows_set = true;
}
/**
* Set sorted to true if the list items should be
* sorted in locale-specific alphabetical order. The sorting is
* performed using a Collator configured
* with the locale from the FacesContext.
*/
@Property(name = "sorted", displayName = "Sorted", category = "Advanced")
private boolean sorted = false;
private boolean sorted_set = false;
/**
* Set sorted to true if the list items should be
* sorted in locale-specific alphabetical order. The sorting is
* performed using a Collator configured
* with the locale from the FacesContext.
*/
public boolean isSorted() {
if (this.sorted_set) {
return this.sorted;
}
ValueExpression _vb = getValueExpression("sorted");
if (_vb != null) {
Object _result = _vb.getValue(getFacesContext().getELContext());
if (_result == null) {
return false;
} else {
return ((Boolean) _result).booleanValue();
}
}
return false;
}
/**
* Set this attribute to true if the list items should be
* sorted in locale-specific alphabetical order. The sorting is
* performed using a Collator configured
* with the locale from the FacesContext.
* @see #isSorted()
*/
public void setSorted(boolean sorted) {
this.sorted = sorted;
this.sorted_set = true;
}
/**
* CSS style(s) to be applied to the outermost HTML element when this
* component is rendered.
*/
@Property(name = "style", displayName = "CSS Style(s)", category = "Appearance",
editorClassName = "com.sun.jsfcl.std.css.CssStylePropertyEditor")
private String style = null;
/**
* CSS style(s) to be applied to the outermost HTML element when this
* component is rendered.
*/
public String getStyle() {
if (this.style != null) {
return this.style;
}
ValueExpression _vb = getValueExpression("style");
if (_vb != null) {
return (String) _vb.getValue(getFacesContext().getELContext());
}
return null;
}
/**
* CSS style(s) to be applied to the outermost HTML element when this
* component is rendered.
* @see #getStyle()
*/
public void setStyle(String style) {
this.style = style;
}
/**
* CSS style class(es) to be applied to the outermost HTML element when this
* component is rendered.
*/
@Property(name = "styleClass", displayName = "CSS Style Class(es)", category = "Appearance",
editorClassName = "com.sun.rave.propertyeditors.StyleClassPropertyEditor")
private String styleClass = null;
/**
* CSS style class(es) to be applied to the outermost HTML element when this
* component is rendered.
*/
public String getStyleClass() {
if (this.styleClass != null) {
return this.styleClass;
}
ValueExpression _vb = getValueExpression("styleClass");
if (_vb != null) {
return (String) _vb.getValue(getFacesContext().getELContext());
}
return null;
}
/**
* CSS style class(es) to be applied to the outermost HTML element when this
* component is rendered.
* @see #getStyleClass()
*/
public void setStyleClass(String styleClass) {
this.styleClass = styleClass;
}
/**
* Position of this element in the tabbing order of the current document.
* Tabbing order determines the sequence in which elements receive
* focus when the tab key is pressed. The value must be an integer
* between 0 and 32767.
*/
@Property(name = "tabIndex", displayName = "Tab Index", category = "Accessibility",
editorClassName = "com.sun.rave.propertyeditors.IntegerPropertyEditor")
private int tabIndex = Integer.MIN_VALUE;
private boolean tabIndex_set = false;
/**
* Position of this element in the tabbing order of the current document.
* Tabbing order determines the sequence in which elements receive
* focus when the tab key is pressed. The value must be an integer
* between 0 and 32767.
*/
public int getTabIndex() {
if (this.tabIndex_set) {
return this.tabIndex;
}
ValueExpression _vb = getValueExpression("tabIndex");
if (_vb != null) {
Object _result = _vb.getValue(getFacesContext().getELContext());
if (_result == null) {
return Integer.MIN_VALUE;
} else {
return ((Integer) _result).intValue();
}
}
return Integer.MIN_VALUE;
}
/**
* Position of this element in the tabbing order of the current document.
* Tabbing order determines the sequence in which elements receive
* focus when the tab key is pressed. The value must be an integer
* between 0 and 32767.
* @see #getTabIndex()
*/
public void setTabIndex(int tabIndex) {
this.tabIndex = tabIndex;
this.tabIndex_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",
editorClassName = "com.sun.rave.propertyeditors.StringPropertyEditor")
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.
*/
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()
*/
public void setToolTip(String toolTip) {
this.toolTip = toolTip;
}
/**
* Use the visible attribute to indicate whether the component should be
* viewable by the user in the rendered HTML page. If set to false, the
* HTML code for the component is present in the page, but the component
* is hidden with style attributes. By default, visible is set to true, so
* HTML for the component HTML is included and visible to the user. If the
* component is not visible, it can still be processed on subsequent form
* submissions because the HTML is present.
*/
@Property(name = "visible", displayName = "Visible", category = "Behavior")
private boolean visible = false;
private boolean visible_set = false;
/**
* Use the visible attribute to indicate whether the component should be
* viewable by the user in the rendered HTML page. If set to false, the
* HTML code for the component is present in the page, but the component
* is hidden with style attributes. By default, visible is set to true, so
* HTML for the component HTML is included and visible to the user. If the
* component is not visible, it can still be processed on subsequent form
* submissions because the HTML is present.
*/
public boolean isVisible() {
if (this.visible_set) {
return this.visible;
}
ValueExpression _vb = getValueExpression("visible");
if (_vb != null) {
Object _result = _vb.getValue(getFacesContext().getELContext());
if (_result == null) {
return false;
} else {
return ((Boolean) _result).booleanValue();
}
}
return true;
}
/**
* Use the visible attribute to indicate whether the component should be
* viewable by the user in the rendered HTML page. If set to false, the
* HTML code for the component is present in the page, but the component
* is hidden with style attributes. By default, visible is set to true, so
* HTML for the component HTML is included and visible to the user. If the
* component is not visible, it can still be processed on subsequent form
* submissions because the HTML is present.
* @see #isVisible()
*/
public void setVisible(boolean visible) {
this.visible = visible;
this.visible_set = true;
}
/**
*
* A validator that will be applied to entries made into the
* textfield. Specify this to be the validate()
* method of a javax.faces.validator.Validator
, or
* to another method with the same argument structure and
* exceptions.
*/
@Property(name = "fieldValidatorExpression", displayName = "Field Validator Expression",
editorClassName = "com.sun.rave.propertyeditors.ValidatorPropertyEditor")
@Property.Method(signature = "void validate(javax.faces.context.FacesContext,javax.faces.component.UIComponent,java.lang.Object)")
private MethodExpression fieldValidatorExpression;
/**
*
* A validator that will be applied to entries made into the
* textfield. Specify this to be the validate()
* method of a javax.faces.validator.Validator
, or
* to another method with the same argument structure and
* exceptions.
*/
public MethodExpression getFieldValidatorExpression() {
return this.fieldValidatorExpression;
}
/**
*
* A validator that will be applied to entries made into the
* textfield. Specify this to be the validate()
* method of a javax.faces.validator.Validator
, or
* to another method with the same argument structure and
* exceptions.
* @see #getFieldValidatorExpression()
*/
public void setFieldValidatorExpression(MethodExpression me) {
this.fieldValidatorExpression = me;
}
/**
*
* A validator which will be applied to the contents of the list
* (e.g. to verify that the list has a minimum number of
* entries). Specify this to be the validate()
* method of a javax.faces.validator.Validator
, or
* to another method with the same argument structure and
* exceptions.
*/
@Property(name = "listValidatorExpression", isHidden = false,
displayName = "List Validator Expression", editorClassName = "com.sun.rave.propertyeditors.ValidatorPropertyEditor")
@Property.Method(signature = "void validate(javax.faces.context.FacesContext,javax.faces.component.UIComponent,java.lang.Object)")
public MethodExpression getListValidatorExpression() {
return getValidatorExpression();
}
/**
*
* A validator which will be applied to the contents of the list
* (e.g. to verify that the list has a minimum number of
* entries). Specify this to be the validate()
* method of a javax.faces.validator.Validator
, or
* to another method with the same argument structure and
* exceptions.
* @see #getListValidatorExpression()
*/
public void setListValidatorExpression(MethodExpression listValidator) {
setValidatorExpression(listValidator);
}
@Property(name = "validatorExpression", isHidden = true, isAttribute = false)
@Override
public MethodExpression getValidatorExpression() {
return super.getValidatorExpression();
}
/**
* Restore the state of this component.
*/
@Override
public void restoreState(FacesContext _context, Object _state) {
Object _values[] = (Object[]) _state;
super.restoreState(_context, _values[0]);
this.disabled = ((Boolean) _values[1]).booleanValue();
this.disabled_set = ((Boolean) _values[2]).booleanValue();
this.fieldLabel = (String) _values[3];
this.labelLevel = ((Integer) _values[4]).intValue();
this.labelLevel_set = ((Boolean) _values[5]).booleanValue();
this.listLabel = (String) _values[6];
this.listOnTop = ((Boolean) _values[7]).booleanValue();
this.listOnTop_set = ((Boolean) _values[8]).booleanValue();
this.maxLength = ((Integer) _values[9]).intValue();
this.maxLength_set = ((Boolean) _values[10]).booleanValue();
this.multiple = ((Boolean) _values[11]).booleanValue();
this.multiple_set = ((Boolean) _values[12]).booleanValue();
this.readOnly = ((Boolean) _values[13]).booleanValue();
this.readOnly_set = ((Boolean) _values[14]).booleanValue();
this.rows = ((Integer) _values[15]).intValue();
this.rows_set = ((Boolean) _values[16]).booleanValue();
this.sorted = ((Boolean) _values[17]).booleanValue();
this.sorted_set = ((Boolean) _values[18]).booleanValue();
this.style = (String) _values[19];
this.styleClass = (String) _values[20];
this.tabIndex = ((Integer) _values[21]).intValue();
this.tabIndex_set = ((Boolean) _values[22]).booleanValue();
this.toolTip = (String) _values[23];
this.visible = ((Boolean) _values[24]).booleanValue();
this.visible_set = ((Boolean) _values[25]).booleanValue();
this.fieldValidatorExpression = (MethodExpression) restoreAttachedState(_context, _values[26]);
}
/**
* Save the state of this component.
*/
@Override
public Object saveState(FacesContext _context) {
Object _values[] = new Object[27];
_values[0] = super.saveState(_context);
_values[1] = this.disabled ? Boolean.TRUE : Boolean.FALSE;
_values[2] = this.disabled_set ? Boolean.TRUE : Boolean.FALSE;
_values[3] = this.fieldLabel;
_values[4] = new Integer(this.labelLevel);
_values[5] = this.labelLevel_set ? Boolean.TRUE : Boolean.FALSE;
_values[6] = this.listLabel;
_values[7] = this.listOnTop ? Boolean.TRUE : Boolean.FALSE;
_values[8] = this.listOnTop_set ? Boolean.TRUE : Boolean.FALSE;
_values[9] = new Integer(this.maxLength);
_values[10] = this.maxLength_set ? Boolean.TRUE : Boolean.FALSE;
_values[11] = this.multiple ? Boolean.TRUE : Boolean.FALSE;
_values[12] = this.multiple_set ? Boolean.TRUE : Boolean.FALSE;
_values[13] = this.readOnly ? Boolean.TRUE : Boolean.FALSE;
_values[14] = this.readOnly_set ? Boolean.TRUE : Boolean.FALSE;
_values[15] = new Integer(this.rows);
_values[16] = this.rows_set ? Boolean.TRUE : Boolean.FALSE;
_values[17] = this.sorted ? Boolean.TRUE : Boolean.FALSE;
_values[18] = this.sorted_set ? Boolean.TRUE : Boolean.FALSE;
_values[19] = this.style;
_values[20] = this.styleClass;
_values[21] = new Integer(this.tabIndex);
_values[22] = this.tabIndex_set ? Boolean.TRUE : Boolean.FALSE;
_values[23] = this.toolTip;
_values[24] = this.visible ? Boolean.TRUE : Boolean.FALSE;
_values[25] = this.visible_set ? Boolean.TRUE : Boolean.FALSE;
_values[26] = saveAttachedState(_context, fieldValidatorExpression);
return _values;
}
}
class AddListener implements ActionListener, Serializable {
private static final long serialVersionUID = -5204715231205041623L;
public void processAction(ActionEvent event) {
UIComponent comp = event.getComponent();
comp = comp.getParent();
if (comp instanceof EditableList) {
((EditableList) comp).processAddAction();
}
}
}
class RemoveListener implements ActionListener, Serializable {
private static final long serialVersionUID = -7559203083988359751L;
public void processAction(ActionEvent event) {
UIComponent comp = event.getComponent();
comp = comp.getParent();
if (comp instanceof EditableList) {
((EditableList) comp).processRemoveAction();
}
}
}