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.ArrayList;
21  import java.util.Collections;
22  import java.util.Iterator;
23  import java.util.NoSuchElementException;
24  import java.util.TreeSet;
25  
26  import javax.naming.NamingEnumeration;
27  import javax.naming.NamingException;
28  
29  
30  /***
31   * NamingEnumeration that enumerates over duplicate values nested into a value 
32   * using a TreeSet.
33   *
34   * @warning The Tuple returned by this listing is always the same instance 
35   * object returned every time. It is reused to for the sake of efficency rather 
36   * than creating a new tuple for each next() call.
37   * 
38   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
39   * @version $Rev: 264732 $
40   */
41  public class DupsEnumeration implements NamingEnumeration
42  {
43      /*** Marker for whether or not next() will return successfully */
44      private boolean hasMore = true;
45      /*** The Tuple to return */
46      private final Tuple returned = new Tuple();
47      /*** The Tuple to store prefetched values with */
48      private final Tuple prefetched = new Tuple();
49      /*** The underlying no duplicates enumeration this enum expands out */
50      private final NoDupsEnumeration underlying;
51  
52      /*** 
53       * The current Tuple returned from the underlying NoDupsEnumeration which
54       * contains TreeSets for Tuple values.  A NoDupsEnumeration on a Table that
55       * allows duplicates essentially returns Strings for keys and TreeSets for 
56       * their values.
57       */
58      private Tuple duplicates;
59      /*** 
60       * The iterator over a set of Tuple values with the same key.  Basically
61       * iterates over the TreeSet values in the duplicates Tuple. 
62       */
63      private Iterator dupIterator;
64  
65  
66      // ------------------------------------------------------------------------
67      // Constructor
68      // ------------------------------------------------------------------------
69  
70  
71      /***
72       * Creates a DupsEnumeration over a enumeration of Tuples holding TreeSets
73       * for values that have the same key.
74       *
75       * @param list the underlying enumeration
76       * @throws NamingException if there is a problem
77       */
78      public DupsEnumeration( NoDupsEnumeration list ) throws NamingException
79      {
80          underlying = list;
81  
82          // Protect against closed cursors
83          if ( ! underlying.hasMore() ) 
84          {
85              close();
86              return;
87          }
88      
89          prefetch();
90      }
91  
92  
93      // ------------------------------------------------------------------------
94      // NamingEnumeration Interface Method Implementations
95      // ------------------------------------------------------------------------
96  
97  
98      /***
99       * Returns the same Tuple every time but with different key/value pairs.
100      * 
101      * @see javax.naming.NamingEnumeration#next()
102      */
103     public Object next() throws NamingException
104     {
105         returned.setKey( prefetched.getKey() );
106         returned.setValue( prefetched.getValue() );
107 
108         prefetch();
109 
110         return returned;
111     }
112     
113     
114     /***
115      * Returns the same Tuple every time but with different key/value pairs.
116      * 
117      * @see java.util.Enumeration#nextElement()
118      */
119     public Object nextElement()
120     {
121         try
122         {
123             return next();
124         }
125         catch ( NamingException ne )
126         {
127             throw new NoSuchElementException();
128         }
129     }
130 
131 
132     /***
133      * @see javax.naming.NamingEnumeration#hasMore()
134      */
135     public boolean hasMore()
136     {
137         return hasMore;
138     }
139 
140 
141     /***
142      * Calls hasMore.
143      *
144      * @see java.util.Enumeration#hasMoreElements()
145      */
146     public boolean hasMoreElements()
147     {
148         return hasMore;
149     }
150 
151 
152     /***
153      * Closes the underlying NamingEnumeration
154      *
155      * @see javax.naming.NamingEnumeration#close()
156      */
157     public void close() 
158     {
159         hasMore = false;
160         underlying.close();
161     }
162 
163 
164     // ------------------------------------------------------------------------
165     // Private/Package Friendly Methods
166     // ------------------------------------------------------------------------
167     
168 
169     /***
170      * Prefetches values into the prefetched Tuple taking into account that 
171      * the returned Tuple values of the underlying enumeration list are really
172      * TreeSets that hold multiple sorted values for the same key.  
173      * 
174      * <p> The values prefetched into the prefetched Tuple are actual values 
175      * taken from the TreeSet.  So this NamingEnumeration simply expands out 
176      * duplicate keyed Tuples which it returns.  iterator is an iteration over
177      * the values held in the TreeSet returned by the underlying enumeration.  
178      * The values pulled off of this iterator are put into prefetched. 
179      * </p>
180      */
181     private void prefetch() throws NamingException
182     {
183         /*
184          * If the iterator over the values of the current key is null or is 
185          * extinguished then we need to advance to the next key.
186          */
187         while ( null == dupIterator || ! dupIterator.hasNext() ) 
188         {
189             /*
190              * If the underlying enumeration has more elements we get the next
191              * key/TreeSet Tuple to work with and get an iterator over it. 
192              */
193             if ( underlying.hasMore() ) 
194             {
195                 duplicates = ( Tuple ) underlying.next();
196                 TreeSet set = ( TreeSet ) duplicates.getValue();
197 
198                 if ( underlying.doAscendingScan() ) 
199                 {
200                     dupIterator = set.iterator();
201                 } 
202                 else 
203                 {
204                     /*
205                      * Need to reverse the list and iterate over the reversed
206                      * list.  
207                      * 
208                      * TODO This can be optimized by using a ReverseIterator 
209                      * over the array list.  I don't think there is a way to 
210                      * do this on the TreeSet.
211                      */ 
212                     ArrayList list = new ArrayList( set.size() );
213                     list.addAll( set );
214                     Collections.reverse( list );
215                     dupIterator = list.iterator();
216                 }
217             } 
218             else 
219             {
220                 close();
221                 return;
222             }
223         }
224 
225         /*
226          * If we get to this point then iterator has more elements and 
227          * duplicates holds the Tuple containing the key and TreeSet of 
228          * values for that key which the iterator iterates over.  All we
229          * need to do is populate the prefetched Tuple with the key and the
230          * next value in the iterator.
231          */
232         prefetched.setKey( duplicates.getKey() );
233         prefetched.setValue( dupIterator.next() );
234     }
235 }