package org.yaac.client.ui;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.collect.Lists.newLinkedList;
import java.util.List;
import org.yaac.client.ui.EditorPropertyManager.PropertyEditCallback;
import org.yaac.shared.SharedConstants;
import org.yaac.shared.SharedConstants.Datastore;
import org.yaac.shared.editor.EntityInfo;
import org.yaac.shared.editor.EntityUpdateInfo;
import org.yaac.shared.editor.PropertyUpdateInfo;
import org.yaac.shared.property.KeyInfo;
import org.yaac.shared.property.NullPropertyInfo;
import org.yaac.shared.property.PropertyInfo;
import org.yaac.shared.property.PropertyType;
import org.yaac.shared.util.StringUtils;
import com.google.gwt.cell.client.ButtonCell;
import com.google.gwt.cell.client.EditTextCell;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.cell.client.SafeHtmlCell;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy.KeyboardSelectionPolicy;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.ListDataProvider;
/**
* @author Max Zhu (thebbsky@gmail.com)
*
*/
public class EditorEntityPanel extends Composite {
private static EditorEntityPanelUiBinder uiBinder = GWT.create(EditorEntityPanelUiBinder.class);
interface EditorEntityPanelUiBinder extends UiBinder<Widget, EditorEntityPanel> {
}
@UiField(provided=true)
CellTable<PropertyUpdateInfo> editor;
@UiField
Button newBtn;
/**
* original key
*/
private final KeyInfo origKey;
private final EditorPropertyManager editorManager;
private final ListDataProvider<PropertyUpdateInfo> dataProvider;
public EditorEntityPanel(EntityInfo entity, EditorPropertyManager arg1) {
this.origKey = entity.getKey();
this.editorManager = arg1;
// init editor
editor = new CellTable<PropertyUpdateInfo>();
editor.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.ENABLED);
{
final EditTextCell cell = new EditTextCell();
Column<PropertyUpdateInfo, String> column = new Column<PropertyUpdateInfo, String>(cell) {
@Override
public String getValue(PropertyUpdateInfo entry) {
return isNullOrEmpty(entry.getNewName()) ? entry.getName() : entry.getNewName();
}
};
column.setFieldUpdater(new FieldUpdater<PropertyUpdateInfo, String>() {
@Override
public void update(int index, PropertyUpdateInfo entry, String value) {
if (isValid(entry, value)) {
entry.setNewName(value);
dataProvider.refresh();
} else {
// restore old value
cell.clearViewData(entry);
editor.redraw();
}
}
private boolean isValid(PropertyUpdateInfo entry, String value) {
if (Datastore.KEY_RESERVED_NAME.equals(entry.getName()) || // check if reserved name __key__ is used
Datastore.KEY_RESERVED_NAME.equals(value)) {
Window.alert(Datastore.KEY_RESERVED_NAME + " is reserved for key");
return false;
} else { // check if the name is duplicated with other entries
boolean nameConflict = false;
for (PropertyUpdateInfo e : dataProvider.getList()) {
if (e != entry) {
// if the value going to be changed has already been used by other property
if (StringUtils.sameStr(value, e.getName())) {
nameConflict = true;
break;
}
}
}
if (nameConflict) {
Window.alert(entry.getName() + " is used by other property");
return false;
} else {
return true;
}
}
}
});
editor.addColumn(column, "Property Name"); // TODO : i18n
}
// property value
{
Column<PropertyUpdateInfo, SafeHtml> column = new Column<PropertyUpdateInfo, SafeHtml>(new SafeHtmlCell()) {
@Override
public SafeHtml getValue(PropertyUpdateInfo entry) {
SafeHtmlBuilder sb = new SafeHtmlBuilder();
if (entry.getInfo() == null) { // for new property, getInfo will return null
sb.appendHtmlConstant(" "); // simply blank, doesn't exist
} else {
String typeRepresentation = PropertyType.typeOf(entry.getInfo()).getRepresentation();
sb.appendHtmlConstant("<div title='" + typeRepresentation + "'>");
sb.appendHtmlConstant(entry.getInfo().asHtml());
sb.appendHtmlConstant("</div>");
}
return sb.toSafeHtml();
}
};
editor.addColumn(column, "Value"); // TODO : i18n
}
// new value
{
Column<PropertyUpdateInfo, SafeHtml> column = new Column<PropertyUpdateInfo, SafeHtml>(new SafeHtmlCell()) {
@Override
public SafeHtml getValue(PropertyUpdateInfo entry) {
SafeHtmlBuilder sb = new SafeHtmlBuilder();
if (entry.isDeleteFlag()) {
sb.appendHtmlConstant("To be deleted"); // TODO : i18n
} else if (entry.getNewInfo() == null) {
sb.appendHtmlConstant("Remain unchanged"); // TODO : i18n
} else {
String typeRepresentation = PropertyType.typeOf(entry.getNewInfo()).getRepresentation();
sb.appendHtmlConstant("<div title='" + typeRepresentation + "'>");
sb.appendHtmlConstant(entry.getNewInfo().asHtml());
sb.appendHtmlConstant("</div>");
}
return sb.toSafeHtml();
}
};
editor.addColumn(column, "New Value"); // TODO : i18n
}
// edit / undo edit
{
Column<PropertyUpdateInfo, String> column = new Column<PropertyUpdateInfo, String>(new ButtonCell()) {
@Override
public String getValue(PropertyUpdateInfo entry) {
return "Edit";
}
};
editor.addColumn(column);
column.setFieldUpdater(new FieldUpdater<PropertyUpdateInfo, String>() {
@Override
public void update(int index, final PropertyUpdateInfo e, String value) {
if (e.isDeleteFlag()) {
Window.alert("You can't edit a deleted property");
return;
}
PropertyEditCallback callback = new PropertyEditCallback() {
@Override
public void onEditSuccess(PropertyInfo newInfo) {
if (e.getInfo() == null) { // new property, always set newInfo
e.setNewInfo(newInfo);
} else if (e.getInfo().equals(newInfo)) { // existing property, nothing changed, ignore change
e.setNewInfo(null);
} else if (SharedConstants.Datastore.KEY_RESERVED_NAME.equals(e.getName()) &&
PropertyType.typeOf(newInfo) != PropertyType.KEY) { // existing property, name conflict, reject
Window.alert("__key__ property only accept key type");
}else { // existing property, something changed
e.setNewInfo(newInfo);
}
dataProvider.refresh();
}
};
editorManager.edit(origKey.getKeyString(),
e.getNewInfo() == null ? e.getInfo() : e.getNewInfo(), callback);
}
});
}
// delete / undo delete
{
Column<PropertyUpdateInfo, String> column = new Column<PropertyUpdateInfo, String>(new ButtonCell()) {
@Override
public String getValue(PropertyUpdateInfo entry) {
if (entry.isDeleteFlag()) {
return "Undo(X)";
} else {
return "X";
}
}
};
editor.addColumn(column);
column.setFieldUpdater(new FieldUpdater<PropertyUpdateInfo, String>() {
@Override
public void update(int index, final PropertyUpdateInfo e, String value) {
if (SharedConstants.Datastore.KEY_RESERVED_NAME.equals(e.getName())) {
Window.alert("__key__ property can't be deleted");
return;
}
if (e.isDeleteFlag()) {
e.setDeleteFlag(false);
} else {
e.setDeleteFlag(true);
}
dataProvider.refresh();
}
});
}
initWidget(uiBinder.createAndBindUi(this));
// init data
dataProvider = new ListDataProvider<PropertyUpdateInfo>(PropertyUpdateInfo.fromEntity(entity));
dataProvider.addDataDisplay(editor);
}
public EntityUpdateInfo updatedInfo() {
List<PropertyUpdateInfo> delta = newLinkedList();
for(PropertyUpdateInfo update : dataProvider.getList()) {
if (update.hasChanged()) {
delta.add(update);
}
}
if (delta.isEmpty()) {
return null;
} else {
return new EntityUpdateInfo(this.origKey, delta);
}
}
public KeyInfo getOrigKey() {
return origKey;
}
@UiHandler("newBtn")
void onNewBtnClick(ClickEvent event) {
String propertyName = null;
do {
propertyName =
Window.prompt("Input new property name(make sure it's not duplicated with existing ones)",
"newProperty");
} while (propertyName != null && isPropertyNameUsed(propertyName));
if (propertyName == null) { // action cancelled
return;
} else { // create new property
PropertyUpdateInfo newProperty = new PropertyUpdateInfo(propertyName, null);
newProperty.setNewInfo(new NullPropertyInfo());
dataProvider.getList().add(newProperty);
dataProvider.refresh();
}
}
private boolean isPropertyNameUsed(String propertyName) {
for (PropertyUpdateInfo e : dataProvider.getList()) {
if (StringUtils.sameStr(propertyName, e.getName())) {
return true;
}
}
return false;
}
}