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

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

Go to download

The JGoodies Validation helps you validate user input in Swing apps and report validation errors and warnings. It has been designed to work with different architectures and programming flavors.

The newest version!
/*
 * Copyright (c) 2003-2014 JGoodies Software GmbH. 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 Software GmbH 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.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.jgoodies.common.base.Strings;
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 ********************************************************

    /**
     * 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 ValidationResult addError(String text) {
        return addError((Object) null, text);
    }


    /**
     * 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 is {@code null}
     * @throws UnsupportedOperationException  if the result is unmodifiable
     *
     * @see #add(ValidationMessage)
     * @see #addWarning(String)
     *
     * @since 2.3
     */
    public ValidationResult addError(String text, Object key) {
        return addError(key, text);
    }


    /**
     * Creates and adds an error message to the list of validation messages
     * using the given validation message key, (format) text and optional
     * format arguments.
     *
     * @param key    the key used to associate messages and components
     * @param text   the format text to add
     * @param args   optional text format arguments
     * 
     *
     * @throws NullPointerException if the message text is {@code null}
     * @throws IllegalArgumentException if the message text is empty or whitespace
     * @throws UnsupportedOperationException  if the result is unmodifiable
     *
     * @see #add(ValidationMessage)
     * @see #addWarning(String)
     *
     * @since 2.6
     */
    private ValidationResult addError(Object key, String text, Object... args) {
        checkModifiable();
        add(new SimpleValidationMessage(Strings.get(text, args), Severity.ERROR, key));
        return this;
    }


    /**
     * 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 ValidationResult addWarning(String text) {
        return addWarning((Object) null, text);
    }


    /**
     * 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 ValidationResult addWarning(String text, Object key) {
        return addWarning(key, text);
    }


    /**
     * Creates and adds a warning message to the list of validation messages
     * using the given validation message key, (format) text and optional
     * format arguments.
     *
     * @param key    the key used to associate messages and components
     * @param text   the format text to add
     * @param args   optional text format arguments
     * 
     *
     * @throws NullPointerException if the message text is {@code null}
     * @throws IllegalArgumentException if the message text is empty or whitespace
     * @throws UnsupportedOperationException  if the result is unmodifiable
     *
     * @see #add(ValidationMessage)
     * @see #addError(String)
     *
     * @since 2.6
     */
    private ValidationResult addWarning(Object key, String text, Object... args) {
        checkModifiable();
        add(new SimpleValidationMessage(Strings.get(text, args), Severity.WARNING, key));
        return this;
    }


    /**
     * 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 ValidationResult addInfo(String text) {
        return addInfo((Object) null, text);
    }


    /**
     * Creates and adds an 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 ValidationResult addInfo(String text, Object key) {
        return addInfo(key, text);
    }


    /**
     * Creates and adds an info message to the list of validation messages
     * using the given validation message key, (format) text and optional
     * format arguments.
     *
     * @param key    the key used to associate messages and components
     * @param text   the format text to add
     * @param args   optional text format arguments
     * 
     *
     * @throws NullPointerException if the message text is {@code null}
     * @throws IllegalArgumentException if the message text is empty or whitespace
     * @throws UnsupportedOperationException  if the result is unmodifiable
     *
     * @see #add(ValidationMessage)
     * @see #addError(String)
     *
     * @since 2.6
     */
    private ValidationResult addInfo(Object key, String text, Object... args) {
        checkModifiable();
        add(new SimpleValidationMessage(Strings.get(text, args), Severity.INFO, key));
        return this;
    }


    // Adding Messages ********************************************************
    
    /**
     * Adds a new ValidationMessage to the list of messages.
     *
     * @param validationMessage   the message to add
     *
     * @return this validation result
     *
     * @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 ValidationResult 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);
        return this;
    }


    /**
     * 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 - 2024 Weber Informatics LLC | Privacy Policy