View Javadoc

1   /***
2    * LikeStringFilter.java
3    *
4    * This file is part of the creme library.
5    * The creme library intends to ease the development effort of large
6    * applications by providing easy to use support classes.
7    *
8    * Copyright (C) 2002 Denis Bregeon
9    *
10   * This library is free software; you can redistribute it and/or
11   * modify it under the terms of the GNU Lesser General Public
12   * License as published by the Free Software Foundation; either
13   * version 2.1 of the License, or (at your option) any later version.
14   *
15   * This library is distributed in the hope that it will be useful,
16   * but WITHOUT ANY WARRANTY; without even the implied warranty of
17   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18   * Lesser General Public License for more details.
19   *
20   * You should have received a copy of the GNU Lesser General Public
21   * License along with this library; if not, write to the Free Software
22   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23   *
24   * contact information: dbregeon@sourceforge.net
25   */
26  package org.jcreme.filters;
27  
28  import java.io.Serializable;
29  import java.util.StringTokenizer;
30  
31  /***
32   * A filter that filters strings based on a comparison operator (which can
33   * basically be LIKE or NOT_LIKE) , and a stem that given strings have to be
34   * 'like' or 'not like'.
35   * 
36   * @author $Author: dbregeon $
37   * @version $Revision: 1.4 $
38   */
39  public class LikeStringFilter implements Filter, Serializable {
40  	static final long serialVersionUID = -146567890525271208L;
41  
42  	/***
43  	 * The comparison operator used in this filter.
44  	 */
45  	private final StringComparisonOperator operator;
46  
47  	/***
48  	 * The stem used to filter strings. It is made of letters and two special
49  	 * characters : % standing for any number of letters, and _ standing for one
50  	 * letter.
51  	 */
52  	private final String stem;
53  
54  	/***
55  	 * Creates new LikeStringComparator
56  	 * 
57  	 * @param op
58  	 *            The operator to be applied.
59  	 * @param stem
60  	 *            The stem to be used to filter strings.
61  	 * @throws IllegalArgumentException
62  	 *             In case any of the passed in parameters is null, which is not
63  	 *             wanted here.
64  	 */
65  	public LikeStringFilter(StringComparisonOperator op, String stem)
66  			throws IllegalArgumentException {
67  		if ((op == null) || (stem == null)) {
68  			throw new IllegalArgumentException("Null is not a valid value.");
69  		}
70  		this.operator = op;
71  		this.stem = stem;
72  	}
73  
74  	/***
75  	 * Tells whether the given object 'passes' the filter or not.
76  	 * 
77  	 * @return True if the passed in parameter is accepted by the filter, false
78  	 *         otherwise.
79  	 * @param obj
80  	 *            The object you want to 'test'.
81  	 */
82  	public boolean accept(final Object obj) {
83  		boolean result = (obj != null) && (obj instanceof String);
84  		if (result) {
85  			final String str = (String) obj;
86  			final StringTokenizer tokenizer = new StringTokenizer(this.stem,
87  					"_%", true);
88  			String current = null;
89  			int index = 0;
90  			while ((result) && (tokenizer.hasMoreTokens())
91  					&& (index <= str.length())) {
92  				current = tokenizer.nextToken();
93  				// _ is a wildcard for a single character
94  				// we can advance the parsing index of one character.
95  				if ("_".equals(current)) {
96  					index++;
97  				} else if (!"%".equals(current)) {
98  					// We have to match the current token !
99  					result = str.substring(index,
100 							Math.min(index + current.length(), str.length()))
101 							.equals(current);
102 					if (result) {
103 						index += current.length();
104 					}
105 				} else if ("%".equals(current)) {
106 					while ((tokenizer.hasMoreTokens()) && ("%".equals(current))) {
107 						current = tokenizer.nextToken();
108 					}
109 					if (("%".equals(current)) && (!tokenizer.hasMoreTokens())) {
110 						index = str.length();
111 					} else if ("_".equals(current)) {
112 						index++;
113 					} else {
114 						final int tmp = str.indexOf(current, index);
115 						result = result && (tmp != -1);
116 						if (result) {
117 							index = tmp + current.length();
118 						}
119 					}
120 				}
121 			}
122 			result = result && (index == str.length());
123 			if (this.operator == StringComparisonOperator.NOT_LIKE) {
124 				result = !result;
125 			}
126 		}
127 		return result;
128 	}
129 
130 	/***
131 	 * Gives access to the comparison operator used by the filter.
132 	 * 
133 	 * @return The comparison operator used by this filter.
134 	 */
135 	public ComparisonOperator getOperator() {
136 		return this.operator;
137 	}
138 
139 	/***
140 	 * Gives access to the stem used by this filter.
141 	 * 
142 	 * @return The stem used by this filter.
143 	 */
144 	public String getStem() {
145 		return this.stem;
146 	}
147 
148 	/***
149 	 * Gives a SQL-like string representation of this filter.
150 	 * 
151 	 * @return the string representation of the filter.
152 	 */
153 	public String toString() {
154 		return this.operator + " '" + this.stem + "'";
155 	}
156 
157 	/***
158 	 * Overloads the 'dummy' implementation in Object, so that it returns true
159 	 * only if the passed in parameter is a filter of the same kind as this one,
160 	 * and accepts exactly the same objects.
161 	 * 
162 	 * @return True if the passed in object is a LikeStringFilter that accepts
163 	 *         exactly the same objects as this one.
164 	 * @param o
165 	 *            The object to be compared to this one.
166 	 */
167 	public boolean equals(final Object o) {
168 		boolean result = false;
169 		if (o instanceof LikeStringFilter) {
170 			final LikeStringFilter filter = (LikeStringFilter) o;
171 			result = ((this.operator == filter.getOperator()) && (this.stem
172 					.equals(filter.getStem())));
173 		}
174 		return result;
175 	}
176 
177 	/***
178 	 * Overloads the 'dummy' implementation in Object so that it fulfills the
179 	 * implicit hashCode contract, i.e. same filters have the same hashCode and
180 	 * different ones have different hashCodes.
181 	 * 
182 	 * @return The hash code for this filter.
183 	 */
184 	public int hashCode() {
185 		return this.operator.hashCode() ^ this.stem.hashCode();
186 	}
187 
188 	/***
189 	 * This forces override of the Object class clone method. it makes a deep
190 	 * copy so a change in a subfilter does not affect the cloned version.
191 	 * 
192 	 * @return the cloned object.
193 	 */
194 	public Object clone() {
195 		Object result = null;
196 		try {
197 			result = super.clone();
198 		} catch (CloneNotSupportedException e) {
199 			e.printStackTrace();
200 		}
201 		return result;
202 	}
203 }