001    /*******************************************************************************
002     * Portions created by Sebastian Thomschke are copyright (c) 2005-2011 Sebastian
003     * Thomschke.
004     * 
005     * All Rights Reserved. This program and the accompanying materials
006     * are made available under the terms of the Eclipse Public License v1.0
007     * which accompanies this distribution, and is available at
008     * http://www.eclipse.org/legal/epl-v10.html
009     * 
010     * Contributors:
011     *     Sebastian Thomschke - initial implementation.
012     *******************************************************************************/
013    package net.sf.oval.constraint;
014    
015    import static net.sf.oval.Validator.getCollectionFactory;
016    
017    import java.text.DateFormat;
018    import java.text.ParseException;
019    import java.text.SimpleDateFormat;
020    import java.util.Calendar;
021    import java.util.Date;
022    import java.util.Map;
023    
024    import net.sf.oval.ConstraintTarget;
025    import net.sf.oval.Validator;
026    import net.sf.oval.configuration.annotation.AbstractAnnotationCheck;
027    import net.sf.oval.context.OValContext;
028    import net.sf.oval.exception.InvalidConfigurationException;
029    import net.sf.oval.internal.Log;
030    
031    /**
032     * @author Sebastian Thomschke
033     */
034    public class DateRangeCheck extends AbstractAnnotationCheck<DateRange>
035    {
036            private static final Log LOG = Log.getLog(DateRangeCheck.class);
037    
038            private static final long serialVersionUID = 1L;
039    
040            private String format;
041            private String max;
042            private String min;
043    
044            private transient Long maxMillis;
045            private transient Long minMillis;
046            private long tolerance;
047    
048            /**
049             * {@inheritDoc}
050             */
051            @Override
052            public void configure(final DateRange constraintAnnotation)
053            {
054                    super.configure(constraintAnnotation);
055                    setMin(constraintAnnotation.min());
056                    setMax(constraintAnnotation.max());
057                    setFormat(constraintAnnotation.format());
058                    setTolerance(constraintAnnotation.tolerance());
059            }
060    
061            /**
062             * {@inheritDoc}
063             */
064            @Override
065            protected Map<String, String> createMessageVariables()
066            {
067                    final Map<String, String> messageVariables = getCollectionFactory().createMap(3);
068                    messageVariables.put("min", min == null ? ".." : min);
069                    messageVariables.put("max", max == null ? ".." : max);
070                    messageVariables.put("format", format);
071                    return messageVariables;
072            }
073    
074            /**
075             * {@inheritDoc}
076             */
077            @Override
078            protected ConstraintTarget[] getAppliesToDefault()
079            {
080                    return new ConstraintTarget[]{ConstraintTarget.VALUES};
081            }
082    
083            /**
084             * @return the format
085             */
086            public String getFormat()
087            {
088                    return format;
089            }
090    
091            /**
092             * @return the max
093             */
094            public String getMax()
095            {
096                    return max;
097            }
098    
099            private long getMaxMillis() throws InvalidConfigurationException
100            {
101                    if (maxMillis == null)
102                    {
103                            if (max == null || max.length() == 0) return Long.MAX_VALUE;
104    
105                            if ("now".equals(max)) return System.currentTimeMillis() + tolerance;
106    
107                            if ("today".equals(max))
108                            {
109                                    final Calendar cal = Calendar.getInstance();
110                                    cal.set(Calendar.HOUR_OF_DAY, 0);
111                                    cal.set(Calendar.MINUTE, 0);
112                                    cal.set(Calendar.SECOND, 0);
113                                    cal.set(Calendar.MILLISECOND, 0);
114                                    cal.add(Calendar.DAY_OF_YEAR, 1);
115                                    cal.add(Calendar.MILLISECOND, -1);
116                                    return cal.getTimeInMillis() + tolerance;
117                            }
118    
119                            if ("tomorrow".equals(max))
120                            {
121                                    final Calendar cal = Calendar.getInstance();
122                                    cal.set(Calendar.HOUR_OF_DAY, 0);
123                                    cal.set(Calendar.MINUTE, 0);
124                                    cal.set(Calendar.SECOND, 0);
125                                    cal.set(Calendar.MILLISECOND, 0);
126                                    cal.add(Calendar.DAY_OF_YEAR, 2);
127                                    cal.add(Calendar.MILLISECOND, -1);
128                                    return cal.getTimeInMillis() + tolerance;
129                            }
130    
131                            if ("yesterday".equals(max))
132                            {
133                                    final Calendar cal = Calendar.getInstance();
134                                    cal.set(Calendar.HOUR_OF_DAY, 0);
135                                    cal.set(Calendar.MINUTE, 0);
136                                    cal.set(Calendar.SECOND, 0);
137                                    cal.set(Calendar.MILLISECOND, 0);
138                                    cal.add(Calendar.MILLISECOND, -1);
139                                    return cal.getTimeInMillis() + tolerance;
140                            }
141    
142                            if (format != null && format.length() > 0)
143                            {
144                                    final SimpleDateFormat sdf = new SimpleDateFormat(format);
145                                    try
146                                    {
147                                            maxMillis = sdf.parse(max).getTime() + tolerance;
148                                    }
149                                    catch (final ParseException e)
150                                    {
151                                            throw new InvalidConfigurationException("Unable to parse the max Date String", e);
152                                    }
153                            }
154                            else
155                                    try
156                                    {
157                                            maxMillis = DateFormat.getDateTimeInstance().parse(max).getTime() + tolerance;
158                                    }
159                                    catch (final ParseException e)
160                                    {
161                                            throw new InvalidConfigurationException("Unable to parse the max Date String", e);
162                                    }
163                    }
164                    return maxMillis;
165            }
166    
167            /**
168             * @return the min
169             */
170            public String getMin()
171            {
172                    return min;
173            }
174    
175            private long getMinMillis() throws InvalidConfigurationException
176            {
177                    if (minMillis == null)
178                    {
179                            if (min == null || min.length() == 0) return 0L;
180    
181                            if ("now".equals(min)) return System.currentTimeMillis() - tolerance;
182    
183                            if ("today".equals(min))
184                            {
185                                    final Calendar cal = Calendar.getInstance();
186                                    cal.set(Calendar.HOUR_OF_DAY, 0);
187                                    cal.set(Calendar.MINUTE, 0);
188                                    cal.set(Calendar.SECOND, 0);
189                                    cal.set(Calendar.MILLISECOND, 0);
190                                    return cal.getTimeInMillis() - tolerance;
191                            }
192    
193                            if ("tomorrow".equals(min))
194                            {
195                                    final Calendar cal = Calendar.getInstance();
196                                    cal.set(Calendar.HOUR_OF_DAY, 0);
197                                    cal.set(Calendar.MINUTE, 0);
198                                    cal.set(Calendar.SECOND, 0);
199                                    cal.set(Calendar.MILLISECOND, 0);
200                                    cal.add(Calendar.DAY_OF_YEAR, 1);
201                                    return cal.getTimeInMillis() - tolerance;
202                            }
203    
204                            if ("yesterday".equals(min))
205                            {
206                                    final Calendar cal = Calendar.getInstance();
207                                    cal.set(Calendar.HOUR_OF_DAY, 0);
208                                    cal.set(Calendar.MINUTE, 0);
209                                    cal.set(Calendar.SECOND, 0);
210                                    cal.set(Calendar.MILLISECOND, 0);
211                                    cal.add(Calendar.DAY_OF_YEAR, -1);
212                                    return cal.getTimeInMillis() - tolerance;
213                            }
214    
215                            if (format != null && format.length() > 0)
216                            {
217                                    final SimpleDateFormat sdf = new SimpleDateFormat(format);
218                                    try
219                                    {
220                                            minMillis = sdf.parse(min).getTime() - tolerance;
221                                    }
222                                    catch (final ParseException e)
223                                    {
224                                            throw new InvalidConfigurationException("Unable to parse the min Date String", e);
225                                    }
226                            }
227                            else
228                                    try
229                                    {
230                                            minMillis = DateFormat.getDateTimeInstance().parse(min).getTime() - tolerance;
231                                    }
232                                    catch (final ParseException e)
233                                    {
234                                            throw new InvalidConfigurationException("Unable to parse the min Date String", e);
235                                    }
236                    }
237                    return minMillis;
238            }
239    
240            /**
241             * @return the tolerance
242             */
243            public long getTolerance()
244            {
245                    return tolerance;
246            }
247    
248            public boolean isSatisfied(final Object validatedObject, final Object valueToValidate, final OValContext context,
249                            final Validator validator)
250            {
251                    if (valueToValidate == null) return true;
252    
253                    long valueInMillis = -1;
254    
255                    // check if the value is a Date
256                    if (valueToValidate instanceof Date)
257                            valueInMillis = ((Date) valueToValidate).getTime();
258                    else if (valueToValidate instanceof Calendar)
259                            valueInMillis = ((Calendar) valueToValidate).getTime().getTime();
260                    else
261                    {
262                            // see if we can extract a date based on the object's String representation
263                            final String stringValue = valueToValidate.toString();
264                            try
265                            {
266                                    if (format != null) try
267                                    {
268                                            valueInMillis = new SimpleDateFormat(format).parse(stringValue).getTime();
269                                    }
270                                    catch (final ParseException ex)
271                                    {
272                                            LOG.debug("valueToValidate not parsable with specified format {1}", format, ex);
273                                    }
274    
275                                    if (valueInMillis == -1) valueInMillis = DateFormat.getDateTimeInstance().parse(stringValue).getTime();
276                            }
277                            catch (final ParseException ex)
278                            {
279                                    LOG.debug("valueToValidate is unparsable.", ex);
280                                    return false;
281                            }
282                    }
283    
284                    return valueInMillis >= getMinMillis() && valueInMillis <= getMaxMillis();
285            }
286    
287            /**
288             * @param format the format to set
289             */
290            public void setFormat(final String format)
291            {
292                    this.format = format;
293                    requireMessageVariablesRecreation();
294            }
295    
296            /**
297             * @param max the max to set
298             */
299            public void setMax(final String max)
300            {
301                    this.max = max;
302                    maxMillis = null;
303                    requireMessageVariablesRecreation();
304            }
305    
306            /**
307             * @param min the min to set
308             */
309            public void setMin(final String min)
310            {
311                    this.min = min;
312                    minMillis = null;
313                    requireMessageVariablesRecreation();
314            }
315    
316            /**
317             * @param tolerance the tolerance to set
318             */
319            public void setTolerance(final long tolerance)
320            {
321                    this.tolerance = tolerance;
322                    minMillis = null;
323                    maxMillis = null;
324            }
325    }