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.markup.repeater;
018
019import java.util.Iterator;
020
021import org.apache.wicket.markup.repeater.util.ModelIteratorAdapter;
022import org.apache.wicket.model.IModel;
023import org.apache.wicket.util.lang.Generics;
024
025
026/**
027 * An abstract repeater view that provides refreshing functionality to its subclasses. The view is
028 * refreshed every request, making it well suited for displaying dynamic data.
029 * <p>
030 * The view is populated by implementing {@link RefreshingView#getItemModels() } and
031 * {@link RefreshingView#populateItem(Item) } methods. RefreshingView builds the items that will be
032 * rendered by looping over the models retrieved from {@link RefreshingView#getItemModels() } and
033 * calling the {@link RefreshingView#newItem(String, int, IModel) } to generate the child item
034 * container followed by a call to {@link RefreshingView#populateItem(Item) } to let the user
035 * populate the newly created item container with custom components.
036 * </p>
037 * <p>
038 * The provided {@link ModelIteratorAdapter} can make implementing
039 * {@link RefreshingView#getItemModels() } easier if you have an iterator over item objects.
040 * </p>
041 * 
042 * @see RepeatingView
043 * @see ModelIteratorAdapter
044 * 
045 * @author Igor Vaynberg (ivaynberg)
046 * 
047 * @param <T>
048 *            type of elements contained in the model's list
049 */
050public abstract class RefreshingView<T> extends RepeatingView
051{
052        private static final long serialVersionUID = 1L;
053
054        /**
055         * The item reuse strategy that will be used to recycle items when the page is changed or the
056         * view is redrawn.
057         * 
058         * @see IItemReuseStrategy
059         */
060        private IItemReuseStrategy itemReuseStrategy;
061
062        /**
063         * Constructor
064         * 
065         * @param id
066         *            component id
067         */
068        public RefreshingView(String id)
069        {
070                super(id);
071        }
072
073        /**
074         * Constructor
075         * 
076         * @param id
077         *            component id
078         * @param model
079         *            model
080         */
081        public RefreshingView(String id, IModel<?> model)
082        {
083                super(id, model);
084        }
085
086        /**
087         * Refresh the items in the view. Delegates the creation of items to the selected item reuse
088         * strategy
089         */
090        @Override
091        protected final void onPopulate()
092        {
093                Iterator<IModel<T>> models = getItemModels();
094                Iterator<Item<T>> items = getItemReuseStrategy().getItems(newItemFactory(), models,
095                        getItems());
096                removeAll();
097                addItems(items);
098        }
099
100        /**
101         * Create a new IItemFactory based upon the RefreshingView
102         * 
103         * @return An Item factory that delegates to the RefreshingView
104         */
105        protected IItemFactory<T> newItemFactory()
106        {
107                return new IItemFactory<T>()
108                {
109                        @Override
110                        public Item<T> newItem(int index, IModel<T> model)
111                        {
112                                String id = RefreshingView.this.newChildId();
113                                Item<T> item = RefreshingView.this.newItem(id, index, model);
114                                RefreshingView.this.populateItem(item);
115                                return item;
116                        }
117                };
118        }
119
120
121        /**
122         * Returns an iterator over models for items that will be added to this view
123         * 
124         * @return an iterator over models for items that will be added to this view
125         */
126        protected abstract Iterator<IModel<T>> getItemModels();
127
128        /**
129         * Populate the given Item container.
130         * <p>
131         * <b>be careful</b> to add any components to the item and not the view itself. So, don't do:
132         * 
133         * <pre>
134         * add(new Label(&quot;foo&quot;, &quot;bar&quot;));
135         * </pre>
136         * 
137         * but:
138         * 
139         * <pre>
140         * item.add(new Label(&quot;foo&quot;, &quot;bar&quot;));
141         * </pre>
142         * 
143         * </p>
144         * 
145         * @param item
146         *            The item to populate
147         */
148        protected abstract void populateItem(final Item<T> item);
149
150        /**
151         * Factory method for Item container. Item containers are simple MarkupContainer used to
152         * aggregate the user added components for a row inside the view.
153         * 
154         * @see Item
155         * @param id
156         *            component id for the new data item
157         * @param index
158         *            the index of the new data item
159         * @param model
160         *            the model for the new data item
161         * 
162         * @return DataItem created DataItem
163         */
164        protected Item<T> newItem(final String id, int index, final IModel<T> model)
165        {
166                return new Item<>(id, index, model);
167        }
168
169        /**
170         * @return iterator over item instances that exist as children of this view
171         */
172        public Iterator<Item<T>> getItems()
173        {
174                return Generics.iterator(iterator());
175        }
176
177        /**
178         * Add items to the view. Prior to this all items were removed so every request this function
179         * starts from a clean slate.
180         * 
181         * @param items
182         *            item instances to be added to this view
183         */
184        protected void addItems(Iterator<Item<T>> items)
185        {
186                int index = 0;
187                while (items.hasNext())
188                {
189                        Item<T> item = items.next();
190                        item.setIndex(index);
191                        add(item);
192                        ++index;
193                }
194        }
195
196        // /////////////////////////////////////////////////////////////////////////
197        // ITEM GENERATION
198        // /////////////////////////////////////////////////////////////////////////
199
200        /**
201         * @return currently set item reuse strategy. Defaults to <code>DefaultItemReuseStrategy</code>
202         *         if none was set.
203         * 
204         * @see DefaultItemReuseStrategy
205         */
206        public IItemReuseStrategy getItemReuseStrategy()
207        {
208                if (itemReuseStrategy == null)
209                {
210                        return DefaultItemReuseStrategy.getInstance();
211                }
212                return itemReuseStrategy;
213        }
214
215        /**
216         * Sets the item reuse strategy. This strategy controls the creation of {@link Item}s.
217         * 
218         * @see IItemReuseStrategy
219         * 
220         * @param strategy
221         *            item reuse strategy
222         * @return this for chaining
223         */
224        public RefreshingView<T> setItemReuseStrategy(IItemReuseStrategy strategy)
225        {
226                if (strategy == null)
227                {
228                        throw new IllegalArgumentException();
229                }
230
231                if (!strategy.equals(itemReuseStrategy))
232                {
233                        if (isVersioned())
234                        {
235                                addStateChange();
236                        }
237                        itemReuseStrategy = strategy;
238                }
239                return this;
240        }
241
242
243}