/*
* Copyright 2015 TWO SIGMA OPEN SOURCE, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.twosigma.beaker.easyform;
import com.twosigma.beaker.easyform.formitem.ButtonComponent;
import com.twosigma.beaker.easyform.formitem.ListComponent;
import com.twosigma.beaker.easyform.formitem.LoadValuesButton;
import com.twosigma.beaker.easyform.formitem.SaveValuesButton;
import com.twosigma.beaker.easyform.formitem.TextArea;
import com.twosigma.beaker.easyform.formitem.widgets.ButtonComponentWidget;
import com.twosigma.beaker.easyform.formitem.widgets.CheckBoxGroupWidget;
import com.twosigma.beaker.easyform.formitem.widgets.CheckBoxWidget;
import com.twosigma.beaker.easyform.formitem.widgets.ComboBoxWidget;
import com.twosigma.beaker.easyform.formitem.widgets.DatePickerComponentWidget;
import com.twosigma.beaker.easyform.formitem.widgets.SelectMultipleSingleWidget;
import com.twosigma.beaker.easyform.formitem.widgets.SelectMultipleWidget;
import com.twosigma.beaker.easyform.formitem.widgets.RadioButtonComponentWidget;
import com.twosigma.beaker.easyform.formitem.widgets.TextAreaWidget;
import com.twosigma.beaker.easyform.formitem.widgets.TextFieldWidget;
import com.twosigma.beaker.widgets.DOMWidget;
import com.twosigma.beaker.widgets.DisplayableWidget;
import com.twosigma.beaker.widgets.Widget;
import org.apache.commons.lang3.StringUtils;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@SuppressWarnings("unchecked")
public class EasyForm extends ObservableMap<String, Object> implements DisplayableWidget {
public static final Integer HORIZONTAL = 1;
public static final Integer VERTICAL = 2;
private static final Integer AUTO_WIDTH = -1;
private final String caption;
private String id;
private Boolean ready = Boolean.FALSE;
private Map<String, EasyFormComponent> componentMap = new LinkedHashMap<>();
private SaveValuesButton saveValuesButton;
private LoadValuesButton loadValuesButton;
public EasyForm(final String caption) {
this.caption = caption;
}
public void setId(final String id) {
this.id = id;
}
public void addSaveValuesButton(final String path) {
SaveValuesButton button = new SaveValuesButton();
button.setPath(path);
this.saveValuesButton = button;
}
public void addLoadValuesButton(final String path) {
LoadValuesButton button = new LoadValuesButton();
button.setPath(path);
this.loadValuesButton = button;
}
public EasyFormComponent addTextField(final String label) throws Exception {
return addTextField(label, -1);
}
public EasyFormComponent addTextField(final String label, final Integer width) throws Exception {
TextFieldWidget textField = new TextFieldWidget();
textField.registerUpdateValueCallback(textField::fireChanged);
textField.setLabel(label);
textField.setWidth(width);
return addComponentOrThrow(label, textField);
}
public EasyFormComponent addTextArea(final String label) throws Exception {
return addTextArea(label, null, TextArea.AUTO_WIDTH, TextArea.AUTO_HEIGHT);
}
public EasyFormComponent addTextArea(final String label, final Integer width, final Integer height)
throws Exception {
return addTextArea(label, null, width, height);
}
public EasyFormComponent addTextArea(final String label, final String initialValue) throws Exception {
return addTextArea(label, initialValue, TextArea.AUTO_WIDTH, TextArea.AUTO_HEIGHT);
}
public EasyFormComponent addTextArea(final String label,
final String initialValue,
final Integer width,
final Integer height) throws Exception {
TextAreaWidget textArea = new TextAreaWidget();
textArea.registerUpdateValueCallback(textArea::fireChanged);
textArea.setLabel(label);
textArea.setWidth(width);
textArea.setHeight(height);
textArea.setValue(initialValue);
return addComponentOrThrow(label, textArea);
}
public EasyFormComponent addCheckBox(final String label) throws Exception {
return addCheckBox(label, Boolean.FALSE);
}
public EasyFormComponent addCheckBox(final String label, final Boolean value) throws Exception {
CheckBoxWidget checkBox = new CheckBoxWidget();
checkBox.registerUpdateValueCallback(checkBox::fireChanged);
checkBox.setLabel(label);
checkBox.setValue(String.valueOf(value));
return addComponentOrThrow(label, checkBox);
}
public EasyFormComponent addComboBox(final String label,
final Collection<String> values) throws Exception {
return addComboBox(label, values, Boolean.FALSE);
}
public EasyFormComponent addComboBox(final String label,
final Collection<String> values,
final Boolean editable) throws Exception {
return addComboBox(label, values, editable, EasyForm.AUTO_WIDTH);
}
public EasyFormComponent addComboBox(final String label,
final Collection<String> values,
final Boolean editable,
final Integer width) throws Exception {
ComboBoxWidget comboBox = new ComboBoxWidget();
comboBox.registerUpdateValueCallback(comboBox::fireChanged);
comboBox.setLabel(label);
comboBox.setEditable(editable);
comboBox.setValues(values);
comboBox.setWidth(width);
if (values != null && values.size() > 0) {
comboBox.setValue(values.iterator().next());
}
return addComponentOrThrow(label, comboBox);
}
public EasyFormComponent addList(final String label,
final Collection<String> values) throws Exception {
return addList(label, values, Boolean.TRUE, values.size());
}
public EasyFormComponent addList(final String label,
final Collection<String> values,
final Boolean multipleSelection) throws Exception {
return addList(label, values, multipleSelection, values.size());
}
public EasyFormComponent addList(final String label,
final Collection<String> values,
final Integer size) throws Exception {
return addList(label, values, Boolean.TRUE, size);
}
public EasyFormComponent addList(final String label,
final Collection<String> values,
final Boolean multipleSelection,
final Integer size) throws Exception {
ListComponent list = (multipleSelection) ? createSelectMultipleWidget() : createSelectMultipleSingleWidget();
list.setLabel(label);
list.setSize(size);
list.setMultipleSelection(multipleSelection);
list.setValues(values);
if (values != null && values.size() > 0) {
list.setValue(values.iterator().next());
}
return addComponentOrThrow(label, list);
}
private ListComponent createSelectMultipleSingleWidget() {
SelectMultipleSingleWidget list = new SelectMultipleSingleWidget();
list.registerUpdateValueCallback(list::fireChanged);
return list;
}
private ListComponent createSelectMultipleWidget() {
SelectMultipleWidget list = new SelectMultipleWidget();
list.registerUpdateValueCallback(list::fireChanged);
return list;
}
public EasyFormComponent addRadioButtons(final String label,
final Collection<String> values) throws Exception {
return addRadioButtons(label, values, EasyForm.VERTICAL);
}
public EasyFormComponent addRadioButtons(final String label,
final Collection<String> values,
final Integer orientation) throws Exception {
RadioButtonComponentWidget radioButtonComponent = new RadioButtonComponentWidget();
radioButtonComponent.registerUpdateValueCallback(radioButtonComponent::fireChanged);
radioButtonComponent.setLabel(label);
radioButtonComponent.setHorizontal(
orientation != null && EasyForm.HORIZONTAL.equals(orientation));
radioButtonComponent.setValues(values);
return addComponentOrThrow(label, radioButtonComponent);
}
public EasyFormComponent addCheckBoxes(final String label,
final Collection<String> values) throws Exception {
return addCheckBoxes(label, values, EasyForm.VERTICAL);
}
public EasyFormComponent addCheckBoxes(final String label,
final Collection<String> values,
final Integer orientation) throws Exception {
CheckBoxGroupWidget checkBoxGroup = new CheckBoxGroupWidget();
checkBoxGroup.setLabel(label);
checkBoxGroup.setHorizontal(orientation != null && EasyForm.HORIZONTAL.equals(orientation));
checkBoxGroup.setValues(values);
checkBoxGroup.createWidget();
checkBoxGroup.registerUpdateValueCallback(checkBoxGroup::fireChanged);
return addComponentOrThrow(label, checkBoxGroup);
}
public EasyFormComponent addDatePicker(final String label) throws Exception {
return addDatePicker(label, Boolean.FALSE);
}
public EasyFormComponent addDateTimePicker(final String label) throws Exception {
return addDatePicker(label, Boolean.TRUE);
}
public EasyFormComponent addDatePicker(final String label, final Boolean showTime) throws Exception {
DatePickerComponentWidget datePickerComponent = new DatePickerComponentWidget();
datePickerComponent.registerUpdateValueCallback(datePickerComponent::fireChanged);
datePickerComponent.setLabel(label);
datePickerComponent.setShowTime(showTime);
return addComponentOrThrow(label, datePickerComponent);
}
public ButtonComponent addButton(final String label) throws Exception {
return addButton(label, null);
}
public ButtonComponent addButton(final String label, final String actionCellTag) throws Exception {
ButtonComponentWidget buttonComponent = new ButtonComponentWidget();
buttonComponent.registerUpdateValueCallback(buttonComponent::fireChanged);
buttonComponent.setLabel(label);
buttonComponent.setTag(actionCellTag);
addComponentOrThrow(label, buttonComponent);
return buttonComponent;
}
private EasyFormComponent addComponentOrThrow(final String label,
final EasyFormComponent component) throws Exception {
if (getComponentMap().containsKey(label)) {
throw new Exception(
String.format("EasyForm already contains component with such label: %s.", label));
} else {
getComponentMap().put(label, component);
}
return component;
}
public Map<String, EasyFormComponent> getComponentMap() {
return componentMap;
}
public DOMWidget get(String key) {
return getComponentMap().get(key).getWidget();
}
public List<Widget> getCommFunctionalities() {
return componentMap.values().stream().map(EasyFormComponent::getWidget).collect(Collectors.toList());
}
public boolean hasComponents() {
return getComponentMap().size() > 0;
}
public boolean hasSaveValuesButton() {
return this.saveValuesButton != null;
}
public boolean hasLoadValuesButton() {
return this.loadValuesButton != null;
}
public SaveValuesButton getSaveValuesButton() {
return saveValuesButton;
}
public LoadValuesButton getLoadValuesButton() {
return loadValuesButton;
}
public String getCaption() {
return caption;
}
private HashMap<String, Object> getValuesMap() {
return this.mapInstance;
}
@Override
public String get(final Object key) {
checkComponentExists((String) key);
return getComponentMap().get(key).getValue();
}
@Override
public String put(final String key, final Object value) {
checkComponentExists(key);
final EasyFormComponent component = getComponentMap().get(key);
if (!component.checkValue(value)) {
throw new IllegalArgumentException(
String.format("\"%s\" is not a valid option for %s \"%s\".",
value, component.getClass().getSimpleName(), key));
}
final String currentValue = component.formatValue(value);
final String previousValue = component.getValue();
component.setValue(currentValue);
getValuesMap().put(key, currentValue);
setChanged();
notifyObservers();
component.fireChanged();
return previousValue;
}
private void checkComponentExists(final String key) {
if (!componentExists(key)) {
throw new IllegalArgumentException(
String.format("The requested component \"%s\" does not exist.", key));
}
}
private boolean componentExists(final String key) {
return getComponentMap().containsKey(key);
}
public void setEnabled(final String label, final Boolean enabled) {
if (StringUtils.isNotEmpty(label) && componentMap.containsKey(label)) {
componentMap.get(label).setEnabled(enabled);
setChanged();
notifyObservers();
}
}
public Boolean isReady() {
return ready;
}
public void setReady() {
this.ready = Boolean.TRUE;
for (EasyFormComponent component : getComponentMap().values()) {
if (!component.isButton()) {
getValuesMap().put(component.getLabel(), component.getValue());
}
component.fireInit();
}
}
public void setNotReady() {
this.ready = Boolean.FALSE;
}
@Override
public void display() {
DisplayEasyForm.display(this);
}
}