View Javadoc
1   /*
2    * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  package com.sun.jndi.toolkit.dir;
26  
27  import javax.naming.*;
28  import javax.naming.directory.SearchControls;
29  import java.util.*;
30  
31  /**
32    * A class for recursively enumerating the contents of a Context;
33    *
34    * @author Jon Ruiz
35    */
36  public class ContextEnumerator implements NamingEnumeration<Binding> {
37  
38      private static boolean debug = false;
39      private NamingEnumeration<Binding> children = null;
40      private Binding currentChild = null;
41      private boolean currentReturned = false;
42      private Context root;
43      private ContextEnumerator currentChildEnum = null;
44      private boolean currentChildExpanded = false;
45      private boolean rootProcessed = false;
46      private int scope = SearchControls.SUBTREE_SCOPE;
47      private String contextName = "";
48  
49      public ContextEnumerator(Context context) throws NamingException {
50          this(context, SearchControls.SUBTREE_SCOPE);
51      }
52  
53      public ContextEnumerator(Context context, int scope)
54          throws NamingException {
55              // return this object except when searching single-level
56          this(context, scope, "", scope != SearchControls.ONELEVEL_SCOPE);
57     }
58  
59      protected ContextEnumerator(Context context, int scope, String contextName,
60                               boolean returnSelf)
61          throws NamingException {
62          if(context == null) {
63              throw new IllegalArgumentException("null context passed");
64          }
65  
66          root = context;
67  
68          // No need to list children if we're only searching object
69          if (scope != SearchControls.OBJECT_SCOPE) {
70              children = getImmediateChildren(context);
71          }
72          this.scope = scope;
73          this.contextName = contextName;
74          // pretend root is processed, if we're not supposed to return ourself
75          rootProcessed = !returnSelf;
76          prepNextChild();
77      }
78  
79      // Subclass should override if it wants to avoid calling obj factory
80      protected NamingEnumeration<Binding> getImmediateChildren(Context ctx)
81          throws NamingException {
82              return ctx.listBindings("");
83      }
84  
85      // Subclass should override so that instance is of same type as subclass
86      protected ContextEnumerator newEnumerator(Context ctx, int scope,
87          String contextName, boolean returnSelf) throws NamingException {
88              return new ContextEnumerator(ctx, scope, contextName, returnSelf);
89      }
90  
91      public boolean hasMore() throws NamingException {
92          return !rootProcessed ||
93              (scope != SearchControls.OBJECT_SCOPE && hasMoreDescendants());
94      }
95  
96      public boolean hasMoreElements() {
97          try {
98              return hasMore();
99          } catch (NamingException e) {
100             return false;
101         }
102     }
103 
104     public Binding nextElement() {
105         try {
106             return next();
107         } catch (NamingException e) {
108             throw new NoSuchElementException(e.toString());
109         }
110     }
111 
112     public Binding next() throws NamingException {
113         if (!rootProcessed) {
114             rootProcessed = true;
115             return new Binding("", root.getClass().getName(),
116                                root, true);
117         }
118 
119         if (scope != SearchControls.OBJECT_SCOPE && hasMoreDescendants()) {
120             return getNextDescendant();
121         }
122 
123         throw new NoSuchElementException();
124     }
125 
126     public void close() throws NamingException {
127         root = null;
128     }
129 
130     private boolean hasMoreChildren() throws NamingException {
131         return children != null && children.hasMore();
132     }
133 
134     private Binding getNextChild() throws NamingException {
135         Binding oldBinding = children.next();
136         Binding newBinding = null;
137 
138         // if the name is relative, we need to add it to the name of this
139         // context to keep it relative w.r.t. the root context we are
140         // enumerating
141         if(oldBinding.isRelative() && !contextName.equals("")) {
142             NameParser parser = root.getNameParser("");
143             Name newName = parser.parse(contextName);
144             newName.add(oldBinding.getName());
145             if(debug) {
146                 System.out.println("ContextEnumerator: adding " + newName);
147             }
148             newBinding = new Binding(newName.toString(),
149                                      oldBinding.getClassName(),
150                                      oldBinding.getObject(),
151                                      oldBinding.isRelative());
152         } else {
153             if(debug) {
154                 System.out.println("ContextEnumerator: using old binding");
155             }
156             newBinding = oldBinding;
157         }
158 
159         return newBinding;
160     }
161 
162     private boolean hasMoreDescendants() throws NamingException {
163         // if the current child is expanded, see if it has more elements
164         if (!currentReturned) {
165             if(debug) {System.out.println("hasMoreDescendants returning " +
166                                           (currentChild != null) ); }
167             return currentChild != null;
168         } else if (currentChildExpanded && currentChildEnum.hasMore()) {
169 
170             if(debug) {System.out.println("hasMoreDescendants returning " +
171                 "true");}
172 
173             return true;
174         } else {
175             if(debug) {System.out.println("hasMoreDescendants returning " +
176                 "hasMoreChildren");}
177             return hasMoreChildren();
178         }
179     }
180 
181     private Binding getNextDescendant() throws NamingException {
182 
183         if (!currentReturned) {
184             // returning parent
185             if(debug) {System.out.println("getNextDescedant: simple case");}
186 
187             currentReturned = true;
188             return currentChild;
189 
190         } else if (currentChildExpanded && currentChildEnum.hasMore()) {
191 
192             if(debug) {System.out.println("getNextDescedant: expanded case");}
193 
194             // if the current child is expanded, use it's enumerator
195             return currentChildEnum.next();
196 
197         } else {
198 
199             // Ready to go onto next child
200             if(debug) {System.out.println("getNextDescedant: next case");}
201 
202             prepNextChild();
203             return getNextDescendant();
204         }
205     }
206 
207     private void prepNextChild() throws NamingException {
208         if(hasMoreChildren()) {
209             try {
210                 currentChild = getNextChild();
211                 currentReturned = false;
212             } catch (NamingException e){
213                 if (debug) System.out.println(e);
214                 if (debug) e.printStackTrace();
215             }
216         } else {
217             currentChild = null;
218             return;
219         }
220 
221         if(scope == SearchControls.SUBTREE_SCOPE &&
222            currentChild.getObject() instanceof Context) {
223             currentChildEnum = newEnumerator(
224                                           (Context)(currentChild.getObject()),
225                                           scope, currentChild.getName(),
226                                           false);
227             currentChildExpanded = true;
228             if(debug) {System.out.println("prepNextChild: expanded");}
229         } else {
230             currentChildExpanded = false;
231             currentChildEnum = null;
232             if(debug) {System.out.println("prepNextChild: normal");}
233         }
234     }
235 }