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.

Tree.java 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765
  1. /*
  2. * Copyright 2000-2016 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.lang.reflect.Method;
  18. import java.util.Collection;
  19. import java.util.HashMap;
  20. import java.util.HashSet;
  21. import java.util.Map;
  22. import java.util.Objects;
  23. import java.util.Set;
  24. import com.vaadin.data.Binder;
  25. import com.vaadin.data.HasHierarchicalDataProvider;
  26. import com.vaadin.data.SelectionModel;
  27. import com.vaadin.data.TreeData;
  28. import com.vaadin.data.provider.DataGenerator;
  29. import com.vaadin.data.provider.DataProvider;
  30. import com.vaadin.data.provider.HierarchicalDataProvider;
  31. import com.vaadin.data.provider.TreeDataProvider;
  32. import com.vaadin.event.CollapseEvent;
  33. import com.vaadin.event.CollapseEvent.CollapseListener;
  34. import com.vaadin.event.ConnectorEvent;
  35. import com.vaadin.event.ExpandEvent;
  36. import com.vaadin.event.ExpandEvent.ExpandListener;
  37. import com.vaadin.event.SerializableEventListener;
  38. import com.vaadin.event.selection.SelectionListener;
  39. import com.vaadin.server.ErrorMessage;
  40. import com.vaadin.server.Resource;
  41. import com.vaadin.shared.Registration;
  42. import com.vaadin.shared.ui.ContentMode;
  43. import com.vaadin.shared.ui.grid.HeightMode;
  44. import com.vaadin.shared.ui.tree.TreeMultiSelectionModelState;
  45. import com.vaadin.shared.ui.tree.TreeRendererState;
  46. import com.vaadin.ui.Grid.SelectionMode;
  47. import com.vaadin.ui.components.grid.MultiSelectionModelImpl;
  48. import com.vaadin.ui.renderers.AbstractRenderer;
  49. import com.vaadin.util.ReflectTools;
  50. import elemental.json.JsonObject;
  51. /**
  52. * Tree component. A Tree can be used to select an item from a hierarchical set
  53. * of items.
  54. *
  55. * @author Vaadin Ltd
  56. * @since 8.1
  57. *
  58. * @param <T>
  59. * the data type
  60. */
  61. public class Tree<T> extends Composite
  62. implements HasHierarchicalDataProvider<T> {
  63. @Deprecated
  64. private static final Method ITEM_CLICK_METHOD = ReflectTools
  65. .findMethod(ItemClickListener.class, "itemClick", ItemClick.class);
  66. /**
  67. * A listener for item click events.
  68. *
  69. * @param <T>
  70. * the tree item type
  71. *
  72. * @see ItemClick
  73. * @see Registration
  74. * @since 8.1
  75. */
  76. @FunctionalInterface
  77. public interface ItemClickListener<T> extends SerializableEventListener {
  78. /**
  79. * Invoked when this listener receives a item click event from a Tree to
  80. * which it has been added.
  81. *
  82. * @param event
  83. * the received event, not {@code null}
  84. */
  85. public void itemClick(Tree.ItemClick<T> event);
  86. }
  87. /**
  88. * Tree item click event.
  89. *
  90. * @param <T>
  91. * the data type of tree
  92. * @since 8.1
  93. */
  94. public static class ItemClick<T> extends ConnectorEvent {
  95. private final T item;
  96. /**
  97. * Constructs a new item click.
  98. *
  99. * @param source
  100. * the tree component
  101. * @param item
  102. * the clicked item
  103. */
  104. protected ItemClick(Tree<T> source, T item) {
  105. super(source);
  106. this.item = item;
  107. }
  108. /**
  109. * Returns the clicked item.
  110. *
  111. * @return the clicked item
  112. */
  113. public T getItem() {
  114. return item;
  115. }
  116. @SuppressWarnings("unchecked")
  117. @Override
  118. public Tree<T> getSource() {
  119. return (Tree<T>) super.getSource();
  120. }
  121. }
  122. /**
  123. * String renderer that handles icon resources and stores their identifiers
  124. * into data objects.
  125. *
  126. * @since 8.1
  127. */
  128. public final class TreeRenderer extends AbstractRenderer<T, String>
  129. implements DataGenerator<T> {
  130. /**
  131. * Constructs a new TreeRenderer.
  132. */
  133. protected TreeRenderer() {
  134. super(String.class);
  135. }
  136. private Map<T, String> resourceKeyMap = new HashMap<>();
  137. private int counter = 0;
  138. @Override
  139. public void generateData(T item, JsonObject jsonObject) {
  140. Resource resource = iconProvider.apply(item);
  141. if (resource == null) {
  142. destroyData(item);
  143. return;
  144. }
  145. if (!resourceKeyMap.containsKey(item)) {
  146. resourceKeyMap.put(item, "icon" + (counter++));
  147. }
  148. setResource(resourceKeyMap.get(item), resource);
  149. jsonObject.put("itemIcon", resourceKeyMap.get(item));
  150. }
  151. @Override
  152. public void destroyData(T item) {
  153. if (resourceKeyMap.containsKey(item)) {
  154. setResource(resourceKeyMap.get(item), null);
  155. resourceKeyMap.remove(item);
  156. }
  157. }
  158. @Override
  159. public void destroyAllData() {
  160. Set<T> keys = new HashSet<>(resourceKeyMap.keySet());
  161. for (T key : keys) {
  162. destroyData(key);
  163. }
  164. }
  165. @Override
  166. protected TreeRendererState getState() {
  167. return (TreeRendererState) super.getState();
  168. }
  169. @Override
  170. protected TreeRendererState getState(boolean markAsDirty) {
  171. return (TreeRendererState) super.getState(markAsDirty);
  172. }
  173. }
  174. /**
  175. * Custom MultiSelectionModel for Tree. TreeMultiSelectionModel does not
  176. * show selection column.
  177. *
  178. * @param <T>
  179. * the tree item type
  180. *
  181. * @since 8.1
  182. */
  183. public final static class TreeMultiSelectionModel<T>
  184. extends MultiSelectionModelImpl<T> {
  185. @Override
  186. protected TreeMultiSelectionModelState getState() {
  187. return (TreeMultiSelectionModelState) super.getState();
  188. }
  189. @Override
  190. protected TreeMultiSelectionModelState getState(boolean markAsDirty) {
  191. return (TreeMultiSelectionModelState) super.getState(markAsDirty);
  192. }
  193. }
  194. private TreeGrid<T> treeGrid = new TreeGrid<>();
  195. private ItemCaptionGenerator<T> captionGenerator = String::valueOf;
  196. private IconGenerator<T> iconProvider = t -> null;
  197. /**
  198. * Constructs a new Tree Component.
  199. */
  200. public Tree() {
  201. setCompositionRoot(treeGrid);
  202. TreeRenderer renderer = new TreeRenderer();
  203. treeGrid.getDataCommunicator().addDataGenerator(renderer);
  204. treeGrid.addColumn(i -> captionGenerator.apply(i), renderer)
  205. .setId("column");
  206. treeGrid.setHierarchyColumn("column");
  207. while (treeGrid.getHeaderRowCount() > 0) {
  208. treeGrid.removeHeaderRow(0);
  209. }
  210. treeGrid.setPrimaryStyleName("v-tree8");
  211. treeGrid.setRowHeight(28);
  212. setWidth("100%");
  213. treeGrid.setHeightUndefined();
  214. treeGrid.setHeightMode(HeightMode.UNDEFINED);
  215. treeGrid.addExpandListener(e -> fireExpandEvent(e.getExpandedItem(),
  216. e.isUserOriginated()));
  217. treeGrid.addCollapseListener(e -> fireCollapseEvent(
  218. e.getCollapsedItem(), e.isUserOriginated()));
  219. treeGrid.addItemClickListener(
  220. e -> fireEvent(new ItemClick<>(this, e.getItem())));
  221. }
  222. /**
  223. * Constructs a new Tree Component with given caption.
  224. *
  225. * @param caption
  226. * the caption for component
  227. */
  228. public Tree(String caption) {
  229. this();
  230. setCaption(caption);
  231. }
  232. /**
  233. * Constructs a new Tree Component with given caption and {@code TreeData}.
  234. *
  235. * @param caption
  236. * the caption for component
  237. * @param treeData
  238. * the tree data for component
  239. */
  240. public Tree(String caption, TreeData<T> treeData) {
  241. this(caption, new TreeDataProvider<>(treeData));
  242. }
  243. /**
  244. * Constructs a new Tree Component with given caption and
  245. * {@code HierarchicalDataProvider}.
  246. *
  247. * @param caption
  248. * the caption for component
  249. * @param dataProvider
  250. * the hierarchical data provider for component
  251. */
  252. public Tree(String caption, HierarchicalDataProvider<T, ?> dataProvider) {
  253. this(caption);
  254. treeGrid.setDataProvider(dataProvider);
  255. }
  256. /**
  257. * Constructs a new Tree Component with given
  258. * {@code HierarchicalDataProvider}.
  259. *
  260. * @param dataProvider
  261. * the hierarchical data provider for component
  262. */
  263. public Tree(HierarchicalDataProvider<T, ?> dataProvider) {
  264. this(null, dataProvider);
  265. }
  266. @Override
  267. public HierarchicalDataProvider<T, ?> getDataProvider() {
  268. return treeGrid.getDataProvider();
  269. }
  270. @Override
  271. public void setDataProvider(DataProvider<T, ?> dataProvider) {
  272. treeGrid.setDataProvider(dataProvider);
  273. }
  274. /**
  275. * Adds an ExpandListener to this Tree.
  276. *
  277. * @see ExpandEvent
  278. *
  279. * @param listener
  280. * the listener to add
  281. * @return a registration for the listener
  282. */
  283. public Registration addExpandListener(ExpandListener<T> listener) {
  284. return addListener(ExpandEvent.class, listener,
  285. ExpandListener.EXPAND_METHOD);
  286. }
  287. /**
  288. * Adds a CollapseListener to this Tree.
  289. *
  290. * @see CollapseEvent
  291. *
  292. * @param listener
  293. * the listener to add
  294. * @return a registration for the listener
  295. */
  296. public Registration addCollapseListener(CollapseListener<T> listener) {
  297. return addListener(CollapseEvent.class, listener,
  298. CollapseListener.COLLAPSE_METHOD);
  299. }
  300. /**
  301. * Fires an expand event with given item.
  302. *
  303. * @param item
  304. * the expanded item
  305. * @param userOriginated
  306. * whether the expand was triggered by a user interaction or the
  307. * server
  308. */
  309. protected void fireExpandEvent(T item, boolean userOriginated) {
  310. fireEvent(new ExpandEvent<>(this, item, userOriginated));
  311. }
  312. /**
  313. * Fires a collapse event with given item.
  314. *
  315. * @param item
  316. * the collapsed item
  317. * @param userOriginated
  318. * whether the collapse was triggered by a user interaction or
  319. * the server
  320. */
  321. protected void fireCollapseEvent(T item, boolean userOriginated) {
  322. fireEvent(new CollapseEvent<>(this, item, userOriginated));
  323. }
  324. /**
  325. * Expands the given items.
  326. * <p>
  327. * If an item is currently expanded, does nothing. If an item does not have
  328. * any children, does nothing.
  329. *
  330. * @param items
  331. * the items to expand
  332. */
  333. public void expand(T... items) {
  334. treeGrid.expand(items);
  335. }
  336. /**
  337. * Expands the given items.
  338. * <p>
  339. * If an item is currently expanded, does nothing. If an item does not have
  340. * any children, does nothing.
  341. *
  342. * @param items
  343. * the items to expand
  344. */
  345. public void expand(Collection<T> items) {
  346. treeGrid.expand(items);
  347. }
  348. /**
  349. * Collapse the given items.
  350. * <p>
  351. * For items that are already collapsed, does nothing.
  352. *
  353. * @param items
  354. * the collection of items to collapse
  355. */
  356. public void collapse(T... items) {
  357. treeGrid.collapse(items);
  358. }
  359. /**
  360. * Collapse the given items.
  361. * <p>
  362. * For items that are already collapsed, does nothing.
  363. *
  364. * @param items
  365. * the collection of items to collapse
  366. */
  367. public void collapse(Collection<T> items) {
  368. treeGrid.collapse(items);
  369. }
  370. /**
  371. * This method is a shorthand that delegates to the currently set selection
  372. * model.
  373. *
  374. * @see #getSelectionModel()
  375. *
  376. * @return set of selected items
  377. */
  378. public Set<T> getSelectedItems() {
  379. return treeGrid.getSelectedItems();
  380. }
  381. /**
  382. * This method is a shorthand that delegates to the currently set selection
  383. * model.
  384. *
  385. * @param item
  386. * item to select
  387. *
  388. * @see SelectionModel#select(Object)
  389. * @see #getSelectionModel()
  390. */
  391. public void select(T item) {
  392. treeGrid.select(item);
  393. }
  394. /**
  395. * This method is a shorthand that delegates to the currently set selection
  396. * model.
  397. *
  398. * @param item
  399. * item to deselect
  400. *
  401. * @see SelectionModel#deselect(Object)
  402. * @see #getSelectionModel()
  403. */
  404. public void deselect(T item) {
  405. treeGrid.deselect(item);
  406. }
  407. /**
  408. * Adds a selection listener to the current selection model.
  409. * <p>
  410. * <strong>NOTE:</strong> If selection mode is switched with
  411. * {@link setSelectionMode(SelectionMode)}, then this listener is not
  412. * triggered anymore when selection changes!
  413. *
  414. * @param listener
  415. * the listener to add
  416. * @return a registration handle to remove the listener
  417. *
  418. * @throws UnsupportedOperationException
  419. * if selection has been disabled with
  420. * {@link SelectionMode.NONE}
  421. */
  422. public Registration addSelectionListener(SelectionListener<T> listener) {
  423. return treeGrid.addSelectionListener(listener);
  424. }
  425. /**
  426. * Use this tree as a single select in {@link Binder}. Throws
  427. * {@link IllegalStateException} if the tree is not using
  428. * {@link SelectionMode#SINGLE}.
  429. *
  430. * @return the single select wrapper that can be used in binder
  431. */
  432. public SingleSelect<T> asSingleSelect() {
  433. return treeGrid.asSingleSelect();
  434. }
  435. /**
  436. * Returns the selection model for this Tree.
  437. *
  438. * @return the selection model, not <code>null</code>
  439. */
  440. public SelectionModel<T> getSelectionModel() {
  441. return treeGrid.getSelectionModel();
  442. }
  443. /**
  444. * Sets the item caption generator that is used to produce the strings shown
  445. * as the text for each item. By default, {@link String#valueOf(Object)} is
  446. * used.
  447. *
  448. * @param captionGenerator
  449. * the item caption provider to use, not <code>null</code>
  450. */
  451. public void setItemCaptionGenerator(
  452. ItemCaptionGenerator<T> captionGenerator) {
  453. Objects.requireNonNull(captionGenerator,
  454. "Caption generator must not be null");
  455. this.captionGenerator = captionGenerator;
  456. treeGrid.getDataCommunicator().reset();
  457. }
  458. /**
  459. * Sets the item icon generator that is used to produce custom icons for
  460. * items. The generator can return <code>null</code> for items with no icon.
  461. *
  462. * @see IconGenerator
  463. *
  464. * @param iconGenerator
  465. * the item icon generator to set, not <code>null</code>
  466. * @throws NullPointerException
  467. * if {@code itemIconGenerator} is {@code null}
  468. */
  469. public void setItemIconGenerator(IconGenerator<T> iconGenerator) {
  470. Objects.requireNonNull(iconGenerator,
  471. "Item icon generator must not be null");
  472. this.iconProvider = iconGenerator;
  473. treeGrid.getDataCommunicator().reset();
  474. }
  475. /**
  476. * Sets the item collapse allowed provider for this Tree. The provider
  477. * should return {@code true} for any item that the user can collapse.
  478. * <p>
  479. * <strong>Note:</strong> This callback will be accessed often when sending
  480. * data to the client. The callback should not do any costly operations.
  481. *
  482. * @param provider
  483. * the item collapse allowed provider, not {@code null}
  484. */
  485. public void setItemCollapseAllowedProvider(
  486. ItemCollapseAllowedProvider<T> provider) {
  487. treeGrid.setItemCollapseAllowedProvider(provider);
  488. }
  489. /**
  490. * Sets the style generator that is used for generating class names for
  491. * items in this tree. Returning null from the generator results in no
  492. * custom style name being set.
  493. *
  494. * @see StyleGenerator
  495. *
  496. * @param styleGenerator
  497. * the item style generator to set, not {@code null}
  498. * @throws NullPointerException
  499. * if {@code styleGenerator} is {@code null}
  500. */
  501. public void setStyleGenerator(StyleGenerator<T> styleGenerator) {
  502. treeGrid.setStyleGenerator(styleGenerator);
  503. }
  504. /**
  505. * Gets the item caption generator.
  506. *
  507. * @return the item caption generator
  508. */
  509. public ItemCaptionGenerator<T> getItemCaptionGenerator() {
  510. return captionGenerator;
  511. }
  512. /**
  513. * Gets the item icon generator.
  514. *
  515. * @see IconGenerator
  516. *
  517. * @return the item icon generator
  518. */
  519. public IconGenerator<T> getItemIconGenerator() {
  520. return iconProvider;
  521. }
  522. /**
  523. * Gets the item collapse allowed provider.
  524. *
  525. * @return the item collapse allowed provider
  526. */
  527. public ItemCollapseAllowedProvider<T> getItemCollapseAllowedProvider() {
  528. return treeGrid.getItemCollapseAllowedProvider();
  529. }
  530. /**
  531. * Gets the style generator.
  532. *
  533. * @see StyleGenerator
  534. *
  535. * @return the item style generator
  536. */
  537. public StyleGenerator<T> getStyleGenerator() {
  538. return treeGrid.getStyleGenerator();
  539. }
  540. /**
  541. * Adds an item click listener. The listener is called when an item of this
  542. * {@code Tree} is clicked.
  543. *
  544. * @param listener
  545. * the item click listener, not null
  546. * @return a registration for the listener
  547. */
  548. public Registration addItemClickListener(ItemClickListener<T> listener) {
  549. return addListener(ItemClick.class, listener, ITEM_CLICK_METHOD);
  550. }
  551. /**
  552. * Sets the tree's selection mode.
  553. * <p>
  554. * The built-in selection modes are:
  555. * <ul>
  556. * <li>{@link SelectionMode#SINGLE} <b>the default model</b></li>
  557. * <li>{@link SelectionMode#MULTI}</li>
  558. * <li>{@link SelectionMode#NONE} preventing selection</li>
  559. * </ul>
  560. *
  561. * @param selectionMode
  562. * the selection mode to switch to, not {@code null}
  563. * @return the used selection model
  564. *
  565. * @see SelectionMode
  566. */
  567. public SelectionModel<T> setSelectionMode(SelectionMode selectionMode) {
  568. Objects.requireNonNull(selectionMode,
  569. "Can not set selection mode to null");
  570. switch (selectionMode) {
  571. case MULTI:
  572. TreeMultiSelectionModel<T> model = new TreeMultiSelectionModel<>();
  573. treeGrid.setSelectionModel(model);
  574. return model;
  575. default:
  576. return treeGrid.setSelectionMode(selectionMode);
  577. }
  578. }
  579. /**
  580. * @deprecated This component's height is always set to be undefined.
  581. * Calling this method will have no effect.
  582. */
  583. @Override
  584. @Deprecated
  585. public void setHeight(String height) {
  586. }
  587. /**
  588. * @deprecated This component's height is always set to be undefined.
  589. * Calling this method will have no effect.
  590. */
  591. @Override
  592. @Deprecated
  593. public void setHeight(float height, Unit unit) {
  594. }
  595. /**
  596. * @deprecated This component's height is always set to be undefined.
  597. * Calling this method will have no effect.
  598. */
  599. @Override
  600. @Deprecated
  601. public void setHeightUndefined() {
  602. }
  603. @Override
  604. public void setCaption(String caption) {
  605. treeGrid.setCaption(caption);
  606. }
  607. @Override
  608. public String getCaption() {
  609. return treeGrid.getCaption();
  610. }
  611. @Override
  612. public void setIcon(Resource icon) {
  613. treeGrid.setIcon(icon);
  614. }
  615. @Override
  616. public Resource getIcon() {
  617. return treeGrid.getIcon();
  618. }
  619. @Override
  620. public String getStyleName() {
  621. return treeGrid.getStyleName();
  622. }
  623. @Override
  624. public void setStyleName(String style) {
  625. treeGrid.setStyleName(style);
  626. }
  627. @Override
  628. public void setStyleName(String style, boolean add) {
  629. treeGrid.setStyleName(style, add);
  630. }
  631. @Override
  632. public void addStyleName(String style) {
  633. treeGrid.addStyleName(style);
  634. }
  635. @Override
  636. public void removeStyleName(String style) {
  637. treeGrid.removeStyleName(style);
  638. }
  639. @Override
  640. public String getPrimaryStyleName() {
  641. return treeGrid.getPrimaryStyleName();
  642. }
  643. @Override
  644. public void setPrimaryStyleName(String style) {
  645. treeGrid.setPrimaryStyleName(style);
  646. }
  647. @Override
  648. public void setId(String id) {
  649. treeGrid.setId(id);
  650. }
  651. @Override
  652. public String getId() {
  653. return treeGrid.getId();
  654. }
  655. @Override
  656. public void setCaptionAsHtml(boolean captionAsHtml) {
  657. treeGrid.setCaptionAsHtml(captionAsHtml);
  658. }
  659. @Override
  660. public boolean isCaptionAsHtml() {
  661. return treeGrid.isCaptionAsHtml();
  662. }
  663. @Override
  664. public void setDescription(String description) {
  665. treeGrid.setDescription(description);
  666. }
  667. @Override
  668. public void setDescription(String description, ContentMode mode) {
  669. treeGrid.setDescription(description, mode);
  670. }
  671. @Override
  672. public ErrorMessage getErrorMessage() {
  673. return treeGrid.getErrorMessage();
  674. }
  675. @Override
  676. public ErrorMessage getComponentError() {
  677. return treeGrid.getComponentError();
  678. }
  679. @Override
  680. public void setComponentError(ErrorMessage componentError) {
  681. treeGrid.setComponentError(componentError);
  682. }
  683. }