001    /**
002     * Copyright 2005-2012 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.rice.krad.datadictionary.validation.constraint;
017    
018    import java.util.ArrayList;
019    import java.util.Arrays;
020    import java.util.Collections;
021    import java.util.List;
022    
023    import org.apache.commons.lang.StringUtils;
024    import org.kuali.rice.core.api.CoreConstants;
025    import org.kuali.rice.core.api.config.property.ConfigContext;
026    import org.kuali.rice.core.api.config.property.ConfigurationService;
027    import org.kuali.rice.krad.service.KRADServiceLocator;
028    import org.kuali.rice.krad.uif.UifConstants;
029    
030    /**
031     * DatePatternConstraint constrains a field to only allow dates which are part of the formats
032     * defined in the system. Constraining a field all these formats is often not appropriate for
033     * fields, and you may want to constrain the input to a subset of the allowed formats in the system.
034     * This can be done by setting the allowed formats to this subset (see BasicDatePatternConstraint
035     * bean for example)
036     * 
037     * @author Kuali Rice Team (rice.collab@kuali.org)
038     */
039    public class DatePatternConstraint extends ValidDataPatternConstraint {
040    
041        private List<String> allowedFormats;
042    
043        /**
044         * Returns a regex representing all the allowed formats in the system. If allowedFormats is
045         * supplied, returns a regex representing only those formats.
046         * 
047         * @see org.kuali.rice.krad.datadictionary.validation.constraint.ValidDataPatternConstraint#getRegexString()
048         */
049        @Override
050        protected String getRegexString() {
051            List<String> dateFormatParams =
052                    parseConfigValues(ConfigContext.getCurrentContextConfig().getProperty(
053                            CoreConstants.STRING_TO_DATE_FORMATS));
054            if (allowedFormats != null && !allowedFormats.isEmpty()) {
055                if (dateFormatParams.containsAll(allowedFormats)) {
056                    dateFormatParams = allowedFormats;
057                } else {
058                    //throw new Exception("Some of these formats do not exist in configured allowed date formats: " + allowedFormats.toString());
059                }
060            }
061    
062            if (dateFormatParams.isEmpty()) {
063                //exception
064            }
065            String regex = "";
066            int i = 0;
067            for (String format : dateFormatParams) {
068                if (i == 0) {
069                    regex = "(^" + convertDateFormatToRegex(format.trim()) + "$)";
070                } else {
071                    regex = regex + "|(^" + convertDateFormatToRegex(format.trim()) + "$)";
072                }
073                i++;
074            }
075            return regex;
076        }
077    
078        /**
079         * Converts a date format supplied to the appropriate date format regex equivalent
080         * 
081         * @param format
082         * @return
083         */
084        private String convertDateFormatToRegex(String format) {
085            format = format.replace("\\", "\\\\")
086                    .replace(".", "\\.")
087                    .replace("-", "\\-")
088                    .replace("+", "\\+")
089                    .replace("(", "\\(")
090                    .replace(")", "\\)")
091                    .replace("[", "\\[")
092                    .replace("]", "\\]")
093                    .replace("|", "\\|")
094                    .replace("yyyy", "((19|2[0-9])[0-9]{2})")
095                    .replace("yy", "([0-9]{2})")
096                    .replaceAll("M{4,}", "([@]+)") //"(January|February|March|April|May|June|July|August|September|October|November|December)")
097                    .replace("MMM", "([@]{3})") //"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)")
098                    .replace("MM", "(0[1-9]|1[012])")
099                    .replace("M", "(0?[1-9]|1[012])")
100                    .replace("dd", "(0[1-9]|[12][0-9]|3[01])")
101                    .replace("d", "(0?[1-9]|[12][0-9]|3[01])")
102                    .replace("hh", "(1[0-2]|0[1-9])")
103                    .replace("h", "(1[0-2]|0?[1-9])")
104                    .replace("HH", "(2[0-3]|1[0-9]|0[0-9])")
105                    .replace("H", "(2[0-3]|1[0-9]|0?[0-9])")
106                    .replace("kk", "(2[0-4]|1[0-9]|0[1-9])")
107                    .replace("k", "(2[0-4]|1[0-9]|0?[1-9])")
108                    .replace("KK", "(1[01]|0[0-9])")
109                    .replace("K", "(1[01]|0?[0-9])")
110                    .replace("mm", "([0-5][0-9])")
111                    .replace("m", "([1-5][0-9]|0?[0-9])")
112                    .replace("ss", "([0-5][0-9])")
113                    .replace("s", "([1-5][0-9]|0?[0-9])")
114                    .replace("SSS", "([0-9][0-9][0-9])")
115                    .replace("SS", "([0-9][0-9][0-9]?)")
116                    .replace("S", "([0-9][0-9]?[0-9]?)")
117                    .replaceAll("E{4,}", "([@]+)")//"(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)")
118                    .replaceAll("E{1,3}", "([@]{3})")//"(Mon|Tue|Wed|Thu|Fri|Sat|Sun)")
119                    .replace("DDD", "(3[0-6][0-5]|[1-2][0-9][0-9]|0[0-9][1-9])")
120                    .replace("DD", "(3[0-6][0-5]|[1-2][0-9][0-9]|0?[0-9][1-9])")
121                    .replace("D", "(3[0-6][0-5]|[1-2][0-9][0-9]|0?[0-9]?[1-9])")
122                    .replace("F", "([1-5])")
123                    .replace("ww", "(5[0-3]|[1-4][0-9]|0[1-9])")
124                    .replace("w", "(5[0-3]|[1-4][0-9]|[1-9])")
125                    .replace("W", "([1-5])")
126                    .replaceAll("z{4,}", "([@]+)")
127                    .replaceAll("z{1,3}", "([@]{1,4})")
128                    .replaceAll("a{1,}", "([aApP][mM])")
129                    .replaceAll("G{1,}", "([aA][dD]|[bB][cC])")
130                    .replace(" ", "\\s")
131                    .replace("@", "a-zA-Z");
132    
133            return format;
134    
135        }
136    
137        /**
138         * 
139         * The dateTime config vars are ';' seperated.
140         * 
141         * @param configValue
142         * @return
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        public List<String> getAllowedFormats() {
155            return this.allowedFormats;
156        }
157    
158        /**
159         * Sets the alloweFormats for this constraint, this must be a subset of the system configured
160         * formats for a date - this list should be used for most fields where you are expecting a user
161         * to enter a date in a specific format
162         * @param allowedFormats the allowedFormats to set
163         */
164        public void setAllowedFormats(List<String> allowedFormats) {
165            this.allowedFormats = allowedFormats;
166        }
167    
168        /**
169         * This overridden method ...
170         * 
171         * @see org.kuali.rice.krad.datadictionary.validation.constraint.ValidDataPatternConstraint#getValidationMessageParams()
172         */
173        @Override
174        public List<String> getValidationMessageParams() {
175            if (validationMessageParams == null) {
176                validationMessageParams = new ArrayList<String>();
177                if (allowedFormats != null && !allowedFormats.isEmpty()) {
178                    validationMessageParams.add(StringUtils.join(allowedFormats, ", "));
179                } else {
180                    List<String> dateFormatParams =
181                            parseConfigValues(ConfigContext.getCurrentContextConfig().getProperty(
182                                    CoreConstants.STRING_TO_DATE_FORMATS));
183                    validationMessageParams.add(StringUtils.join(dateFormatParams, ", "));
184                }
185            }
186            return validationMessageParams;
187        }
188    
189    }