001/** 002 * Copyright 2005-2017 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.kew.docsearch; 017 018import org.apache.commons.lang.StringUtils; 019import org.kuali.rice.core.api.search.SearchOperator; 020import org.kuali.rice.core.framework.persistence.jdbc.sql.SQLUtils; 021 022import java.util.regex.Matcher; 023import java.util.regex.Pattern; 024 025/** 026 * Abstract base class for numeric searchable attributes. 027 * 028 * <p>Contains common logic for validation along with a template method for retrieving a validation Pattern.</p> 029 * 030 * @author Kuali Rice Team (rice.collab@kuali.org) 031 */ 032public abstract class SearchableAttributeNumericBase extends SearchableAttributeBase { 033 034 /** 035 * Returns a Pattern object used for validating the format of number Strings. 036 * 037 * <p>{@link Pattern}s are immutable and thus safe for concurrent use, so it makes sense to return 038 * a pre-compiled static instance.</p> 039 * 040 * <p>The pattern should only match valid String representations of the numeric type</p> 041 * 042 * @return the Pattern used for validating number Strings. 043 */ 044 abstract protected Pattern getDefaultValidationPattern(); 045 046 /** 047 * is the given value valid for searching against this attribute? 048 * 049 * <p>This method detects the binary operators defined by 050 * {@link org.kuali.rice.core.api.search.SearchOperator#BETWEEN}, 051 * {@link org.kuali.rice.core.api.search.SearchOperator#AND}, and 052 * {@link org.kuali.rice.core.api.search.SearchOperator#OR} and validates their operands by recursing on them. 053 * It also strips off other valid numeric operators before parsing the leaf operands. 054 * </p> 055 * 056 * <p>A Pattern which is provided by the template method {@link #getDefaultValidationPattern()} is used for parsing 057 * the numeric strings themselves.</p> 058 * 059 * <p>Note that the parsing of expressions done here is very rudimentary, this method is mostly focused on 060 * validating that any operands are valid numeric strings for the attribute type.</p> 061 * 062 * @param valueEntered 063 * @return true if the valueEntered is considered valid 064 */ 065 @Override 066 public boolean isPassesDefaultValidation(String valueEntered) { 067 068 boolean isValid = true; 069 070 if (StringUtils.contains(valueEntered, SearchOperator.AND.op())) { 071 isValid = isOperandsValid(valueEntered, SearchOperator.AND); 072 } else if (StringUtils.contains(valueEntered, SearchOperator.OR.op())) { 073 isValid = isOperandsValid(valueEntered, SearchOperator.OR); 074 } else if (StringUtils.contains(valueEntered, SearchOperator.BETWEEN.op())) { 075 isValid = isOperandsValid(valueEntered, SearchOperator.BETWEEN); 076 } else { 077 // default case is a plain old number, no splitting or recursion required 078 079 Pattern pattern = getDefaultValidationPattern(); 080 Matcher matcher = pattern.matcher(SQLUtils.cleanNumericOfValidOperators(valueEntered).trim()); 081 082 isValid = matcher.matches(); 083 } 084 085 return isValid; 086 } 087 088 /** 089 * Tests that (if the given binaryOperator is present in the valueEntered) the operands are valid. 090 * 091 * <p>The operand test is done by calling isPassesDefaultValidation. If the binaryOperator is not present, 092 * true is returned.</p> 093 * 094 * @param valueEntered the string being validated 095 * @param binaryOperator the operator to test 096 * @return whether the operands are valid for the given binaryOperator 097 */ 098 private boolean isOperandsValid(String valueEntered, SearchOperator binaryOperator) { 099 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}