001    
002    /*
003     * Copyright (C) 2011 Archie L. Cobbs. All rights reserved.
004     *
005     * $Id: SortedValidator.java 144 2011-10-13 22:31:52Z archie.cobbs $
006     */
007    
008    package org.dellroad.stuff.validation;
009    
010    import java.util.Arrays;
011    import java.util.Collection;
012    import java.util.Comparator;
013    import java.util.Map;
014    
015    import javax.validation.ConstraintValidatorContext;
016    
017    /**
018     * Validator for the @{@link Sorted} constraint.
019     *
020     * @see Sorted
021     */
022    public class SortedValidator extends AbstractValidator<Sorted, Object> {
023    
024        @Override
025        @SuppressWarnings("unchecked")
026        public boolean isValid(Object value, ConstraintValidatorContext context) {
027    
028            // Ignore null values
029            if (value == null)
030                return true;
031    
032            // Value must be an array, collection or map
033            Iterable<?> iterable;
034            if (value instanceof Object[])
035                iterable = Arrays.asList((Object[])value);
036            else if (value instanceof Collection)
037                iterable = (Collection<?>)value;
038            else if (value instanceof Map)
039                iterable = ((Map<?, ?>)value).keySet();
040            else {
041                this.setViolation(context, "@Sorted constraint only applies to non-primitive arrays, collections and maps");
042                return false;
043            }
044    
045            // Get comparator
046            Comparator comparator;
047            if (this.annotation.comparator() == Comparator.class) {
048                comparator = new Comparator() {
049                    @Override
050                    public int compare(Object x, Object y) {
051                        return ((Comparable)x).compareTo(y);
052                    }
053                };
054            } else {
055                try {
056                    comparator = this.annotation.comparator().newInstance();
057                } catch (Exception e) {
058                    this.setViolation(context, "Cannot instantiate comparator for @Sorted constraint: " + e);
059                    return false;
060                }
061            }
062    
063            // Check sorted-ness
064            Object prev = null;
065            int index = -1;
066            for (Object next : iterable) {
067                index++;
068                if (prev == null) {
069                    prev = next;
070                    continue;
071                }
072                if (next == null)
073                    continue;
074                int diff;
075                try {
076                    diff = comparator.compare(prev, next);
077                } catch (ClassCastException e) {
078                    this.setViolation(context, "@Sorted constraint only applies to Comparable elements: " + e);
079                    return false;
080                }
081                if (diff > 0 || (this.annotation.strict() && diff == 0)) {
082                    this.setViolation(context, "elements are not properly sorted (mis-ordered at index " + index + ")");
083                    return false;
084                }
085            }
086    
087            // Done
088            return true;
089        }
090    }
091