/*
* 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.apache.wicket.markup.repeater.data;
import java.util.Iterator;
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.util.lang.Generics;
import org.apache.wicket.version.undo.Change;
/**
* A pageable DataView which breaks the data in the IDataProvider into a number of data-rows,
* depending on the column size. 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
*
* <pre>
* <tbody>
* <tr wicket:id="rows" class"even">
* <td wicket:id="cols">
* <span wicket:id="id">Test ID</span>
* </td>
* </tr>
* </tbody>
* </pre>
*
* and in java:
*
* <pre>
* add(new GridView("rows", dataProvider).setColumns(3));
* </pre>
*
* @author Igor Vaynberg
* @author Christian Essl
* @param <T>
*
*/
public 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(new Change()
{
private static final long serialVersionUID = 1L;
final int old = columns;
@Override
public void undo()
{
columns = old;
}
@Override
public String toString()
{
return "GridViewColumnsChange[component: " + getPath() +
", removed columns: " + old + "]";
}
});
}
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(new Change()
{
private static final long serialVersionUID = 1L;
final int old = GridView.this.rows;
@Override
public void undo()
{
GridView.this.rows = old;
}
@Override
public String toString()
{
return "GridViewRowsChange[component: " + getPath() + ", removed rows: " +
old + "]";
}
});
}
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()
{
int items = Integer.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;
}
internalSetRowsPerPage(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
*
* @author igor
* @param <T>
*
*/
private 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 = rows;
findNext();
}
/**
* @see java.util.Iterator#remove()
*/
public void remove()
{
throw new UnsupportedOperationException();
}
/**
* @see java.util.Iterator#hasNext()
*/
public boolean hasNext()
{
return next != null;
}
/**
* @see java.util.Iterator#next()
*/
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;
}
}
}
}
}
}