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.data;
018
019import java.util.Iterator;
020
021import org.apache.wicket.Component;
022import org.apache.wicket.MarkupContainer;
023import org.apache.wicket.markup.repeater.Item;
024import org.apache.wicket.markup.repeater.RepeatingView;
025import org.apache.wicket.util.lang.Args;
026import org.apache.wicket.util.lang.Generics;
027
028
029/**
030 * A pageable DataView which breaks the data in the IDataProvider into a number of data-rows,
031 * depending on the column size. A typical use case is to show items in a table with ie 3 columns
032 * where the table is filled left to right top-down so that for each third item a new row is
033 * created.
034 * <p>
035 * Example
036 * 
037 * <pre>
038 *   &lt;tbody&gt;
039 *     &lt;tr wicket:id=&quot;rows&quot; class&quot;even&quot;&gt;
040 *       &lt;td wicket:id=&quot;cols&quot;&gt;
041 *         &lt;span wicket:id=&quot;id&quot;&gt;Test ID&lt;/span&gt;
042 *       &lt;/td&gt;
043 *     &lt;/tr&gt;
044 *   &lt;/tbody&gt;
045 * </pre>
046 * 
047 * and in java:
048 * 
049 * <pre>
050 * add(new GridView(&quot;rows&quot;, dataProvider).setColumns(3));
051 * </pre>
052 * 
053 * @author Igor Vaynberg
054 * @author Christian Essl
055 * @param <T>
056 * 
057 */
058public abstract class GridView<T> extends DataViewBase<T>
059{
060        private static final long serialVersionUID = 1L;
061
062        private int columns = 1;
063        private int rows = Integer.MAX_VALUE;
064
065
066        /**
067         * @param id
068         *            component id
069         * @param dataProvider
070         *            data provider
071         */
072        public GridView(String id, IDataProvider<T> dataProvider)
073        {
074                super(id, dataProvider);
075        }
076
077
078        /**
079         * @return number of columns
080         */
081        public int getColumns()
082        {
083                return columns;
084        }
085
086        /**
087         * Sets number of columns
088         * 
089         * @param cols
090         *            number of columns
091         * @return this for chaining
092         */
093        public GridView<T> setColumns(int cols)
094        {
095                if (cols < 1)
096                {
097                        throw new IllegalArgumentException();
098                }
099
100                if (columns != cols)
101                {
102                        if (isVersioned())
103                        {
104                                addStateChange();
105                        }
106                        columns = cols;
107                }
108                updateItemsPerPage();
109                return this;
110        }
111
112        /**
113         * @return number of rows per page
114         */
115        public int getRows()
116        {
117                return rows;
118        }
119
120        /**
121         * Sets number of rows per page
122         * 
123         * @param rows
124         *            number of rows
125         * @return this for chaining
126         */
127        public GridView<T> setRows(int rows)
128        {
129                if (rows < 1)
130                {
131                        throw new IllegalArgumentException();
132                }
133
134                if (this.rows != rows)
135                {
136                        if (isVersioned())
137                        {
138                                addStateChange();
139                        }
140                        this.rows = rows;
141                }
142
143                // TODO Post 1.2: Performance: Can this be moved into the this.rows != rows if
144                // block for optimization?
145                updateItemsPerPage();
146                return this;
147        }
148
149        private void updateItemsPerPage()
150        {
151                long items = Long.MAX_VALUE;
152
153                long result = (long)rows * (long)columns;
154
155                // overflow check
156                int desiredHiBits = -((int)(result >>> 31) & 1);
157                int actualHiBits = (int)(result >>> 32);
158
159                if (desiredHiBits == actualHiBits)
160                {
161                        items = (int)result;
162                }
163                setItemsPerPage(items);
164        }
165
166
167        @Override
168        protected void addItems(Iterator<Item<T>> items)
169        {
170                if (items.hasNext())
171                {
172                        final int cols = getColumns();
173
174                        int row = 0;
175
176                        do
177                        {
178                                // Build a row
179                                Item<?> rowItem = newRowItem(newChildId(), row);
180                                RepeatingView rowView = new RepeatingView("cols");
181                                rowItem.add(rowView);
182                                add(rowItem);
183
184                                // Populate the row
185                                for (int index = 0; index < cols; index++)
186                                {
187                                        final Item<T> cellItem;
188                                        if (items.hasNext())
189                                        {
190                                                cellItem = items.next();
191                                        }
192                                        else
193                                        {
194                                                cellItem = newEmptyItem(newChildId(), index);
195                                                populateEmptyItem(cellItem);
196                                        }
197                                        rowView.add(cellItem);
198                                }
199
200                                // increase row
201                                row++;
202
203                        }
204                        while (items.hasNext());
205                }
206
207        }
208
209        /**
210         * @return data provider
211         */
212        public IDataProvider<T> getDataProvider()
213        {
214                return internalGetDataProvider();
215        }
216
217        /**
218         * @see org.apache.wicket.markup.repeater.AbstractPageableView#getItems()
219         */
220        @Override
221        public Iterator<Item<T>> getItems()
222        {
223                Iterator<MarkupContainer> rows = Generics.iterator(iterator());
224                return new ItemsIterator<T>(rows);
225        }
226
227        /**
228         * Add component to an Item for which there is no model anymore and is shown in a cell
229         * 
230         * @param item
231         *            Item object
232         */
233        abstract protected void populateEmptyItem(Item<T> item);
234
235        /**
236         * Create a Item which represents an empty cell (there is no model for it in the DataProvider)
237         * 
238         * @param id
239         * @param index
240         * @return created item
241         */
242        protected Item<T> newEmptyItem(String id, int index)
243        {
244                return new Item<T>(id, index, null);
245        }
246
247        /**
248         * Create a new Item which will hold a row.
249         * 
250         * @param id
251         * @param index
252         * @return created Item
253         */
254        protected Item<?> newRowItem(String id, int index)
255        {
256                return new Item<Object>(id, index, null);
257        }
258
259        /**
260         * Iterator that iterates over all items in the cells
261         * 
262         * @author igor
263         * @param <T>
264         */
265        public static class ItemsIterator<T> implements Iterator<Item<T>>
266        {
267                private final Iterator<MarkupContainer> rows;
268                private Iterator<Item<T>> cells;
269
270                private Item<T> next;
271
272                /**
273                 * @param rows
274                 *            iterator over child row views
275                 */
276                public ItemsIterator(Iterator<MarkupContainer> rows)
277                {
278                        this.rows = Args.notNull(rows, "rows");
279                        findNext();
280                }
281
282                /**
283                 * @see java.util.Iterator#remove()
284                 */
285                @Override
286                public void remove()
287                {
288                        throw new UnsupportedOperationException();
289                }
290
291                /**
292                 * @see java.util.Iterator#hasNext()
293                 */
294                @Override
295                public boolean hasNext()
296                {
297                        return next != null;
298                }
299
300                /**
301                 * @see java.util.Iterator#next()
302                 */
303                @Override
304                public Item<T> next()
305                {
306                        Item<T> item = next;
307                        findNext();
308                        return item;
309                }
310
311                private void findNext()
312                {
313                        next = null;
314
315                        if (cells != null && cells.hasNext())
316                        {
317                                next = cells.next();
318                        }
319                        else
320                        {
321                                while (rows.hasNext())
322                                {
323                                        MarkupContainer row = rows.next();
324
325                                        final Iterator<? extends Component> rawCells;
326                                        rawCells = ((MarkupContainer)row.iterator().next()).iterator();
327                                        cells = Generics.iterator(rawCells);
328                                        if (cells.hasNext())
329                                        {
330                                                next = cells.next();
331                                                break;
332                                        }
333                                }
334                        }
335                }
336
337        }
338}