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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  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.ui;
  17. import java.util.ArrayList;
  18. import java.util.Arrays;
  19. import java.util.Collection;
  20. import java.util.List;
  21. import java.util.Objects;
  22. import java.util.Optional;
  23. import java.util.stream.Stream;
  24. import org.jsoup.nodes.Attributes;
  25. import org.jsoup.nodes.Element;
  26. import org.jsoup.select.Elements;
  27. import com.vaadin.data.HasHierarchicalDataProvider;
  28. import com.vaadin.data.HasValue;
  29. import com.vaadin.data.PropertyDefinition;
  30. import com.vaadin.data.PropertySet;
  31. import com.vaadin.data.TreeData;
  32. import com.vaadin.data.ValueProvider;
  33. import com.vaadin.data.provider.DataProvider;
  34. import com.vaadin.data.provider.HierarchicalDataCommunicator;
  35. import com.vaadin.data.provider.HierarchicalDataProvider;
  36. import com.vaadin.data.provider.HierarchicalQuery;
  37. import com.vaadin.data.provider.TreeDataProvider;
  38. import com.vaadin.event.CollapseEvent;
  39. import com.vaadin.event.CollapseEvent.CollapseListener;
  40. import com.vaadin.event.ExpandEvent;
  41. import com.vaadin.event.ExpandEvent.ExpandListener;
  42. import com.vaadin.shared.Registration;
  43. import com.vaadin.shared.ui.grid.ScrollDestination;
  44. import com.vaadin.shared.ui.treegrid.FocusParentRpc;
  45. import com.vaadin.shared.ui.treegrid.FocusRpc;
  46. import com.vaadin.shared.ui.treegrid.NodeCollapseRpc;
  47. import com.vaadin.shared.ui.treegrid.TreeGridClientRpc;
  48. import com.vaadin.shared.ui.treegrid.TreeGridState;
  49. import com.vaadin.ui.declarative.DesignAttributeHandler;
  50. import com.vaadin.ui.declarative.DesignContext;
  51. import com.vaadin.ui.declarative.DesignFormatter;
  52. /**
  53. * A grid component for displaying hierarchical tabular data.
  54. *
  55. * Visual hierarchy depth positioning of rows is done via styles, see
  56. * <code>_treegrid.scss</code> from Valo theme.
  57. *
  58. * @author Vaadin Ltd
  59. * @since 8.1
  60. *
  61. * @param <T>
  62. * the grid bean type
  63. */
  64. public class TreeGrid<T> extends Grid<T>
  65. implements HasHierarchicalDataProvider<T> {
  66. /**
  67. * Creates a new {@code TreeGrid} without support for creating columns based
  68. * on property names. Use an alternative constructor, such as
  69. * {@link TreeGrid#TreeGrid(Class)}, to create a {@code TreeGrid} that
  70. * automatically sets up columns based on the type of presented data.
  71. */
  72. public TreeGrid() {
  73. this(new HierarchicalDataCommunicator<>());
  74. }
  75. /**
  76. * Creates a new {@code TreeGrid} that uses reflection based on the provided
  77. * bean type to automatically set up an initial set of columns. All columns
  78. * will be configured using the same {@link Object#toString()} renderer that
  79. * is used by {@link #addColumn(ValueProvider)}.
  80. *
  81. * @param beanType
  82. * the bean type to use, not {@code null}
  83. */
  84. public TreeGrid(Class<T> beanType) {
  85. super(beanType, new HierarchicalDataCommunicator<>());
  86. registerTreeGridRpc();
  87. }
  88. /**
  89. * Creates a new {@code TreeGrid} using the given
  90. * {@code HierarchicalDataProvider}, without support for creating columns
  91. * based on property names. Use an alternative constructor, such as
  92. * {@link TreeGrid#TreeGrid(Class)}, to create a {@code TreeGrid} that
  93. * automatically sets up columns based on the type of presented data.
  94. *
  95. * @param dataProvider
  96. * the data provider, not {@code null}
  97. */
  98. public TreeGrid(HierarchicalDataProvider<T, ?> dataProvider) {
  99. this();
  100. setDataProvider(dataProvider);
  101. }
  102. /**
  103. * Creates a {@code TreeGrid} using a custom {@link PropertySet}
  104. * implementation and custom data communicator.
  105. * <p>
  106. * Property set is used for configuring the initial columns and resolving
  107. * property names for {@link #addColumn(String)} and
  108. * {@link Column#setEditorComponent(HasValue)}.
  109. *
  110. * @param propertySet
  111. * the property set implementation to use, not {@code null}
  112. * @param dataCommunicator
  113. * the data communicator to use, not {@code null}
  114. */
  115. protected TreeGrid(PropertySet<T> propertySet,
  116. HierarchicalDataCommunicator<T> dataCommunicator) {
  117. super(propertySet, dataCommunicator);
  118. registerTreeGridRpc();
  119. }
  120. /**
  121. * Creates a new TreeGrid with the given data communicator and without
  122. * support for creating columns based on property names.
  123. *
  124. * @param dataCommunicator
  125. * the custom data communicator to set
  126. */
  127. protected TreeGrid(HierarchicalDataCommunicator<T> dataCommunicator) {
  128. this(new PropertySet<T>() {
  129. @Override
  130. public Stream<PropertyDefinition<T, ?>> getProperties() {
  131. // No columns configured by default
  132. return Stream.empty();
  133. }
  134. @Override
  135. public Optional<PropertyDefinition<T, ?>> getProperty(String name) {
  136. throw new IllegalStateException(
  137. "A TreeGrid created without a bean type class literal or a custom property set"
  138. + " doesn't support finding properties by name.");
  139. }
  140. }, dataCommunicator);
  141. }
  142. /**
  143. * Creates a {@code TreeGrid} using a custom {@link PropertySet}
  144. * implementation for creating a default set of columns and for resolving
  145. * property names with {@link #addColumn(String)} and
  146. * {@link Column#setEditorComponent(HasValue)}.
  147. * <p>
  148. * This functionality is provided as static method instead of as a public
  149. * constructor in order to make it possible to use a custom property set
  150. * without creating a subclass while still leaving the public constructors
  151. * focused on the common use cases.
  152. *
  153. * @see TreeGrid#TreeGrid()
  154. * @see TreeGrid#TreeGrid(Class)
  155. *
  156. * @param propertySet
  157. * the property set implementation to use, not {@code null}
  158. * @return a new tree grid using the provided property set, not {@code null}
  159. */
  160. public static <BEAN> TreeGrid<BEAN> withPropertySet(
  161. PropertySet<BEAN> propertySet) {
  162. return new TreeGrid<BEAN>(propertySet,
  163. new HierarchicalDataCommunicator<>());
  164. }
  165. private void registerTreeGridRpc() {
  166. registerRpc((NodeCollapseRpc) (rowKey, rowIndex, collapse,
  167. userOriginated) -> {
  168. T item = getDataCommunicator().getKeyMapper().get(rowKey);
  169. if (collapse && getDataCommunicator().isExpanded(item)) {
  170. getDataCommunicator().collapse(item, rowIndex);
  171. fireCollapseEvent(
  172. getDataCommunicator().getKeyMapper().get(rowKey),
  173. userOriginated);
  174. } else if (!collapse && !getDataCommunicator().isExpanded(item)) {
  175. getDataCommunicator().expand(item, rowIndex);
  176. fireExpandEvent(
  177. getDataCommunicator().getKeyMapper().get(rowKey),
  178. userOriginated);
  179. }
  180. });
  181. registerRpc((FocusParentRpc) (rowKey, cellIndex) -> {
  182. Integer parentIndex = getDataCommunicator().getParentIndex(
  183. getDataCommunicator().getKeyMapper().get(rowKey));
  184. if (parentIndex != null) {
  185. getRpcProxy(FocusRpc.class).focusCell(parentIndex, cellIndex);
  186. }
  187. });
  188. }
  189. /**
  190. * This method is inherited from Grid but should never be called directly
  191. * with a TreeGrid
  192. */
  193. @Override
  194. @Deprecated
  195. public void scrollTo(int row) throws IllegalArgumentException {
  196. super.scrollTo(row);
  197. }
  198. /**
  199. * This method is inherited from Grid but should never be called directly
  200. * with a TreeGrid
  201. */
  202. @Deprecated
  203. @Override
  204. public void scrollTo(int row, ScrollDestination destination) {
  205. super.scrollTo(row, destination);
  206. }
  207. /**
  208. * Adds an ExpandListener to this TreeGrid.
  209. *
  210. * @see ExpandEvent
  211. *
  212. * @param listener
  213. * the listener to add
  214. * @return a registration for the listener
  215. */
  216. public Registration addExpandListener(ExpandListener<T> listener) {
  217. return addListener(ExpandEvent.class, listener,
  218. ExpandListener.EXPAND_METHOD);
  219. }
  220. /**
  221. * Adds a CollapseListener to this TreeGrid.
  222. *
  223. * @see CollapseEvent
  224. *
  225. * @param listener
  226. * the listener to add
  227. * @return a registration for the listener
  228. */
  229. public Registration addCollapseListener(CollapseListener<T> listener) {
  230. return addListener(CollapseEvent.class, listener,
  231. CollapseListener.COLLAPSE_METHOD);
  232. }
  233. @Override
  234. public void setDataProvider(DataProvider<T, ?> dataProvider) {
  235. if (!(dataProvider instanceof HierarchicalDataProvider)) {
  236. throw new IllegalArgumentException(
  237. "TreeGrid only accepts hierarchical data providers");
  238. }
  239. getRpcProxy(TreeGridClientRpc.class).clearPendingExpands();
  240. super.setDataProvider(dataProvider);
  241. }
  242. /**
  243. * Get the currently set hierarchy column.
  244. *
  245. * @return the currently set hierarchy column, or {@code null} if no column
  246. * has been explicitly set
  247. */
  248. public Column<T, ?> getHierarchyColumn() {
  249. return getColumnByInternalId(getState(false).hierarchyColumnId);
  250. }
  251. /**
  252. * Set the column that displays the hierarchy of this grid's data. By
  253. * default the hierarchy will be displayed in the first column.
  254. * <p>
  255. * Setting a hierarchy column by calling this method also sets the column to
  256. * be visible and not hidable.
  257. * <p>
  258. * <strong>Note:</strong> Changing the Renderer of the hierarchy column is
  259. * not supported.
  260. *
  261. * @param column
  262. * the column to use for displaying hierarchy
  263. */
  264. public void setHierarchyColumn(Column<T, ?> column) {
  265. Objects.requireNonNull(column, "column may not be null");
  266. if (!getColumns().contains(column)) {
  267. throw new IllegalArgumentException(
  268. "Given column is not a column of this TreeGrid");
  269. }
  270. column.setHidden(false);
  271. column.setHidable(false);
  272. getState().hierarchyColumnId = getInternalIdForColumn(column);
  273. }
  274. /**
  275. * Set the column that displays the hierarchy of this grid's data. By
  276. * default the hierarchy will be displayed in the first column.
  277. * <p>
  278. * Setting a hierarchy column by calling this method also sets the column to
  279. * be visible and not hidable.
  280. * <p>
  281. * <strong>Note:</strong> Changing the Renderer of the hierarchy column is
  282. * not supported.
  283. *
  284. * @see Column#setId(String)
  285. *
  286. * @param id
  287. * id of the column to use for displaying hierarchy
  288. */
  289. public void setHierarchyColumn(String id) {
  290. Objects.requireNonNull(id, "id may not be null");
  291. if (getColumn(id) == null) {
  292. throw new IllegalArgumentException("No column found for given id");
  293. }
  294. setHierarchyColumn(getColumn(id));
  295. }
  296. /**
  297. * Sets the item collapse allowed provider for this TreeGrid. The provider
  298. * should return {@code true} for any item that the user can collapse.
  299. * <p>
  300. * <strong>Note:</strong> This callback will be accessed often when sending
  301. * data to the client. The callback should not do any costly operations.
  302. * <p>
  303. * This method is a shortcut to method with the same name in
  304. * {@link HierarchicalDataCommunicator}.
  305. *
  306. * @param provider
  307. * the item collapse allowed provider, not {@code null}
  308. *
  309. * @see HierarchicalDataCommunicator#setItemCollapseAllowedProvider(ItemCollapseAllowedProvider)
  310. */
  311. public void setItemCollapseAllowedProvider(
  312. ItemCollapseAllowedProvider<T> provider) {
  313. getDataCommunicator().setItemCollapseAllowedProvider(provider);
  314. }
  315. /**
  316. * Expands the given items.
  317. * <p>
  318. * If an item is currently expanded, does nothing. If an item does not have
  319. * any children, does nothing.
  320. *
  321. * @param items
  322. * the items to expand
  323. */
  324. public void expand(T... items) {
  325. expand(Arrays.asList(items));
  326. }
  327. /**
  328. * Expands the given items.
  329. * <p>
  330. * If an item is currently expanded, does nothing. If an item does not have
  331. * any children, does nothing.
  332. *
  333. * @param items
  334. * the items to expand
  335. */
  336. public void expand(Collection<T> items) {
  337. HierarchicalDataCommunicator<T> communicator = getDataCommunicator();
  338. items.forEach(item -> {
  339. if (!communicator.isExpanded(item)
  340. && communicator.hasChildren(item)) {
  341. communicator.expand(item);
  342. fireExpandEvent(item, false);
  343. }
  344. });
  345. }
  346. /**
  347. * Expands the given items and their children recursively until the given
  348. * depth.
  349. * <p>
  350. * {@code depth} describes the maximum distance between a given item and its
  351. * descendant, meaning that {@code expandRecursively(items, 0)} expands only
  352. * the given items while {@code expandRecursively(items, 2)} expands the
  353. * given items as well as their children and grandchildren.
  354. * <p>
  355. * This method will <i>not</i> fire events for expanded nodes.
  356. *
  357. * @param items
  358. * the items to expand recursively
  359. * @param depth
  360. * the maximum depth of recursion
  361. * @since 8.4
  362. */
  363. public void expandRecursively(Collection<T> items, int depth) {
  364. expandRecursively(items.stream(), depth);
  365. }
  366. /**
  367. * Expands the given items and their children recursively until the given
  368. * depth.
  369. * <p>
  370. * {@code depth} describes the maximum distance between a given item and its
  371. * descendant, meaning that {@code expandRecursively(items, 0)} expands only
  372. * the given items while {@code expandRecursively(items, 2)} expands the
  373. * given items as well as their children and grandchildren.
  374. * <p>
  375. * This method will <i>not</i> fire events for expanded nodes.
  376. *
  377. * @param items
  378. * the items to expand recursively
  379. * @param depth
  380. * the maximum depth of recursion
  381. * @since 8.4
  382. */
  383. public void expandRecursively(Stream<T> items, int depth) {
  384. if (depth < 0) {
  385. return;
  386. }
  387. HierarchicalDataCommunicator<T> communicator = getDataCommunicator();
  388. items.forEach(item -> {
  389. if (communicator.hasChildren(item)) {
  390. communicator.expand(item, false);
  391. expandRecursively(
  392. getDataProvider().fetchChildren(
  393. new HierarchicalQuery<>(null, item)),
  394. depth - 1);
  395. }
  396. });
  397. getDataProvider().refreshAll();
  398. }
  399. /**
  400. * Collapse the given items.
  401. * <p>
  402. * For items that are already collapsed, does nothing.
  403. *
  404. * @param items
  405. * the collection of items to collapse
  406. */
  407. public void collapse(T... items) {
  408. collapse(Arrays.asList(items));
  409. }
  410. /**
  411. * Collapse the given items.
  412. * <p>
  413. * For items that are already collapsed, does nothing.
  414. *
  415. * @param items
  416. * the collection of items to collapse
  417. */
  418. public void collapse(Collection<T> items) {
  419. HierarchicalDataCommunicator<T> communicator = getDataCommunicator();
  420. items.forEach(item -> {
  421. if (communicator.isExpanded(item)) {
  422. communicator.collapse(item);
  423. fireCollapseEvent(item, false);
  424. }
  425. });
  426. }
  427. /**
  428. * Collapse the given items and their children recursively until the given
  429. * depth.
  430. * <p>
  431. * {@code depth} describes the maximum distance between a given item and its
  432. * descendant, meaning that {@code collapseRecursively(items, 0)} collapses
  433. * only the given items while {@code collapseRecursively(items, 2)}
  434. * collapses the given items as well as their children and grandchildren.
  435. * <p>
  436. * This method will <i>not</i> fire events for collapsed nodes.
  437. *
  438. * @param items
  439. * the items to collapse recursively
  440. * @param depth
  441. * the maximum depth of recursion
  442. * @since 8.4
  443. */
  444. public void collapseRecursively(Collection<T> items, int depth) {
  445. collapseRecursively(items.stream(), depth);
  446. }
  447. /**
  448. * Collapse the given items and their children recursively until the given
  449. * depth.
  450. * <p>
  451. * {@code depth} describes the maximum distance between a given item and its
  452. * descendant, meaning that {@code collapseRecursively(items, 0)} collapses
  453. * only the given items while {@code collapseRecursively(items, 2)}
  454. * collapses the given items as well as their children and grandchildren.
  455. * <p>
  456. * This method will <i>not</i> fire events for collapsed nodes.
  457. *
  458. * @param items
  459. * the items to collapse recursively
  460. * @param depth
  461. * the maximum depth of recursion
  462. * @since 8.4
  463. */
  464. public void collapseRecursively(Stream<T> items, int depth) {
  465. if (depth < 0) {
  466. return;
  467. }
  468. HierarchicalDataCommunicator<T> communicator = getDataCommunicator();
  469. items.forEach(item -> {
  470. if (communicator.hasChildren(item)) {
  471. collapseRecursively(
  472. getDataProvider().fetchChildren(
  473. new HierarchicalQuery<>(null, item)),
  474. depth - 1);
  475. communicator.collapse(item, false);
  476. }
  477. });
  478. getDataProvider().refreshAll();
  479. }
  480. /**
  481. * Returns whether a given item is expanded or collapsed.
  482. *
  483. * @param item
  484. * the item to check
  485. * @return true if the item is expanded, false if collapsed
  486. */
  487. public boolean isExpanded(T item) {
  488. return getDataCommunicator().isExpanded(item);
  489. }
  490. @Override
  491. protected TreeGridState getState() {
  492. return (TreeGridState) super.getState();
  493. }
  494. @Override
  495. protected TreeGridState getState(boolean markAsDirty) {
  496. return (TreeGridState) super.getState(markAsDirty);
  497. }
  498. @Override
  499. public HierarchicalDataCommunicator<T> getDataCommunicator() {
  500. return (HierarchicalDataCommunicator<T>) super.getDataCommunicator();
  501. }
  502. @Override
  503. public HierarchicalDataProvider<T, ?> getDataProvider() {
  504. if (!(super.getDataProvider() instanceof HierarchicalDataProvider)) {
  505. return null;
  506. }
  507. return (HierarchicalDataProvider<T, ?>) super.getDataProvider();
  508. }
  509. @Override
  510. protected void doReadDesign(Element design, DesignContext context) {
  511. super.doReadDesign(design, context);
  512. Attributes attrs = design.attributes();
  513. if (attrs.hasKey("hierarchy-column")) {
  514. setHierarchyColumn(DesignAttributeHandler
  515. .readAttribute("hierarchy-column", attrs, String.class));
  516. }
  517. }
  518. @Override
  519. protected void readData(Element body,
  520. List<DeclarativeValueProvider<T>> providers) {
  521. getSelectionModel().deselectAll();
  522. List<T> selectedItems = new ArrayList<>();
  523. TreeData<T> data = new TreeData<T>();
  524. for (Element row : body.children()) {
  525. T item = deserializeDeclarativeRepresentation(row.attr("item"));
  526. T parent = null;
  527. if (row.hasAttr("parent")) {
  528. parent = deserializeDeclarativeRepresentation(
  529. row.attr("parent"));
  530. }
  531. data.addItem(parent, item);
  532. if (row.hasAttr("selected")) {
  533. selectedItems.add(item);
  534. }
  535. Elements cells = row.children();
  536. int i = 0;
  537. for (Element cell : cells) {
  538. providers.get(i).addValue(item, cell.html());
  539. i++;
  540. }
  541. }
  542. setDataProvider(new TreeDataProvider<>(data));
  543. selectedItems.forEach(getSelectionModel()::select);
  544. }
  545. @Override
  546. protected void doWriteDesign(Element design, DesignContext designContext) {
  547. super.doWriteDesign(design, designContext);
  548. if (getColumnByInternalId(getState(false).hierarchyColumnId) != null) {
  549. String hierarchyColumn = getColumnByInternalId(
  550. getState(false).hierarchyColumnId).getId();
  551. DesignAttributeHandler.writeAttribute("hierarchy-column",
  552. design.attributes(), hierarchyColumn, null, String.class,
  553. designContext);
  554. }
  555. }
  556. @Override
  557. protected void writeData(Element body, DesignContext designContext) {
  558. getDataProvider().fetch(new HierarchicalQuery<>(null, null))
  559. .forEach(item -> writeRow(body, item, null, designContext));
  560. }
  561. private void writeRow(Element container, T item, T parent,
  562. DesignContext context) {
  563. Element tableRow = container.appendElement("tr");
  564. tableRow.attr("item", serializeDeclarativeRepresentation(item));
  565. if (parent != null) {
  566. tableRow.attr("parent", serializeDeclarativeRepresentation(parent));
  567. }
  568. if (getSelectionModel().isSelected(item)) {
  569. tableRow.attr("selected", true);
  570. }
  571. for (Column<T, ?> column : getColumns()) {
  572. Object value = column.getValueProvider().apply(item);
  573. tableRow.appendElement("td")
  574. .append(Optional.ofNullable(value).map(Object::toString)
  575. .map(DesignFormatter::encodeForTextNode)
  576. .orElse(""));
  577. }
  578. getDataProvider().fetch(new HierarchicalQuery<>(null, item)).forEach(
  579. childItem -> writeRow(container, childItem, item, context));
  580. }
  581. /**
  582. * Emit an expand event.
  583. *
  584. * @param item
  585. * the item that was expanded
  586. * @param userOriginated
  587. * whether the expand was triggered by a user interaction or the
  588. * server
  589. */
  590. private void fireExpandEvent(T item, boolean userOriginated) {
  591. fireEvent(new ExpandEvent<>(this, item, userOriginated));
  592. }
  593. /**
  594. * Emit a collapse event.
  595. *
  596. * @param item
  597. * the item that was collapsed
  598. * @param userOriginated
  599. * whether the collapse was triggered by a user interaction or
  600. * the server
  601. */
  602. private void fireCollapseEvent(T item, boolean userOriginated) {
  603. fireEvent(new CollapseEvent<>(this, item, userOriginated));
  604. }
  605. /**
  606. * Gets the item collapse allowed provider.
  607. *
  608. * @return the item collapse allowed provider
  609. */
  610. public ItemCollapseAllowedProvider<T> getItemCollapseAllowedProvider() {
  611. return getDataCommunicator().getItemCollapseAllowedProvider();
  612. }
  613. }