1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.ldap.server.db;
18
19
20 import javax.naming.NamingEnumeration;
21 import javax.naming.NamingException;
22 import javax.naming.directory.SearchControls;
23 import javax.naming.directory.SearchResult;
24 import javax.naming.ldap.LdapContext;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.List;
28
29
30 /***
31 * A enumeration decorator which filters database search results as they are
32 * being enumerated back to the client caller.
33 *
34 * @see SearchResultFilter
35 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
36 * @version $Rev: 159259 $
37 */
38 public class ResultFilteringEnumeration implements NamingEnumeration
39 {
40 /*** the list of filters to be applied */
41 private final List filters;
42 /*** the underlying decorated enumeration */
43 private final NamingEnumeration decorated;
44
45 /*** the first accepted search result that is prefetched */
46 private SearchResult prefetched;
47 /*** flag storing closed state of this naming enumeration */
48 private boolean isClosed = false;
49 /*** the controls associated with the search operation */
50 private final SearchControls searchControls;
51 /*** the LDAP context that made the search creating this enumeration */
52 private final LdapContext ctx;
53
54
55
56
57
58
59
60 /***
61 * Creates a new database result filtering enumeration to decorate an
62 * underlying enumeration.
63 *
64 * @param decorated the underlying decorated enumeration
65 * @param searchControls the search controls associated with the search
66 * creating this enumeration
67 * @param ctx the LDAP context that made the search creating this
68 * enumeration
69 */
70 public ResultFilteringEnumeration( NamingEnumeration decorated,
71 SearchControls searchControls,
72 LdapContext ctx,
73 SearchResultFilter filter )
74 throws NamingException
75 {
76 this.searchControls = searchControls;
77 this.ctx = ctx;
78 this.filters = new ArrayList();
79 this.filters.add( filter );
80 this.decorated = decorated;
81
82 if ( ! decorated.hasMore() )
83 {
84 close();
85 return;
86 }
87
88 prefetch();
89 }
90
91
92 /***
93 * Creates a new database result filtering enumeration to decorate an
94 * underlying enumeration.
95 *
96 * @param decorated the underlying decorated enumeration
97 * @param searchControls the search controls associated with the search
98 * creating this enumeration
99 * @param ctx the LDAP context that made the search creating this
100 * enumeration
101 */
102 public ResultFilteringEnumeration( NamingEnumeration decorated,
103 SearchControls searchControls,
104 LdapContext ctx,
105 List filters )
106 throws NamingException
107 {
108 this.searchControls = searchControls;
109 this.ctx = ctx;
110 this.filters = new ArrayList();
111 this.filters.addAll( filters );
112 this.decorated = decorated;
113
114 if ( ! decorated.hasMore() )
115 {
116 close();
117 return;
118 }
119
120 prefetch();
121 }
122
123
124
125
126
127
128
129 /***
130 * Adds a database search result filter to this filtering enumeration at
131 * the very end of the filter list. Filters are applied in the order of
132 * addition.
133 *
134 * @param filter a filter to apply to the results
135 * @return the result of {@link List#add(Object)}
136 */
137 public boolean addResultFilter( SearchResultFilter filter )
138 {
139 return filters.add( filter );
140 }
141
142
143 /***
144 * Removes a database search result filter from the filter list of this
145 * filtering enumeration.
146 *
147 * @param filter a filter to remove from the filter list
148 * @return the result of {@link List#remove(Object)}
149 */
150 public boolean removeResultFilter( SearchResultFilter filter )
151 {
152 return filters.remove( filter );
153 }
154
155
156 /***
157 * Gets an unmodifiable list of filters.
158 *
159 * @return the result of {@link Collections#unmodifiableList(List)}
160 */
161 public List getFilters()
162 {
163 return Collections.unmodifiableList( filters );
164 }
165
166
167
168
169
170
171
172 public void close() throws NamingException
173 {
174 isClosed = true;
175 decorated.close();
176 }
177
178
179 public boolean hasMore()
180 {
181 return !isClosed;
182 }
183
184
185 public Object next() throws NamingException
186 {
187 SearchResult retVal = this.prefetched;
188 prefetch();
189 return retVal;
190 }
191
192
193
194
195
196
197
198 public boolean hasMoreElements()
199 {
200 return !isClosed;
201 }
202
203
204 public Object nextElement()
205 {
206 SearchResult retVal = this.prefetched;
207
208 try
209 {
210 prefetch();
211 }
212 catch ( NamingException e )
213 {
214 e.printStackTrace();
215 }
216
217 return retVal;
218 }
219
220
221
222
223
224
225
226 /***
227 * Keeps getting results from the underlying decorated filter and applying
228 * the filters until a result is accepted by all and set as the prefetced
229 * result to return on the next() result request. If no prefetched value
230 * can be found before exhausting the decorated enumeration, then this and
231 * the underlying enumeration is closed.
232 *
233 * @throws NamingException if there are problems getting results from the
234 * underlying enumeration
235 */
236 private void prefetch() throws NamingException
237 {
238 SearchResult tmp = null;
239
240 while( decorated.hasMore() )
241 {
242 boolean accepted = true;
243 tmp = ( SearchResult ) decorated.next();
244
245
246 if ( filters.isEmpty() )
247 {
248 this.prefetched = tmp;
249 return;
250 }
251 else if ( filters.size() == 1 )
252 {
253 accepted = ( ( SearchResultFilter ) filters.get( 0 ) )
254 .accept( ctx, tmp, searchControls );
255 if ( accepted )
256 {
257 this.prefetched = tmp;
258 return;
259 }
260
261 continue;
262 }
263
264
265 for ( int ii = 0; ii < filters.size(); ii ++ )
266 {
267 SearchResultFilter filter = ( SearchResultFilter ) filters.get( ii );
268 accepted &= filter.accept( ctx, tmp, searchControls );
269
270 if ( ! accepted )
271 {
272 continue;
273 }
274 }
275
276
277
278
279
280
281 this.prefetched = tmp;
282 return;
283 }
284
285
286
287
288
289 close();
290 }
291 }