View Javadoc

1   /*
2    * Copyright 2006-2010 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package net.sourceforge.domian.specification;
17  
18  
19  import java.text.ParseException;
20  import java.text.SimpleDateFormat;
21  import java.util.Calendar;
22  import java.util.Date;
23  import java.util.Locale;
24  
25  import org.apache.commons.lang.builder.HashCodeBuilder;
26  
27  import static java.lang.Boolean.FALSE;
28  import static org.apache.commons.lang.StringUtils.isBlank;
29  import static org.apache.commons.lang.StringUtils.isEmpty;
30  import static org.apache.commons.lang.StringUtils.isNumeric;
31  
32  
33  /**
34   * Specification for approval of strings representing a date.
35   * The date must be a valid one.
36   * <p/>
37   * This specification is value bound.
38   * The value is a date pattern which must be given in a
39   * <a href="http://java.sun.com/javase/6/docs/api/java/text/SimpleDateFormat.html"><code>java.text.SimpleDateFormat</code></a> date format.
40   * Candidate string objects must be successfully parsed using this date pattern value to be approved by this specification.
41   *
42   * @author Eirik Torske
43   * @see <a href="http://java.sun.com/javase/6/docs/api/java/text/SimpleDateFormat.html"><code>java.text.SimpleDateFormat</code></a>
44   * @since 0.1
45   */
46  final class DateStringSpecification extends AbstractSpecification<String> implements ValueBoundSpecification<String> {
47  
48      private final SimpleDateFormat simpleDateFormat;
49  
50      DateStringSpecification(final String datePattern) {
51          if (datePattern == null) { throw new IllegalArgumentException("Date pattern parameter cannot be null"); }
52          if (isEmpty(datePattern)) { throw new IllegalArgumentException("Date pattern parameter cannot be empty"); }
53          if (isBlank(datePattern)) { throw new IllegalArgumentException("Date pattern parameter cannot be blank"); }
54          try {
55              this.simpleDateFormat = new SimpleDateFormat(datePattern, Locale.getDefault());
56              this.simpleDateFormat.setLenient(false);
57  
58          } catch (IllegalArgumentException e) {
59              throw new IllegalArgumentException("\"" + datePattern + "\" is not a valid date pattern");
60          }
61      }
62  
63      /** @return a string representation of the <code>simpleDateFormat</code> member object */
64      @Override
65      public String getValue() {
66          return this.simpleDateFormat.toPattern();
67      }
68  
69      @Override
70      public Boolean isSatisfiedBy(final String candidate) {
71          if (candidate == null) {
72              return FALSE;
73          }
74          if (candidate.length() != this.simpleDateFormat.toPattern().length()) {
75              return FALSE;
76          }
77          try {
78              Date date = this.simpleDateFormat.parse(candidate);
79  
80              /* Workaround for strange SimpleDateFormat year behaviour */
81              return isNumeric(candidate) || !lengthOfYearStringIsNotEqualToLengthOfYearPatternString(date, this.simpleDateFormat);
82  
83          } catch (ParseException e) {
84              return FALSE;
85          }
86      }
87  
88      @Override
89      public int hashCode() {
90          return new HashCodeBuilder(423, 19843).append(this.simpleDateFormat).toHashCode();
91      }
92  
93      @Override
94      public boolean equals(final Object otherObject) {
95          if (otherObject == null) { return false; }
96          if (!(otherObject instanceof DateStringSpecification)) { return false; }
97          return this.simpleDateFormat.equals(((DateStringSpecification) otherObject).simpleDateFormat);
98      }
99  
100     /* Workaround for strange SimpleDateFormat behaviour */
101     private static boolean lengthOfYearStringIsNotEqualToLengthOfYearPatternString(final Date date, final SimpleDateFormat datePattern) {
102         final Calendar calendar = Calendar.getInstance();
103         calendar.setLenient(false);
104         calendar.setTime(date);
105         final String yearAsString = String.valueOf(calendar.get(Calendar.YEAR));
106 
107         final String datePatternString = datePattern.toPattern();
108         final String yearDatePatternString = datePatternString.substring(datePatternString.indexOf('y'), datePatternString.lastIndexOf('y') + 1);
109 
110         return yearAsString.length() != yearDatePatternString.length();
111     }
112 }