package org.ovirt.engine.core.bll;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.ovirt.engine.core.common.errors.EngineMessage;
import org.ovirt.engine.core.common.utils.ToStringBuilder;
/**
* Used to represent the validation result for a "Can Do Action" validation operation, which can either return that the
* validation succeeded, or that it failed with a specific message.<br>
* This result can then be parsed by "Can Do Action" to decide if it should proceed or not.
*/
public final class ValidationResult {
/**
* A single instance for cases in which the outcome of the validation is ok.
*/
public static final ValidationResult VALID = new ValidationResult();
/**
* In case the validation succeeded it is {@code null}, otherwise it contains the validation failure message.
*/
private final List<EngineMessage> messages = new ArrayList<>();
/**
* If there are any replacements for variables in the message, they can be set here.
*/
private final List<String> variableReplacements;
/**
* Default validation result is success with no message.
* This constructor is private, it is only used to create a 'valid' result. Please use {@link ValidationResult#VALID}
*/
private ValidationResult() {
variableReplacements = Collections.emptyList();
}
/**
* Validation result for failure with given messages.
*
* @param messages
* The validation failure messages.
* @param variableReplacements
* Replacements for variables that appear in the messages, in syntax: "$var text" where $var is the
* variable to be replaced, and the text is the replacement.
*/
public ValidationResult(List<EngineMessage> messages, String... variableReplacements) {
if (messages == null || messages.isEmpty()) {
throw new IllegalArgumentException("messages must not be empty");
}
this.messages.addAll(messages);
this.variableReplacements = variableReplacements == null || variableReplacements.length == 0 ?
Collections.emptyList() :
Collections.unmodifiableList(Arrays.asList(variableReplacements));
}
/**
* Validation result for failure with a given message.
*
* @param message
* The validation failure message.
* @param variableReplacements
* Replacements for variables that appear in the message, in syntax: "$var text" where $var is the
* variable to be replaced, and the text is the replacement.
*/
public ValidationResult(EngineMessage message, String... variableReplacements) {
this(Collections.singletonList(message), variableReplacements);
if (message == null) {
throw new IllegalArgumentException("message must not be null");
}
}
/**
* Validation result for failure with a given message.
*
* @param message
* The validation failure message.
* @param variableReplacements
* Replacements for variables that appear in the message, in syntax: "$var text" where $var is the
* variable to be replaced, and the text is the replacement.
*/
public ValidationResult(EngineMessage message, Collection<String> variableReplacements) {
this(message, variableReplacements.toArray(new String[variableReplacements.size()]));
}
/**
* @return Did the validation succeed or not?
*/
public boolean isValid() {
return messages.isEmpty();
}
/**
* @return an empty {@code List} in case the validation succeeded, otherwise the validation failure messages
*/
public List<EngineMessage> getMessages() {
return messages;
}
/**
* @return an empty {@code List} in case the validation succeeded, otherwise the validation
* failure messages as Strings
*/
public List<String> getMessagesAsStrings() {
return messages.stream().map(Enum::name).collect(Collectors.toList());
}
/**
* @return {@code null} in case there are no replacements, otherwise a list of the replacements for message
* variables.
*/
public List<String> getVariableReplacements() {
return variableReplacements;
}
@Override
public int hashCode() {
return Objects.hash(
messages,
variableReplacements
);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof ValidationResult)) {
return false;
}
ValidationResult other = (ValidationResult) obj;
return Objects.equals(messages, other.messages)
&& Objects.equals(variableReplacements, other.variableReplacements);
}
@Override
public String toString() {
return ToStringBuilder.forInstance(this)
.append("messages", messages.toArray())
.append("variableReplacements", getVariableReplacements())
.build();
}
/**
* Return an error if the following validation is not successful, or a valid result if it is.<br>
* <br>
* For example, if we want to make sure that <b>num doesn't equal 2</b> then we would do:
*
* <pre>
* int num = 1;
* ValidationResult.failWith(EngineMessage.ERROR_CONST).when(num == 2);
* </pre>
*
* <br>
* Which would return a valid result since 1 != 2.<br>
* If we were to set <b>num = 2</b> in the example then the desired validation error would return.<br>
* <br>
* Conveniently, we can also check that <b>num equals 2</b> by doing:
*
* <pre>
* int num = 1;
* ValidationResult.failWith((EngineMessage.ERROR_CONST, "$COMPARED_NUM 2").unless(num == 2);
* </pre>
*
* <br>
* This time the desired validation error would return since 1 != 2.<br>
* If we were to set <b>num = 2</b> in the example then the result would be valid.<br>
* In addition, the replacement will contain the substitutions for the message.<br>
*
* @param expectedError
* The error we expect should the validation fail.
* @param replacements
* The replacements to be associated with the validation result
* @return A helper object that returns the correct validation result depending on the condition.
*/
public static ValidationResultBuilder failWith(EngineMessage expectedError, String... replacements) {
return new ValidationResultBuilder(expectedError, replacements);
}
public static ValidationResultBuilder failWith(EngineMessage expectedError, Collection<String> replacements) {
return new ValidationResultBuilder(expectedError, replacements.toArray(new String[replacements.size()]));
}
/**
* Helper class to chain calls that produce a {@link ValidationResult}.
*/
public static class ValidationResultBuilder {
private ValidationResult expectedValidation;
private ValidationResultBuilder(EngineMessage expectedError, String... replacements) {
expectedValidation = new ValidationResult(expectedError, replacements);
}
/**
* Return the expected error when the condition occurs, or a valid result if it doesn't.<br>
* <br>
* For example, if we want to make sure that <b>num doesn't equal 2</b> then we would do:
* <pre>
* int num = 1;
* ValidationResult.failWith((EngineMessage.ERROR_CONST).when(num == 2);</pre>
* <br>
* Which would return a valid result since 1 != 2.<br>
* If we were to set <b>num = 2</b> in the example then the desired validation error would return.<br>
*
* @param conditionOccurs
* Indication if the condition for the validation occurs or not.
* @return The erroneous validation result if the condition occurs, or a valid result of it doesn't.
*/
public ValidationResult when(boolean conditionOccurs) {
return conditionOccurs ? expectedValidation : VALID;
}
/**
* Return the expected error unless the condition occurs, or a valid result if it does.<br>
* <br>
* For example, if we want to make sure that <b>num equals 2</b> then we would do:
* <pre>
* int num = 1;
* ValidationResult.failWith(EngineMessage.ERROR_CONST).unless(num == 2);</pre>
* <br>
* Which would return the desired validation error since 1 != 2.<br>
* If we were to set <b>num = 2</b> in the example then the result would be valid.<br>
*
* @param conditionOccurs
* Indication if the condition for the validation occurs or not.
* @return A valid result if the condition occurs, or the erroneous validation result if it doesn't.
*/
public ValidationResult unless(boolean conditionOccurs) {
return when(!conditionOccurs);
}
}
}