/*
* RHQ Management Platform
* Copyright (C) 2009-2010 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.core.gui.table.component;
import org.ajax4jsf.component.UIDataAdaptor;
import org.rhq.core.gui.util.FacesComponentUtility;
import org.rhq.core.gui.util.FacesExpressionUtility;
import javax.el.ValueExpression;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIData;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
/**
* An HTML component that allows rows in data tables (i.e. h:dataTables or rich:*dataTables) to be selected via either
* checkboxes (if in multi-select mode) or via radio buttons (if in single-select mode).
*
* NOTE: It is preferred to use the disabled attribute of this component instead of the rendered attribute. This will
* provide consistency to the rendered table such as regardless of what items appear in the table, the check-able
* column will always be rendered with a consistent width.
*
* @author Ian Springer
*/
public class RowSelectorComponent extends UIInput {
public static final String COMPONENT_TYPE = "org.rhq.RowSelector";
public static final String COMPONENT_FAMILY = "org.rhq.RowSelector";
private static final Mode DEFAULT_MODE = Mode.multi;
private Mode mode;
@Override
public String getFamily() {
return COMPONENT_FAMILY;
}
public Mode getMode() {
if (this.mode != null) {
return this.mode;
}
Mode mode = (Mode) getBinding("mode");
this.mode = (mode != null) ? mode : DEFAULT_MODE;
return this.mode;
}
public void setMode(Mode mode) {
this.mode = mode;
}
@Override
public Object getValue() {
if (getValueExpression("value") == null) {
throw new IllegalStateException("rowSelector 'value' attribute must be set to an EL expression.");
}
return super.getValue();
}
/**
* By default, getClientId() would return something like
* "myForm:myDataTable:10001:myRowSelector", where "myForm:myDataTable:10001" is the client id of our enclosing
* data table, and "10001" is the index of the current row. For each row, we want ourselves to be rendered as a
* HTML checkbox with the *same* "name" attribute as the checkboxes for the other rows, so it is easier to
* obtain the set of checkboxes via JavaScript. Since the client id is used as the checkbox "name" attribute, we
* need to make sure to return a client id that does not include the rowIndex
* (e.g. "myForm:myDataTable:myRowSelector". Isn't JSF fun?
*
* @param context the JSF context
* @return our client id
*/
@Override
public String getClientId(FacesContext context) {
// Get our enclosing data table.
UIData data = FacesComponentUtility.getAncestorOfType(this, UIData.class);
String baseClientId;
if (data instanceof UIDataAdaptor) {
// It's a RichFaces data table, which conveniently provides a public method that returns its base client id.
UIDataAdaptor dataAdaptor = (UIDataAdaptor) data;
baseClientId = dataAdaptor.getBaseClientId(context);
} else {
// It's not a RichFaces data table.
int originalRowIndex = data.getRowIndex();
// Temporarily set its row index to -1 to force it to return a client id for itself that doesn't include the
// row index.
data.setRowIndex(-1);
baseClientId = data.getClientId(context);
data.setRowIndex(originalRowIndex);
}
// Now append our id.
String id = getId();
if (id == null) {
id = context.getViewRoot().createUniqueId();
setId(id);
}
return baseClientId + NamingContainer.SEPARATOR_CHAR + id;
}
@Override
public Object saveState(FacesContext context) {
Object[] values = new Object[2];
values[0] = super.saveState(context);
values[1] = this.mode;
return values;
}
@Override
public void restoreState(FacesContext context, Object state) {
Object[] values = (Object[]) state;
super.restoreState(context, values[0]);
this.mode = (Mode) values[1];
}
public Object getBinding(String attr) {
if (attr == null) {
throw new NullPointerException("passed attribute is null");
}
ValueExpression valueExpression = this.getValueExpression(attr);
@SuppressWarnings({"UnnecessaryLocalVariable"})
Object attribValue = (valueExpression != null) ? FacesExpressionUtility.getValue(valueExpression, Object.class)
: null;
return attribValue;
}
public enum Mode {
/**
* Only one row can be selected at a time.
*/
single,
/**
* Multiple rows can be selected simultaneously.
*/
multi
}
}