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