6200 |
08 Apr 21 |
nicklas |
1 |
package net.sf.basedb.reggie.plugins.cmd; |
6200 |
08 Apr 21 |
nicklas |
2 |
|
6200 |
08 Apr 21 |
nicklas |
3 |
import java.text.DateFormat; |
6200 |
08 Apr 21 |
nicklas |
4 |
import java.text.SimpleDateFormat; |
6200 |
08 Apr 21 |
nicklas |
5 |
import java.util.Date; |
6200 |
08 Apr 21 |
nicklas |
6 |
|
6201 |
09 Apr 21 |
nicklas |
7 |
import net.sf.basedb.core.DbControl; |
6200 |
08 Apr 21 |
nicklas |
8 |
|
6201 |
09 Apr 21 |
nicklas |
9 |
|
6200 |
08 Apr 21 |
nicklas |
10 |
/** |
6200 |
08 Apr 21 |
nicklas |
Validator for Date values. Date values are represented |
6200 |
08 Apr 21 |
nicklas |
as strings in the JSON file. A date format string can be |
6200 |
08 Apr 21 |
nicklas |
specified for converting the string to a Date instance. |
7026 |
08 Feb 23 |
nicklas |
14 |
|
7026 |
08 Feb 23 |
nicklas |
Note that we still use the SimpleDateFormat class in this |
7026 |
08 Feb 23 |
nicklas |
implementation since we need the non-lenient support. Multi-threading |
7026 |
08 Feb 23 |
nicklas |
issues (see ticket #1454) are solved by synchronized blocks. |
7026 |
08 Feb 23 |
nicklas |
18 |
|
6200 |
08 Apr 21 |
nicklas |
@since 4.32 |
6200 |
08 Apr 21 |
nicklas |
20 |
*/ |
6200 |
08 Apr 21 |
nicklas |
21 |
public class DateValidator |
6206 |
12 Apr 21 |
nicklas |
22 |
implements ValueValidator<String, Date>, Cloneable |
6200 |
08 Apr 21 |
nicklas |
23 |
{ |
6200 |
08 Apr 21 |
nicklas |
24 |
/** |
6200 |
08 Apr 21 |
nicklas |
Date validator for dates in 'yyyy-MM-dd' format. |
6200 |
08 Apr 21 |
nicklas |
26 |
*/ |
6200 |
08 Apr 21 |
nicklas |
27 |
public static final DateValidator YYYY_MM_DD = new DateValidator("yyyy-MM-dd"); |
6200 |
08 Apr 21 |
nicklas |
28 |
|
6200 |
08 Apr 21 |
nicklas |
29 |
/** |
6217 |
19 Apr 21 |
nicklas |
Date validator for dates in 'yyyy-MM-dd HH:mm' format. |
6217 |
19 Apr 21 |
nicklas |
31 |
*/ |
6217 |
19 Apr 21 |
nicklas |
32 |
public static final DateValidator YYYY_MM_DD_HH_MM = new DateValidator("yyyy-MM-dd HH:mm"); |
6217 |
19 Apr 21 |
nicklas |
33 |
|
6217 |
19 Apr 21 |
nicklas |
34 |
/** |
6704 |
26 Apr 22 |
nicklas |
Date validator for dates in 'yyyy-MM-dd [HH:mm]' format (time is optional). |
6704 |
26 Apr 22 |
nicklas |
@since 4.39 |
6704 |
26 Apr 22 |
nicklas |
37 |
*/ |
6704 |
26 Apr 22 |
nicklas |
38 |
public static final DateValidator YYYY_MM_DD_OPT_HH_MM = new DateValidator("yyyy-MM-dd HH:mm", "yyyy-MM-dd"); |
6704 |
26 Apr 22 |
nicklas |
39 |
|
6704 |
26 Apr 22 |
nicklas |
40 |
/** |
6200 |
08 Apr 21 |
nicklas |
Date validator for dates in 'yyMMdd' format. |
6200 |
08 Apr 21 |
nicklas |
42 |
*/ |
6200 |
08 Apr 21 |
nicklas |
43 |
public static final DateValidator YYMMDD = new DateValidator("yyMMdd"); |
6200 |
08 Apr 21 |
nicklas |
44 |
|
6201 |
09 Apr 21 |
nicklas |
45 |
/** |
6201 |
09 Apr 21 |
nicklas |
Date+time validator for timestamps in 'yyMMdd HH:mm:ss' format. |
6201 |
09 Apr 21 |
nicklas |
47 |
*/ |
6201 |
09 Apr 21 |
nicklas |
48 |
public static final DateValidator YYMMDD_HH_MM_SS = new DateValidator("yyMMdd HH:mm:ss"); |
6201 |
09 Apr 21 |
nicklas |
49 |
|
6201 |
09 Apr 21 |
nicklas |
50 |
|
6200 |
08 Apr 21 |
nicklas |
51 |
private final String format; |
6200 |
08 Apr 21 |
nicklas |
52 |
private final DateFormat dateFormat; |
6704 |
26 Apr 22 |
nicklas |
53 |
private final DateFormat alternateDateFormat; |
6200 |
08 Apr 21 |
nicklas |
54 |
|
6206 |
12 Apr 21 |
nicklas |
55 |
private Date notAfter; |
6206 |
12 Apr 21 |
nicklas |
56 |
private Date notBefore; |
6206 |
12 Apr 21 |
nicklas |
57 |
|
6200 |
08 Apr 21 |
nicklas |
58 |
public DateValidator(String format) |
6200 |
08 Apr 21 |
nicklas |
59 |
{ |
6704 |
26 Apr 22 |
nicklas |
60 |
this(format, null); |
6704 |
26 Apr 22 |
nicklas |
61 |
} |
6704 |
26 Apr 22 |
nicklas |
62 |
|
6704 |
26 Apr 22 |
nicklas |
63 |
public DateValidator(String format, String alternateFormat) |
6704 |
26 Apr 22 |
nicklas |
64 |
{ |
6704 |
26 Apr 22 |
nicklas |
65 |
this.format = format+(alternateFormat == null ? "" : " or "+alternateFormat); |
6200 |
08 Apr 21 |
nicklas |
66 |
this.dateFormat = new SimpleDateFormat(format); |
6906 |
30 Nov 22 |
nicklas |
67 |
this.dateFormat.setLenient(false); |
6704 |
26 Apr 22 |
nicklas |
68 |
this.alternateDateFormat = alternateFormat == null ? null : new SimpleDateFormat(alternateFormat); |
6906 |
30 Nov 22 |
nicklas |
69 |
if (this.alternateDateFormat != null) this.alternateDateFormat.setLenient(false); |
6200 |
08 Apr 21 |
nicklas |
70 |
} |
6200 |
08 Apr 21 |
nicklas |
71 |
|
6217 |
19 Apr 21 |
nicklas |
72 |
@Override |
6217 |
19 Apr 21 |
nicklas |
73 |
protected DateValidator clone() |
6217 |
19 Apr 21 |
nicklas |
74 |
{ |
6217 |
19 Apr 21 |
nicklas |
75 |
try |
6217 |
19 Apr 21 |
nicklas |
76 |
{ |
6217 |
19 Apr 21 |
nicklas |
77 |
return (DateValidator)super.clone(); |
6217 |
19 Apr 21 |
nicklas |
78 |
} |
6217 |
19 Apr 21 |
nicklas |
79 |
catch (CloneNotSupportedException e) |
6217 |
19 Apr 21 |
nicklas |
80 |
{ |
6217 |
19 Apr 21 |
nicklas |
// Should never happen |
6217 |
19 Apr 21 |
nicklas |
82 |
throw new UnsupportedOperationException("clone()"); |
6217 |
19 Apr 21 |
nicklas |
83 |
} |
6217 |
19 Apr 21 |
nicklas |
84 |
} |
6217 |
19 Apr 21 |
nicklas |
85 |
|
6206 |
12 Apr 21 |
nicklas |
86 |
/** |
6206 |
12 Apr 21 |
nicklas |
Wrap this date validator with a validator that issue a warning |
6206 |
12 Apr 21 |
nicklas |
if the date is in the future. |
6206 |
12 Apr 21 |
nicklas |
89 |
*/ |
6206 |
12 Apr 21 |
nicklas |
90 |
public DateValidator warnIfFuture() |
6206 |
12 Apr 21 |
nicklas |
91 |
{ |
6217 |
19 Apr 21 |
nicklas |
92 |
DateValidator wrap = clone(); |
6919 |
01 Dec 22 |
nicklas |
93 |
wrap.notAfter = alignToFormat(new Date()); |
6206 |
12 Apr 21 |
nicklas |
94 |
return wrap; |
6206 |
12 Apr 21 |
nicklas |
95 |
} |
6206 |
12 Apr 21 |
nicklas |
96 |
|
6206 |
12 Apr 21 |
nicklas |
97 |
/** |
6206 |
12 Apr 21 |
nicklas |
Wrap this date validator with a validator that issue a warning |
6206 |
12 Apr 21 |
nicklas |
if the date is in the future or older than the specified number of |
6206 |
12 Apr 21 |
nicklas |
days. |
6206 |
12 Apr 21 |
nicklas |
101 |
*/ |
6206 |
12 Apr 21 |
nicklas |
102 |
public DateValidator warnIfFutureOrOlder(int numDays) |
6206 |
12 Apr 21 |
nicklas |
103 |
{ |
6206 |
12 Apr 21 |
nicklas |
104 |
DateValidator wrap = warnIfFuture(); |
6919 |
01 Dec 22 |
nicklas |
105 |
wrap.notBefore = alignToFormat(new Date(wrap.notAfter.getTime()-numDays*86400l*1000l)); |
6206 |
12 Apr 21 |
nicklas |
106 |
return wrap; |
6206 |
12 Apr 21 |
nicklas |
107 |
} |
6206 |
12 Apr 21 |
nicklas |
108 |
|
6206 |
12 Apr 21 |
nicklas |
109 |
/** |
6206 |
12 Apr 21 |
nicklas |
Wrap this date validator with a validator that issue a warning |
6206 |
12 Apr 21 |
nicklas |
if the date is in the future or older than the specified date. |
6893 |
25 Nov 22 |
nicklas |
If multiple dates are given, the first non-null date is used. |
6206 |
12 Apr 21 |
nicklas |
113 |
*/ |
6893 |
25 Nov 22 |
nicklas |
114 |
public DateValidator warnIfFutureOrOlder(Date... oldDates) |
6206 |
12 Apr 21 |
nicklas |
115 |
{ |
6206 |
12 Apr 21 |
nicklas |
116 |
DateValidator wrap = warnIfFuture(); |
6893 |
25 Nov 22 |
nicklas |
117 |
for (Date d : oldDates) |
6893 |
25 Nov 22 |
nicklas |
118 |
{ |
6893 |
25 Nov 22 |
nicklas |
119 |
if (d != null) |
6893 |
25 Nov 22 |
nicklas |
120 |
{ |
6919 |
01 Dec 22 |
nicklas |
121 |
wrap.notBefore = alignToFormat(d); |
6893 |
25 Nov 22 |
nicklas |
122 |
break; |
6893 |
25 Nov 22 |
nicklas |
123 |
} |
6893 |
25 Nov 22 |
nicklas |
124 |
} |
6206 |
12 Apr 21 |
nicklas |
125 |
return wrap; |
6206 |
12 Apr 21 |
nicklas |
126 |
} |
6206 |
12 Apr 21 |
nicklas |
127 |
|
6200 |
08 Apr 21 |
nicklas |
128 |
@Override |
6201 |
09 Apr 21 |
nicklas |
129 |
public Date isValid(DbControl dc, String value, JsonSection section, String entryKey) |
6200 |
08 Apr 21 |
nicklas |
130 |
{ |
6206 |
12 Apr 21 |
nicklas |
131 |
Date result = null; |
6510 |
03 Dec 21 |
nicklas |
132 |
if (value != null) |
6200 |
08 Apr 21 |
nicklas |
133 |
{ |
6704 |
26 Apr 22 |
nicklas |
134 |
DateFormat df = dateFormat; |
6704 |
26 Apr 22 |
nicklas |
135 |
result = parseWith(dateFormat, value); |
6704 |
26 Apr 22 |
nicklas |
136 |
if (result == null && alternateDateFormat != null) |
6206 |
12 Apr 21 |
nicklas |
137 |
{ |
6704 |
26 Apr 22 |
nicklas |
138 |
df = alternateDateFormat; |
6704 |
26 Apr 22 |
nicklas |
139 |
result = parseWith(alternateDateFormat, value); |
6206 |
12 Apr 21 |
nicklas |
140 |
} |
6704 |
26 Apr 22 |
nicklas |
141 |
if (result == null) |
6206 |
12 Apr 21 |
nicklas |
142 |
{ |
6510 |
03 Dec 21 |
nicklas |
143 |
section.addErrorMessage("Invalid date in JSON: "+entryKey+"="+value+" (expected format: "+format+")"); |
6206 |
12 Apr 21 |
nicklas |
144 |
} |
6704 |
26 Apr 22 |
nicklas |
145 |
else |
6510 |
03 Dec 21 |
nicklas |
146 |
{ |
6907 |
30 Nov 22 |
nicklas |
// Checks if the parsed date is the string-wise equal to the value from JSON |
7026 |
08 Feb 23 |
nicklas |
148 |
synchronized (df) |
6510 |
03 Dec 21 |
nicklas |
149 |
{ |
7026 |
08 Feb 23 |
nicklas |
150 |
String parsedAs = df.format(result); |
7026 |
08 Feb 23 |
nicklas |
151 |
boolean sameResult = parsedAs.equals(value); |
7026 |
08 Feb 23 |
nicklas |
152 |
if (notAfter != null && result.after(notAfter)) |
7026 |
08 Feb 23 |
nicklas |
153 |
{ |
7026 |
08 Feb 23 |
nicklas |
154 |
section.addWarningMessage("Future date in JSON: "+entryKey+"="+value+(sameResult?"":" (parsed as '"+parsedAs+"')")); |
7026 |
08 Feb 23 |
nicklas |
155 |
} |
7026 |
08 Feb 23 |
nicklas |
156 |
if (notBefore != null && result.before(notBefore)) |
7026 |
08 Feb 23 |
nicklas |
157 |
{ |
7026 |
08 Feb 23 |
nicklas |
158 |
section.addWarningMessage("Old date in JSON: "+entryKey+"="+value+ |
7026 |
08 Feb 23 |
nicklas |
159 |
" ("+(sameResult?"":"parsed as '"+parsedAs+"'; ")+"expected after "+df.format(notBefore)+")"); |
7026 |
08 Feb 23 |
nicklas |
160 |
} |
6510 |
03 Dec 21 |
nicklas |
161 |
} |
6510 |
03 Dec 21 |
nicklas |
162 |
} |
6206 |
12 Apr 21 |
nicklas |
163 |
} |
6206 |
12 Apr 21 |
nicklas |
164 |
return result; |
6200 |
08 Apr 21 |
nicklas |
165 |
} |
6200 |
08 Apr 21 |
nicklas |
166 |
|
6704 |
26 Apr 22 |
nicklas |
167 |
private Date parseWith(DateFormat df, String value) |
6704 |
26 Apr 22 |
nicklas |
168 |
{ |
6704 |
26 Apr 22 |
nicklas |
169 |
try |
6704 |
26 Apr 22 |
nicklas |
170 |
{ |
7026 |
08 Feb 23 |
nicklas |
171 |
synchronized (df) |
7026 |
08 Feb 23 |
nicklas |
172 |
{ |
7026 |
08 Feb 23 |
nicklas |
173 |
return df.parse(value); |
7026 |
08 Feb 23 |
nicklas |
174 |
} |
6704 |
26 Apr 22 |
nicklas |
175 |
} |
6704 |
26 Apr 22 |
nicklas |
176 |
catch (Exception ex) |
6704 |
26 Apr 22 |
nicklas |
177 |
{} |
6704 |
26 Apr 22 |
nicklas |
178 |
return null; |
6704 |
26 Apr 22 |
nicklas |
179 |
} |
6704 |
26 Apr 22 |
nicklas |
180 |
|
6200 |
08 Apr 21 |
nicklas |
181 |
@Override |
6200 |
08 Apr 21 |
nicklas |
182 |
public Class<String> getExpectedClass() |
6200 |
08 Apr 21 |
nicklas |
183 |
{ |
6200 |
08 Apr 21 |
nicklas |
184 |
return String.class; |
6200 |
08 Apr 21 |
nicklas |
185 |
} |
6919 |
01 Dec 22 |
nicklas |
186 |
|
6919 |
01 Dec 22 |
nicklas |
187 |
/** |
6919 |
01 Dec 22 |
nicklas |
Align the given date to the precision used by the format. This will avoid situations |
6919 |
01 Dec 22 |
nicklas |
where one date is considered older than another. For example 2022-12-01 is "older than" |
6919 |
01 Dec 22 |
nicklas |
2022-12-01 14:47 but not really if we only consider the date part. |
6919 |
01 Dec 22 |
nicklas |
191 |
*/ |
6919 |
01 Dec 22 |
nicklas |
192 |
private Date alignToFormat(Date d) |
6919 |
01 Dec 22 |
nicklas |
193 |
{ |
6919 |
01 Dec 22 |
nicklas |
194 |
try |
6919 |
01 Dec 22 |
nicklas |
195 |
{ |
7026 |
08 Feb 23 |
nicklas |
196 |
synchronized (dateFormat) |
7026 |
08 Feb 23 |
nicklas |
197 |
{ |
7026 |
08 Feb 23 |
nicklas |
198 |
d = dateFormat.parse(dateFormat.format(d)); |
7026 |
08 Feb 23 |
nicklas |
199 |
} |
6919 |
01 Dec 22 |
nicklas |
200 |
} |
6919 |
01 Dec 22 |
nicklas |
201 |
catch (Exception ex) |
6919 |
01 Dec 22 |
nicklas |
202 |
{} |
6919 |
01 Dec 22 |
nicklas |
203 |
return d; |
6919 |
01 Dec 22 |
nicklas |
204 |
|
6919 |
01 Dec 22 |
nicklas |
205 |
} |
6200 |
08 Apr 21 |
nicklas |
206 |
|
6200 |
08 Apr 21 |
nicklas |
207 |
} |