All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jgoodies.validation.ValidationResult Maven / Gradle / Ivy

/*
 * Copyright (c) 2003-2012 JGoodies Karsten Lentzsch. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  o Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  o Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 *  o Neither the name of JGoodies Karsten Lentzsch nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.jgoodies.validation;

import static com.jgoodies.common.base.Preconditions.checkArgument;
import static com.jgoodies.common.base.Preconditions.checkNotNull;

import java.io.Serializable;
import java.util.*;

import com.jgoodies.validation.message.SimpleValidationMessage;

/**
 * Describes a validation result as a list of ValidationMessages.
 * You can add single validation messages, single text messages,
 * lists of messages, and all messages from another ValidationResult.
 *
 * @author  Karsten Lentzsch
 * @version $Revision: 1.24 $
 *
 * @see     ValidationMessage
 * @see     Validator
 */
public final class ValidationResult implements Iterable,
    Serializable {

    /**
     * A constant for an empty and unmodifiable validation result.
     */
    public static final ValidationResult EMPTY =
        new ValidationResult(Collections.emptyList(), false);

    /**
     * Holds a List of ValidationMessages.
     */
    private final List messageList;

    /**
     * Describes if this result can be modified or not.
     */
    private final boolean modifiable;


    // Instance Creation ******************************************************

    /**
     * Constructs an empty modifiable ValidationResult.
     */
    public ValidationResult() {
        this(new ArrayList(), true);
    }


    /**
     * Constructs a ValidationResult on the given message list.
     * Used for constructing the {@code EMPTY} validation result.
     *
     * @param messageList   an initial message list
     * @param modifiable    true to allow modifications, false to prevent them
     */
    private ValidationResult(List messageList, boolean modifiable) {
        this.messageList = messageList;
        this.modifiable  = modifiable;
    }


    /**
     * Returns an unmodifiable view of the given ValidationResult.
     * Useful to provide users with "read-only" access to internal results,
     * or to indicate to other validation result processors that a result
     * is not intended to be modified. Attempts to modify the returned
     * validation result throw an {@code UnsupportedOperationException}.
     *
     * @param validationResult  the result for which an unmodifiable view is to be returned
     * @return an unmodifiable view of the specified validation result
     */
    public static ValidationResult unmodifiableResult(ValidationResult validationResult) {
        return validationResult.modifiable
            ? new ValidationResult(new ArrayList(validationResult.messageList),
                                   false)
            : validationResult;
    }


    // Adding Messages ********************************************************

    /**
     * Adds a new ValidationMessage to the list of messages.
     *
     * @param validationMessage   the message to add
     *
     * @throws NullPointerException           if the message is {@code null}
     * @throws UnsupportedOperationException  if the result is unmodifiable
     * @throws IllegalArgumentException       if the severity is {@code OK}
     *
     * @see #addError(String)
     * @see #addWarning(String)
     */
    public void add(ValidationMessage validationMessage) {
        checkModifiable();
        checkNotNull(validationMessage, "The validation message must not be null.");
        checkArgument(validationMessage.severity() != Severity.OK,
            "You must not add a validation message with severity OK.");
        messageList.add(validationMessage);
    }


    /**
     * Creates and adds an error message to the list of validation messages
     * using the given text.
     *
     * @param text   the error text to add
     *
     * @throws NullPointerException if the message text {@code null}
     * @throws UnsupportedOperationException  if the result is unmodifiable
     *
     * @see #add(ValidationMessage)
     * @see #addWarning(String)
     */
    public void addError(String text) {
        addError(text, null);
    }


    /**
     * Creates and adds an error message to the list of validation messages
     * using the given text and validation message key.
     *
     * @param text   the error text to add
     * @param key    the optional messages key
     *
     * @throws NullPointerException if the message text {@code null}
     * @throws UnsupportedOperationException  if the result is unmodifiable
     *
     * @see #add(ValidationMessage)
     * @see #addWarning(String)
     *
     * @since 2.3
     */
    public void addError(String text, String key) {
        checkModifiable();
        add(new SimpleValidationMessage(text, Severity.ERROR, key));
    }


    /**
     * Creates and adds a warning message to the list of validation messages
     * using the given text.
     *
     * @param text   the warning text to add
     *
     * @throws NullPointerException if the message text {@code null}
     * @throws UnsupportedOperationException  if the result is unmodifiable
     *
     * @see #add(ValidationMessage)
     * @see #addError(String)
     */
    public void addWarning(String text) {
        addWarning(text, null);
    }


    /**
     * Creates and adds a warning message to the list of validation messages
     * using the given text.
     *
     * @param text   the warning text to add
     * @param key    the optional message key
     *
     * @throws NullPointerException if the message text {@code null}
     * @throws UnsupportedOperationException  if the result is unmodifiable
     *
     * @see #add(ValidationMessage)
     * @see #addError(String)
     *
     * @since 2.3
     */
    public void addWarning(String text, String key) {
        checkModifiable();
        add(new SimpleValidationMessage(text, Severity.WARNING, key));
    }


    /**
     * Creates and adds an info message to the list of validation messages
     * using the given text.
     *
     * @param text   the info text to add
     *
     * @throws NullPointerException if the message text {@code null}
     * @throws UnsupportedOperationException  if the result is unmodifiable
     *
     * @see #add(ValidationMessage)
     */
    public void addInfo(String text) {
        addInfo(text, null);
    }


    /**
     * Creates and adds a info message to the list of validation messages
     * using the given text and key.
     *
     * @param text   the info text to add
     * @param key    the optional message key
     *
     * @throws NullPointerException if the message text {@code null}
     * @throws UnsupportedOperationException  if the result is unmodifiable
     *
     * @see #add(ValidationMessage)
     */
    public void addInfo(String text, String key) {
        checkModifiable();
        add(new SimpleValidationMessage(text, Severity.INFO, key));
    }


    /**
     * Adds all messages from the given list to this validation result.
     *
     * @param messages  the messages to be added
     *
     * @throws NullPointerException           if the messages list is {@code null}
     * @throws UnsupportedOperationException  if the result is unmodifiable
     * @throws IllegalArgumentException       if the messages list contains
     *     a message with severity {@code OK}
     *
     * @see #addAllFrom(ValidationResult)
     */
    public void addAll(List messages) {
        checkModifiable();
        checkNotNull(messages, "The messages list must not be null.");
        for (ValidationMessage message : messages) {
            checkArgument(message.severity() != Severity.OK,
                    "You must not add a validation message with severity OK.");
        }
        messageList.addAll(messages);
    }


    /**
     * Adds all messages from the given ValidationResult
     * to the list of messages that this validation result holds.
     *
     * @param validationResult  the validation result to add messages from
     *
     * @throws NullPointerException if the validation result is {@code null}
     * @throws UnsupportedOperationException  if the result is unmodifiable
     *
     * @see #addAll(List)
     */
    public void addAllFrom(ValidationResult validationResult) {
        checkModifiable();
        checkNotNull(validationResult, "The validation result to add must not be null.");
        addAll(validationResult.messageList);
    }


    // List Operations ********************************************************

    /**
     * Checks and answers whether this validation result contains no messages.
     *
     * @return true if this validation result contains no messages
     *
     * @see #hasErrors()
     * @see #hasWarnings()
     */
    public boolean isEmpty() {
        return messageList.isEmpty();
    }


    /**
     * Returns the number of messages in this result.
     *
     * @return the number of elements in this list
     */
    public int size() {
        return messageList.size();
    }


    /**
     * Checks and answers whether this result contains the specified message.
     * More formally, returns {@code true} if and only if this result
     * contains at least one message {@code m} such that
     * {@code (message.equals(m))}.
     *
     * @param message message whose presence in this result is to be tested
     * @return {@code true} if this result contains the specified message
     * @throws NullPointerException if the specified message is
     *         {@code null}
     */
    public boolean contains(ValidationMessage message) {
        return messageList.contains(message);
    }


    /**
     * Returns the message at the specified position in this result.
     *
     * @param index index of the message to return.
     * @return the ValidationMessage at the specified position in this result's
     *    underlying message list.
     *
     * @throws IndexOutOfBoundsException if the index is out of range (index
     *        < 0 || index >= size()).
     *
     * @since 2.1.0
     */
    public ValidationMessage get(int index) {
        return messageList.get(index);
    }


    /**
     * Returns an iterator over the validation messages in this result
     * in proper sequence.
     *
     * @return an iterator over the ValidationMessages in this result
     *     in proper sequence.
     *
     * @since 2.1.0
     */
    @Override
	public Iterator iterator() {
        return messageList.iterator();
    }


    /**
     * Returns an unmodifiable view of the portion of this result between
     * the specified {@code fromIndex}, inclusive, and {@code toIndex},
     * exclusive.
     * (If {@code fromIndex} and {@code toIndex} are equal,
     * the returned result is empty.)  The returned result is a copy,
     * so changes in the returned result won't affect this result,
     * and vice-versa.
     *
     * @param fromIndex  low end point (inclusive) of the subResult
     * @param toIndex    high end point (exclusive) of the subResult
     * @return a view of the specified range within this result.
     *
     * @throws IndexOutOfBoundsException for an illegal end point index value
     *     (fromIndex < 0 || toIndex > size || fromIndex > toIndex).
     *
     * @see #subResult(Object)
     */
    public ValidationResult subResult(int fromIndex, int toIndex) {
        List messages = messageList.subList(fromIndex, toIndex);
        return new ValidationResult(messages, false);
    }


    /**
     * Returns an unmodifiable sub result of this result that consists of
     * all messages that share the specified message key. If the specified key
     * is {@code null}, this method returns an empty result.
     * The returned result is a copy, so changes in this result won't affect it.
     *
     * @param messageKey    the key to look for, can be {@code null}
     * @return a sub result containing all messages that share the
     *     specified key, or the empty result if the key is {@code null}
     *
     * @see #subResult(int, int)
     */
    public ValidationResult subResult(Object messageKey) {
        if (messageKey == null) {
            return EMPTY;
        }
        List messages = new ArrayList();
        for (ValidationMessage message : messageList) {
            if (messageKey.equals(message.key())) {
                messages.add(message);
            }
        }
        return new ValidationResult(messages, false);
    }


    /**
     * Returns an unmodifiable sub result of this result that consists of
     * all messages that share the specified message keys. If the array of keys
     * is {@code null}, this method returns an empty result.
     * The returned result is a copy, so changes in this result won't affect it.
     *
     * @param messageKeys    the keys to look for, can be {@code null}
     * @return a sub result containing all messages that share the specified
     * keys, or the empty result if the key array is {@code null}
     *
     * @see #subResult(int, int)
     *
     * @since 1.4
     */
    public ValidationResult subResult(Object[] messageKeys) {
        if (messageKeys == null) {
            return EMPTY;
        }
        List messages = new ArrayList();
        for (ValidationMessage message : messageList) {
            Object messageKey = message.key();
            for (Object key : messageKeys) {
                if (messageKey.equals(key)) {
                    messages.add(message);
                }
            }
        }
        return new ValidationResult(messages, false);
    }


    /**
     * Creates and returns an unmodifiable Map that maps the message keys
     * of this validation result to unmodifiable sub results that share the key.

* * More formally: * for each key {@code key} in the created map {@code map}, * {@code map.get(key)} returns a {@code ValidationResult} * {@code result}, such that for each {@code ValidationMessage} * {@code message} in {@code result} we have: * {@code message.key().equals(key)}. * * @return a mapping from message key to an associated validation result * that consist only of messages with this key * * @see ValidationMessage#key() */ public Map keyMap() { Map> messageMap = new HashMap>(); for (ValidationMessage message : messageList) { Object key = message.key(); List associatedMessages = messageMap.get(key); if (associatedMessages == null) { associatedMessages = new LinkedList(); messageMap.put(key, associatedMessages); } associatedMessages.add(message); } Map resultMap = new HashMap(messageMap.size()); for (Map.Entry> entry : messageMap.entrySet()) { Object key = entry.getKey(); List messages = entry.getValue(); resultMap.put(key, new ValidationResult(messages, false)); } return Collections.unmodifiableMap(resultMap); } // Requesting Information ************************************************* /** * Returns the highest severity of this result's messages, * {@code Severity.OK} if there are no messages. * * @return the highest severity of this result's messages, * {@code Severity.OK} if there are no messages * * @see #hasMessages() * @see #hasErrors() * @see #hasWarnings() */ public Severity getSeverity() { return getSeverity(messageList); } /** * Checks and answers whether this validation result has messages or not. * * @return true if there are messages, false if not * * @see #getSeverity() * @see #hasErrors() * @see #hasWarnings() */ public boolean hasMessages() { return !isEmpty(); } /** * Checks and answers whether this validation result * contains a message of type {@code ERROR}. * * @return true if there are error messages, false if not * * @see #getSeverity() * @see #hasMessages() * @see #hasWarnings() */ public boolean hasErrors() { return hasSeverity(messageList, Severity.ERROR); } /** * Checks and answers whether this validation result * contains a message of type {@code WARNING}.

* * Note that this method checks for warning messages only. * It'll return false, if there are errors but no warnings. * If you want to test whether this result contains * warning and/or errors, use {@code #hasMessages} instead. * * @return true if there are warnings, false if not * * @see #getSeverity() * @see #hasMessages() * @see #hasErrors() */ public boolean hasWarnings() { return hasSeverity(messageList, Severity.WARNING); } /** * Checks and answers whether this validation result * contains a message of type {@code INFO}. * * @return true if there are info messages, false if not * * @see #getSeverity() * @see #hasMessages() * @see #hasErrors() * @see #hasWarnings() */ public boolean hasInfos() { return hasSeverity(messageList, Severity.INFO); } /** * Returns an unmodifiable List of all validation messages. * * @return the {@code List} of all validation messages * * @see #getErrors() * @see #getWarnings() */ public List getMessages() { return Collections.unmodifiableList(messageList); } /** * Returns an unmodifiable List of the validation messages * that indicate errors. * * @return the List of error validation messages * * @see #getMessages() * @see #getWarnings() */ public List getErrors() { return getMessagesWithSeverity(messageList, Severity.ERROR); } /** * Returns an unmodifiable List of the validation messages * that indicate warnings. * * @return the List of validation warnings * * @see #getMessages() * @see #getErrors() */ public List getWarnings() { return getMessagesWithSeverity(messageList, Severity.WARNING); } /** * Returns an unmodifiable List of the validation messages * that indicate informations. * * @return the List of info validation messages * * @see #getMessages() * @see #getErrors() * @see #getWarnings() */ public List getInfos() { return getMessagesWithSeverity(messageList, Severity.INFO); } // Requesting State ******************************************************* /** * Returns if this validation result is modifiable or not. * Can be used to cache data from unmodifiable result. * * @return true if modifiable, false if unmodifiable */ public boolean isModifiable() { return modifiable; } // String Conversion ****************************************************** /** * Returns a string representation of the message list. * * @return a string representation of the message list */ public String getMessagesText() { return getMessagesText(messageList); } /** * Returns a string representation intended for debugging purposes. * * @return a string representation intended for debugging * @see Object#toString() */ @Override public String toString() { if (isEmpty()) { return "Empty ValidationResult"; } StringBuilder builder = new StringBuilder(); builder.append(modifiable ? "Modifiable" : "Unmodifiable"); builder.append(" ValidationResult:"); for (ValidationMessage message : messageList) { builder.append("\n\t").append(message); } return builder.toString(); } // Comparison and Hashing ************************************************* /** * Compares the specified object with this validation result for equality. * Returns {@code true} if and only if the specified object is also * a validation result, both results have the same size, and all * corresponding pairs of validation messages in the two validation results * are equal. (Two validation messages {@code m1} and * {@code m2} are equal if {@code (m1==null ? m2==null : * m1.equals(m2))}.) In other words, two validation results * are defined to be equal if and only if they contain the same * validation messages in the same order.

* * This implementation first checks if the specified object is this * validation result. If so, it returns {@code true}; * if not, it checks if the specified object is a validation result. * If not, it returns {@code false}; if so, it checks and returns * if the lists of messages in both results are equal. * * @param o the object to be compared for equality with this validation result. * * @return {@code true} if the specified object is equal * to this validation result. * * @see List#equals(java.lang.Object) * @see Object#equals(java.lang.Object) */ @Override public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof ValidationResult)) { return false; } return messageList.equals(((ValidationResult) o).messageList); } /** * Returns the hash code value for this validation result. This * implementation returns the hash from the List of messages. * * @return the hash code value for this validation result. * * @see List#hashCode() * @see Object#hashCode() */ @Override public int hashCode() { return messageList.hashCode(); } // Helper Code ************************************************************ /** * Throws an {@link UnsupportedOperationException} if this * completion manager is unmodifiable. */ private void checkModifiable() { if (!modifiable) { throw new UnsupportedOperationException( "This validation result is unmodifiable."); } } /** * Returns the highest severity of this result's messages, * {@code Severity.OK} if there are no messages. * A single validation message can have only the severity * error or warning. Hence, this method returns the error severity * if there's at least one error message; and it returns * the warning severity, otherwise - assuming that there are * no other severities.

* * @param messages the List of ValidationMessages to check * @return the highest severity of this result's messages, * {@code Severity.OK} if there are no messages */ private static Severity getSeverity(List messages) { Severity severity = Severity.OK; for (ValidationMessage message : messages) { severity = Severity.max(severity, message.severity()); if (severity.ordinal() == 0) { // we found the maximum severity. break; } } return severity; } /** * Checks and answers whether the given list of validation messages * includes message with the specified Severity. * * @param messages the List of ValidationMessages to check * @param severity the Severity to check * @return true if the given messages list includes error messages, * false if not */ private static boolean hasSeverity( List messages, Severity severity) { for (ValidationMessage message : messages) { if (message.severity() == severity) { return true; } } return false; } /** * Returns an unmodifiable List of ValidationMessage that * that is the sublist of message with the given Severity. * * @param messages the List of ValidationMessages to iterate * @param severity the Severity to look for * @return the sublist of error messages */ private static List getMessagesWithSeverity( List messages, Severity severity) { List errorMessages = new ArrayList(); for (ValidationMessage message : messages) { if (message.severity() == severity) { errorMessages.add(message); } } return Collections.unmodifiableList(errorMessages); } /** * Returns a string representation of the given list of messages. * * @param messages the List of ValidationMessages to iterate * @return a string representation of the given list of messages */ private static String getMessagesText(List messages) { if (messages.isEmpty()) { return "OK"; } StringBuilder builder = new StringBuilder(); for (ValidationMessage message : messages) { if (builder.length() > 0) { builder.append('\n'); } builder.append(message.formattedText()); } return builder.toString(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy