View Javadoc
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 }