001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.util.visit; 018 019import java.util.Collections; 020import java.util.Iterator; 021 022import org.apache.wicket.util.lang.Args; 023 024/** 025 * Utility class that contains visitor/traversal related code 026 */ 027public class Visits 028{ 029 /** Constructor */ 030 private Visits() 031 { 032 } 033 034 private static class SingletonIterable<T> implements Iterable<T> 035 { 036 private final T singleton; 037 038 public SingletonIterable(final T singleton) 039 { 040 this.singleton = singleton; 041 } 042 043 @Override 044 public Iterator<T> iterator() 045 { 046 return Collections.singleton(singleton).iterator(); 047 } 048 } 049 050 /** 051 * Visits container and its children pre-order (parent first). Children are determined by 052 * calling {@link Iterable#iterator()}. 053 * 054 * @param <S> 055 * the type of object that will be visited, notice that {@code container} is not 056 * declared as {@code Iterable<S>} because it may return a generalization of 057 * {@code S} 058 * @param <R> 059 * the type of object that should be returned from the visitor, use {@link Void} if 060 * no return value is needed 061 * @param container 062 * object whose children will be visited 063 * @param visitor 064 * the visitor 065 * @return return value from the {@code visitor} or {@code null} if none 066 */ 067 @SuppressWarnings({ "unchecked", "rawtypes" }) 068 public static <S, R> R visit(final Iterable<? super S> container, 069 final IVisitor<S, R> visitor) 070 { 071 return (R)visitChildren(new SingletonIterable(container), visitor, IVisitFilter.ANY); 072 } 073 074 /** 075 * Visits container and its children pre-order (parent first). Children are determined by 076 * calling {@link Iterable#iterator()}. 077 * 078 * @param <S> 079 * the type of object that will be visited, notice that {@code container} is not 080 * declared as {@code Iterable<S>} because it may return a generalization of 081 * {@code S} 082 * @param <R> 083 * the type of object that should be returned from the visitor, use {@link Void} if 084 * no return value is needed 085 * @param container 086 * object whose children will be visited 087 * @param visitor 088 * the visitor 089 * @param filter 090 * filter used to limit the types of objects that will be visited 091 * @return return value from the {@code visitor} or {@code null} if none 092 */ 093 @SuppressWarnings({ "unchecked", "rawtypes" }) 094 public static <S, R> R visit(final Iterable<? super S> container, 095 final IVisitor<S, R> visitor, final IVisitFilter filter) 096 { 097 return (R)visitChildren(new SingletonIterable(container), visitor, filter); 098 } 099 100 /** 101 * Visits children of the specified {@link Iterable} pre-order (parent first). Children are 102 * determined by calling {@link Iterable#iterator()}. 103 * 104 * @param <S> 105 * the type of object that will be visited, notice that {@code container} is not 106 * declared as {@code Iterable<S>} because it may return a generalization of 107 * {@code S} 108 * @param <R> 109 * the type of object that should be returned from the visitor, use {@link Void} if 110 * no return value is needed 111 * @param container 112 * object whose children will be visited 113 * @param visitor 114 * the visitor 115 * @param filter 116 * filter used to limit the types of objects that will be visited 117 * @return return value from the {@code visitor} or {@code null} if none 118 */ 119 public static <S, R> R visitChildren(final Iterable<? super S> container, 120 final IVisitor<S, R> visitor, final IVisitFilter filter) 121 { 122 Visit<R> visit = new Visit<>(); 123 visitChildren(container, visitor, filter, visit); 124 return visit.getResult(); 125 } 126 127 @SuppressWarnings("unchecked") 128 private static <S, R> void visitChildren(final Iterable<? super S> container, 129 final IVisitor<S, R> visitor, final IVisitFilter filter, final Visit<R> visit) 130 { 131 Args.notNull(visitor, "visitor"); 132 133 // Iterate through children of this container 134 for (final Object child : container) 135 { 136 // Get next child component 137 // Is the child of the correct class (or was no class specified)? 138 if (filter.visitObject(child)) 139 { 140 Visit<R> childTraversal = new Visit<>(); 141 142 // Call visitor 143 S s = (S)child; 144 visitor.component(s, childTraversal); 145 146 if (childTraversal.isStopped()) 147 { 148 visit.stop(childTraversal.getResult()); 149 return; 150 } 151 else if (childTraversal.isDontGoDeeper()) 152 { 153 continue; 154 } 155 } 156 157 // If child is a container 158 if (!visit.isDontGoDeeper() && (child instanceof Iterable<?>) && 159 filter.visitChildren(child)) 160 { 161 // visit the children in the container 162 visitChildren((Iterable<? super S>)child, visitor, filter, visit); 163 164 if (visit.isStopped()) 165 { 166 return; 167 } 168 } 169 } 170 } 171 172 /** 173 * Visits children of the specified {@link Iterable} pre-order (parent first). Children are 174 * determined by calling {@link Iterable#iterator()}. 175 * 176 * @param <S> 177 * the type of object that will be visited, notice that {@code container} is not 178 * declared as {@code Iterable<S>} because it may return a generalization of 179 * {@code S} 180 * @param <R> 181 * the type of object that should be returned from the visitor, use {@link Void} if 182 * no return value is needed 183 * @param container 184 * object whose children will be visited 185 * @param visitor 186 * the visitor 187 * @return return value from the {@code visitor} or {@code null} if none 188 */ 189 public static <S, R> R visitChildren(final Iterable<? super S> container, 190 final IVisitor<S, R> visitor) 191 { 192 return visitChildren(container, visitor, IVisitFilter.ANY); 193 } 194 195 /** 196 * Visits the specified object and any of its children using a post-order (child first) 197 * traversal. Children are determined by calling {@link Iterable#iterator()} if the object 198 * implements {@link Iterable}. 199 * 200 * @param <S> 201 * the type of object that will be visited, notice that {@code container} is not 202 * declared as {@code Iterable<S>} because it may return a generalization of 203 * {@code S} 204 * @param <R> 205 * the type of object that should be returned from the visitor, use {@link Void} if 206 * no return value is needed 207 * @param root 208 * root object that will be visited 209 * @param visitor 210 * the visitor 211 * @return return value from the {@code visitor} or {@code null} if none 212 */ 213 public static <S, R> R visitPostOrder(final S root, 214 final org.apache.wicket.util.visit.IVisitor<S, R> visitor) 215 { 216 return visitPostOrder(root, visitor, IVisitFilter.ANY); 217 } 218 219 /** 220 * Visits the specified object and any of its children using a post-order (child first) 221 * traversal. Children are determined by calling {@link Iterable#iterator()} if the object 222 * implements {@link Iterable}. 223 * 224 * @param <S> 225 * the type of object that will be visited, notice that {@code container} is not 226 * declared as {@code Iterable<S>} because it may return a generalization of 227 * {@code S} 228 * @param <R> 229 * the type of object that should be returned from the visitor, use {@link Void} if 230 * no return value is needed 231 * @param root 232 * root object that will be visited 233 * @param visitor 234 * the visitor 235 * @param filter 236 * filter used to limit the types of objects that will be visited 237 * @return return value from the {@code visitor} or {@code null} if none 238 */ 239 public static <S, R> R visitPostOrder(final Object root, 240 final org.apache.wicket.util.visit.IVisitor<S, R> visitor, final IVisitFilter filter) 241 { 242 Args.notNull(visitor, "visitor"); 243 244 Visit<R> visit = new Visit<>(); 245 visitPostOrderHelper(root, visitor, filter, visit); 246 return visit.getResult(); 247 } 248 249 @SuppressWarnings("unchecked") 250 private static <S, R> void visitPostOrderHelper(final Object component, 251 final org.apache.wicket.util.visit.IVisitor<S, R> visitor, final IVisitFilter filter, 252 final Visit<R> visit) 253 { 254 if (component instanceof Iterable<?>) 255 { 256 final Iterable<?> container = (Iterable<?>)component; 257 if (filter.visitChildren(container)) 258 { 259 Visit<R> childTraversal = new Visit<>(); 260 for (final Object child : ((Iterable<?>)component)) 261 { 262 visitPostOrderHelper(child, visitor, filter, childTraversal); 263 if (childTraversal.isStopped()) 264 { 265 visit.stop(childTraversal.getResult()); 266 return; 267 } 268 } 269 } 270 } 271 272 if (filter.visitObject(component)) 273 { 274 visitor.component((S)component, visit); 275 } 276 } 277}