ItEr17S12CUAltaTipoParteDeTraballo: Creating a macro component called TwoWaySelector.

This commit is contained in:
Manuel Rego Casasnovas 2009-07-15 16:34:00 +02:00 committed by Javier Moran Rua
parent 1d7532d759
commit cdd8560676
4 changed files with 333 additions and 0 deletions

View file

@ -32,6 +32,15 @@ public class Util {
}
}
public static void saveBindings(Component... toReload) {
for (Component reload : toReload) {
DataBinder binder = Util.getBinder(reload);
if (binder != null) {
binder.saveComponent(reload);
}
}
}
public static DataBinder getBinder(Component component) {
return (DataBinder) component.getVariable("binder", false);
}

View file

@ -0,0 +1,268 @@
package org.navalplanner.web.common.components;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.navalplanner.web.common.Util;
import org.zkoss.lang.Objects;
import org.zkoss.zk.ui.HtmlMacroComponent;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listcell;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.ListitemRenderer;
/**
* ZK macro component that shows two {@link Listbox} allowing to move objects
* between each other.
*
* In the {@link Listbox} on the left you will have the assigned objects and in
* the right the possible other objects to be assigned.
*
* Finally it provides methods to get the current assigned and unassigned
* objects.
*
* @author Manuel Rego Casasnovas <mrego@igalia.com>
*/
public class TwoWaySelector extends HtmlMacroComponent {
/**
* A {@link Set} of objects that are assigned (so they're shown on the left
* {@link Listbox})
*/
private Set assignedObjects = new HashSet();
/**
* Title for the left {@link Listbox} (where assigned objects are shown)
*/
private String assignedTitle = "Assigned";
/**
* A {@link Set} of objects that are not assigned (so they're shown on the
* right {@link Listbox})
*/
private Set unassignedObjects = new HashSet();
/**
* Title for the right {@link Listbox} (where unassigned objects are shown)
*/
private String unassignedTitle = "Unassigned";
/**
* A {@link List} of properties to be shown on the {@link Listbox} for each
* object.
*/
private List<String> columns = null;
/**
* {@link ListitemRenderer} that knows how to paint an object according to
* the {@link List} stored in the columns attribute. If columns is null then
* the object will be rendered as a string.
*
* @author Manuel Rego Casasnovas <mrego@igalia.com>
*/
private ListitemRenderer renderer = new ListitemRenderer() {
@Override
public void render(Listitem item, Object data) throws Exception {
Class<? extends Object> klass = data.getClass();
Map<String, PropertyDescriptor> propertiesByName = getProperties(klass);
// If a list of attributes is defined
if (columns != null) {
// For each attribute
for (String column : columns) {
// Call the method to get the information
PropertyDescriptor propertyDescriptor = propertiesByName
.get(column);
if (propertyDescriptor == null) {
throw new RuntimeException(
"Unknown attribute '"
+ column + "' in class " + klass.getName());
}
String label = Objects.toString(propertyDescriptor
.getReadMethod().invoke(data));
// Add a new Listcell
item.appendChild(new Listcell(label));
}
} else { // If the list of attributes is not defined
// Render the object as string
item.setLabel(Objects.toString(data));
}
item.setValue(data);
}
/**
* A {@link Map} that stores the information about the attributes for a
* class.
*
* The information about attributes is stored with another Map where
* keys are the properties name and the values the
* {@link PropertyDescriptor}.
*/
private Map<Class<?>, Map<String, PropertyDescriptor>> propertiesMapsCached = new HashMap<Class<?>, Map<String, PropertyDescriptor>>();
/**
* Creates a {@link Map} that relates the properties and their
* {@link PropertyDescriptor} from the {@link BeanInfo}.
*
* @param info
* Information about the bean
* @return A {@link Map} that relates properties name and
* {@link PropertyDescriptor}
*/
private Map<String, PropertyDescriptor> buildPropertyDescriptorsMap(
BeanInfo info) {
PropertyDescriptor[] propertyDescriptors = info
.getPropertyDescriptors();
Map<String, PropertyDescriptor> propertiesByName = new HashMap<String, PropertyDescriptor>();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
propertiesByName.put(propertyDescriptor.getName(),
propertyDescriptor);
}
return propertiesByName;
}
/**
* Gets the attributes of a {@link Class} together with the
* {@link PropertyDescriptor} of each property.
*
* @param klass
* The {@link Class} to get the properties
* @return A {@link Map} that relates properties name and
* {@link PropertyDescriptor}
* @throws IntrospectionException
*/
private Map<String, PropertyDescriptor> getProperties(
Class<? extends Object> klass) throws IntrospectionException {
// If it's already cached
if (propertiesMapsCached.containsKey(klass)) {
return propertiesMapsCached.get(klass);
}
BeanInfo beanInfo = Introspector.getBeanInfo(klass);
Map<String, PropertyDescriptor> result = buildPropertyDescriptorsMap(beanInfo);
// Store in cache
propertiesMapsCached.put(klass, result);
return result;
}
};
public void setAssignedTitle(String assignedTitle) {
if (assignedTitle != null) {
this.assignedTitle = assignedTitle;
}
}
public String getAssignedTitle() {
return assignedTitle;
}
public void setUnassignedTitle(String unassignedTitle) {
if (unassignedTitle != null) {
this.unassignedTitle = unassignedTitle;
}
}
public String getUnassignedTitle() {
return unassignedTitle;
}
public void setAssignedObjects(Set assignedObjects) {
if (assignedObjects != null) {
this.assignedObjects = assignedObjects;
}
}
public Set getAssignedObjects() {
return assignedObjects;
}
public void setUnassignedObjects(Set unassignedObjects) {
if (assignedObjects != null) {
this.unassignedObjects = unassignedObjects;
}
}
public Set getUnassignedObjects() {
return unassignedObjects;
}
/**
* Sets the list of attributes to be shown when an object is renderer.
*
* @param columns
* A comma-separated string
*/
public void setColumns(String columns) {
if (columns != null) {
// Remove white spaces
columns = columns.replaceAll("\\s", "");
if (!columns.isEmpty()) {
// Split the string
this.columns = Arrays.asList(columns.split(","));
}
}
}
public List<String> getColumns() {
return columns;
}
public ListitemRenderer getRenderer() {
return renderer;
}
/**
* Assign (move to the left {@link Listbox}) the selected items from the
* right {@link Listbox}. And reload both {@link Listbox} in order to
* relfect the changes.
*
* @param unassignedObjectsListbox
* The right {@link Listbox}
*/
public void assign(Listbox unassignedObjectsListbox) {
Set<Listitem> selectedItems = unassignedObjectsListbox
.getSelectedItems();
for (Listitem listitem : selectedItems) {
Object value = listitem.getValue();
unassignedObjects.remove(value);
assignedObjects.add(value);
}
Util.reloadBindings(unassignedObjectsListbox.getParent());
Util.saveBindings(this);
}
/**
* Unassign (move to the rigth {@link Listbox}) the selected items from the
* left {@link Listbox}. And reload both {@link Listbox} in order to relfect
* the changes.
*
* @param assignedObjectsListbox
* The left {@link Listbox}
*/
public void unassign(Listbox assignedObjectsListbox) {
Set<Listitem> selectedItems = assignedObjectsListbox.getSelectedItems();
for (Listitem listitem : selectedItems) {
Object value = listitem.getValue();
assignedObjects.remove(value);
unassignedObjects.add(value);
}
Util.reloadBindings(assignedObjectsListbox.getParent());
Util.saveBindings(this);
}
}

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<language-addon>
<addon-name>navalplanner-webapp</addon-name>
<language-name>xul/html</language-name>
<component>
<component-name>twowayselector</component-name>
<component-class>org.navalplanner.web.common.components.TwoWaySelector</component-class>
<macro-uri>/common/components/twowayselector.zul</macro-uri>
</component>
</language-addon>

View file

@ -0,0 +1,42 @@
<zk>
<zscript>
<![CDATA[
top = self;
]]>
</zscript>
<vbox>
<hbox>
<listbox id="assignedObjects"
model="@{top.assignedObjects}"
itemRenderer="@{top.renderer}"
multiple="true" checkmark="true">
<listhead>
<listheader label="@{top.assignedTitle}" />
</listhead>
</listbox>
<vbox>
<button id="assignButton" label="&lt;&lt;"
onClick="top.assign(unassignedObjects);" />
<button id="unassignButton" label="&gt;&gt;"
onClick="top.unassign(assignedObjects);" />
</vbox>
<listbox id="unassignedObjects"
model="@{top.unassignedObjects}"
itemRenderer="@{top.renderer}"
multiple="true" checkmark="true">
<listhead>
<listheader label="@{top.unassignedTitle}" />
</listhead>
</listbox>
</hbox>
</vbox>
</zk>