/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.cyclop.web.components.iterablegrid;
import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.RepeatingView;
import org.apache.wicket.markup.repeater.data.IDataProvider;
import org.apache.wicket.util.lang.Args;
import org.apache.wicket.util.lang.Generics;
import java.util.Iterator;
// ####################################################################################################################
// This class has been copied from wicket 6.14.0
// Only minimal modifications has been made in order to simplify future updates to new wicket version, those are:
// - GridView extends DataViewBase from this package and not the original wicket class
// - GridView is package protected
// ####################################################################################################################
/**
* A pageable DataView which breaks the data in the IDataProvider into a number
* of data-rows, depending on the column readSize. A typical use case is to show
* items in a table with ie 3 columns where the table is filled left to right
* top-down so that for each third item a new row is created.
* <p/>
* Example
* <p/>
*
* <pre>
* <tbody>
* <tr wicket:id="rows" class"even">
* <td wicket:id="cols">
* <span wicket:id="id">Test ID</span>
* </td>
* </tr>
* </tbody>
* </pre>
* <p/>
* and in java:
* <p/>
*
* <pre>
* add(new GridView("rows", dataProvider).setColumns(3));
* </pre>
*
* @param <T>
* @author Igor Vaynberg
* @author Christian Essl
*/
abstract class GridView<T> extends DataViewBase<T> {
private static final long serialVersionUID = 1L;
private int columns = 1;
private int rows = Integer.MAX_VALUE;
/**
* @param id
* component id
* @param dataProvider
* data provider
*/
public GridView(String id, IDataProvider<T> dataProvider) {
super(id, dataProvider);
}
/** @return number of columns */
public int getColumns() {
return columns;
}
/**
* Sets number of columns
*
* @param cols
* number of columns
* @return this for chaining
*/
public GridView<T> setColumns(int cols) {
if (cols < 1) {
throw new IllegalArgumentException();
}
if (columns != cols) {
if (isVersioned()) {
addStateChange();
}
columns = cols;
}
updateItemsPerPage();
return this;
}
/** @return number of rows per page */
public int getRows() {
return rows;
}
/**
* Sets number of rows per page
*
* @param rows
* number of rows
* @return this for chaining
*/
public GridView<T> setRows(int rows) {
if (rows < 1) {
throw new IllegalArgumentException();
}
if (this.rows != rows) {
if (isVersioned()) {
addStateChange();
}
this.rows = rows;
}
// TODO Post 1.2: Performance: Can this be moved into the this.rows !=
// rows if
// block for optimization?
updateItemsPerPage();
return this;
}
private void updateItemsPerPage() {
long items = Long.MAX_VALUE;
long result = (long) rows * (long) columns;
// overflow check
int desiredHiBits = -((int) (result >>> 31) & 1);
int actualHiBits = (int) (result >>> 32);
if (desiredHiBits == actualHiBits) {
items = (int) result;
}
setItemsPerPage(items);
}
@Override
protected void addItems(Iterator<Item<T>> items) {
if (items.hasNext()) {
final int cols = getColumns();
int row = 0;
do {
// Build a row
Item<?> rowItem = newRowItem(newChildId(), row);
RepeatingView rowView = new RepeatingView("cols");
rowItem.add(rowView);
add(rowItem);
// Populate the row
for (int index = 0; index < cols; index++) {
final Item<T> cellItem;
if (items.hasNext()) {
cellItem = items.next();
} else {
cellItem = newEmptyItem(newChildId(), index);
populateEmptyItem(cellItem);
}
rowView.add(cellItem);
}
// increase row
row++;
} while (items.hasNext());
}
}
/** @return data provider */
public IDataProvider<T> getDataProvider() {
return internalGetDataProvider();
}
/** @see org.apache.wicket.markup.repeater.AbstractPageableView#getItems() */
@Override
public Iterator<Item<T>> getItems() {
Iterator<MarkupContainer> rows = Generics.iterator(iterator());
return new ItemsIterator<T>(rows);
}
/**
* Add component to an Item for which there is no model anymore and is shown
* in a cell
*
* @param item
* Item object
*/
abstract protected void populateEmptyItem(Item<T> item);
/**
* Create a Item which represents an empty cell (there is no model for it in
* the DataProvider)
*
* @param id
* @param index
* @return created item
*/
protected Item<T> newEmptyItem(String id, int index) {
return new Item<T>(id, index, null);
}
/**
* Create a new Item which will hold a row.
*
* @param id
* @param index
* @return created Item
*/
protected Item<?> newRowItem(String id, int index) {
return new Item<Object>(id, index, null);
}
/**
* Iterator that iterates over all items in the cells
*
* @param <T>
* @author igor
*/
public static class ItemsIterator<T> implements Iterator<Item<T>> {
private final Iterator<MarkupContainer> rows;
private Iterator<Item<T>> cells;
private Item<T> next;
/**
* @param rows
* iterator over child row views
*/
public ItemsIterator(Iterator<MarkupContainer> rows) {
this.rows = Args.notNull(rows, "rows");
findNext();
}
/** @see Iterator#remove() */
@Override
public void remove() {
throw new UnsupportedOperationException();
}
/** @see Iterator#hasNext() */
@Override
public boolean hasNext() {
return next != null;
}
/** @see Iterator#next() */
@Override
public Item<T> next() {
Item<T> item = next;
findNext();
return item;
}
private void findNext() {
next = null;
if (cells != null && cells.hasNext()) {
next = cells.next();
} else {
while (rows.hasNext()) {
MarkupContainer row = rows.next();
final Iterator<? extends Component> rawCells;
rawCells = ((MarkupContainer) row.iterator().next()).iterator();
cells = Generics.iterator(rawCells);
if (cells.hasNext()) {
next = cells.next();
break;
}
}
}
}
}
}