
org.nuiton.validator.bean.BeanValidatorContext Maven / Gradle / Ivy
The newest version!
package org.nuiton.validator.bean;
/*-
* #%L
* Validation :: API
* %%
* Copyright (C) 2021 - 2024 Ultreia.io
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* .
* #L%
*/
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.nuiton.validator.NuitonValidator;
import org.nuiton.validator.NuitonValidatorResult;
import org.nuiton.validator.NuitonValidatorScope;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* Created on 26/01/2024.
*
* @author Tony Chemit - [email protected]
* @since 2.0.0
*/
public class BeanValidatorContext {
private static final Logger log = LogManager.getLogger(BeanValidatorContext.class);
/**
* map of conversion errors detected by this validator
*/
protected final Map conversionErrors;
/**
* Bean to validate.
*/
protected O bean;
/**
* State of validation (keep all messages of validation for the filled
* bean).
*/
protected NuitonValidatorResult messages;
/**
* Validator.
*/
protected NuitonValidator validator;
/**
* State to know if the validator can be used (we keep this state for
* performance reasons : do not want to compute this value each time a
* validation is asked...).
*/
protected boolean canValidate;
public BeanValidatorContext() {
conversionErrors = new TreeMap<>();
}
public O getBean() {
return bean;
}
public void setBean(O bean) {
if (log.isDebugEnabled()) {
log.debug(this + " : " + bean);
}
// clean conversions of previous bean
conversionErrors.clear();
this.bean = bean;
setCanValidate(!validator.getEffectiveFields().isEmpty() && bean != null);
}
protected BeanValidatorEvent createEvent(BeanValidator source,
O bean,
String field,
NuitonValidatorScope scope,
String[] toAdd,
String[] toDelete) {
return new BeanValidatorEvent<>(
source,
field,
scope,
toAdd,
toDelete
);
}
public NuitonValidator getValidator() {
return validator;
}
public void setValidator(NuitonValidator validator) {
this.validator = validator;
}
public NuitonValidatorResult getMessages() {
return messages;
}
public boolean isCanValidate() {
return canValidate;
}
public void setCanValidate(boolean canValidate) {
this.canValidate = canValidate;
}
public boolean isValid() {
return messages == null || messages.isValid();
}
public boolean hasFatalErrors() {
return messages != null && messages.hasFatalMessages();
}
public boolean hasErrors() {
return messages != null && messages.hasErrorMessagess();
}
public boolean hasWarnings() {
return messages != null && messages.hasWarningMessages();
}
public boolean hasInfos() {
return messages != null && messages.hasInfoMessages();
}
public boolean isValid(String fieldName) {
// field is valid if no fatal messages nor error messages
return !(
messages.hasMessagesForScope(fieldName, NuitonValidatorScope.FATAL) ||
messages.hasMessagesForScope(fieldName, NuitonValidatorScope.ERROR));
}
public NuitonValidatorScope getHighestScope(String field) {
return messages.getFieldHighestScope(field);
}
public NuitonValidatorResult validate() {
NuitonValidatorResult result = validator.validate(bean, null);
// treate conversion errors
// reinject them
for (Map.Entry entry : conversionErrors.entrySet()) {
// remove from validation, errors occurs on this field
String field = entry.getKey();
List errors = result.getErrorMessages(field);
String conversionError = entry.getValue();
if (errors != null) {
errors.clear();
errors.add(conversionError);
} else {
errors = Collections.singletonList(conversionError);
}
result.setMessagesForScope(NuitonValidatorScope.ERROR, field, errors);
}
return result;
}
public List> mergeMessages(BeanValidator beanValidator,
NuitonValidatorResult newMessages) {
if (newMessages == null && messages == null) {
// no messages ever registered and ask to delete them, so nothing
// to do
return null;
}
Set scopes = getValidator().getEffectiveScopes();
// list of events to send after the merge of messages
List> events = new LinkedList<>();
for (NuitonValidatorScope scope : scopes) {
// do the merge at scope level
mergeMessages(beanValidator, scope, newMessages, events);
}
if (newMessages != null) {
//TODO tchemit 2011-01-23 Perhaps it will necessary to clear the messages for memory performance ?
// finally keep the new messages as the current messages
this.messages = newMessages;
}
return events;
}
protected void mergeMessages(BeanValidator beanValidator,
NuitonValidatorScope scope,
NuitonValidatorResult newMessages,
List> events) {
if (newMessages == null) {
// special case to empty all messages
List fieldsForScope = messages.getFieldsForScope(scope);
for (String field : fieldsForScope) {
List messagesForScope = messages.getMessagesForScope(field, scope);
events.add(createEvent(beanValidator, bean, field, scope, null, messagesForScope.toArray(new String[0])));
}
// suppress all messages for this scope
messages.clearMessagesForScope(scope);
} else {
List newFields = newMessages.getFieldsForScope(scope);
if (messages == null) {
// first time of a merge, just add new messages
for (String field : newFields) {
List messagesForScope = newMessages.getMessagesForScope(field, scope);
events.add(createEvent(beanValidator, bean, field, scope, messagesForScope.toArray(new String[0]), null));
}
// nothing else to do
return;
}
List oldFields = messages.getFieldsForScope(scope);
Iterator itr;
// detects field with only new messages
itr = newFields.iterator();
while (itr.hasNext()) {
String newField = itr.next();
if (!oldFields.contains(newField)) {
// this fields has now messages but not before : new messages
List messagesForScope = newMessages.getMessagesForScope(newField, scope);
events.add(createEvent(beanValidator, bean, newField, scope, messagesForScope.toArray(new String[0]), null));
// treated field
itr.remove();
}
}
// detects fields with only obsolete messages
itr = oldFields.iterator();
while (itr.hasNext()) {
String oldField = itr.next();
if (!newFields.contains(oldField)) {
// this fields has no more messages
List messagesForScope = messages.getMessagesForScope(oldField, scope);
events.add(createEvent(beanValidator, bean, oldField, scope, null, messagesForScope.toArray(new String[0])));
// treated field
itr.remove();
}
}
// now deal with mixte field (toAdd and toDelete)
for (String field : newFields) {
List newMessagesForScope = newMessages.getMessagesForScope(field, scope);
List oldMessagesForScope = messages.getMessagesForScope(field, scope);
// get old obsoletes messages to delete
Set toDelete = new HashSet<>(oldMessagesForScope);
newMessagesForScope.forEach(toDelete::remove);
// get new messages to add
Set toAdd = new HashSet<>(newMessagesForScope);
oldMessagesForScope.forEach(toAdd::remove);
events.add(createEvent(
beanValidator,
bean,
field,
scope,
toAdd.isEmpty() ? null : toAdd.toArray(new String[0]),
toDelete.isEmpty() ? null : toDelete.toArray(new String[0])
));
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy