123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- /*
- * Copyright 2000-2018 Vaadin Ltd.
- *
- * Licensed 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 com.vaadin.v7.client.widget.escalator;
-
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.Iterator;
- import java.util.List;
-
- import com.google.gwt.dom.client.TableRowElement;
-
- /**
- * An internal implementation of the {@link Row} interface.
- * <p>
- * There is only one instance per Escalator. This is designed to be re-used when
- * rendering rows.
- *
- * @since 7.4
- * @author Vaadin Ltd
- * @see com.vaadin.v7.client.widgets.Escalator.AbstractRowContainer#refreshRow(TableRowElement,
- * int) Escalator.AbstractRowContainer#refreshRow(TableRowElement, int)
- */
- public class FlyweightRow implements Row {
-
- static class CellIterator implements Iterator<FlyweightCell> {
- /** A defensive copy of the cells in the current row. */
- private final List<FlyweightCell> cells;
- private final boolean cellsAttached;
- private int cursor = 0;
- private int skipNext = 0;
-
- /**
- * Creates a new iterator of attached flyweight cells. A cell is
- * attached if it has a corresponding {@link FlyweightCell#getElement()
- * DOM element} attached to the row element.
- *
- * @param cells
- * the collection of cells to iterate
- */
- public static CellIterator attached(
- final Collection<FlyweightCell> cells) {
- return new CellIterator(cells, true);
- }
-
- /**
- * Creates a new iterator of unattached flyweight cells. A cell is
- * unattached if it does not have a corresponding
- * {@link FlyweightCell#getElement() DOM element} attached to the row
- * element.
- *
- * @param cells
- * the collection of cells to iterate
- */
- public static CellIterator unattached(
- final Collection<FlyweightCell> cells) {
- return new CellIterator(cells, false);
- }
-
- private CellIterator(final Collection<FlyweightCell> cells,
- final boolean attached) {
- this.cells = new ArrayList<FlyweightCell>(cells);
- cellsAttached = attached;
- }
-
- @Override
- public boolean hasNext() {
- return cursor + skipNext < cells.size();
- }
-
- @Override
- public FlyweightCell next() {
- // if we needed to skip some cells since the last invocation.
- for (int i = 0; i < skipNext; i++) {
- cells.remove(cursor);
- }
- skipNext = 0;
-
- final FlyweightCell cell = cells.get(cursor++);
- cell.setup(this);
- return cell;
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException(
- "Cannot remove cells via iterator");
- }
-
- /**
- * Sets the number of cells to skip when {@link #next()} is called the
- * next time. Cell hiding is also handled eagerly in this method.
- *
- * @param colspan
- * the number of cells to skip on next invocation of
- * {@link #next()}
- */
- public void setSkipNext(final int colspan) {
- assert colspan > 0 : "Number of cells didn't make sense: "
- + colspan;
- skipNext = colspan;
- }
-
- /**
- * Gets the next <code>n</code> cells in the iterator, ignoring any
- * possibly spanned cells.
- *
- * @param n
- * the number of next cells to retrieve
- * @return A list of next <code>n</code> cells, or less if there aren't
- * enough cells to retrieve
- */
- public List<FlyweightCell> rawPeekNext(final int n) {
- final int from = Math.min(cursor, cells.size());
- final int to = Math.min(cursor + n, cells.size());
- List<FlyweightCell> nextCells = cells.subList(from, to);
- for (FlyweightCell cell : nextCells) {
- cell.setup(this);
- }
- return nextCells;
- }
-
- public boolean areCellsAttached() {
- return cellsAttached;
- }
- }
-
- private static final int BLANK = Integer.MIN_VALUE;
-
- private int row;
- private TableRowElement element;
- private double[] columnWidths = null;
- private final List<FlyweightCell> cells = new ArrayList<FlyweightCell>();
-
- public void setup(final TableRowElement e, final int row,
- double[] columnWidths) {
- element = e;
- this.row = row;
- this.columnWidths = columnWidths;
- }
-
- /**
- * Tear down the state of the Row.
- * <p>
- * This is an internal check method, to prevent retrieving uninitialized
- * data by calling {@link #getRow()}, {@link #getElement()} or
- * {@link #getCells()} at an improper time.
- * <p>
- * This should only be used with asserts ("
- * <code>assert flyweightRow.teardown()</code> ") so that the code is never
- * run when asserts aren't enabled.
- *
- * @return always <code>true</code>
- */
- public boolean teardown() {
- element = null;
- row = BLANK;
- columnWidths = null;
- for (final FlyweightCell cell : cells) {
- assert cell.teardown();
- }
- return true;
- }
-
- @Override
- public int getRow() {
- assertSetup();
- return row;
- }
-
- @Override
- public TableRowElement getElement() {
- assertSetup();
- return element;
- }
-
- public void addCells(final int index, final int numberOfColumns) {
- for (int i = 0; i < numberOfColumns; i++) {
- final int col = index + i;
- cells.add(col, new FlyweightCell(this, col));
- }
- updateRestOfCells(index + numberOfColumns);
- }
-
- public void removeCells(final int index, final int numberOfColumns) {
- cells.subList(index, index + numberOfColumns).clear();
- updateRestOfCells(index);
- }
-
- private void updateRestOfCells(final int startPos) {
- // update the column number for the cells to the right
- for (int col = startPos; col < cells.size(); col++) {
- cells.set(col, new FlyweightCell(this, col));
- }
- }
-
- /**
- * Returns flyweight cells for the client code to render. The cells get
- * their associated {@link FlyweightCell#getElement() elements} from the row
- * element.
- * <p>
- * Precondition: each cell has a corresponding element in the row
- *
- * @return an iterable of flyweight cells
- *
- * @see #setup(TableRowElement, int, int[])
- * @see #teardown()
- */
- public Iterable<FlyweightCell> getCells() {
- return getCells(0, cells.size());
- }
-
- /**
- * Returns a subrange of flyweight cells for the client code to render. The
- * cells get their associated {@link FlyweightCell#getElement() elements}
- * from the row element.
- * <p>
- * Precondition: each cell has a corresponding element in the row
- *
- * @param offset
- * the index of the first cell to return
- * @param numberOfCells
- * the number of cells to return
- * @return an iterable of flyweight cells
- */
- public Iterable<FlyweightCell> getCells(final int offset,
- final int numberOfCells) {
- assertSetup();
- assert offset >= 0 && offset + numberOfCells <= cells
- .size() : "Invalid range of cells";
- return new Iterable<FlyweightCell>() {
- @Override
- public Iterator<FlyweightCell> iterator() {
- return CellIterator.attached(
- cells.subList(offset, offset + numberOfCells));
- }
- };
- }
-
- /**
- * Returns a subrange of unattached flyweight cells. Unattached cells do not
- * have {@link FlyweightCell#getElement() elements} associated. Note that
- * FlyweightRow does not keep track of whether cells in actuality have
- * corresponding DOM elements or not; it is the caller's responsibility to
- * invoke this method with correct parameters.
- * <p>
- * Precondition: the range [offset, offset + numberOfCells) must be valid
- *
- * @param offset
- * the index of the first cell to return
- * @param numberOfCells
- * the number of cells to return
- * @return an iterable of flyweight cells
- */
- public Iterable<FlyweightCell> getUnattachedCells(final int offset,
- final int numberOfCells) {
- assertSetup();
- assert offset >= 0 && offset + numberOfCells <= cells
- .size() : "Invalid range of cells";
- return new Iterable<FlyweightCell>() {
- @Override
- public Iterator<FlyweightCell> iterator() {
- return CellIterator.unattached(
- cells.subList(offset, offset + numberOfCells));
- }
- };
- }
-
- /**
- * Asserts that the flyweight row has properly been set up before trying to
- * access any of its data.
- */
- private void assertSetup() {
- assert element != null && row != BLANK
- && columnWidths != null : "Flyweight row was not "
- + "properly initialized. Make sure the setup-method is "
- + "called before retrieving data. This is either a bug "
- + "in Escalator, or the instance of the flyweight row "
- + "has been stored and accessed.";
- }
-
- double getColumnWidth(int column) {
- assertSetup();
- return columnWidths[column];
- }
- }
|