Coverage Report - org.kuali.rice.krad.datadictionary.validation.processor.ValidCharactersConstraintProcessor
 
Classes in this File Line Coverage Branch Coverage Complexity
ValidCharactersConstraintProcessor
0%
0/102
0%
0/52
5.875
 
 1  
 /*
 2  
  * Copyright 2011 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 1.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/ecl1.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.processor;
 17  
 
 18  
 import java.lang.reflect.Method;
 19  
 import java.util.List;
 20  
 
 21  
 import org.apache.commons.lang.StringUtils;
 22  
 import org.apache.log4j.Logger;
 23  
 import org.kuali.rice.core.api.CoreApiServiceLocator;
 24  
 import org.kuali.rice.core.framework.logic.LogicalOperator;
 25  
 import org.kuali.rice.core.util.ClassLoaderUtils;
 26  
 import org.kuali.rice.core.util.RiceKeyConstants;
 27  
 import org.kuali.rice.core.web.format.DateFormatter;
 28  
 import org.kuali.rice.krad.datadictionary.exception.AttributeValidationException;
 29  
 import org.kuali.rice.krad.datadictionary.validation.AttributeValueReader;
 30  
 import org.kuali.rice.krad.datadictionary.validation.ValidationUtils;
 31  
 import org.kuali.rice.krad.datadictionary.validation.capability.Constrainable;
 32  
 import org.kuali.rice.krad.datadictionary.validation.capability.Formatable;
 33  
 import org.kuali.rice.krad.datadictionary.validation.constraint.Constraint;
 34  
 import org.kuali.rice.krad.datadictionary.validation.constraint.ValidCharactersConstraint;
 35  
 import org.kuali.rice.krad.datadictionary.validation.result.ConstraintValidationResult;
 36  
 import org.kuali.rice.krad.datadictionary.validation.result.DictionaryValidationResult;
 37  
 import org.kuali.rice.krad.datadictionary.validation.result.ProcessorResult;
 38  
 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
 39  
 import org.kuali.rice.krad.util.KRADConstants;
 40  
 
 41  
 /**
 42  
  * This class defines a constraint processor to ensure that attribute values are constrained to valid characters, as defined by some regular expression. Of the 
 43  
  * constraint processors written for this version, this one is potentially the most difficult to understand because it holds on to a lot of legacy processing.
 44  
  * 
 45  
  * @author Kuali Rice Team (rice.collab@kuali.org) 
 46  
  */
 47  0
 public class ValidCharactersConstraintProcessor extends MandatoryElementConstraintProcessor<ValidCharactersConstraint> {
 48  
 
 49  
         public static final String VALIDATE_METHOD = "validate";
 50  
         
 51  0
         private static final Logger LOG = Logger.getLogger(ValidCharactersConstraintProcessor.class);
 52  0
         private static final String[] DATE_RANGE_ERROR_PREFIXES = { KRADConstants.LOOKUP_RANGE_LOWER_BOUND_PROPERTY_PREFIX, KRADConstants.LOOKUP_RANGE_UPPER_BOUND_PROPERTY_PREFIX };
 53  
     
 54  
         private static final String CONSTRAINT_NAME = "valid characters constraint";
 55  
         
 56  
         /**
 57  
          * @see org.kuali.rice.krad.datadictionary.validation.processor.ConstraintProcessor#process(DictionaryValidationResult, Object, org.kuali.rice.krad.datadictionary.validation.capability.Validatable, org.kuali.rice.krad.datadictionary.validation.AttributeValueReader)
 58  
          */
 59  
         @Override
 60  
         public ProcessorResult process(DictionaryValidationResult result, Object value, ValidCharactersConstraint constraint, AttributeValueReader attributeValueReader)        throws AttributeValidationException {
 61  
                 
 62  0
             return new ProcessorResult(processSingleValidCharacterConstraint(result, value, constraint, attributeValueReader));
 63  
         }
 64  
 
 65  
         @Override 
 66  
         public String getName() {
 67  0
                 return CONSTRAINT_NAME;
 68  
         }
 69  
         
 70  
         /**
 71  
          * @see org.kuali.rice.krad.datadictionary.validation.processor.ConstraintProcessor#getConstraintType()
 72  
          */
 73  
         @Override
 74  
         public Class<? extends Constraint> getConstraintType() {
 75  0
                 return ValidCharactersConstraint.class;
 76  
         }
 77  
         
 78  
         
 79  
         protected ConstraintValidationResult processSingleValidCharacterConstraint(DictionaryValidationResult result, Object value, ValidCharactersConstraint constraint, AttributeValueReader attributeValueReader) throws AttributeValidationException {
 80  
                 
 81  0
                 if (constraint == null) 
 82  0
                         return result.addNoConstraint(attributeValueReader, CONSTRAINT_NAME);
 83  
                 
 84  0
                 if (ValidationUtils.isNullOrEmpty(value))
 85  0
                         return result.addSkipped(attributeValueReader, CONSTRAINT_NAME);
 86  
                 
 87  
                 // This mix-in interface is here to allow some definitions to avoid the extra processing that goes on in KNS
 88  
                 // to decipher and validate things like date range strings -- something that looks like "02/02/2002..03/03/2003"
 89  0
                 Constrainable definition = attributeValueReader.getDefinition(attributeValueReader.getAttributeName());
 90  0
             if (definition instanceof Formatable) {
 91  0
                     return doProcessFormattableValidCharConstraint(result, constraint, (Formatable)definition, value, attributeValueReader);
 92  
             } 
 93  
             
 94  0
             ConstraintValidationResult constraintValidationResult = doProcessValidCharConstraint(constraint, value);
 95  0
             if (constraintValidationResult == null)
 96  0
                     return result.addSuccess(attributeValueReader, CONSTRAINT_NAME);
 97  
             
 98  0
             result.addConstraintValidationResult(attributeValueReader, constraintValidationResult);
 99  0
             return constraintValidationResult;
 100  
         }
 101  
         
 102  
     protected ConstraintValidationResult doProcessFormattableValidCharConstraint(DictionaryValidationResult result, ValidCharactersConstraint validCharsConstraint, Formatable definition, Object value, AttributeValueReader attributeValueReader) throws AttributeValidationException {
 103  0
             String entryName = attributeValueReader.getEntryName();
 104  0
             String attributeName = attributeValueReader.getAttributeName();
 105  
             
 106  
             // This is a strange KNS thing for validating searchable fields -- they sometimes come in a date range format, for example 2/12/2010..2/14/2010, and need to be split up
 107  0
                 List<String> parsedAttributeValues = attributeValueReader.getCleanSearchableValues(attributeName);
 108  
                 
 109  0
                 if (parsedAttributeValues != null) {
 110  
                         
 111  0
                         Class<?> formatterClass = null;
 112  0
                         Boolean doValidateDateRangeOrder = null;
 113  
                         
 114  
                         // It can't be a date range if it's more than two fields, for example "a .. b | c" is not a date range -- this saves us a tiny bit of processing later
 115  0
                         if (parsedAttributeValues.size() != 2)
 116  0
                                 doValidateDateRangeOrder = Boolean.FALSE;
 117  
                         
 118  
                         // Use integer to iterate since we need to track which field we're looking at
 119  0
                         for (int i=0;i<parsedAttributeValues.size();i++) {
 120  0
                                 String parsedAttributeValue = parsedAttributeValues.get(i);
 121  
                                 
 122  0
                                 ConstraintValidationResult constraintValidationResult = doProcessValidCharConstraint(validCharsConstraint, parsedAttributeValue);
 123  
                 
 124  
                                 // If this is an error then some non-null validation result will be returned
 125  0
                                 if (constraintValidationResult != null) {
 126  
                                         // Another strange KNS thing -- if the validation fails (not sure why only in that case) then some further error checking is done using the formatter, if one exists
 127  0
                                         if (formatterClass == null) {
 128  0
                                             String formatterClassName = definition.getFormatterClass();
 129  0
                                             if (formatterClassName != null)
 130  0
                                                     formatterClass = ClassLoaderUtils.getClass(formatterClassName);
 131  
                                         }
 132  
                                         
 133  0
                                         if (formatterClass != null) {
 134  
                                                 // Use the Boolean value being null to ensure we only do this once
 135  0
                                                 if (doValidateDateRangeOrder == null) {
 136  
                                                         // We only want to validate a date range if we're dealing with something that has a date formatter on it and that looks like an actual range (is made up of 2 values with a between operator between them)
 137  0
                                                     doValidateDateRangeOrder = Boolean.valueOf(DateFormatter.class.isAssignableFrom(formatterClass) && StringUtils.contains(ValidationUtils.getString(value), LogicalOperator.BETWEEN.toString())); 
 138  
                                                 }
 139  
                                                 
 140  0
                                                 constraintValidationResult = processFormatterValidation(result, formatterClass, entryName, attributeName, parsedAttributeValue, DATE_RANGE_ERROR_PREFIXES[i]);
 141  
                                                 
 142  0
                                                 if (constraintValidationResult != null) {
 143  0
                                                         result.addConstraintValidationResult(attributeValueReader, constraintValidationResult);
 144  0
                                                         return constraintValidationResult;
 145  
                                                 }
 146  
                                         } else {
 147  
                                                 // Otherwise, just report the validation result (apparently the formatter can't provide any fall-through validation because it doesn't exist)
 148  0
                                                 result.addConstraintValidationResult(attributeValueReader, constraintValidationResult);
 149  0
                                                 return constraintValidationResult;
 150  
                                         }
 151  
                                 }
 152  
                         }
 153  
                         
 154  0
                     if (doValidateDateRangeOrder != null && doValidateDateRangeOrder.booleanValue()) {
 155  0
                             ConstraintValidationResult dateOrderValidationResult = validateDateOrder(parsedAttributeValues.get(0), parsedAttributeValues.get(1), entryName, attributeName);
 156  
                             
 157  0
                             if (dateOrderValidationResult != null) {
 158  0
                                     result.addConstraintValidationResult(attributeValueReader, dateOrderValidationResult);
 159  0
                                         return dateOrderValidationResult;
 160  
                             }
 161  
                     }
 162  
                     
 163  0
                     return result.addSuccess(attributeValueReader, CONSTRAINT_NAME);
 164  
                 }
 165  0
                 return result.addSkipped(attributeValueReader, CONSTRAINT_NAME);
 166  
     }
 167  
         
 168  
     protected ConstraintValidationResult doProcessValidCharConstraint(ValidCharactersConstraint validCharsConstraint, Object value) {
 169  
 
 170  0
         StringBuilder fieldValue = new StringBuilder();
 171  0
         String validChars = validCharsConstraint.getValue();
 172  
 
 173  0
         fieldValue.append(ValidationUtils.getString(value));
 174  
 
 175  
 //        int typIdx = validChars.indexOf(":");
 176  
 //        String processorType = "regex";
 177  
 //        if (-1 == typIdx) {
 178  
 //            validChars = "[" + validChars + "]*";
 179  
 //        } else {
 180  
 //            processorType = validChars.substring(0, typIdx);
 181  
 //            validChars = validChars.substring(typIdx + 1);
 182  
 //        }
 183  
 
 184  
 //        if ("regex".equalsIgnoreCase(processorType) && !validChars.equals(".*")) {
 185  0
             if (!fieldValue.toString().matches(validChars)) {
 186  0
                     ConstraintValidationResult constraintValidationResult = new ConstraintValidationResult(CONSTRAINT_NAME);
 187  0
                     if (validCharsConstraint.getLabelKey() != null) {
 188  
                             // FIXME: This shouldn't surface label key itself to the user - it should look up the label key, but this needs to be implemented in Rice
 189  0
                             constraintValidationResult.setError(RiceKeyConstants.ERROR_CUSTOM, validCharsConstraint.getLabelKey());
 190  0
                             return constraintValidationResult;
 191  
                     } 
 192  
                     
 193  0
                     constraintValidationResult.setError(RiceKeyConstants.ERROR_INVALID_FORMAT, fieldValue.toString());
 194  0
                     return constraintValidationResult;
 195  
             }
 196  
 //        }
 197  
         
 198  0
         return null;
 199  
     }
 200  
 
 201  
     protected ConstraintValidationResult processFormatterValidation(DictionaryValidationResult result, Class<?> formatterClass, String entryName, String attributeName, String parsedAttributeValue, String errorKeyPrefix) {
 202  
             
 203  0
             boolean isError = false;
 204  
             
 205  
             try {
 206  0
                     Method validatorMethod = formatterClass.getDeclaredMethod(VALIDATE_METHOD, new Class<?>[] {String.class});
 207  0
                     Object o = validatorMethod.invoke(formatterClass.newInstance(), parsedAttributeValue);
 208  0
                     if (o instanceof Boolean) {
 209  0
                             isError = !((Boolean)o).booleanValue();
 210  
                     }
 211  0
             } catch (Exception e) {
 212  0
                     if ( LOG.isDebugEnabled() ) 
 213  0
                             LOG.debug(e.getMessage(), e);
 214  
 
 215  0
                     isError = true;
 216  0
             }
 217  
 
 218  0
             if (isError) {
 219  0
                     String errorMessageKey = getDataDictionaryService().getAttributeValidatingErrorMessageKey(entryName, attributeName);
 220  0
                     String[] errorMessageParameters = getDataDictionaryService().getAttributeValidatingErrorMessageParameters(entryName, attributeName);
 221  
                     
 222  0
                     ConstraintValidationResult constraintValidationResult = new ConstraintValidationResult(CONSTRAINT_NAME);
 223  0
                         constraintValidationResult.setEntryName(entryName);
 224  0
                         constraintValidationResult.setAttributeName(errorKeyPrefix + attributeName);
 225  0
                         constraintValidationResult.setError(errorMessageKey, errorMessageParameters);
 226  
                     
 227  0
                         return constraintValidationResult;
 228  
             }
 229  
                         
 230  0
                 return null;
 231  
     }
 232  
     
 233  
         protected ConstraintValidationResult validateDateOrder(String firstDateTime, String secondDateTime, String entryName, String attributeName) {
 234  
                 // this means that we only have 2 values and it's a date range.
 235  0
                 java.sql.Timestamp lVal = null;
 236  0
                 java.sql.Timestamp uVal = null;
 237  
                 try {
 238  0
                         lVal = CoreApiServiceLocator.getDateTimeService().convertToSqlTimestamp(firstDateTime);
 239  0
                         uVal = CoreApiServiceLocator.getDateTimeService().convertToSqlTimestamp(secondDateTime);
 240  0
                 } catch (Exception ex){
 241  
                         // this shouldn't happen because the tests passed above.
 242  0
                         String errorMessageKey = KRADServiceLocatorWeb.getDataDictionaryService().getAttributeValidatingErrorMessageKey(entryName, attributeName);
 243  0
                         String[] errorMessageParameters = KRADServiceLocatorWeb.getDataDictionaryService().getAttributeValidatingErrorMessageParameters(entryName, attributeName);
 244  0
                         ConstraintValidationResult constraintValidationResult = new ConstraintValidationResult(CONSTRAINT_NAME);
 245  0
                         constraintValidationResult.setEntryName(entryName);
 246  0
                         constraintValidationResult.setAttributeName(
 247  
                     KRADConstants.LOOKUP_RANGE_LOWER_BOUND_PROPERTY_PREFIX + attributeName);
 248  0
                         constraintValidationResult.setError(errorMessageKey, errorMessageParameters);
 249  0
                         return constraintValidationResult;
 250  0
                 }
 251  
 
 252  0
                 if (lVal != null && lVal.compareTo(uVal) > 0){ // check the bounds
 253  0
                         String errorMessageKey = KRADServiceLocatorWeb.getDataDictionaryService().getAttributeValidatingErrorMessageKey(entryName, attributeName);
 254  0
                         String[] errorMessageParameters = KRADServiceLocatorWeb.getDataDictionaryService().getAttributeValidatingErrorMessageParameters(entryName, attributeName);
 255  0
                         ConstraintValidationResult constraintValidationResult = new ConstraintValidationResult(CONSTRAINT_NAME);
 256  0
                         constraintValidationResult.setEntryName(entryName);
 257  0
                         constraintValidationResult.setAttributeName(
 258  
                     KRADConstants.LOOKUP_RANGE_LOWER_BOUND_PROPERTY_PREFIX + attributeName);
 259  0
                         constraintValidationResult.setError(errorMessageKey + ".range", errorMessageParameters);
 260  0
                         return constraintValidationResult;
 261  
                 }
 262  
                 
 263  0
                 return null;
 264  
         }
 265  
     
 266  
 }