View Javadoc
1   /**
2    * Copyright 2005-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
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 org.kuali.rice.krad.datadictionary.validation.constraint;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.core.api.CoreConstants;
20  import org.kuali.rice.core.api.config.property.ConfigContext;
21  import org.kuali.rice.krad.datadictionary.parse.BeanTag;
22  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
23  import org.kuali.rice.krad.datadictionary.parse.BeanTags;
24  
25  import java.util.ArrayList;
26  import java.util.Arrays;
27  import java.util.Collections;
28  import java.util.List;
29  
30  /**
31   * DatePatternConstraint constrains a field to only allow dates which are part of the formats
32   * defined in the system. Constraining a field all these formats is often not appropriate for
33   * fields, and you may want to constrain the input to a subset of the allowed formats in the system.
34   * This can be done by setting the allowed formats to this subset (see BasicDatePatternConstraint
35   * bean for example)
36   *
37   * @author Kuali Rice Team (rice.collab@kuali.org)
38   */
39  @BeanTags({@BeanTag(name = "datePatternConstraint-bean", parent = "DatePatternConstraint"),
40          @BeanTag(name = "basicDatePatternConstraint-bean", parent = "BasicDatePatternConstraint")})
41  public class DatePatternConstraint extends ValidDataPatternConstraint {
42  
43      private List<String> allowedFormats;
44  
45      /**
46       * Returns a regex representing all the allowed formats in the system. If allowedFormats is
47       * supplied, returns a regex representing only those formats.
48       *
49       * @see org.kuali.rice.krad.datadictionary.validation.constraint.ValidDataPatternConstraint#getRegexString()
50       */
51      @Override
52      protected String getRegexString() {
53          List<String> dateFormatParams = loadFormats(CoreConstants.STRING_TO_DATE_FORMATS, CoreConstants.STRING_TO_DATE_FORMATS_DEFAULT);
54          if (allowedFormats != null && !allowedFormats.isEmpty()) {
55              if (dateFormatParams.containsAll(allowedFormats)) {
56                  dateFormatParams = allowedFormats;
57              } else {
58                  //throw new Exception("Some of these formats do not exist in configured allowed date formats: " + allowedFormats.toString());
59              }
60          }
61  
62          String regex = "";
63          int i = 0;
64          for (String format : dateFormatParams) {
65              if (i == 0) {
66                  regex = "(^" + convertDateFormatToRegex(format.trim()) + "$)";
67              } else {
68                  regex = regex + "|(^" + convertDateFormatToRegex(format.trim()) + "$)";
69              }
70              i++;
71          }
72          return regex;
73      }
74  
75      /**
76       * Converts a date format supplied to the appropriate date format regex equivalent
77       *
78       * @param format date format
79       * @return regex for validating the date format
80       */
81      private String convertDateFormatToRegex(String format) {
82          format = format.replace("\\", "\\\\").replace(".", "\\.").replace("-", "\\-").replace("+", "\\+").replace("(",
83                  "\\(").replace(")", "\\)").replace("[", "\\[").replace("]", "\\]").replace("|", "\\|").replace("yyyy",
84                  "((19|2[0-9])[0-9]{2})").replace("yy", "([0-9]{2})").replaceAll("M{4,}",
85                  "([@]+)") //"(January|February|March|April|May|June|July|August|September|October|November|December)")
86                  .replace("MMM", "([@]{3})") //"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)")
87                  .replace("MM", "(0[1-9]|1[012])").replace("M", "(0?[1-9]|1[012])").replace("dd",
88                          "(0[1-9]|[12][0-9]|3[01])").replace("d", "(0?[1-9]|[12][0-9]|3[01])").replace("hh",
89                          "(1[0-2]|0[1-9])").replace("h", "(1[0-2]|0?[1-9])").replace("HH", "(2[0-3]|1[0-9]|0[0-9])")
90                  .replace("H", "(2[0-3]|1[0-9]|0?[0-9])").replace("kk", "(2[0-4]|1[0-9]|0[1-9])").replace("k",
91                          "(2[0-4]|1[0-9]|0?[1-9])").replace("KK", "(1[01]|0[0-9])").replace("K", "(1[01]|0?[0-9])")
92                  .replace("mm", "([0-5][0-9])").replace("m", "([1-5][0-9]|0?[0-9])").replace("ss", "([0-5][0-9])")
93                  .replace("s", "([1-5][0-9]|0?[0-9])").replace("SSS", "([0-9][0-9][0-9])").replace("SS",
94                          "([0-9][0-9][0-9]?)").replace("S", "([0-9][0-9]?[0-9]?)").replaceAll("E{4,}",
95                          "([@]+)")//"(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)")
96                  .replaceAll("E{1,3}", "([@]{3})")//"(Mon|Tue|Wed|Thu|Fri|Sat|Sun)")
97                  .replace("DDD", "(3[0-6][0-5]|[1-2][0-9][0-9]|0[0-9][1-9])").replace("DD",
98                          "(3[0-6][0-5]|[1-2][0-9][0-9]|0?[0-9][1-9])").replace("D",
99                          "(3[0-6][0-5]|[1-2][0-9][0-9]|0?[0-9]?[1-9])").replace("F", "([1-5])").replace("ww",
100                         "(5[0-3]|[1-4][0-9]|0[1-9])").replace("w", "(5[0-3]|[1-4][0-9]|[1-9])").replace("W", "([1-5])")
101                 .replaceAll("z{4,}", "([@]+)").replaceAll("z{1,3}", "([@]{1,4})").replaceAll("a{1,}", "([aApP][mM])")
102                 .replaceAll("G{1,}", "([aA][dD]|[bB][cC])").replace(" ", "\\s").replace("@", "a-zA-Z");
103 
104         return format;
105 
106     }
107 
108     /**
109      * Loads a list of date formats from the config, using a default for fallback.
110      *
111      * @param property the config property
112      * @param defaultValue the default value
113      *
114      * @return the config or default value
115      */
116     private List<String> loadFormats(String property, String defaultValue) {
117         return parseConfigValues(loadFormat(property, defaultValue));
118     }
119 
120     /**
121      * Loads a particular date format from the config, using a default for fallback.
122      *
123      * @param property the config property
124      * @param defaultValue the default value
125      *
126      * @return the config value or default value
127      */
128     private String loadFormat(String property, String defaultValue) {
129         String format = ConfigContext.getCurrentContextConfig().getProperty(property);
130 
131         if (StringUtils.isNotBlank(format)) {
132             return format;
133         }
134 
135         return defaultValue;
136     }
137 
138     /**
139      * The dateTime config vars are ';' seperated.
140      *
141      * @param configValue configuration value
142      * @return list of tokens in the config value, split on ';'
143      */
144     private List<String> parseConfigValues(String configValue) {
145         if (configValue == null || "".equals(configValue)) {
146             return Collections.emptyList();
147         }
148         return Arrays.asList(configValue.split(";"));
149     }
150 
151     /**
152      * @return the allowedFormats
153      */
154     @BeanTagAttribute(name = "allowedFormats", type = BeanTagAttribute.AttributeType.LISTVALUE)
155     public List<String> getAllowedFormats() {
156         return this.allowedFormats;
157     }
158 
159     /**
160      * Sets the alloweFormats for this constraint, this must be a subset of the system configured
161      * formats for a date - this list should be used for most fields where you are expecting a user
162      * to enter a date in a specific format
163      *
164      * @param allowedFormats the allowedFormats to set
165      */
166     public void setAllowedFormats(List<String> allowedFormats) {
167         this.allowedFormats = allowedFormats;
168     }
169 
170     /**
171      * This overridden method ...
172      *
173      * @see org.kuali.rice.krad.datadictionary.validation.constraint.ValidDataPatternConstraint#getValidationMessageParams()
174      */
175     @Override
176     public List<String> getValidationMessageParams() {
177         if (validationMessageParams == null) {
178             validationMessageParams = new ArrayList<String>();
179             if (allowedFormats != null && !allowedFormats.isEmpty()) {
180                 validationMessageParams.add(StringUtils.join(allowedFormats, ", "));
181             } else {
182                 List<String> dateFormatParams = loadFormats(CoreConstants.STRING_TO_DATE_FORMATS, CoreConstants.STRING_TO_DATE_FORMATS_DEFAULT);
183                 validationMessageParams.add(StringUtils.join(dateFormatParams, ", "));
184             }
185         }
186         return validationMessageParams;
187     }
188 
189 }