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