001/**
002 * Copyright 2005-2016 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 */
016package org.kuali.rice.krad.datadictionary.validation.constraint;
017
018import java.util.ArrayList;
019import java.util.Arrays;
020import java.util.Collections;
021import java.util.List;
022
023import org.apache.commons.lang.StringUtils;
024import org.kuali.rice.core.api.CoreConstants;
025import org.kuali.rice.core.api.config.property.ConfigContext;
026import org.kuali.rice.core.api.config.property.ConfigurationService;
027import org.kuali.rice.krad.service.KRADServiceLocator;
028import 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 */
039public 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}