View Javadoc

1   /***
2    *  Copyright 2003-2010 Terracotta, Inc.
3    *
4    *  Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
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  
17  package net.sf.ehcache.search.expression;
18  
19  import java.util.Map;
20  import java.util.regex.Pattern;
21  
22  import net.sf.ehcache.Element;
23  import net.sf.ehcache.search.SearchException;
24  import net.sf.ehcache.search.attribute.AttributeExtractor;
25  
26  /***
27   * A regular expression criteria that matches attribute string values. For non <code>java.lang.String attributes</code>,
28   * the <code>toString()</code> form is used in the comparison. <br>
29   * <br>
30   * Expressions are <b>always case insensitive</b><br>
31   * <br>
32   * The following special characters are supported:<br>
33   * <ul>
34   * <li>'?' - match any one single character
35   * <li>'*' - match any multiple character(s) (including zero)
36   * </ul>
37   * The supported wildcard characters can be escaped with a backslash '\', and a literal backslash can be included with '//'<br>
38   * <br>
39   * WARN: Expressions starting with a leading wildcard character are potentially very expensive (ie. full scan) for indexed caches
40   * <p/>
41   *
42   * @author teck
43   */
44  public class ILike extends BaseCriteria {
45  
46      private final String attributeName;
47      private final String regex;
48      private final Pattern pattern;
49  
50      /***
51       * Construct a "like" criteria for the given expression
52       *
53       * @param attributeName attribute name
54       * @param regex expression
55       */
56      public ILike(String attributeName, String regex) {
57          if ((attributeName == null) || (regex == null)) {
58              throw new SearchException("Both the attribute name and regex must be non null.");
59          }
60  
61          this.attributeName = attributeName;
62          this.regex = regex;
63          this.pattern = convertRegex(regex.trim());
64      }
65  
66      /***
67       * Return attribute name.
68       *
69       * @return String attribute name
70       */
71      public String getAttributeName() {
72          return attributeName;
73      }
74  
75      /***
76       * Return regex string.
77       *
78       * @return String regex.
79       */
80      public String getRegex() {
81          return regex;
82      }
83  
84      private static Pattern convertRegex(final String expr) {
85          if (expr.length() == 0) {
86              throw new SearchException("Zero length regex");
87          }
88  
89          StringBuilder javaRegex = new StringBuilder("^");
90  
91          boolean escape = false;
92          for (int i = 0; i < expr.length(); i++) {
93              char ch = expr.charAt(i);
94  
95              if (escape) {
96                  switch (ch) {
97                      case '//':
98                      case '?':
99                      case '*': {
100                         javaRegex.append(Pattern.quote(lowerCase(ch)));
101                         break;
102                     }
103 
104                     default: {
105                         throw new SearchException("Illegal escape character (" + ch + ") in regex: " + expr);
106                     }
107                 }
108 
109                 escape = false;
110             } else {
111                 switch (ch) {
112                     case '//': {
113                         escape = true;
114                         break;
115                     }
116                     case '?': {
117                         javaRegex.append(".");
118                         break;
119                     }
120                     case '*': {
121                         javaRegex.append(".*");
122                         break;
123                     }
124                     default: {
125                         javaRegex.append(Pattern.quote(lowerCase(ch)));
126                     }
127                 }
128             }
129         }
130 
131         javaRegex.append("$");
132 
133         return Pattern.compile(javaRegex.toString(), Pattern.DOTALL);
134     }
135 
136     private static String lowerCase(char ch) {
137         // heeding the advice in Character.toLowerCase() and using String.toLowerCase() instead here
138         return Character.toString(ch).toLowerCase();
139     }
140 
141     /***
142      * {@inheritDoc}
143      */
144     public boolean execute(Element e, Map<String, AttributeExtractor> attributeExtractors) {
145         Object value = attributeExtractors.get(attributeName).attributeFor(e, attributeName);
146         if (value == null) {
147             return false;
148         }
149 
150         String asString = value.toString().toLowerCase();
151 
152         return pattern.matcher(asString).matches();
153     }
154 
155 }