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.extensions.markup.html.repeater.tree.table;
018
019import java.util.Iterator;
020
021import org.apache.wicket.extensions.markup.html.repeater.tree.ITreeProvider;
022import org.apache.wicket.markup.repeater.data.IDataProvider;
023import org.apache.wicket.model.IModel;
024
025/**
026 * An adapter of a {@link ITreeProvider} to a {@link IDataProvider}.
027 * 
028 * @author svenmeier
029 * @param <T>
030 *            node type
031 */
032public abstract class TreeDataProvider<T> implements ITreeDataProvider<T>
033{
034        private static final long serialVersionUID = 1L;
035
036        private final ITreeProvider<T> provider;
037
038        private transient Branch<T> currentBranch;
039
040        private transient Branch<T> previousBranch;
041
042        private int size = -1;
043
044        /**
045         * Construct.
046         * 
047         * @param provider
048         *            the provider to adapt
049         */
050        protected TreeDataProvider(ITreeProvider<T> provider)
051        {
052                this.provider = provider;
053        }
054
055        @Override
056        public long size()
057        {
058                if (size == -1)
059                {
060                        size = 0;
061
062                        Iterator<? extends T> iterator = iterator(0, Integer.MAX_VALUE);
063                        while (iterator.hasNext())
064                        {
065                                iterator.next();
066
067                                size++;
068                        }
069                }
070                return size;
071        }
072
073        @Override
074        public Iterator<? extends T> iterator(long first, long count)
075        {
076                currentBranch = new Branch<>(null, provider.getRoots());
077
078                Iterator<T> iterator = new Iterator<T>()
079                {
080                        @Override
081                        public boolean hasNext()
082                        {
083                                while (currentBranch != null)
084                                {
085                                        if (currentBranch.hasNext())
086                                        {
087                                                return true;
088                                        }
089                                        currentBranch = currentBranch.parent;
090                                }
091
092                                return false;
093                        }
094
095                        @Override
096                        public T next()
097                        {
098                                if (!hasNext())
099                                {
100                                        throw new IllegalStateException();
101                                }
102
103                                T next = currentBranch.next();
104
105                                previousBranch = currentBranch;
106
107                                if (iterateChildren(next))
108                                {
109                                        currentBranch = new Branch<>(previousBranch, provider.getChildren(next));
110                                }
111
112                                return next;
113                        }
114
115                        @Override
116                        public void remove()
117                        {
118                                throw new UnsupportedOperationException();
119                        }
120                };
121
122                for (int i = 0; i < first; i++)
123                {
124                        iterator.next();
125                }
126
127                return iterator;
128        }
129
130        /**
131         * Hook method to decide whether the given node's children should be iterated.
132         * 
133         * @param node
134         *            node
135         * 
136         * @return {@code true} if the node's children should be iterated
137         */
138        protected abstract boolean iterateChildren(T node);
139
140        @Override
141        public NodeModel<T> model(T object)
142        {
143                return previousBranch.wrapModel(provider.model(object));
144        }
145
146        @Override
147        public void detach()
148        {
149                currentBranch = null;
150                previousBranch = null;
151                size = -1;
152        }
153
154        private static class Branch<T> implements Iterator<T>
155        {
156                private Branch<T> parent;
157
158                private Iterator<? extends T> children;
159
160                Branch(Branch<T> parent, Iterator<? extends T> children)
161                {
162                        this.parent = parent;
163                        this.children = children;
164                }
165
166                NodeModel<T> wrapModel(IModel<T> model)
167                {
168                        boolean[] branches = new boolean[getDepth()];
169
170                        Branch<T> branch = this;
171                        for (int c = branches.length - 1; c >= 0; c--)
172                        {
173                                branches[c] = branch.hasNext();
174
175                                branch = branch.parent;
176                        }
177
178                        return new NodeModel<>(model, branches);
179                }
180
181                int getDepth()
182                {
183                        if (parent == null)
184                        {
185                                return 1;
186                        }
187                        else
188                        {
189                                return parent.getDepth() + 1;
190                        }
191                }
192
193                @Override
194                public boolean hasNext()
195                {
196                        return children.hasNext();
197                }
198
199                @Override
200                public T next()
201                {
202                        if (!hasNext())
203                        {
204                                throw new IllegalStateException();
205                        }
206
207                        return children.next();
208                }
209
210                @Override
211                public void remove()
212                {
213                        throw new UnsupportedOperationException();
214                }
215        }
216}