View Javadoc

1   /*
2    *   Copyright 2004 The Apache Software Foundation
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 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      // C O N S T R U C T O R S
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          // Close this cursor if their are no children.
71          if ( children.length <= 0 ) 
72          {
73              hasMore = false;
74              return;
75          }
76  
77          // Advance to the first cursor that has a candidate for us.
78          while ( ! children[index].hasMore() ) 
79          {
80              index++;
81  
82              // Close and return if we exhaust the cursors without finding a
83              // valid candidate to return.
84              if ( index >= children.length ) 
85              {
86                  close();
87                  return;
88              }
89          }
90  
91          // Grab the next candidate and add it's id to the LUT/hash of candidates
92          IndexRecord rec = ( IndexRecord ) children[index].next();
93          prefetched.copy( rec );
94          candidates.put( rec.getEntryId(), rec.getEntryId() );
95      }
96  
97  
98      // ------------------------------------------------------------------------
99      // java.util.Enumeration Implementation Methods 
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     // NamingEnumeration Method Implementations
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         // Store the last prefetched candidate to return in candidate
145         candidate.copy( prefetched );
146 
147         do 
148         {
149             // Advance to a Cursor that has the next valid candidate for us.
150             while ( ! children[index].hasMore() ) 
151             {
152                 index++;
153         
154                 /* Close and return existing prefetched candidate if we
155                  * have exhausted the underlying Cursors without finding a
156                  * valid candidate to return.
157                  */
158                 if ( index >= children.length ) 
159                 {
160                     close();
161                     return candidate;
162                 }
163             }
164 
165             // Grab next candidate!
166             IndexRecord rec = ( IndexRecord ) children[index].next();
167             prefetched.copy( rec );
168 
169             // Break through do/while if the candidate is seen for the first
170             // time, meaning we have not returned it already.
171         } while ( candidates.containsKey( prefetched.getEntryId() ) );
172 
173         // Add candidate to LUT of encountered candidates.
174         candidates.put( candidate.getEntryId(), candidate.getEntryId() );
175 
176         // Return the original value saved before overwriting prefetched
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                 // Close all children but don't fail fast meaning don't stop
209                 // closing all children if one fails to close for some reason.
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 }