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 java.util.HashMap;
23 import java.util.Map;
24 import java.util.NoSuchElementException;
25
26
27 /***
28 * A Cursor of Cursors performing a union on all underlying Cursors resulting
29 * in the disjunction of expressions represented by the constituant child
30 * Cursors. This cursor prefetches underlying Cursor values so that it can
31 * comply with the defined Cursor semantics.
32 *
33 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
34 * @version $Rev: 159259 $
35 */
36 public class DisjunctionEnumeration implements NamingEnumeration
37 {
38 /*** The underlying child enumerations */
39 private final NamingEnumeration [] children;
40 /*** LUT used to avoid returning duplicates */
41 private final Map candidates = new HashMap();
42 /*** Index of current cursor used */
43 private int index = 0;
44 /*** Candidate to return */
45 private final IndexRecord candidate = new IndexRecord();
46 /*** Prefetched record returned */
47 private final IndexRecord prefetched = new IndexRecord();
48 /*** Used to determine if this enumeration has been exhausted */
49 private boolean hasMore = true;
50
51
52
53
54
55
56
57 /***
58 * Creates a DisjunctionEnumeration over a set of child NamingEnumerations.
59 * The returned result is the union of all underlying NamingEnumerations
60 * without duplicates.
61 *
62 * @param children array of child NamingInstances
63 * @throws NamingException if something goes wrong
64 */
65 public DisjunctionEnumeration( NamingEnumeration [] children )
66 throws NamingException
67 {
68 this.children = children;
69
70
71 if ( children.length <= 0 )
72 {
73 hasMore = false;
74 return;
75 }
76
77
78 while ( ! children[index].hasMore() )
79 {
80 index++;
81
82
83
84 if ( index >= children.length )
85 {
86 close();
87 return;
88 }
89 }
90
91
92 IndexRecord rec = ( IndexRecord ) children[index].next();
93 prefetched.copy( rec );
94 candidates.put( rec.getEntryId(), rec.getEntryId() );
95 }
96
97
98
99
100
101
102
103 /***
104 * @see java.util.Enumeration#nextElement()
105 */
106 public Object nextElement()
107 {
108 try
109 {
110 return next();
111 }
112 catch ( NamingException e )
113 {
114 throw new NoSuchElementException();
115 }
116 }
117
118
119 /***
120 * @see java.util.Enumeration#hasMoreElements()
121 */
122 public boolean hasMoreElements()
123 {
124 return hasMore();
125 }
126
127
128
129
130
131
132
133 /***
134 * Advances this Cursor one position. Duplicates are not returned so if
135 * underlying cursors keep returning duplicates the child cursors will be
136 * advanced until a unique candidate is found or all child cursors are
137 * exhausted.
138 *
139 * @return a candidate element
140 * @throws NamingException if an error occurs
141 */
142 public Object next() throws NamingException
143 {
144
145 candidate.copy( prefetched );
146
147 do
148 {
149
150 while ( ! children[index].hasMore() )
151 {
152 index++;
153
154
155
156
157
158 if ( index >= children.length )
159 {
160 close();
161 return candidate;
162 }
163 }
164
165
166 IndexRecord rec = ( IndexRecord ) children[index].next();
167 prefetched.copy( rec );
168
169
170
171 } while ( candidates.containsKey( prefetched.getEntryId() ) );
172
173
174 candidates.put( candidate.getEntryId(), candidate.getEntryId() );
175
176
177 return candidate;
178 }
179
180
181 /***
182 * Tests if a prefetched value exists and a call to advance will hence
183 * succeed.
184 *
185 * @return true if a call to advance will succeed false otherwise.
186 */
187 public boolean hasMore()
188 {
189 return hasMore;
190 }
191
192
193 /***
194 * Closes all the underlying Cursors and not fail fast. All enumerations
195 * will have close attempts made on them.
196 *
197 * @throws NamingException if we cannot close all enumerations
198 */
199 public void close() throws NamingException
200 {
201 Throwable throwable = null;
202 hasMore = false;
203
204 for ( int ii = 0; ii < children.length; ii++ )
205 {
206 try
207 {
208
209
210 children[ii].close();
211 }
212 catch ( Throwable t )
213 {
214 throwable = t;
215 }
216 }
217
218 if ( null != throwable && throwable instanceof NamingException )
219 {
220 throw ( NamingException ) throwable;
221 }
222 else if ( null != throwable )
223 {
224 NamingException ne = new NamingException();
225 ne.setRootCause( throwable );
226 throw ne;
227 }
228 }
229 }