/
ValidateOrder.java
158 lines (132 loc) · 5.97 KB
/
ValidateOrder.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*
* Copyright OmniFaces
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.omnifaces.component.validator;
import static java.lang.String.format;
import static java.util.Arrays.asList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.TreeSet;
import java.util.function.Function;
import jakarta.faces.component.FacesComponent;
import jakarta.faces.component.UIInput;
import jakarta.faces.context.FacesContext;
import org.omnifaces.util.State;
import org.omnifaces.validator.MultiFieldValidator;
/**
* <p>
* The <code><o:validateOrder></code> validates if the values of the given {@link UIInput} components as specified
* in the <code>components</code> attribute are in the order as specified by the <code>type</code> attribute which
* accepts the following values:
* <ul>
* <li><code>lt</code> (default): from least to greatest, without duplicates.</li>
* <li><code>lte</code>: from least to greatest, allowing duplicates (equal values next to each other).</li>
* <li><code>gt</code>: from greatest to least, without duplicates.</li>
* <li><code>gte</code>: from greatest to least, allowing duplicates (equal values next to each other).</li>
* </ul>
* <p>
* This validator has the additional requirement that the to-be-validated values must implement {@link Comparable}.
* This validator throws an {@link IllegalArgumentException} when one or more of the values do not implement it. Note
* that when this validator is placed <em>before</em> all of the components, then it will only compare the raw
* unconverted submitted string values, not the converted object values. If you need to compare by the converted object
* values, then you need to place this validator <em>after</em> all of the components.
* <p>
* The default message is
* <blockquote>{0}: Please fill out the values of all those fields in order</blockquote>
* <p>
* For general usage instructions, refer {@link ValidateMultipleFields} documentation.
*
* @author Bauke Scholtz
* @see ValidateMultipleFields
* @see ValidatorFamily
* @see MultiFieldValidator
*/
@FacesComponent(ValidateOrder.COMPONENT_TYPE)
@SuppressWarnings({ "unchecked", "rawtypes" }) // We don't care about the actual Comparable type.
public class ValidateOrder extends ValidateMultipleFields {
// Public constants -----------------------------------------------------------------------------------------------
/** The component type, which is {@value org.omnifaces.component.validator.ValidateOrder#COMPONENT_TYPE}. */
public static final String COMPONENT_TYPE = "org.omnifaces.component.validator.ValidateOrder";
// Private constants ----------------------------------------------------------------------------------------------
private enum Type {
LT(values -> new ArrayList<>(new TreeSet<>(values)).equals(values)),
LTE(values -> {
List<Comparable> sortedValues = new ArrayList<>(values);
Collections.sort(sortedValues);
return sortedValues.equals(values);
}),
GT(values -> {
List<Comparable> sortedValues = new ArrayList<>(new TreeSet<>(values));
Collections.reverse(sortedValues);
return sortedValues.equals(values);
}),
GTE(values -> {
List<Comparable> sortedValues = new ArrayList<>(values);
Collections.sort(sortedValues, Collections.reverseOrder());
return sortedValues.equals(values);
});
private Function<List<Comparable>, Boolean> callback;
private Type(Function<List<Comparable>, Boolean> callback) {
this.callback = callback;
}
public boolean validateOrder(List<Comparable> values) {
return callback.apply(values);
}
}
private static final String DEFAULT_TYPE = Type.LT.name();
private static final String ERROR_INVALID_TYPE = "Invalid type '%s'. Only 'lt', 'lte', 'gt' and 'gte' are allowed.";
private static final String ERROR_VALUES_NOT_COMPARABLE = "All values must implement java.lang.Comparable.";
private enum PropertyKeys {
// Cannot be uppercased. They have to exactly match the attribute names.
type;
}
// Variables ------------------------------------------------------------------------------------------------------
private final State state = new State(getStateHelper());
// Actions --------------------------------------------------------------------------------------------------------
/**
* Validate if all values are in specified order.
*/
@Override
public boolean validateValues(FacesContext context, List<UIInput> components, List<Object> values) {
try {
Object tmp = values; // https://bugs.eclipse.org/bugs/show_bug.cgi?id=158870
List<Comparable> comparableValues = new ArrayList<>((List<Comparable>) tmp);
comparableValues.removeAll(asList(null, "")); // Empty checking job is up to required="true".
return Type.valueOf(getType().toUpperCase()).validateOrder(comparableValues);
}
catch (ClassCastException e) {
throw new IllegalArgumentException(ERROR_VALUES_NOT_COMPARABLE, e);
}
}
// Getters/setters ------------------------------------------------------------------------------------------------
/**
* Returns the ordering type to be used.
* @return The ordering type to be used.
*/
public String getType() {
return state.get(PropertyKeys.type, DEFAULT_TYPE);
}
/**
* Sets the ordering type to be used.
* @param type The ordering type to be used.
*/
public void setType(String type) {
try {
Type.valueOf(type.toUpperCase());
}
catch (IllegalArgumentException e) {
throw new IllegalArgumentException(format(ERROR_INVALID_TYPE, type), e);
}
state.put(PropertyKeys.type, type);
}
}