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.

TreeTable.java 34KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092
  1. /*
  2. * Copyright 2000-2014 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.ui;
  17. import java.io.Serializable;
  18. import java.util.ArrayList;
  19. import java.util.Collection;
  20. import java.util.Collections;
  21. import java.util.HashSet;
  22. import java.util.List;
  23. import java.util.Map;
  24. import java.util.Set;
  25. import java.util.Stack;
  26. import java.util.logging.Level;
  27. import java.util.logging.Logger;
  28. import org.jsoup.nodes.Element;
  29. import com.vaadin.data.Collapsible;
  30. import com.vaadin.data.Container;
  31. import com.vaadin.data.Item;
  32. import com.vaadin.data.Container.Hierarchical;
  33. import com.vaadin.data.Container.ItemSetChangeEvent;
  34. import com.vaadin.data.util.ContainerHierarchicalWrapper;
  35. import com.vaadin.data.util.HierarchicalContainer;
  36. import com.vaadin.data.util.HierarchicalContainerOrderedWrapper;
  37. import com.vaadin.server.PaintException;
  38. import com.vaadin.server.PaintTarget;
  39. import com.vaadin.server.Resource;
  40. import com.vaadin.shared.ui.treetable.TreeTableConstants;
  41. import com.vaadin.shared.ui.treetable.TreeTableState;
  42. import com.vaadin.ui.Tree.CollapseEvent;
  43. import com.vaadin.ui.Tree.CollapseListener;
  44. import com.vaadin.ui.Tree.ExpandEvent;
  45. import com.vaadin.ui.Tree.ExpandListener;
  46. import com.vaadin.ui.declarative.DesignAttributeHandler;
  47. import com.vaadin.ui.declarative.DesignContext;
  48. import com.vaadin.ui.declarative.DesignException;
  49. /**
  50. * TreeTable extends the {@link Table} component so that it can also visualize a
  51. * hierarchy of its Items in a similar manner that {@link Tree} does. The tree
  52. * hierarchy is always displayed in the first actual column of the TreeTable.
  53. * <p>
  54. * The TreeTable supports the usual {@link Table} features like lazy loading, so
  55. * it should be no problem to display lots of items at once. Only required rows
  56. * and some cache rows are sent to the client.
  57. * <p>
  58. * TreeTable supports standard {@link Hierarchical} container interfaces, but
  59. * also a more fine tuned version - {@link Collapsible}. A container
  60. * implementing the {@link Collapsible} interface stores the collapsed/expanded
  61. * state internally and can this way scale better on the server side than with
  62. * standard Hierarchical implementations. Developer must however note that
  63. * {@link Collapsible} containers can not be shared among several users as they
  64. * share UI state in the container.
  65. * <p>
  66. * <strong>Note:</strong> Constructing a big hierarchical data set using the
  67. * methods in {@link TreeTable} may cause a decrease in performance. Instead a
  68. * {@link Container.Hierarchical} container should be populated before setting it
  69. * to the {@code TreeTable}.
  70. */
  71. @SuppressWarnings({ "serial" })
  72. public class TreeTable extends Table implements Hierarchical {
  73. private interface ContainerStrategy extends Serializable {
  74. public int size();
  75. public boolean isNodeOpen(Object itemId);
  76. public int getDepth(Object itemId);
  77. public void toggleChildVisibility(Object itemId);
  78. public Object getIdByIndex(int index);
  79. public int indexOfId(Object id);
  80. public Object nextItemId(Object itemId);
  81. public Object lastItemId();
  82. public Object prevItemId(Object itemId);
  83. public boolean isLastId(Object itemId);
  84. public Collection<?> getItemIds();
  85. public void containerItemSetChange(ItemSetChangeEvent event);
  86. }
  87. private abstract class AbstractStrategy implements ContainerStrategy {
  88. /**
  89. * Consider adding getDepth to {@link Collapsible}, might help
  90. * scalability with some container implementations.
  91. */
  92. @Override
  93. public int getDepth(Object itemId) {
  94. int depth = 0;
  95. Hierarchical hierarchicalContainer = getContainerDataSource();
  96. while (!hierarchicalContainer.isRoot(itemId)) {
  97. depth++;
  98. itemId = hierarchicalContainer.getParent(itemId);
  99. }
  100. return depth;
  101. }
  102. @Override
  103. public void containerItemSetChange(ItemSetChangeEvent event) {
  104. }
  105. }
  106. /**
  107. * This strategy is used if current container implements {@link Collapsible}
  108. * .
  109. *
  110. * open-collapsed logic diverted to container, otherwise use default
  111. * implementations.
  112. */
  113. private class CollapsibleStrategy extends AbstractStrategy {
  114. private Collapsible c() {
  115. return (Collapsible) getContainerDataSource();
  116. }
  117. @Override
  118. public void toggleChildVisibility(Object itemId) {
  119. c().setCollapsed(itemId, !c().isCollapsed(itemId));
  120. }
  121. @Override
  122. public boolean isNodeOpen(Object itemId) {
  123. return !c().isCollapsed(itemId);
  124. }
  125. @Override
  126. public int size() {
  127. return TreeTable.super.size();
  128. }
  129. @Override
  130. public Object getIdByIndex(int index) {
  131. return TreeTable.super.getIdByIndex(index);
  132. }
  133. @Override
  134. public int indexOfId(Object id) {
  135. return TreeTable.super.indexOfId(id);
  136. }
  137. @Override
  138. public boolean isLastId(Object itemId) {
  139. // using the default impl
  140. return TreeTable.super.isLastId(itemId);
  141. }
  142. @Override
  143. public Object lastItemId() {
  144. // using the default impl
  145. return TreeTable.super.lastItemId();
  146. }
  147. @Override
  148. public Object nextItemId(Object itemId) {
  149. return TreeTable.super.nextItemId(itemId);
  150. }
  151. @Override
  152. public Object prevItemId(Object itemId) {
  153. return TreeTable.super.prevItemId(itemId);
  154. }
  155. @Override
  156. public Collection<?> getItemIds() {
  157. return TreeTable.super.getItemIds();
  158. }
  159. }
  160. /**
  161. * Strategy for Hierarchical but not Collapsible container like
  162. * {@link HierarchicalContainer}.
  163. *
  164. * Store collapsed/open states internally, fool Table to use preorder when
  165. * accessing items from container via Ordered/Indexed methods.
  166. */
  167. private class HierarchicalStrategy extends AbstractStrategy {
  168. private final HashSet<Object> openItems = new HashSet<Object>();
  169. @Override
  170. public boolean isNodeOpen(Object itemId) {
  171. return openItems.contains(itemId);
  172. }
  173. @Override
  174. public int size() {
  175. return getPreOrder().size();
  176. }
  177. @Override
  178. public Collection<Object> getItemIds() {
  179. return Collections.unmodifiableCollection(getPreOrder());
  180. }
  181. @Override
  182. public boolean isLastId(Object itemId) {
  183. if (itemId == null) {
  184. return false;
  185. }
  186. return itemId.equals(lastItemId());
  187. }
  188. @Override
  189. public Object lastItemId() {
  190. if (getPreOrder().size() > 0) {
  191. return getPreOrder().get(getPreOrder().size() - 1);
  192. } else {
  193. return null;
  194. }
  195. }
  196. @Override
  197. public Object nextItemId(Object itemId) {
  198. int indexOf = getPreOrder().indexOf(itemId);
  199. if (indexOf == -1) {
  200. return null;
  201. }
  202. indexOf++;
  203. if (indexOf == getPreOrder().size()) {
  204. return null;
  205. } else {
  206. return getPreOrder().get(indexOf);
  207. }
  208. }
  209. @Override
  210. public Object prevItemId(Object itemId) {
  211. int indexOf = getPreOrder().indexOf(itemId);
  212. indexOf--;
  213. if (indexOf < 0) {
  214. return null;
  215. } else {
  216. return getPreOrder().get(indexOf);
  217. }
  218. }
  219. @Override
  220. public void toggleChildVisibility(Object itemId) {
  221. boolean removed = openItems.remove(itemId);
  222. if (!removed) {
  223. openItems.add(itemId);
  224. getLogger().log(Level.FINEST, "Item {0} is now expanded",
  225. itemId);
  226. } else {
  227. getLogger().log(Level.FINEST, "Item {0} is now collapsed",
  228. itemId);
  229. }
  230. clearPreorderCache();
  231. }
  232. private void clearPreorderCache() {
  233. preOrder = null; // clear preorder cache
  234. }
  235. List<Object> preOrder;
  236. /**
  237. * Preorder of ids currently visible
  238. *
  239. * @return
  240. */
  241. private List<Object> getPreOrder() {
  242. if (preOrder == null) {
  243. preOrder = new ArrayList<Object>();
  244. Collection<?> rootItemIds = getContainerDataSource()
  245. .rootItemIds();
  246. for (Object id : rootItemIds) {
  247. preOrder.add(id);
  248. addVisibleChildTree(id);
  249. }
  250. }
  251. return preOrder;
  252. }
  253. private void addVisibleChildTree(Object id) {
  254. if (isNodeOpen(id)) {
  255. Collection<?> children = getContainerDataSource()
  256. .getChildren(id);
  257. if (children != null) {
  258. for (Object childId : children) {
  259. preOrder.add(childId);
  260. addVisibleChildTree(childId);
  261. }
  262. }
  263. }
  264. }
  265. @Override
  266. public int indexOfId(Object id) {
  267. return getPreOrder().indexOf(id);
  268. }
  269. @Override
  270. public Object getIdByIndex(int index) {
  271. return getPreOrder().get(index);
  272. }
  273. @Override
  274. public void containerItemSetChange(ItemSetChangeEvent event) {
  275. // preorder becomes invalid on sort, item additions etc.
  276. clearPreorderCache();
  277. super.containerItemSetChange(event);
  278. }
  279. }
  280. /**
  281. * Creates an empty TreeTable with a default container.
  282. */
  283. public TreeTable() {
  284. super(null, new HierarchicalContainer());
  285. }
  286. /**
  287. * Creates an empty TreeTable with a default container.
  288. *
  289. * @param caption
  290. * the caption for the TreeTable
  291. */
  292. public TreeTable(String caption) {
  293. this();
  294. setCaption(caption);
  295. }
  296. /**
  297. * Creates a TreeTable instance with given captions and data source.
  298. *
  299. * @param caption
  300. * the caption for the component
  301. * @param dataSource
  302. * the dataSource that is used to list items in the component
  303. */
  304. public TreeTable(String caption, Container dataSource) {
  305. super(caption, dataSource);
  306. }
  307. private ContainerStrategy cStrategy;
  308. private Object focusedRowId = null;
  309. private Object hierarchyColumnId;
  310. /**
  311. * The item id that was expanded or collapsed during this request. Reset at
  312. * the end of paint and only used for determining if a partial or full paint
  313. * should be done.
  314. *
  315. * Can safely be reset to null whenever a change occurs that would prevent a
  316. * partial update from rendering the correct result, e.g. rows added or
  317. * removed during an expand operation.
  318. */
  319. private Object toggledItemId;
  320. private boolean animationsEnabled;
  321. private boolean clearFocusedRowPending;
  322. /**
  323. * If the container does not send item set change events, always do a full
  324. * repaint instead of a partial update when expanding/collapsing nodes.
  325. */
  326. private boolean containerSupportsPartialUpdates;
  327. private ContainerStrategy getContainerStrategy() {
  328. if (cStrategy == null) {
  329. if (getContainerDataSource() instanceof Collapsible) {
  330. cStrategy = new CollapsibleStrategy();
  331. } else {
  332. cStrategy = new HierarchicalStrategy();
  333. }
  334. }
  335. return cStrategy;
  336. }
  337. @Override
  338. protected void paintRowAttributes(PaintTarget target, Object itemId)
  339. throws PaintException {
  340. super.paintRowAttributes(target, itemId);
  341. target.addAttribute("depth", getContainerStrategy().getDepth(itemId));
  342. if (getContainerDataSource().areChildrenAllowed(itemId)) {
  343. target.addAttribute("ca", true);
  344. target.addAttribute("open",
  345. getContainerStrategy().isNodeOpen(itemId));
  346. }
  347. }
  348. @Override
  349. protected void paintRowIcon(PaintTarget target, Object[][] cells,
  350. int indexInRowbuffer) throws PaintException {
  351. // always paint if present (in parent only if row headers visible)
  352. if (getRowHeaderMode() == ROW_HEADER_MODE_HIDDEN) {
  353. Resource itemIcon = getItemIcon(
  354. cells[CELL_ITEMID][indexInRowbuffer]);
  355. if (itemIcon != null) {
  356. target.addAttribute("icon", itemIcon);
  357. }
  358. } else if (cells[CELL_ICON][indexInRowbuffer] != null) {
  359. target.addAttribute("icon",
  360. (Resource) cells[CELL_ICON][indexInRowbuffer]);
  361. }
  362. }
  363. @Override
  364. protected boolean rowHeadersAreEnabled() {
  365. if (getRowHeaderMode() == RowHeaderMode.ICON_ONLY) {
  366. return false;
  367. }
  368. return super.rowHeadersAreEnabled();
  369. }
  370. @Override
  371. public void changeVariables(Object source, Map<String, Object> variables) {
  372. super.changeVariables(source, variables);
  373. if (variables.containsKey("toggleCollapsed")) {
  374. String object = (String) variables.get("toggleCollapsed");
  375. Object itemId = itemIdMapper.get(object);
  376. toggledItemId = itemId;
  377. toggleChildVisibility(itemId, false);
  378. if (variables.containsKey("selectCollapsed")) {
  379. // ensure collapsed is selected unless opened with selection
  380. // head
  381. if (isSelectable()) {
  382. select(itemId);
  383. }
  384. }
  385. } else if (variables.containsKey("focusParent")) {
  386. String key = (String) variables.get("focusParent");
  387. Object refId = itemIdMapper.get(key);
  388. Object itemId = getParent(refId);
  389. focusParent(itemId);
  390. }
  391. }
  392. private void focusParent(Object itemId) {
  393. boolean inView = false;
  394. Object inPageId = getCurrentPageFirstItemId();
  395. for (int i = 0; inPageId != null && i < getPageLength(); i++) {
  396. if (inPageId.equals(itemId)) {
  397. inView = true;
  398. break;
  399. }
  400. inPageId = nextItemId(inPageId);
  401. i++;
  402. }
  403. if (!inView) {
  404. setCurrentPageFirstItemId(itemId);
  405. }
  406. // Select the row if it is selectable.
  407. if (isSelectable()) {
  408. if (isMultiSelect()) {
  409. setValue(Collections.singleton(itemId));
  410. } else {
  411. setValue(itemId);
  412. }
  413. }
  414. setFocusedRow(itemId);
  415. }
  416. private void setFocusedRow(Object itemId) {
  417. focusedRowId = itemId;
  418. if (focusedRowId == null) {
  419. // Must still inform the client that the focusParent request has
  420. // been processed
  421. clearFocusedRowPending = true;
  422. }
  423. markAsDirty();
  424. }
  425. @Override
  426. public void paintContent(PaintTarget target) throws PaintException {
  427. if (focusedRowId != null) {
  428. target.addAttribute("focusedRow", itemIdMapper.key(focusedRowId));
  429. focusedRowId = null;
  430. } else if (clearFocusedRowPending) {
  431. // Must still inform the client that the focusParent request has
  432. // been processed
  433. target.addAttribute("clearFocusPending", true);
  434. clearFocusedRowPending = false;
  435. }
  436. target.addAttribute("animate", animationsEnabled);
  437. if (hierarchyColumnId != null) {
  438. Object[] visibleColumns2 = getVisibleColumns();
  439. for (int i = 0; i < visibleColumns2.length; i++) {
  440. Object object = visibleColumns2[i];
  441. if (hierarchyColumnId.equals(object)) {
  442. target.addAttribute(
  443. TreeTableConstants.ATTRIBUTE_HIERARCHY_COLUMN_INDEX,
  444. i);
  445. break;
  446. }
  447. }
  448. }
  449. super.paintContent(target);
  450. toggledItemId = null;
  451. }
  452. /*
  453. * Override methods for partial row updates and additions when expanding /
  454. * collapsing nodes.
  455. */
  456. @Override
  457. protected boolean isPartialRowUpdate() {
  458. return toggledItemId != null && containerSupportsPartialUpdates
  459. && !isRowCacheInvalidated();
  460. }
  461. @Override
  462. protected int getFirstAddedItemIndex() {
  463. return indexOfId(toggledItemId) + 1;
  464. }
  465. @Override
  466. protected int getAddedRowCount() {
  467. return countSubNodesRecursively(getContainerDataSource(),
  468. toggledItemId);
  469. }
  470. private int countSubNodesRecursively(Hierarchical hc, Object itemId) {
  471. int count = 0;
  472. // we need the number of children for toggledItemId no matter if its
  473. // collapsed or expanded. Other items' children are only counted if the
  474. // item is expanded.
  475. if (getContainerStrategy().isNodeOpen(itemId)
  476. || itemId == toggledItemId) {
  477. Collection<?> children = hc.getChildren(itemId);
  478. if (children != null) {
  479. count += children != null ? children.size() : 0;
  480. for (Object id : children) {
  481. count += countSubNodesRecursively(hc, id);
  482. }
  483. }
  484. }
  485. return count;
  486. }
  487. @Override
  488. protected int getFirstUpdatedItemIndex() {
  489. return indexOfId(toggledItemId);
  490. }
  491. @Override
  492. protected int getUpdatedRowCount() {
  493. return 1;
  494. }
  495. @Override
  496. protected boolean shouldHideAddedRows() {
  497. return !getContainerStrategy().isNodeOpen(toggledItemId);
  498. }
  499. private void toggleChildVisibility(Object itemId,
  500. boolean forceFullRefresh) {
  501. getContainerStrategy().toggleChildVisibility(itemId);
  502. // ensure that page still has first item in page, DON'T clear the
  503. // caches.
  504. setCurrentPageFirstItemIndex(getCurrentPageFirstItemIndex(), false);
  505. if (isCollapsed(itemId)) {
  506. fireCollapseEvent(itemId);
  507. } else {
  508. fireExpandEvent(itemId);
  509. }
  510. if (containerSupportsPartialUpdates && !forceFullRefresh) {
  511. markAsDirty();
  512. } else {
  513. // For containers that do not send item set change events, always do
  514. // full repaint instead of partial row update.
  515. refreshRowCache();
  516. }
  517. }
  518. @Override
  519. public int size() {
  520. return getContainerStrategy().size();
  521. }
  522. @Override
  523. public Hierarchical getContainerDataSource() {
  524. return (Hierarchical) super.getContainerDataSource();
  525. }
  526. @Override
  527. public void setContainerDataSource(Container newDataSource) {
  528. cStrategy = null;
  529. // FIXME: This disables partial updates until TreeTable is fixed so it
  530. // does not change component hierarchy during paint
  531. containerSupportsPartialUpdates = (newDataSource instanceof ItemSetChangeNotifier)
  532. && false;
  533. if (newDataSource != null && !(newDataSource instanceof Hierarchical)) {
  534. newDataSource = new ContainerHierarchicalWrapper(newDataSource);
  535. }
  536. if (newDataSource != null && !(newDataSource instanceof Ordered)) {
  537. newDataSource = new HierarchicalContainerOrderedWrapper(
  538. (Hierarchical) newDataSource);
  539. }
  540. super.setContainerDataSource(newDataSource);
  541. }
  542. @Override
  543. public void containerItemSetChange(Container.ItemSetChangeEvent event) {
  544. // Can't do partial repaints if items are added or removed during the
  545. // expand/collapse request
  546. toggledItemId = null;
  547. getContainerStrategy().containerItemSetChange(event);
  548. super.containerItemSetChange(event);
  549. }
  550. @Override
  551. protected Object getIdByIndex(int index) {
  552. return getContainerStrategy().getIdByIndex(index);
  553. }
  554. @Override
  555. protected int indexOfId(Object itemId) {
  556. return getContainerStrategy().indexOfId(itemId);
  557. }
  558. @Override
  559. public Object nextItemId(Object itemId) {
  560. return getContainerStrategy().nextItemId(itemId);
  561. }
  562. @Override
  563. public Object lastItemId() {
  564. return getContainerStrategy().lastItemId();
  565. }
  566. @Override
  567. public Object prevItemId(Object itemId) {
  568. return getContainerStrategy().prevItemId(itemId);
  569. }
  570. @Override
  571. public boolean isLastId(Object itemId) {
  572. return getContainerStrategy().isLastId(itemId);
  573. }
  574. @Override
  575. public Collection<?> getItemIds() {
  576. return getContainerStrategy().getItemIds();
  577. }
  578. @Override
  579. public boolean areChildrenAllowed(Object itemId) {
  580. return getContainerDataSource().areChildrenAllowed(itemId);
  581. }
  582. @Override
  583. public Collection<?> getChildren(Object itemId) {
  584. return getContainerDataSource().getChildren(itemId);
  585. }
  586. @Override
  587. public Object getParent(Object itemId) {
  588. return getContainerDataSource().getParent(itemId);
  589. }
  590. @Override
  591. public boolean hasChildren(Object itemId) {
  592. return getContainerDataSource().hasChildren(itemId);
  593. }
  594. @Override
  595. public boolean isRoot(Object itemId) {
  596. return getContainerDataSource().isRoot(itemId);
  597. }
  598. @Override
  599. public Collection<?> rootItemIds() {
  600. return getContainerDataSource().rootItemIds();
  601. }
  602. @Override
  603. public boolean setChildrenAllowed(Object itemId, boolean areChildrenAllowed)
  604. throws UnsupportedOperationException {
  605. return getContainerDataSource().setChildrenAllowed(itemId,
  606. areChildrenAllowed);
  607. }
  608. /**
  609. * {@inheritDoc}
  610. * <p>
  611. * <strong>Note:</strong> Constructing a big hierarchical data set using the
  612. * methods in {@link TreeTable} may cause a decrease in performance.
  613. */
  614. @Override
  615. public boolean setParent(Object itemId, Object newParentId)
  616. throws UnsupportedOperationException {
  617. return getContainerDataSource().setParent(itemId, newParentId);
  618. }
  619. /**
  620. * {@inheritDoc}
  621. * <p>
  622. * <strong>Note:</strong> Constructing a big hierarchical data set using the
  623. * methods in {@link TreeTable} may cause a decrease in performance. Instead a
  624. * {@link Container.Hierarchical} container should be populated before setting it
  625. * to the {@code TreeTable}.
  626. */
  627. @Override
  628. public Object addItem() throws UnsupportedOperationException {
  629. return super.addItem();
  630. }
  631. /**
  632. * {@inheritDoc}
  633. * <p>
  634. * <strong>Note:</strong> Constructing a big hierarchical data set using the
  635. * methods in {@link TreeTable} may cause a decrease in performance. Instead a
  636. * {@link Container.Hierarchical} container should be populated before setting it
  637. * to the {@code TreeTable}.
  638. */
  639. @Override
  640. public Item addItem(Object itemId) throws UnsupportedOperationException {
  641. return super.addItem(itemId);
  642. }
  643. /**
  644. * {@inheritDoc}
  645. * <p>
  646. * <strong>Note:</strong> Constructing a big hierarchical data set using the
  647. * methods in {@link TreeTable} may cause a decrease in performance. Instead a
  648. * {@link Container.Hierarchical} container should be populated before setting it
  649. * to the {@code TreeTable}.
  650. */
  651. @Override
  652. public Object addItem(Object[] cells, Object itemId)
  653. throws UnsupportedOperationException {
  654. return super.addItem(cells, itemId);
  655. }
  656. /**
  657. * {@inheritDoc}
  658. * <p>
  659. * <strong>Note:</strong> Constructing a big hierarchical data set using the
  660. * methods in {@link TreeTable} may cause a decrease in performance. Instead a
  661. * {@link Container.Hierarchical} container should be populated before setting it
  662. * to the {@code TreeTable}.
  663. */
  664. @Override
  665. public Item addItemAfter(Object previousItemId, Object newItemId)
  666. throws UnsupportedOperationException {
  667. return super.addItemAfter(previousItemId, newItemId);
  668. }
  669. /**
  670. * {@inheritDoc}
  671. * <p>
  672. * <strong>Note:</strong> Constructing a big hierarchical data set using the
  673. * methods in {@link TreeTable} may cause a decrease in performance. Instead a
  674. * {@link Container.Hierarchical} container should be populated before setting it
  675. * to the {@code TreeTable}.
  676. */
  677. @Override
  678. public Object addItemAfter(Object previousItemId)
  679. throws UnsupportedOperationException {
  680. return super.addItemAfter(previousItemId);
  681. }
  682. /**
  683. * {@inheritDoc}
  684. * <p>
  685. * <strong>Note:</strong> Constructing a big hierarchical data set using the
  686. * methods in {@link TreeTable} may cause a decrease in performance. Instead a
  687. * {@link Container.Hierarchical} container should be populated before setting it
  688. * to the {@code TreeTable}.
  689. */
  690. @Override
  691. public void addItems(Collection<?> itemIds)
  692. throws UnsupportedOperationException {
  693. super.addItems(itemIds);
  694. }
  695. /**
  696. * {@inheritDoc}
  697. * <p>
  698. * <strong>Note:</strong> Constructing a big hierarchical data set using the
  699. * methods in {@link TreeTable} may cause a decrease in performance. Instead a
  700. * {@link Container.Hierarchical} container should be populated before setting it
  701. * to the {@code TreeTable}.
  702. */
  703. @Override
  704. public void addItems(Object... itemId)
  705. throws UnsupportedOperationException {
  706. super.addItems(itemId);
  707. }
  708. /**
  709. * Sets the Item specified by given identifier as collapsed or expanded. If
  710. * the Item is collapsed, its children are not displayed to the user.
  711. *
  712. * @param itemId
  713. * the identifier of the Item
  714. * @param collapsed
  715. * true if the Item should be collapsed, false if expanded
  716. */
  717. public void setCollapsed(Object itemId, boolean collapsed) {
  718. if (isCollapsed(itemId) != collapsed) {
  719. if (null == toggledItemId && !isRowCacheInvalidated()
  720. && getVisibleItemIds().contains(itemId)) {
  721. // optimization: partial refresh if only one item is
  722. // collapsed/expanded
  723. toggledItemId = itemId;
  724. toggleChildVisibility(itemId, false);
  725. } else {
  726. // make sure a full refresh takes place - otherwise neither
  727. // partial nor full repaint of table content is performed
  728. toggledItemId = null;
  729. toggleChildVisibility(itemId, true);
  730. }
  731. }
  732. }
  733. /**
  734. * Checks if Item with given identifier is collapsed in the UI.
  735. *
  736. * <p>
  737. *
  738. * @param itemId
  739. * the identifier of the checked Item
  740. * @return true if the Item with given id is collapsed
  741. * @see Collapsible#isCollapsed(Object)
  742. */
  743. public boolean isCollapsed(Object itemId) {
  744. return !getContainerStrategy().isNodeOpen(itemId);
  745. }
  746. /**
  747. * Explicitly sets the column in which the TreeTable visualizes the
  748. * hierarchy. If hierarchyColumnId is not set, the hierarchy is visualized
  749. * in the first visible column.
  750. *
  751. * @param hierarchyColumnId
  752. */
  753. public void setHierarchyColumn(Object hierarchyColumnId) {
  754. this.hierarchyColumnId = hierarchyColumnId;
  755. }
  756. /**
  757. * @return the identifier of column into which the hierarchy will be
  758. * visualized or null if the column is not explicitly defined.
  759. */
  760. public Object getHierarchyColumnId() {
  761. return hierarchyColumnId;
  762. }
  763. /**
  764. * Adds an expand listener.
  765. *
  766. * @param listener
  767. * the Listener to be added.
  768. */
  769. public void addExpandListener(ExpandListener listener) {
  770. addListener(ExpandEvent.class, listener, ExpandListener.EXPAND_METHOD);
  771. }
  772. /**
  773. * @deprecated As of 7.0, replaced by
  774. * {@link #addExpandListener(ExpandListener)}
  775. **/
  776. @Deprecated
  777. public void addListener(ExpandListener listener) {
  778. addExpandListener(listener);
  779. }
  780. /**
  781. * Removes an expand listener.
  782. *
  783. * @param listener
  784. * the Listener to be removed.
  785. */
  786. public void removeExpandListener(ExpandListener listener) {
  787. removeListener(ExpandEvent.class, listener,
  788. ExpandListener.EXPAND_METHOD);
  789. }
  790. /**
  791. * @deprecated As of 7.0, replaced by
  792. * {@link #removeExpandListener(ExpandListener)}
  793. **/
  794. @Deprecated
  795. public void removeListener(ExpandListener listener) {
  796. removeExpandListener(listener);
  797. }
  798. /**
  799. * Emits an expand event.
  800. *
  801. * @param itemId
  802. * the item id.
  803. */
  804. protected void fireExpandEvent(Object itemId) {
  805. fireEvent(new ExpandEvent(this, itemId));
  806. }
  807. /**
  808. * Adds a collapse listener.
  809. *
  810. * @param listener
  811. * the Listener to be added.
  812. */
  813. public void addCollapseListener(CollapseListener listener) {
  814. addListener(CollapseEvent.class, listener,
  815. CollapseListener.COLLAPSE_METHOD);
  816. }
  817. /**
  818. * @deprecated As of 7.0, replaced by
  819. * {@link #addCollapseListener(CollapseListener)}
  820. **/
  821. @Deprecated
  822. public void addListener(CollapseListener listener) {
  823. addCollapseListener(listener);
  824. }
  825. /**
  826. * Removes a collapse listener.
  827. *
  828. * @param listener
  829. * the Listener to be removed.
  830. */
  831. public void removeCollapseListener(CollapseListener listener) {
  832. removeListener(CollapseEvent.class, listener,
  833. CollapseListener.COLLAPSE_METHOD);
  834. }
  835. /**
  836. * @deprecated As of 7.0, replaced by
  837. * {@link #removeCollapseListener(CollapseListener)}
  838. **/
  839. @Deprecated
  840. public void removeListener(CollapseListener listener) {
  841. removeCollapseListener(listener);
  842. }
  843. /**
  844. * Emits a collapse event.
  845. *
  846. * @param itemId
  847. * the item id.
  848. */
  849. protected void fireCollapseEvent(Object itemId) {
  850. fireEvent(new CollapseEvent(this, itemId));
  851. }
  852. /**
  853. * @return true if animations are enabled
  854. */
  855. public boolean isAnimationsEnabled() {
  856. return animationsEnabled;
  857. }
  858. /**
  859. * Animations can be enabled by passing true to this method. Currently
  860. * expanding rows slide in from the top and collapsing rows slide out the
  861. * same way. NOTE! not supported in Internet Explorer 6 or 7.
  862. *
  863. * @param animationsEnabled
  864. * true or false whether to enable animations or not.
  865. */
  866. public void setAnimationsEnabled(boolean animationsEnabled) {
  867. this.animationsEnabled = animationsEnabled;
  868. markAsDirty();
  869. }
  870. private static final Logger getLogger() {
  871. return Logger.getLogger(TreeTable.class.getName());
  872. }
  873. @Override
  874. protected List<Object> getItemIds(int firstIndex, int rows) {
  875. List<Object> itemIds = new ArrayList<Object>();
  876. for (int i = firstIndex; i < firstIndex + rows; i++) {
  877. itemIds.add(getIdByIndex(i));
  878. }
  879. return itemIds;
  880. }
  881. @Override
  882. protected void readBody(Element design, DesignContext context) {
  883. Element tbody = design.select("> table > tbody").first();
  884. if (tbody == null) {
  885. return;
  886. }
  887. Set<String> selected = new HashSet<String>();
  888. Stack<Object> parents = new Stack<Object>();
  889. int lastDepth = -1;
  890. for (Element tr : tbody.children()) {
  891. int depth = DesignAttributeHandler.readAttribute("depth",
  892. tr.attributes(), 0, int.class);
  893. if (depth < 0 || depth > lastDepth + 1) {
  894. throw new DesignException(
  895. "Malformed TreeTable item hierarchy at " + tr
  896. + ": last depth was " + lastDepth);
  897. } else if (depth <= lastDepth) {
  898. for (int d = depth; d <= lastDepth; d++) {
  899. parents.pop();
  900. }
  901. }
  902. Object itemId = readItem(tr, selected, context);
  903. setParent(itemId, !parents.isEmpty() ? parents.peek() : null);
  904. parents.push(itemId);
  905. lastDepth = depth;
  906. }
  907. }
  908. @Override
  909. protected Object readItem(Element tr, Set<String> selected,
  910. DesignContext context) {
  911. Object itemId = super.readItem(tr, selected, context);
  912. if (tr.hasAttr("collapsed")) {
  913. boolean collapsed = DesignAttributeHandler
  914. .readAttribute("collapsed", tr.attributes(), boolean.class);
  915. setCollapsed(itemId, collapsed);
  916. }
  917. return itemId;
  918. }
  919. @Override
  920. protected void writeItems(Element design, DesignContext context) {
  921. if (getVisibleColumns().length == 0) {
  922. return;
  923. }
  924. Element tbody = design.child(0).appendElement("tbody");
  925. writeItems(tbody, rootItemIds(), 0, context);
  926. }
  927. protected void writeItems(Element tbody, Collection<?> itemIds, int depth,
  928. DesignContext context) {
  929. for (Object itemId : itemIds) {
  930. Element tr = writeItem(tbody, itemId, context);
  931. DesignAttributeHandler.writeAttribute("depth", tr.attributes(),
  932. depth, 0, int.class);
  933. if (getChildren(itemId) != null) {
  934. writeItems(tbody, getChildren(itemId), depth + 1, context);
  935. }
  936. }
  937. }
  938. @Override
  939. protected Element writeItem(Element tbody, Object itemId,
  940. DesignContext context) {
  941. Element tr = super.writeItem(tbody, itemId, context);
  942. DesignAttributeHandler.writeAttribute("collapsed", tr.attributes(),
  943. isCollapsed(itemId), true, boolean.class);
  944. return tr;
  945. }
  946. @Override
  947. protected TreeTableState getState() {
  948. return (TreeTableState) super.getState();
  949. }
  950. }