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 * <tbody> 039 * <tr wicket:id="rows" class"even"> 040 * <td wicket:id="cols"> 041 * <span wicket:id="id">Test ID</span> 042 * </td> 043 * </tr> 044 * </tbody> 045 * </pre> 046 * 047 * and in java: 048 * 049 * <pre> 050 * add(new GridView("rows", 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}