package pt.ist.renderers.extensions; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.apache.commons.collections.Predicate; import pt.ist.renderers.InputRenderer; import pt.ist.renderers.components.HtmlActionLink; import pt.ist.renderers.components.HtmlBlockContainer; import pt.ist.renderers.components.HtmlComponent; import pt.ist.renderers.components.HtmlContainer; import pt.ist.renderers.components.HtmlMultipleHiddenField; import pt.ist.renderers.components.HtmlTable; import pt.ist.renderers.components.HtmlTableRow; import pt.ist.renderers.components.controllers.HtmlActionLinkController; import pt.ist.renderers.components.state.IViewState; import pt.ist.renderers.contexts.PresentationContext; import pt.ist.renderers.extensions.converters.DomainObjectKeyArrayConverter; import pt.ist.renderers.layouts.Layout; import pt.ist.renderers.layouts.TabularLayout; import pt.ist.renderers.model.MetaObject; import pt.ist.renderers.model.MetaObjectFactory; import pt.ist.renderers.model.MetaSlot; import pt.ist.renderers.model.MetaSlotKey; import pt.ist.renderers.schemas.Schema; import pt.ist.renderers.utils.RenderKit; import pt.ist.renderers.utils.RenderMode; import pt.ist.renderers.utils.RenderUtils; /** * This renderer allows you get a list as input but delegates part of the * list's management to an external action. Each one of the slot's values * will be presented in an html list. The renderer manages the removal of * values from the list. So a delete action link will appear next to each * object's presentation. An add action link will allow the user to start the * external use-case that will add a new value to the list. * *
* Example: *
* The action can obtain a representation of the viewstate suitable for * including in a hidden field by doing: *
* IViewState viewState = RenderUtils.getViewState();
* String encoded = ViewState.encodeToBase64(viewState);
*
* Retrieving the viewstate from it's encoded state and preparing before
* sending control to the inicial page can be done with:
*
* IViewState viewState = ViewState.decodeFromBase64(<encoded viewstate>);
* viewState.setUser(UserIdentityFactory.create(request));
* // update slot in viewstate
* RenderUtils.setViewState(viewState);
*
*
* Updating the viewstate consists of adding a new value to the slot's.
* This must only be done through the meta-object contained in the viewstate:
*
* MetaSlot slot = <find slot in viewState.getMetaObject().getSlots()>;
* List values = new ArrayList((List) slot.getObject());
* values.add(<the new value>);
* slot.setObject(values);
*
*
* The slot that was beeing managed is available in viewstate attributes. You can
* get information about the slot with:
*
* String slotName = viewState.getAttribute(ManageableListRenderer.MANAGED_SLOT_NAME);
* MetaSlotKey slotKey = viewState.getAttribute(ManageableListRenderer.MANAGED_SLOT_KEY);
*
*
* @property
*/
public void setDestination(String destination) {
this.destination = destination;
}
@Override
protected Layout getLayout(Object object, Class type) {
MetaObject metaObject = getInputContext().getMetaObject();
return new ManageableListLayout((Collection) metaObject.getObject());
}
private class ManageableListLayout extends TabularLayout {
private final List objects;
private final HtmlMultipleHiddenField hiddenValues;
public ManageableListLayout(Collection collection) {
this.objects = new ArrayList(collection);
this.hiddenValues = new HtmlMultipleHiddenField();
this.hiddenValues.setTargetSlot((MetaSlotKey) getInputContext().getMetaObject().getKey());
// HACK: severe dependecy with fenix project
this.hiddenValues.setConverter(new DomainObjectKeyArrayConverter());
}
@Override
protected int getNumberOfColumns() {
return 2;
}
@Override
protected int getNumberOfRows() {
return this.objects == null ? 0 : this.objects.size();
}
@Override
protected HtmlComponent getHeaderComponent(int columnIndex) {
return null;
}
@Override
public HtmlComponent createComponent(Object object, Class type) {
HtmlComponent component = super.createComponent(object, type);
HtmlContainer container = new HtmlBlockContainer();
container.addChild(component);
container.addChild(this.hiddenValues);
HtmlActionLink link = new HtmlActionLink();
link.setName(getInputContext().getMetaObject().getKey().toString() + "/add");
link.setText(RenderUtils.getResourceString("renderers.list.management.add"));
link.setController(new FollowDestinationController((MetaSlot) getInputContext().getMetaObject()));
container.addChild(link);
return container;
}
@Override
public void applyStyle(HtmlComponent component) {
HtmlContainer container = (HtmlContainer) component;
super.applyStyle(container.getChildren().get(0));
}
@Override
protected HtmlComponent getComponent(int rowIndex, int columnIndex) {
Object object = this.objects.get(rowIndex);
Schema schema = RenderKit.getInstance().findSchema(getEachSchema());
String layout = getEachLayout();
MetaObject metaObject = MetaObjectFactory.createObject(object, schema);
if (columnIndex == 0) {
this.hiddenValues.addValue(metaObject.getKey().toString());
PresentationContext newContext = getContext().createSubContext(metaObject);
newContext.setLayout(layout);
newContext.setRenderMode(RenderMode.getMode("output"));
RenderKit kit = RenderKit.getInstance();
return kit.render(newContext, object);
} else {
HtmlActionLink link = new HtmlActionLink();
String prefix = getInputContext().getMetaObject().getKey().toString();
link.setName(prefix + "/delete/" + rowIndex);
link.setText(RenderUtils.getResourceString("renderers.list.management.delete"));
HtmlTableRow row = getTable().getRows().get(rowIndex);
link.setController(new RemoveLineController(getTable(), this.hiddenValues, row, prefix));
return link;
}
}
}
class RemoveLineController extends HtmlActionLinkController {
private final HtmlTable table;
private final HtmlTableRow row;
private final HtmlMultipleHiddenField values;
private final String prefix;
public RemoveLineController(HtmlTable table, HtmlMultipleHiddenField values, HtmlTableRow row, String prefix) {
this.table = table;
this.values = values;
this.row = row;
this.prefix = prefix;
}
@Override
public void linkPressed(IViewState viewState, HtmlActionLink link) {
int index = this.table.getRows().indexOf(row);
this.table.removeRow(this.row);
this.values.removeValue(index);
renameLinks();
}
private void renameLinks() {
List