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