Reputation: 2683
I am using a CellTable with also DatePickerCell and I'd like to be able to display also dates that are not set, e.g. "empty" date. But in implementing getValue method I have to return something:
Column<EventProxy, Date> startTimeColumn = new Column<EventProxy, Date>(
new DatePickerCell()) {
@Override
public Date getValue(EventProxy object) {
Date ret = object.getStartTime();
return ret != null ? ret : new Date();
}
};
If object.getStartTime() is null, it means that it is not set and I want to display it as such. Or with empty label or with text "empty". As I've said, method getValue has to return something. If I return null, I get exceptions later, if I return concrete date it displays it as valid date. Is there any other option? Some special date tag or object that DatePickerCell would recognize as empty or unset value?
Upvotes: 1
Views: 2431
Reputation: 21
DatePickerCell
does not support null values. Overriding render
is not enough as NPE
is thrown from method onEnterKeyDown
when you click on the rendered cell.
You have to implement your own cell with null protection in "onEnterKeyDown" :
if (date != null) {
datePicker.setCurrentMonth(date);
}
Complete class :
public class DatePickerCell extends AbstractEditableCell<Date, Date> {
private static final int ESCAPE = 27;
private final DatePicker datePicker;
private final DateTimeFormat format;
private int offsetX = 10;
private int offsetY = 10;
private Object lastKey;
private Element lastParent;
private int lastIndex;
private int lastColumn;
private Date lastValue;
private PopupPanel panel;
private final SafeHtmlRenderer<String> renderer;
private ValueUpdater<Date> valueUpdater;
/**
* Constructs a new DatePickerCell that uses the date/time format given by
* {@link DateTimeFormat#getFullDateFormat}.
*/
@SuppressWarnings("deprecation")
public DatePickerCell() {
this(DateTimeFormat.getFullDateFormat(), SimpleSafeHtmlRenderer.getInstance());
}
/**
* Constructs a new DatePickerCell that uses the given date/time format and
* a {@link SimpleSafeHtmlRenderer}.
* @param format a {@link DateTimeFormat} instance
*/
public DatePickerCell(DateTimeFormat format) {
this(format, SimpleSafeHtmlRenderer.getInstance());
}
/**
* Constructs a new DatePickerCell that uses the date/time format given by
* {@link DateTimeFormat#getFullDateFormat} and the given
* {@link SafeHtmlRenderer}.
* @param renderer a {@link SafeHtmlRenderer SafeHtmlRenderer<String>}
* instance
*/
public DatePickerCell(SafeHtmlRenderer<String> renderer) {
this(DateTimeFormat.getFormat(PredefinedFormat.DATE_FULL), renderer);
}
/**
* Constructs a new DatePickerCell that uses the given date/time format and
* {@link SafeHtmlRenderer}.
* @param format a {@link DateTimeFormat} instance
* @param renderer a {@link SafeHtmlRenderer SafeHtmlRenderer<String>}
* instance
*/
public DatePickerCell(DateTimeFormat format, SafeHtmlRenderer<String> renderer) {
super(CLICK, KEYDOWN);
if (format == null) {
throw new IllegalArgumentException("format == null");
}
if (renderer == null) {
throw new IllegalArgumentException("renderer == null");
}
this.format = format;
this.renderer = renderer;
this.datePicker = new DatePicker();
this.panel = new PopupPanel(true, true) {
@Override
protected void onPreviewNativeEvent(NativePreviewEvent event) {
if (Event.ONKEYUP == event.getTypeInt()) {
if (event.getNativeEvent().getKeyCode() == ESCAPE) {
// Dismiss when escape is pressed
panel.hide();
}
}
}
};
panel.addCloseHandler(new CloseHandler<PopupPanel>() {
public void onClose(CloseEvent<PopupPanel> event) {
lastKey = null;
lastValue = null;
lastIndex = -1;
lastColumn = -1;
if (lastParent != null && !event.isAutoClosed()) {
// Refocus on the containing cell after the user selects a
// value, but
// not if the popup is auto closed.
lastParent.focus();
}
lastParent = null;
}
});
panel.add(datePicker);
// Hide the panel and call valueUpdater.update when a date is selected
datePicker.addValueChangeHandler(new ValueChangeHandler<Date>() {
public void onValueChange(ValueChangeEvent<Date> event) {
// Remember the values before hiding the popup.
Element cellParent = lastParent;
Date oldValue = lastValue;
Object key = lastKey;
int index = lastIndex;
int column = lastColumn;
panel.hide();
// Update the cell and value updater.
Date date = event.getValue();
setViewData(key, date);
setValue(new Context(index, column, key), cellParent, oldValue);
if (valueUpdater != null) {
valueUpdater.update(date);
}
}
});
}
@Override
public boolean isEditing(Context context, Element parent, Date value) {
return lastKey != null && lastKey.equals(context.getKey());
}
@Override
public void onBrowserEvent(Context context, Element parent, Date value, NativeEvent event, ValueUpdater<Date> valueUpdater) {
super.onBrowserEvent(context, parent, value, event, valueUpdater);
if (CLICK.equals(event.getType())) {
onEnterKeyDown(context, parent, value, event, valueUpdater);
}
}
@Override
public void render(Context context, Date value, SafeHtmlBuilder sb) {
// Get the view data.
Object key = context.getKey();
Date viewData = getViewData(key);
if (viewData != null && viewData.equals(value)) {
clearViewData(key);
viewData = null;
}
String s = null;
if (viewData != null) {
s = format.format(viewData);
} else if (value != null) {
s = format.format(value);
}
if (s != null) {
sb.append(renderer.render(s));
}
}
@Override
protected void onEnterKeyDown(Context context, Element parent, Date value, NativeEvent event, ValueUpdater<Date> valueUpdater) {
this.lastKey = context.getKey();
this.lastParent = parent;
this.lastValue = value;
this.lastIndex = context.getIndex();
this.lastColumn = context.getColumn();
this.valueUpdater = valueUpdater;
Date viewData = getViewData(lastKey);
Date date = (viewData == null) ? lastValue : viewData;
if (date != null) {
datePicker.setCurrentMonth(date);
}
datePicker.setValue(date);
panel.setPopupPositionAndShow(new PositionCallback() {
public void setPosition(int offsetWidth, int offsetHeight) {
panel.setPopupPosition(lastParent.getAbsoluteLeft() + offsetX, lastParent.getAbsoluteTop() + offsetY);
}
});
}
}
Upvotes: 2
Reputation: 2674
You are wondering about displaying dates, right? Then shift your focus away from getValue() and look at overriding the render() method (found in the Column class) instead. The render() method has an object parameter, just like getValue(), and a parameter for a SafeHtmlBuilder to which you append your representation of the object's value. Test getStartTime(), and if it is null, append "[unset]" (or whatever) to that SafeHtmlBuilder. You could even append an image of a clock face with a red strikeout running through it (using the HTML img tag), or anything you care to, since render is just appending HTML that will be put into the cell.
Not directly related, but perhaps useful in another context for you, is a new class delivered in Release 10 of Guava. It is called Optional. It is a generic that wraps around the class you're using, in this case, Date. It provides a way to explicitly distinguish between a null value, and unset value, and so on, using the methods provided. Give it a quick read -- since you are dealing with empty dates, this might be useful elsewhere in your design.
Upvotes: 2