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