1 /** 2 * Copyright 2005-2016 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.kew.docsearch; 17 18 import org.apache.commons.lang.StringUtils; 19 import org.kuali.rice.core.api.search.SearchOperator; 20 import org.kuali.rice.core.framework.persistence.jdbc.sql.SQLUtils; 21 22 import java.util.regex.Matcher; 23 import java.util.regex.Pattern; 24 25 /** 26 * Abstract base class for numeric searchable attributes. 27 * 28 * <p>Contains common logic for validation along with a template method for retrieving a validation Pattern.</p> 29 * 30 * @author Kuali Rice Team (rice.collab@kuali.org) 31 */ 32 public abstract class SearchableAttributeNumericBase extends SearchableAttributeBase { 33 34 /** 35 * Returns a Pattern object used for validating the format of number Strings. 36 * 37 * <p>{@link Pattern}s are immutable and thus safe for concurrent use, so it makes sense to return 38 * a pre-compiled static instance.</p> 39 * 40 * <p>The pattern should only match valid String representations of the numeric type</p> 41 * 42 * @return the Pattern used for validating number Strings. 43 */ 44 abstract protected Pattern getDefaultValidationPattern(); 45 46 /** 47 * is the given value valid for searching against this attribute? 48 * 49 * <p>This method detects the binary operators defined by 50 * {@link org.kuali.rice.core.api.search.SearchOperator#BETWEEN}, 51 * {@link org.kuali.rice.core.api.search.SearchOperator#AND}, and 52 * {@link org.kuali.rice.core.api.search.SearchOperator#OR} and validates their operands by recursing on them. 53 * It also strips off other valid numeric operators before parsing the leaf operands. 54 * </p> 55 * 56 * <p>A Pattern which is provided by the template method {@link #getDefaultValidationPattern()} is used for parsing 57 * the numeric strings themselves.</p> 58 * 59 * <p>Note that the parsing of expressions done here is very rudimentary, this method is mostly focused on 60 * validating that any operands are valid numeric strings for the attribute type.</p> 61 * 62 * @param valueEntered 63 * @return true if the valueEntered is considered valid 64 */ 65 @Override 66 public boolean isPassesDefaultValidation(String valueEntered) { 67 68 boolean isValid = true; 69 70 if (StringUtils.contains(valueEntered, SearchOperator.AND.op())) { 71 isValid = isOperandsValid(valueEntered, SearchOperator.AND); 72 } else if (StringUtils.contains(valueEntered, SearchOperator.OR.op())) { 73 isValid = isOperandsValid(valueEntered, SearchOperator.OR); 74 } else if (StringUtils.contains(valueEntered, SearchOperator.BETWEEN.op())) { 75 isValid = isOperandsValid(valueEntered, SearchOperator.BETWEEN); 76 } else { 77 // default case is a plain old number, no splitting or recursion required 78 79 Pattern pattern = getDefaultValidationPattern(); 80 Matcher matcher = pattern.matcher(SQLUtils.cleanNumericOfValidOperators(valueEntered).trim()); 81 82 isValid = matcher.matches(); 83 } 84 85 return isValid; 86 } 87 88 /** 89 * Tests that (if the given binaryOperator is present in the valueEntered) the operands are valid. 90 * 91 * <p>The operand test is done by calling isPassesDefaultValidation. If the binaryOperator is not present, 92 * true is returned.</p> 93 * 94 * @param valueEntered the string being validated 95 * @param binaryOperator the operator to test 96 * @return whether the operands are valid for the given binaryOperator 97 */ 98 private boolean isOperandsValid(String valueEntered, SearchOperator binaryOperator) { 99 if (StringUtils.contains(valueEntered, binaryOperator.op())) { 100 // using this split method to make sure we test both sides of the operator. Using String.split would 101 // throw away empty strings, so e.g. "&&100".split("&&") would return an array with one element, ["100"]. 102 String [] l = StringUtils.splitByWholeSeparatorPreserveAllTokens(valueEntered, binaryOperator.op()); 103 for(String value : l) { 104 if (!isPassesDefaultValidation(value)) { 105 return false; 106 } 107 } 108 } 109 110 return true; 111 } 112 }