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 
ContextEnumerator(Context context)49     public ContextEnumerator(Context context) throws NamingException {
50         this(context, SearchControls.SUBTREE_SCOPE);
51     }
52 
ContextEnumerator(Context context, int scope)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 
ContextEnumerator(Context context, int scope, String contextName, boolean returnSelf)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
getImmediateChildren(Context ctx)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
newEnumerator(Context ctx, int scope, String contextName, boolean returnSelf)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 
hasMore()91     public boolean hasMore() throws NamingException {
92         return !rootProcessed ||
93             (scope != SearchControls.OBJECT_SCOPE && hasMoreDescendants());
94     }
95 
hasMoreElements()96     public boolean hasMoreElements() {
97         try {
98             return hasMore();
99         } catch (NamingException e) {
100             return false;
101         }
102     }
103 
nextElement()104     public Binding nextElement() {
105         try {
106             return next();
107         } catch (NamingException e) {
108             throw new NoSuchElementException(e.toString());
109         }
110     }
111 
next()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 
close()126     public void close() throws NamingException {
127         root = null;
128     }
129 
hasMoreChildren()130     private boolean hasMoreChildren() throws NamingException {
131         return children != null && children.hasMore();
132     }
133 
getNextChild()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.isEmpty()) {
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 
hasMoreDescendants()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 
getNextDescendant()181     private Binding getNextDescendant() throws NamingException {
182 
183         if (!currentReturned) {
184             // returning parent
185             if(debug) {System.out.println("getNextDescendant: simple case");}
186 
187             currentReturned = true;
188             return currentChild;
189 
190         } else if (currentChildExpanded && currentChildEnum.hasMore()) {
191 
192             if(debug) {System.out.println("getNextDescendant: 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("getNextDescendant: next case");}
201 
202             prepNextChild();
203             return getNextDescendant();
204         }
205     }
206 
prepNextChild()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 }
236