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