org.teamapps.ux.component.field.AbstractField Maven / Gradle / Ivy
/*-
* ========================LICENSE_START=================================
* TeamApps
* ---
* Copyright (C) 2014 - 2024 TeamApps.org
* ---
* 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.
* =========================LICENSE_END==================================
*/
package org.teamapps.ux.component.field;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.teamapps.dto.UiEvent;
import org.teamapps.dto.UiField;
import org.teamapps.event.Event;
import org.teamapps.ux.component.AbstractComponent;
import org.teamapps.ux.component.field.validator.FieldValidator;
import org.teamapps.ux.i18n.TeamAppsDictionary;
import org.teamapps.ux.session.CurrentSessionContext;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
public abstract class AbstractField extends AbstractComponent {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractField.class);
private final FieldValidator requiredValidator = (value) ->
this.isEmptyValue(value) ? Collections.singletonList(new FieldMessage(FieldMessage.Severity.ERROR,
CurrentSessionContext.get().getLocalized(TeamAppsDictionary.REQUIRED_FIELD.getKey()))) : List.of();
private final FieldValidator requiredIfVisibleAndEditableValidator = (value) ->
(this.isVisible() && (this.getEditingMode() == FieldEditingMode.EDITABLE || this.getEditingMode() == FieldEditingMode.EDITABLE_IF_FOCUSED) && this.isEmptyValue(value)) ?
Collections.singletonList(new FieldMessage(FieldMessage.Severity.ERROR,
CurrentSessionContext.get().getLocalized(TeamAppsDictionary.REQUIRED_FIELD.getKey()))) : List.of();
public final Event onFocus = new Event<>();
public final Event onBlur = new Event<>();
public final Event onValueChanged = new Event<>();
public final Event onVisibilityChanged = new Event<>();
private FieldEditingMode editingMode = FieldEditingMode.EDITABLE;
private final Set> validators = new HashSet<>();
private final Map, List> fieldMessagesByValidator = new HashMap<>(); // null key for custom field messages (not bound to a validator)
private FieldMessage.Position defaultMessagePosition = FieldMessage.Position.BELOW;
private FieldMessage.Visibility defaultMessageVisibility = FieldMessage.Visibility.ALWAYS_VISIBLE;
private final MultiWriteLockableValue value = new MultiWriteLockableValue<>(null);
private boolean valueChangedByClient;
public FieldEditingMode getEditingMode() {
return editingMode;
}
public void setEditingMode(FieldEditingMode editingMode) {
this.editingMode = editingMode;
queueCommandIfRendered(() -> new UiField.SetEditingModeCommand(getId(), editingMode.toUiFieldEditingMode()));
}
public void setVisible(boolean visible) {
super.setVisible(visible);
onVisibilityChanged.fire(visible);
}
public void focus() {
render();
queueCommandIfRendered(() -> new UiField.FocusCommand(getId()));
}
protected void mapAbstractFieldAttributesToUiField(UiField uiField) {
mapAbstractUiComponentProperties(uiField);
uiField.setValue(convertUxValueToUiValue(this.value.read()));
uiField.setEditingMode(editingMode.toUiFieldEditingMode());
uiField.setFieldMessages(getFieldMessages().stream()
.map(message -> message.createUiFieldMessage(defaultMessagePosition, defaultMessageVisibility))
.collect(Collectors.toList()));
}
public void setValue(VALUE value) {
valueChangedByClient = false;
MultiWriteLockableValue.Lock lock = this.value.writeAndLock(value);
Object uiValue = this.convertUxValueToUiValue(value);
if (isRendered()) {
getSessionContext().queueCommand(new UiField.SetValueCommand(getId(), uiValue), aVoid -> lock.release());
} else {
lock.release();
}
}
protected MultiWriteLockableValue.Lock setAndLockValue(VALUE value) {
return this.value.writeAndLock(value);
}
/**
* Converts a server-side value to a client-side field-specific value.
* Implementations must not have any side effects to the component!
*
* @param value the server-side value
* @return the object to be sent to the ui
*/
public Object convertUxValueToUiValue(VALUE value) {
return value;
}
public VALUE getValue() {
return value.read();
}
@Override
public void handleUiEvent(UiEvent event) {
switch (event.getUiEventType()) {
case UI_FIELD_VALUE_CHANGED:
if (editingMode.isEditable()) {
applyValueFromUi(((UiField.ValueChangedEvent) event).getValue());
validate();
} else {
LOGGER.warn("Got valueChanged event from non-editable field {} {}", this.getClass().getSimpleName(), getDebuggingId());
}
break;
case UI_FIELD_FOCUS_GAINED:
onFocus.fire();
break;
case UI_FIELD_BLUR:
onBlur.fire();
break;
}
}
protected void applyValueFromUi(Object value) {
if (!this.value.isLocked()) {
VALUE transformedValue = convertUiValueToUxValue(value);
if (!this.value.isLocked()) {
this.value.writeIfNotLocked(transformedValue);
valueChangedByClient = true;
onValueChanged.fire(transformedValue);
}
}
}
protected boolean isValueLocked() {
return this.value.isLocked();
}
public VALUE convertUiValueToUxValue(Object value) {
return (VALUE) value;
}
/**
* Whether this value be regarded as empty / "no user input".
* Override for field-specific behaviour.
*
* @return true if the value can be regarded as "empty".
*/
protected boolean isEmptyValue(VALUE value) {
return value == null;
}
public boolean isEmpty() {
return isEmptyValue(getValue());
}
public Collection> getValidators() {
return Collections.unmodifiableSet(validators);
}
public void addValidator(FieldValidator validator) {
validators.add(validator);
}
public void removeValidator(FieldValidator validator) {
validators.remove(validator);
fieldMessagesByValidator.remove(validator);
updateFieldMessages();
}
public List validate() {
List allValidatorMessages = new ArrayList<>();
if (validators.size() > 0) {
for (FieldValidator validator : validators) {
fieldMessagesByValidator.remove(validator);
List messages = validator.validate(getValue());
if (messages == null) {
messages = Collections.emptyList();
}
fieldMessagesByValidator.put(validator, messages);
allValidatorMessages.addAll(messages);
}
updateFieldMessages();
}
return allValidatorMessages;
}
public void clearValidatorMessages() {
fieldMessagesByValidator.clear();
updateFieldMessages();
}
/**
* field may not be null (empty)
*/
public void setRequired(boolean required) {
if (required) {
addValidator(requiredValidator);
} else {
removeValidator(requiredValidator);
}
}
public void setRequiredIfVisibleAndEditable(boolean required) {
if (required) {
addValidator(requiredIfVisibleAndEditableValidator);
} else {
removeValidator(requiredIfVisibleAndEditableValidator);
}
}
public boolean isRequired() {
return validators.contains(requiredValidator);
}
public List getFieldMessages() {
return fieldMessagesByValidator.values().stream()
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
public List getCustomFieldMessages() {
return fieldMessagesByValidator.computeIfAbsent(null, v -> new ArrayList<>());
}
public void setCustomFieldMessages(List fieldMessages) {
fieldMessagesByValidator.put(null, new ArrayList<>(fieldMessages));
updateFieldMessages();
}
public void addCustomFieldMessage(FieldMessage.Severity severity, String text) {
addCustomFieldMessage(new FieldMessage(severity, text));
}
public void addCustomFieldMessage(FieldMessage fieldMessage) {
getCustomFieldMessages().add(fieldMessage);
updateFieldMessages();
}
public void removeCustomFieldMessage(FieldMessage fieldMessage) {
getCustomFieldMessages().remove(fieldMessage);
updateFieldMessages();
}
public void clearCustomFieldMessages() {
getCustomFieldMessages().clear();
updateFieldMessages();
}
private void updateFieldMessages() {
queueCommandIfRendered(() -> new UiField.SetFieldMessagesCommand(getId(), getFieldMessages().stream()
.map(fieldMessage -> fieldMessage.createUiFieldMessage(defaultMessagePosition, defaultMessageVisibility))
.collect(Collectors.toList())));
}
public boolean isValid() {
return getMaxFieldMessageSeverity() != FieldMessage.Severity.ERROR;
}
public FieldMessage.Severity getMaxFieldMessageSeverity() {
return getFieldMessages().stream()
.map(fieldMessage -> fieldMessage.getSeverity())
.max(Comparator.comparing(severity -> severity.ordinal()))
.orElse(null);
}
public FieldMessage.Position getDefaultMessagePosition() {
return defaultMessagePosition;
}
public void setDefaultMessagePosition(FieldMessage.Position defaultMessagePosition) {
this.defaultMessagePosition = defaultMessagePosition;
}
public FieldMessage.Visibility getDefaultMessageVisibility() {
return defaultMessageVisibility;
}
public void setDefaultMessageVisibility(FieldMessage.Visibility defaultMessageVisibility) {
this.defaultMessageVisibility = defaultMessageVisibility;
}
public boolean isValueChangedByClient() {
return valueChangedByClient;
}
public void setValueChangedByClient(boolean valueChangedByClient) {
this.valueChangedByClient = valueChangedByClient;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy