You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

FlyweightRow.java 10.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. /*
  2. * Copyright 2000-2018 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.v7.client.widget.escalator;
  17. import java.util.ArrayList;
  18. import java.util.Collection;
  19. import java.util.Iterator;
  20. import java.util.List;
  21. import com.google.gwt.dom.client.TableRowElement;
  22. /**
  23. * An internal implementation of the {@link Row} interface.
  24. * <p>
  25. * There is only one instance per Escalator. This is designed to be re-used when
  26. * rendering rows.
  27. *
  28. * @since 7.4
  29. * @author Vaadin Ltd
  30. * @see com.vaadin.v7.client.widgets.Escalator.AbstractRowContainer#refreshRow(TableRowElement,
  31. * int) Escalator.AbstractRowContainer#refreshRow(TableRowElement, int)
  32. */
  33. public class FlyweightRow implements Row {
  34. static class CellIterator implements Iterator<FlyweightCell> {
  35. /** A defensive copy of the cells in the current row. */
  36. private final List<FlyweightCell> cells;
  37. private final boolean cellsAttached;
  38. private int cursor = 0;
  39. private int skipNext = 0;
  40. /**
  41. * Creates a new iterator of attached flyweight cells. A cell is
  42. * attached if it has a corresponding {@link FlyweightCell#getElement()
  43. * DOM element} attached to the row element.
  44. *
  45. * @param cells
  46. * the collection of cells to iterate
  47. */
  48. public static CellIterator attached(
  49. final Collection<FlyweightCell> cells) {
  50. return new CellIterator(cells, true);
  51. }
  52. /**
  53. * Creates a new iterator of unattached flyweight cells. A cell is
  54. * unattached if it does not have a corresponding
  55. * {@link FlyweightCell#getElement() DOM element} attached to the row
  56. * element.
  57. *
  58. * @param cells
  59. * the collection of cells to iterate
  60. */
  61. public static CellIterator unattached(
  62. final Collection<FlyweightCell> cells) {
  63. return new CellIterator(cells, false);
  64. }
  65. private CellIterator(final Collection<FlyweightCell> cells,
  66. final boolean attached) {
  67. this.cells = new ArrayList<FlyweightCell>(cells);
  68. cellsAttached = attached;
  69. }
  70. @Override
  71. public boolean hasNext() {
  72. return cursor + skipNext < cells.size();
  73. }
  74. @Override
  75. public FlyweightCell next() {
  76. // if we needed to skip some cells since the last invocation.
  77. for (int i = 0; i < skipNext; i++) {
  78. cells.remove(cursor);
  79. }
  80. skipNext = 0;
  81. final FlyweightCell cell = cells.get(cursor++);
  82. cell.setup(this);
  83. return cell;
  84. }
  85. @Override
  86. public void remove() {
  87. throw new UnsupportedOperationException(
  88. "Cannot remove cells via iterator");
  89. }
  90. /**
  91. * Sets the number of cells to skip when {@link #next()} is called the
  92. * next time. Cell hiding is also handled eagerly in this method.
  93. *
  94. * @param colspan
  95. * the number of cells to skip on next invocation of
  96. * {@link #next()}
  97. */
  98. public void setSkipNext(final int colspan) {
  99. assert colspan > 0 : "Number of cells didn't make sense: "
  100. + colspan;
  101. skipNext = colspan;
  102. }
  103. /**
  104. * Gets the next <code>n</code> cells in the iterator, ignoring any
  105. * possibly spanned cells.
  106. *
  107. * @param n
  108. * the number of next cells to retrieve
  109. * @return A list of next <code>n</code> cells, or less if there aren't
  110. * enough cells to retrieve
  111. */
  112. public List<FlyweightCell> rawPeekNext(final int n) {
  113. final int from = Math.min(cursor, cells.size());
  114. final int to = Math.min(cursor + n, cells.size());
  115. List<FlyweightCell> nextCells = cells.subList(from, to);
  116. for (FlyweightCell cell : nextCells) {
  117. cell.setup(this);
  118. }
  119. return nextCells;
  120. }
  121. public boolean areCellsAttached() {
  122. return cellsAttached;
  123. }
  124. }
  125. private static final int BLANK = Integer.MIN_VALUE;
  126. private int row;
  127. private TableRowElement element;
  128. private double[] columnWidths = null;
  129. private final List<FlyweightCell> cells = new ArrayList<FlyweightCell>();
  130. public void setup(final TableRowElement e, final int row,
  131. double[] columnWidths) {
  132. element = e;
  133. this.row = row;
  134. this.columnWidths = columnWidths;
  135. }
  136. /**
  137. * Tear down the state of the Row.
  138. * <p>
  139. * This is an internal check method, to prevent retrieving uninitialized
  140. * data by calling {@link #getRow()}, {@link #getElement()} or
  141. * {@link #getCells()} at an improper time.
  142. * <p>
  143. * This should only be used with asserts ("
  144. * <code>assert flyweightRow.teardown()</code> ") so that the code is never
  145. * run when asserts aren't enabled.
  146. *
  147. * @return always <code>true</code>
  148. */
  149. public boolean teardown() {
  150. element = null;
  151. row = BLANK;
  152. columnWidths = null;
  153. for (final FlyweightCell cell : cells) {
  154. assert cell.teardown();
  155. }
  156. return true;
  157. }
  158. @Override
  159. public int getRow() {
  160. assertSetup();
  161. return row;
  162. }
  163. @Override
  164. public TableRowElement getElement() {
  165. assertSetup();
  166. return element;
  167. }
  168. public void addCells(final int index, final int numberOfColumns) {
  169. for (int i = 0; i < numberOfColumns; i++) {
  170. final int col = index + i;
  171. cells.add(col, new FlyweightCell(this, col));
  172. }
  173. updateRestOfCells(index + numberOfColumns);
  174. }
  175. public void removeCells(final int index, final int numberOfColumns) {
  176. cells.subList(index, index + numberOfColumns).clear();
  177. updateRestOfCells(index);
  178. }
  179. private void updateRestOfCells(final int startPos) {
  180. // update the column number for the cells to the right
  181. for (int col = startPos; col < cells.size(); col++) {
  182. cells.set(col, new FlyweightCell(this, col));
  183. }
  184. }
  185. /**
  186. * Returns flyweight cells for the client code to render. The cells get
  187. * their associated {@link FlyweightCell#getElement() elements} from the row
  188. * element.
  189. * <p>
  190. * Precondition: each cell has a corresponding element in the row
  191. *
  192. * @return an iterable of flyweight cells
  193. *
  194. * @see #setup(TableRowElement, int, int[])
  195. * @see #teardown()
  196. */
  197. public Iterable<FlyweightCell> getCells() {
  198. return getCells(0, cells.size());
  199. }
  200. /**
  201. * Returns a subrange of flyweight cells for the client code to render. The
  202. * cells get their associated {@link FlyweightCell#getElement() elements}
  203. * from the row element.
  204. * <p>
  205. * Precondition: each cell has a corresponding element in the row
  206. *
  207. * @param offset
  208. * the index of the first cell to return
  209. * @param numberOfCells
  210. * the number of cells to return
  211. * @return an iterable of flyweight cells
  212. */
  213. public Iterable<FlyweightCell> getCells(final int offset,
  214. final int numberOfCells) {
  215. assertSetup();
  216. assert offset >= 0 && offset + numberOfCells <= cells
  217. .size() : "Invalid range of cells";
  218. return new Iterable<FlyweightCell>() {
  219. @Override
  220. public Iterator<FlyweightCell> iterator() {
  221. return CellIterator.attached(
  222. cells.subList(offset, offset + numberOfCells));
  223. }
  224. };
  225. }
  226. /**
  227. * Returns a subrange of unattached flyweight cells. Unattached cells do not
  228. * have {@link FlyweightCell#getElement() elements} associated. Note that
  229. * FlyweightRow does not keep track of whether cells in actuality have
  230. * corresponding DOM elements or not; it is the caller's responsibility to
  231. * invoke this method with correct parameters.
  232. * <p>
  233. * Precondition: the range [offset, offset + numberOfCells) must be valid
  234. *
  235. * @param offset
  236. * the index of the first cell to return
  237. * @param numberOfCells
  238. * the number of cells to return
  239. * @return an iterable of flyweight cells
  240. */
  241. public Iterable<FlyweightCell> getUnattachedCells(final int offset,
  242. final int numberOfCells) {
  243. assertSetup();
  244. assert offset >= 0 && offset + numberOfCells <= cells
  245. .size() : "Invalid range of cells";
  246. return new Iterable<FlyweightCell>() {
  247. @Override
  248. public Iterator<FlyweightCell> iterator() {
  249. return CellIterator.unattached(
  250. cells.subList(offset, offset + numberOfCells));
  251. }
  252. };
  253. }
  254. /**
  255. * Asserts that the flyweight row has properly been set up before trying to
  256. * access any of its data.
  257. */
  258. private void assertSetup() {
  259. assert element != null && row != BLANK
  260. && columnWidths != null : "Flyweight row was not "
  261. + "properly initialized. Make sure the setup-method is "
  262. + "called before retrieving data. This is either a bug "
  263. + "in Escalator, or the instance of the flyweight row "
  264. + "has been stored and accessed.";
  265. }
  266. double getColumnWidth(int column) {
  267. assertSetup();
  268. return columnWidths[column];
  269. }
  270. }