SWF-646 - Improve the SelectionTrackingActionListener to work with any iterator type of component that works with a DataModel.
This commit is contained in:
@@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.faces.model;
|
package org.springframework.faces.model;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
import javax.faces.component.UIComponent;
|
import javax.faces.component.UIComponent;
|
||||||
import javax.faces.component.UIData;
|
import javax.faces.component.UIData;
|
||||||
import javax.faces.component.UIViewRoot;
|
import javax.faces.component.UIViewRoot;
|
||||||
@@ -25,14 +27,16 @@ import javax.faces.event.ActionListener;
|
|||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.faces.webflow.FlowActionListener;
|
import org.springframework.faces.webflow.FlowActionListener;
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom {@link ActionListener} that inspects the {@link UIComponent} that signaled the current {@link ActionEvent} to
|
* Custom {@link ActionListener} that inspects the {@link UIComponent} that signaled the current {@link ActionEvent} to
|
||||||
* determine whether it is a child of a {@link UIData} component that uses a {@link SelectionAware} data model
|
* determine whether it is a child of any iterator type of component (such as {@link UIData}) that uses a
|
||||||
* implementation. If a containing SelectionAware model is found, the row containing the event-signaling component
|
* {@link SelectionAware} data model implementation. If a containing SelectionAware model is found, the row containing
|
||||||
* instance will be selected. This enables convenient access to the selected model state at any time through EL
|
* the event-signaling component instance will be selected. This enables convenient access to the selected model state
|
||||||
* expressions such as #{model.selectedRow.id} without having to rely on the whether or not the current row index is
|
* at any time through EL expressions such as #{model.selectedRow.id} without having to rely on the whether or not the
|
||||||
* pointing to the desired row as it would need to be to use an expression such as #{model.rowData.id}
|
* current row index is pointing to the desired row as it would need to be to use an expression such as
|
||||||
|
* #{model.rowData.id}
|
||||||
*
|
*
|
||||||
* @author Jeremy Grelle
|
* @author Jeremy Grelle
|
||||||
*/
|
*/
|
||||||
@@ -52,23 +56,23 @@ public class SelectionTrackingActionListener implements ActionListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void trackSelection(UIComponent component) {
|
private void trackSelection(UIComponent component) {
|
||||||
// Find parent UIData instance if it exists
|
// Find parent component with a SelectionAware model if it exists
|
||||||
UIData table = null;
|
|
||||||
UIComponent currentComponent = component;
|
UIComponent currentComponent = component;
|
||||||
while (!(currentComponent.getParent() instanceof UIViewRoot)) {
|
while (!(currentComponent.getParent() instanceof UIViewRoot)) {
|
||||||
if (currentComponent.getParent() instanceof UIData) {
|
UIComponent parent = currentComponent.getParent();
|
||||||
table = (UIData) currentComponent.getParent();
|
Method valueAccessor = ReflectionUtils.findMethod(parent.getClass(), "getValue");
|
||||||
break;
|
if (valueAccessor != null) {
|
||||||
|
Object value = ReflectionUtils.invokeMethod(valueAccessor, parent);
|
||||||
|
if (value != null && value instanceof SelectionAware) {
|
||||||
|
((SelectionAware) value).setSelected(true);
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Row selection has been set on the current SelectionAware data model.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
currentComponent = currentComponent.getParent();
|
currentComponent = currentComponent.getParent();
|
||||||
}
|
}
|
||||||
if (table != null && table.getValue() instanceof SelectionAware) {
|
|
||||||
SelectionAware selectableModel = (SelectionAware) table.getValue();
|
|
||||||
selectableModel.setSelected(true);
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("Row selection has been set on the current SelectionAware data model.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
package org.springframework.faces.model;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.faces.component.UIColumn;
|
||||||
|
import javax.faces.component.UICommand;
|
||||||
|
import javax.faces.component.UIData;
|
||||||
|
import javax.faces.component.UIViewRoot;
|
||||||
|
import javax.faces.event.AbortProcessingException;
|
||||||
|
import javax.faces.event.ActionEvent;
|
||||||
|
import javax.faces.event.ActionListener;
|
||||||
|
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
|
import com.sun.facelets.component.UIRepeat;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
public class SelectionTrackingActionListenerTests extends TestCase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The JSF view to simulate
|
||||||
|
*/
|
||||||
|
private UIViewRoot viewToTest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of row data objects to
|
||||||
|
*/
|
||||||
|
private OneSelectionTrackingListDataModel dataModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The delegate action listener that should be called
|
||||||
|
*/
|
||||||
|
private TestDelegateActionListener delegateListener = new TestDelegateActionListener();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class under test
|
||||||
|
*/
|
||||||
|
private ActionListener selectionTrackingListener = new SelectionTrackingActionListener(delegateListener);
|
||||||
|
|
||||||
|
public void setUp() {
|
||||||
|
viewToTest = new UIViewRoot();
|
||||||
|
List rows = new ArrayList();
|
||||||
|
rows.add(new TestRowData());
|
||||||
|
rows.add(new TestRowData());
|
||||||
|
rows.add(new TestRowData());
|
||||||
|
dataModel = new OneSelectionTrackingListDataModel(rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testProcessActionWithUIData() {
|
||||||
|
|
||||||
|
UIData dataTable = new UIData();
|
||||||
|
dataTable.setValue(dataModel);
|
||||||
|
UIColumn column = new UIColumn();
|
||||||
|
UICommand commandButton = new UICommand();
|
||||||
|
column.getChildren().add(commandButton);
|
||||||
|
dataTable.getChildren().add(column);
|
||||||
|
viewToTest.getChildren().add(dataTable);
|
||||||
|
dataTable.setRowIndex(1);
|
||||||
|
|
||||||
|
ActionEvent event = new ActionEvent(commandButton);
|
||||||
|
|
||||||
|
selectionTrackingListener.processAction(event);
|
||||||
|
|
||||||
|
assertTrue(dataModel.isCurrentRowSelected());
|
||||||
|
assertSame(dataModel.getSelectedRow(), dataModel.getRowData());
|
||||||
|
assertTrue(delegateListener.processedEvent);
|
||||||
|
|
||||||
|
dataModel.setRowIndex(2);
|
||||||
|
assertFalse(dataModel.isCurrentRowSelected());
|
||||||
|
assertTrue(dataModel.getSelectedRow() != dataModel.getRowData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testProcessActionWithUIRepeat() {
|
||||||
|
|
||||||
|
UIRepeat uiRepeat = new UIRepeat();
|
||||||
|
uiRepeat.setValue(dataModel);
|
||||||
|
UICommand commandButton = new UICommand();
|
||||||
|
uiRepeat.getChildren().add(commandButton);
|
||||||
|
viewToTest.getChildren().add(uiRepeat);
|
||||||
|
|
||||||
|
Method indexMutator = ReflectionUtils.findMethod(UIRepeat.class, "setIndex", new Class[] { int.class });
|
||||||
|
indexMutator.setAccessible(true);
|
||||||
|
|
||||||
|
ReflectionUtils.invokeMethod(indexMutator, uiRepeat, new Object[] { new Integer(1) });
|
||||||
|
|
||||||
|
ActionEvent event = new ActionEvent(commandButton);
|
||||||
|
|
||||||
|
selectionTrackingListener.processAction(event);
|
||||||
|
|
||||||
|
assertTrue(dataModel.isCurrentRowSelected());
|
||||||
|
assertSame(dataModel.getSelectedRow(), dataModel.getRowData());
|
||||||
|
assertTrue(delegateListener.processedEvent);
|
||||||
|
|
||||||
|
ReflectionUtils.invokeMethod(indexMutator, uiRepeat, new Object[] { new Integer(2) });
|
||||||
|
assertFalse(dataModel.isCurrentRowSelected());
|
||||||
|
assertTrue(dataModel.getSelectedRow() != dataModel.getRowData());
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestRowData {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestDelegateActionListener implements ActionListener {
|
||||||
|
|
||||||
|
public boolean processedEvent = false;
|
||||||
|
|
||||||
|
public void processAction(ActionEvent event) throws AbortProcessingException {
|
||||||
|
processedEvent = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user