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}