1 /*
   2  * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.scene.control;
  27 
  28 import java.util.ArrayList;
  29 import java.util.Collections;
  30 import java.util.List;
  31 
  32 import javafx.beans.property.BooleanProperty;
  33 import javafx.beans.property.BooleanPropertyBase;
  34 import javafx.beans.property.ObjectProperty;
  35 import javafx.beans.property.ObjectPropertyBase;
  36 import javafx.collections.FXCollections;
  37 import javafx.collections.ListChangeListener;
  38 import javafx.collections.ObservableList;
  39 import javafx.event.Event;
  40 import javafx.event.EventDispatchChain;
  41 import javafx.event.EventHandler;
  42 import javafx.event.EventTarget;
  43 import javafx.event.EventType;
  44 import javafx.scene.Node;
  45 
  46 import com.sun.javafx.event.EventHandlerManager;
  47 import java.util.Comparator;
  48 import javafx.beans.property.ReadOnlyBooleanProperty;
  49 import javafx.beans.property.ReadOnlyBooleanWrapper;
  50 import javafx.beans.property.ReadOnlyObjectProperty;
  51 import javafx.beans.property.ReadOnlyObjectWrapper;
  52 
  53 import static javafx.scene.control.TreeSortMode.*;
  54 
  55 /**
  56  * The model for a single node supplying a hierarchy of values to a control such
  57  * as TreeView. The model may be implemented such that values may be loaded in
  58  * memory as they are needed.
  59  * <p>
  60  * The model allows registration of listeners which will be notified as the
  61  * number of items changes, their position or if the values themselves change.
  62  * Note however that a TreeItem is <b>not</b> a Node, and therefore no visual
  63  * events will be fired on the TreeItem. To get these events, it is necessary to
  64  * add relevant observers to the TreeCell instances (via a custom cell factory -
  65  * see the {@link Cell} class documentation for more details).
  66  *
  67  * <p>In the simplest case, TreeItem instances may be created in memory as such:
  68  * <pre><code>
  69  * TreeItem&lt;String&gt; root = new TreeItem&lt;String&gt;("Root Node");
  70  * root.setExpanded(true);
  71  * root.getChildren().addAll(
  72  *     new TreeItem&lt;String&gt;("Item 1"),
  73  *     new TreeItem&lt;String&gt;("Item 2"),
  74  *     new TreeItem&lt;String&gt;("Item 3")
  75  * );
  76  * TreeView&lt;String&gt; treeView = new TreeView&lt;String&gt;(root);
  77  * </code></pre>
  78  *
  79  * This approach works well for simple tree structures, or when the data is not
  80  * excessive (so that it can easily fit in memory). In situations where the size
  81  * of the tree structure is unknown (and therefore potentially huge), there is
  82  * the option of creating TreeItem instances on-demand in a memory-efficient way.
  83  * To demonstrate this, the code below creates a file system browser:
  84  *
  85  * <pre><code>
  86  *  private TreeView buildFileSystemBrowser() {
  87  *      TreeItem&lt;File&gt; root = createNode(new File("/"));
  88  *      return new TreeView&lt;File&gt;(root);
  89  *  }
  90  *
  91  *  // This method creates a TreeItem to represent the given File. It does this
  92  *  // by overriding the TreeItem.getChildren() and TreeItem.isLeaf() methods
  93  *  // anonymously, but this could be better abstracted by creating a
  94  *  // 'FileTreeItem' subclass of TreeItem. However, this is left as an exercise
  95  *  // for the reader.
  96  *  private TreeItem&lt;File&gt; createNode(final File f) {
  97  *      return new TreeItem&lt;File&gt;(f) {
  98  *          // We cache whether the File is a leaf or not. A File is a leaf if
  99  *          // it is not a directory and does not have any files contained within
 100  *          // it. We cache this as isLeaf() is called often, and doing the
 101  *          // actual check on File is expensive.
 102  *          private boolean isLeaf;
 103  *
 104  *          // We do the children and leaf testing only once, and then set these
 105  *          // booleans to false so that we do not check again during this
 106  *          // run. A more complete implementation may need to handle more
 107  *          // dynamic file system situations (such as where a folder has files
 108  *          // added after the TreeView is shown). Again, this is left as an
 109  *          // exercise for the reader.
 110  *          private boolean isFirstTimeChildren = true;
 111  *          private boolean isFirstTimeLeaf = true;
 112  *
 113  *          @Override public ObservableList&lt;TreeItem&lt;File&gt;&gt; getChildren() {
 114  *              if (isFirstTimeChildren) {
 115  *                  isFirstTimeChildren = false;
 116  *
 117  *                  // First getChildren() call, so we actually go off and
 118  *                  // determine the children of the File contained in this TreeItem.
 119  *                  super.getChildren().setAll(buildChildren(this));
 120  *              }
 121  *              return super.getChildren();
 122  *          }
 123  *
 124  *          @Override public boolean isLeaf() {
 125  *              if (isFirstTimeLeaf) {
 126  *                  isFirstTimeLeaf = false;
 127  *                  File f = (File) getValue();
 128  *                  isLeaf = f.isFile();
 129  *              }
 130  *
 131  *              return isLeaf;
 132  *          }
 133  *
 134  *          private ObservableList&lt;TreeItem&lt;File&gt;&gt; buildChildren(TreeItem&lt;File&gt; TreeItem) {
 135  *              File f = TreeItem.getValue();
 136  *              if (f != null &amp;&amp; f.isDirectory()) {
 137  *                  File[] files = f.listFiles();
 138  *                  if (files != null) {
 139  *                      ObservableList&lt;TreeItem&lt;File&gt;&gt; children = FXCollections.observableArrayList();
 140  *
 141  *                      for (File childFile : files) {
 142  *                          children.add(createNode(childFile));
 143  *                      }
 144  *
 145  *                      return children;
 146  *                  }
 147  *              }
 148  *
 149  *              return FXCollections.emptyObservableList();
 150  *          }
 151  *      };
 152  *  }</code></pre>
 153  *
 154  * <strong>TreeItem Events</strong>
 155  * <p>TreeItem supports the same event bubbling concept as elsewhere in the
 156  * scenegraph. This means that it is not necessary to listen for events on all
 157  * TreeItems (and this is certainly not encouraged!). A better, and far more low
 158  * cost solution is to instead attach event listeners to the TreeView
 159  * {@link TreeView#rootProperty() root} item. As long as there is a path between
 160  * where the event occurs and the root TreeItem, the event will be bubbled to the
 161  * root item.
 162  *
 163  * <p>It is important to note however that a TreeItem is <strong>not</strong> a
 164  * Node, which means that only the event types defined in TreeItem will be
 165  * delivered. To listen to general events (for example mouse interactions), it is
 166  * necessary to add the necessary listeners to the {@link Cell cells} contained
 167  * within the TreeView (by providing a {@link TreeView#cellFactoryProperty()
 168  * cell factory}).
 169  *
 170  * <p>The TreeItem class defines a number of events, with a defined hierarchy. These
 171  * are shown below (follow the links to learn more about each event type):
 172  *
 173  * <ul>
 174  *   <li>{@link TreeItem#treeNotificationEvent() TreeItem.treeNotificationEvent()}
 175  *   <ul>
 176  *     <li>{@link TreeItem#valueChangedEvent() TreeItem.valueChangedEvent()}</li>
 177  *     <li>{@link TreeItem#graphicChangedEvent() TreeItem.graphicChangedEvent()}</li>
 178  *     <li>{@link TreeItem#expandedItemCountChangeEvent() TreeItem.expandedItemCountChangeEvent()}
 179  *     <ul>
 180  *       <li>{@link TreeItem#branchExpandedEvent() TreeItem.branchExpandedEvent()}</li>
 181  *       <li>{@link TreeItem#branchCollapsedEvent() TreeItem.branchCollapsedEvent()}</li>
 182  *       <li>{@link TreeItem#childrenModificationEvent() TreeItem.childrenModificationEvent()}</li>
 183  *     </ul>
 184  *     </li>
 185  *   </ul>
 186  *   </li>
 187  * </ul>
 188  *
 189  * <p>The indentation shown above signifies the relationship between event types.
 190  * For example, all TreeItem event types have
 191  * {@link TreeItem#treeNotificationEvent() treeNotificationEvent()} as their
 192  * parent event type, and the branch
 193  * {@link TreeItem#branchExpandedEvent() expand} /
 194  * {@link TreeItem#branchCollapsedEvent() collapse} event types are both
 195  * {@link TreeItem#treeNotificationEvent() treeNotificationEvent()}. For
 196  * performance reasons, it is encouraged to listen
 197  * to only the events you need to listen to. This means that it is encouraged
 198  * that it is better to listen to, for example,
 199  * {@link TreeItem#valueChangedEvent() TreeItem.valueChangedEvent()},
 200  * rather than {@link TreeItem#treeNotificationEvent() TreeItem.treeNotificationEvent()}.
 201  *
 202  * @param <T> The type of the {@link #getValue() value} property within TreeItem.
 203  * @since JavaFX 2.0
 204  */
 205 public class TreeItem<T> implements EventTarget { //, Comparable<TreeItem<T>> {
 206 
 207     /***************************************************************************
 208      *                                                                         *
 209      * Static properties and methods                                           *
 210      *                                                                         *
 211      **************************************************************************/
 212 
 213     /**
 214      * The base EventType used to indicate that an event has occurred within a
 215      * TreeItem. When an event occurs in a TreeItem, the event is fired to any
 216      * listeners on the TreeItem that the event occurs, before it 'bubbles' up the
 217      * TreeItem chain by following the TreeItem parent property. This repeats
 218      * until a TreeItem whose parent TreeItem is null is reached At this point
 219      * the event stops 'bubbling' and goes no further. This means that events
 220      * that occur on a TreeItem can be relatively cheap, as a listener needs only
 221      * be installed on the TreeView root node to be alerted of events happening
 222      * at any point in the tree.
 223      *
 224      * @param <T> The type of the value contained within the TreeItem.
 225      * @return The base EventType when an event has occurred a within a TreeItem
 226      */
 227     @SuppressWarnings("unchecked")
 228     public static <T> EventType<TreeModificationEvent<T>> treeNotificationEvent() {
 229         return (EventType<TreeModificationEvent<T>>) TREE_NOTIFICATION_EVENT;
 230     }
 231     private static final EventType<?> TREE_NOTIFICATION_EVENT
 232             = new EventType<>(Event.ANY, "TreeNotificationEvent");
 233 
 234     /**
 235      * The general EventType used when the TreeItem receives a modification that
 236      * results in the number of children being visible changes.
 237      * This is normally achieved via one of the sub-types of this
 238      * EventType (see {@link #branchExpandedEvent()},
 239      * {@link #branchCollapsedEvent()} and {@link #childrenModificationEvent()}
 240      * for the three sub-types).
 241      *
 242      * @param <T> The type of the value contained within the TreeItem.
 243      * @return The general EventType when the TreeItem receives a modification
 244      * @since JavaFX 8.0
 245      */
 246     @SuppressWarnings("unchecked")
 247     public static <T> EventType<TreeModificationEvent<T>> expandedItemCountChangeEvent() {
 248         return (EventType<TreeModificationEvent<T>>) EXPANDED_ITEM_COUNT_CHANGE_EVENT;
 249     }
 250     private static final EventType<?> EXPANDED_ITEM_COUNT_CHANGE_EVENT
 251             = new EventType<>(treeNotificationEvent(), "ExpandedItemCountChangeEvent");
 252 
 253     /**
 254      * An EventType used when the TreeItem receives a modification to its
 255      * expanded property, such that the TreeItem is now in the expanded state.
 256      *
 257      * @param <T> The type of the value contained within the TreeItem.
 258      * @return The EventType used when the TreeItem receives a modification
 259      */
 260     @SuppressWarnings("unchecked")
 261     public static <T> EventType<TreeModificationEvent<T>> branchExpandedEvent() {
 262         return (EventType<TreeModificationEvent<T>>) BRANCH_EXPANDED_EVENT;
 263     }
 264     private static final EventType<?> BRANCH_EXPANDED_EVENT
 265             = new EventType<>(expandedItemCountChangeEvent(), "BranchExpandedEvent");
 266 
 267     /**
 268      * An EventType used when the TreeItem receives a modification to its
 269      * expanded property, such that the TreeItem is now in the collapsed state.
 270      *
 271      * @param <T> The type of the value contained within the TreeItem.
 272      * @return The EventType when the TreeItem receives a modification
 273      */
 274     @SuppressWarnings("unchecked")
 275     public static <T> EventType<TreeModificationEvent<T>> branchCollapsedEvent() {
 276         return (EventType<TreeModificationEvent<T>>) BRANCH_COLLAPSED_EVENT;
 277     }
 278     private static final EventType<?> BRANCH_COLLAPSED_EVENT
 279             = new EventType<>(expandedItemCountChangeEvent(), "BranchCollapsedEvent");
 280 
 281     /**
 282      * An EventType used when the TreeItem receives a direct modification to its
 283      * children list.
 284      *
 285      * @param <T> The type of the value contained within the TreeItem.
 286      * @return The EventType when the TreeItem receives a direct modification to
 287      * its children list
 288      */
 289     @SuppressWarnings("unchecked")
 290     public static <T> EventType<TreeModificationEvent<T>> childrenModificationEvent() {
 291         return (EventType<TreeModificationEvent<T>>) CHILDREN_MODIFICATION_EVENT;
 292     }
 293     private static final EventType<?> CHILDREN_MODIFICATION_EVENT
 294             = new EventType<>(expandedItemCountChangeEvent(), "ChildrenModificationEvent");
 295 
 296     /**
 297      * An EventType used when the TreeItem receives a modification to its
 298      * value property.
 299      *
 300      * @param <T> The type of the value contained within the TreeItem.
 301      * @return The EventType when the TreeItem receives a modification to its
 302      * value property
 303      */
 304     @SuppressWarnings("unchecked")
 305     public static <T> EventType<TreeModificationEvent<T>> valueChangedEvent() {
 306         return (EventType<TreeModificationEvent<T>>) VALUE_CHANGED_EVENT;
 307     }
 308     private static final EventType<?> VALUE_CHANGED_EVENT
 309             = new EventType<>(treeNotificationEvent(), "ValueChangedEvent");
 310 
 311     /**
 312      * An EventType used when the TreeItem receives a modification to its
 313      * graphic property.
 314      *
 315      * @param <T> The type of the value contained within the TreeItem.
 316      * @return The EventType when the TreeItem receives a modification to its
 317      * graphic property
 318      */
 319     @SuppressWarnings("unchecked")
 320     public static <T> EventType<TreeModificationEvent<T>> graphicChangedEvent() {
 321         return (EventType<TreeModificationEvent<T>>) GRAPHIC_CHANGED_EVENT;
 322     }
 323     private static final EventType<?> GRAPHIC_CHANGED_EVENT
 324             = new EventType<>(treeNotificationEvent(), "GraphicChangedEvent");
 325 
 326 
 327 
 328     /***************************************************************************
 329      *                                                                         *
 330      * Constructors                                                            *
 331      *                                                                         *
 332      **************************************************************************/
 333 
 334     /**
 335      * Creates an empty TreeItem.
 336      */
 337     public TreeItem() {
 338         this(null);
 339     }
 340 
 341     /**
 342      * Creates a TreeItem with the value property set to the provided object.
 343      *
 344      * @param value The object to be stored as the value of this TreeItem.
 345      */
 346     public TreeItem(final T value) {
 347         this(value, (Node)null);
 348     }
 349 
 350     /**
 351      * Creates a TreeItem with the value property set to the provided object, and
 352      * the graphic set to the provided Node.
 353      *
 354      * @param value The object to be stored as the value of this TreeItem.
 355      * @param graphic The Node to show in the TreeView next to this TreeItem.
 356      */
 357     public TreeItem(final T value, final Node graphic) {
 358         setValue(value);
 359         setGraphic(graphic);
 360 
 361         addEventHandler(TreeItem.<Object>expandedItemCountChangeEvent(), itemListener);
 362     }
 363 
 364     private final EventHandler<TreeModificationEvent<Object>> itemListener =
 365         new EventHandler<TreeModificationEvent<Object>>() {
 366             @Override public void handle(TreeModificationEvent<Object> event) {
 367                 expandedDescendentCountDirty = true;
 368             }
 369     };
 370 
 371 
 372     /***************************************************************************
 373      *                                                                         *
 374      * Instance Variables                                                      *
 375      *                                                                         *
 376      **************************************************************************/
 377 
 378     private boolean ignoreSortUpdate = false;
 379 
 380     private boolean expandedDescendentCountDirty = true;
 381 
 382     // The ObservableList containing all children belonging to this TreeItem.
 383     // It is important that interactions with this list go directly into the
 384     // children property, rather than via getChildren(), as this may be
 385     // a very expensive call.
 386     ObservableList<TreeItem<T>> children;
 387 
 388     // Made static based on findings of RT-18344 - EventHandlerManager is an
 389     // expensive class and should be reused amongst classes if at all possible.
 390     private final EventHandlerManager eventHandlerManager =
 391             new EventHandlerManager(this);
 392 
 393 
 394     // Rather than have the TreeView need to (pretty well) constantly determine
 395     // the expanded descendent count of a TreeItem, we instead cache it locally
 396     // based on tree item modification events.
 397     private int expandedDescendentCount = 1;
 398 
 399     // we record the previous value also, so that we can easily determine how
 400     // many items just disappeared on a TreeItem collapse event. Note that the
 401     // actual number of items that disappeared is one less than this value,
 402     // because we obviously are also counting this node, which hasn't disappeared
 403     // when all children are collapsed.
 404     int previousExpandedDescendentCount = 1;
 405 
 406     Comparator<TreeItem<T>> lastComparator = null;
 407     TreeSortMode lastSortMode = null;
 408 
 409     // Refer to the TreeItem.updateChildrenParent method below for more context
 410     // and a description of this field
 411     private int parentLinkCount = 0;
 412 
 413 
 414 
 415     /***************************************************************************
 416      *                                                                         *
 417      * Callbacks and events                                                    *
 418      *                                                                         *
 419      **************************************************************************/
 420 
 421     // called whenever the contents of the children sequence changes
 422     private ListChangeListener<TreeItem<T>> childrenListener = c -> {
 423         expandedDescendentCountDirty = true;
 424         updateChildren(c);
 425     };
 426 
 427 
 428 
 429     /***************************************************************************
 430      *                                                                         *
 431      * Properties                                                              *
 432      *                                                                         *
 433      **************************************************************************/
 434 
 435     // --- Value
 436     private ObjectProperty<T> value;
 437 
 438     /**
 439      * Sets the application-specific data represented by this TreeItem.
 440      * @param value the application-specific data
 441      */
 442     public final void setValue(T value) { valueProperty().setValue(value); }
 443 
 444     /**
 445      * Returns the application-specific data represented by this TreeItem.
 446      * @return the data represented by this TreeItem
 447      */
 448     public final T getValue() { return value == null ? null : value.getValue(); }
 449 
 450     /**
 451      * A property representing the application-specific data contained within
 452      * this TreeItem.
 453      * @return the property representing the application-specific data contained
 454      * within this TreeItem
 455      */
 456     public final ObjectProperty<T> valueProperty() {
 457         if (value == null) {
 458             value = new ObjectPropertyBase<T>() {
 459                 @Override protected void invalidated() {
 460                     fireEvent(new TreeModificationEvent<T>(VALUE_CHANGED_EVENT, TreeItem.this, get()));
 461                 }
 462 
 463                 @Override public Object getBean() {
 464                     return TreeItem.this;
 465                 }
 466 
 467                 @Override public String getName() {
 468                     return "value";
 469                 }
 470             };
 471         }
 472         return value;
 473     }
 474 
 475 
 476     // --- Graphic
 477     private ObjectProperty<Node> graphic;
 478 
 479     /**
 480      * Sets the node that is generally shown to the left of the value property.
 481      * For best effect, this tends to be a 16x16 image.
 482      *
 483      * @param value The graphic node that will be displayed to the user.
 484      */
 485     public final void setGraphic(Node value) { graphicProperty().setValue(value); }
 486 
 487     /**
 488      * Returns the node that is generally shown to the left of the value property.
 489      * For best effect, this tends to be a 16x16 image.
 490      *
 491      * @return The graphic node that will be displayed to the user.
 492      */
 493     public final Node getGraphic() { return graphic == null ? null : graphic.getValue(); }
 494 
 495     /**
 496      * The node that is generally shown to the left of the value property. For
 497      * best effect, this tends to be a 16x16 image.
 498      * @return The node that is generally shown to the left of the value property
 499      */
 500     public final ObjectProperty<Node> graphicProperty() {
 501         if (graphic == null) {
 502             graphic = new ObjectPropertyBase<Node>() {
 503                 @Override protected void invalidated() {
 504                     fireEvent(new TreeModificationEvent<T>(GRAPHIC_CHANGED_EVENT, TreeItem.this));
 505                 }
 506 
 507                 @Override
 508                 public Object getBean() {
 509                     return TreeItem.this;
 510                 }
 511 
 512                 @Override
 513                 public String getName() {
 514                     return "graphic";
 515                 }
 516             };
 517         }
 518         return graphic;
 519     }
 520 
 521 
 522     // --- Expanded
 523     private BooleanProperty expanded;
 524 
 525     /**
 526      * Sets the expanded state of this TreeItem. This has no effect on a TreeItem
 527      * with no children. On a TreeItem with children however, the result of
 528      * toggling this property is that visually the children will either become
 529      * visible or hidden, based on whether expanded is set to true or false.
 530      *
 531      * @param value If this TreeItem has children, calling setExpanded with
 532      *      <code>true</code> will result in the children becoming visible.
 533      *      Calling setExpanded with <code>false</code> will hide any children
 534      *      belonging to the TreeItem.
 535      */
 536     public final void setExpanded(boolean value) {
 537         if (! value && expanded == null) return;
 538         expandedProperty().setValue(value);
 539     }
 540 
 541     /**
 542      * Returns the expanded state of this TreeItem.
 543      *
 544      * @return Returns the expanded state of this TreeItem.
 545      */
 546     public final boolean isExpanded() { return expanded == null ? false : expanded.getValue(); }
 547 
 548     /**
 549      * The expanded state of this TreeItem.
 550      * @return The expanded state property of this TreeItem
 551      */
 552     public final BooleanProperty expandedProperty() {
 553         if (expanded == null) {
 554             expanded = new BooleanPropertyBase() {
 555                 @Override protected void invalidated() {
 556                     // We don't fire expanded events for leaf nodes (RT-32620)
 557                     if (isLeaf()) return;
 558 
 559                     EventType<?> evtType = isExpanded() ?
 560                         BRANCH_EXPANDED_EVENT : BRANCH_COLLAPSED_EVENT;
 561 
 562                     fireEvent(new TreeModificationEvent<T>(evtType, TreeItem.this, isExpanded()));
 563                 }
 564 
 565                 @Override
 566                 public Object getBean() {
 567                     return TreeItem.this;
 568                 }
 569 
 570                 @Override
 571                 public String getName() {
 572                     return "expanded";
 573                 }
 574             };
 575         }
 576         return expanded;
 577     }
 578 
 579 
 580     // --- Leaf
 581     private ReadOnlyBooleanWrapper leaf;
 582     private void setLeaf(boolean value) {
 583         if (value && leaf == null) {
 584             return;
 585         } else if (leaf == null) {
 586             leaf = new ReadOnlyBooleanWrapper(this, "leaf", true);
 587         }
 588         leaf.setValue(value);
 589     }
 590 
 591     /**
 592      * A TreeItem is a leaf if it has no children. The isLeaf method may of
 593      * course be overridden by subclasses to support alternate means of defining
 594      * how a TreeItem may be a leaf, but the general premise is the same: a
 595      * leaf can not be expanded by the user, and as such will not show a
 596      * disclosure node or respond to expansion requests.
 597      * @return true if this TreeItem has no children
 598      */
 599     public boolean isLeaf() { return leaf == null ? true : leaf.getValue(); }
 600 
 601     /**
 602      * Represents the TreeItem leaf property, which is true if the TreeItem has no children.
 603      * @return the TreeItem leaf property
 604      */
 605     public final ReadOnlyBooleanProperty leafProperty() {
 606         if (leaf == null) {
 607             leaf = new ReadOnlyBooleanWrapper(this, "leaf", true);
 608         }
 609         return leaf.getReadOnlyProperty();
 610     }
 611 
 612 
 613     // --- Parent
 614     private ReadOnlyObjectWrapper<TreeItem<T>> parent = new ReadOnlyObjectWrapper<TreeItem<T>>(this, "parent");
 615     private void setParent(TreeItem<T> value) { parent.setValue(value); }
 616 
 617     /**
 618      * The parent of this TreeItem. Each TreeItem can have no more than one
 619      * parent. If a TreeItem has no parent, it represents a root in the tree model.
 620      *
 621      * @return The parent of this TreeItem, or null if the TreeItem has no parent.
 622      */
 623     public final TreeItem<T> getParent() { return parent == null ? null : parent.getValue(); }
 624 
 625     /**
 626      * A property that represents the parent of this TreeItem.
 627      * @return the parent property of this TreeItem
 628      */
 629     public final ReadOnlyObjectProperty<TreeItem<T>> parentProperty() { return parent.getReadOnlyProperty(); }
 630 
 631 
 632 
 633     /***********************************************************************
 634      *                                                                     *
 635      * TreeItem API                                                        *
 636      *                                                                     *
 637      **********************************************************************/
 638 
 639     /**
 640      * The children of this TreeItem. This method is called frequently, and
 641      * it is therefore recommended that the returned list be cached by
 642      * any TreeItem implementations.
 643      *
 644      * @return a list that contains the child TreeItems belonging to the TreeItem.
 645      */
 646     public ObservableList<TreeItem<T>> getChildren() {
 647         if (children == null) {
 648             children = FXCollections.observableArrayList();
 649             children.addListener(childrenListener);
 650         }
 651 
 652         // we need to check if this TreeItem needs to have its children sorted.
 653         // There are two different ways that this could be possible.
 654         if (children.isEmpty()) return children;
 655 
 656         // checkSortState should in almost all instances be called, but there
 657         // are situations where checking the sort state will result in
 658         // unwanted permutation events being fired (if a sort is applied). To
 659         // avoid this (which resolves RT-37593), we set the ignoreSortUpdate
 660         // to true (and of course, we're careful to set it back to false again)
 661         if (!ignoreSortUpdate) {
 662             checkSortState();
 663         }
 664 
 665         return children;
 666     }
 667 
 668 
 669 
 670     /***************************************************************************
 671      *                                                                         *
 672      * Public API                                                              *
 673      *                                                                         *
 674      **************************************************************************/
 675 
 676     /**
 677      * Returns the previous sibling of the TreeItem. Ordering is based on the
 678      * position of the TreeItem relative to its siblings in the children
 679      * list belonging to the parent of the TreeItem.
 680      *
 681      * @return A TreeItem that is the previous sibling of the current TreeItem,
 682      *      or null if no such sibling can be found.
 683      */
 684     public TreeItem<T> previousSibling() {
 685         return previousSibling(this);
 686     }
 687 
 688     /**
 689      * Returns the previous sibling after the given node. Ordering is based on the
 690      * position of the given TreeItem relative to its siblings in the children
 691      * list belonging to the parent of the TreeItem.
 692      *
 693      * @param beforeNode The TreeItem for which the previous sibling is being
 694      *      sought.
 695      * @return A TreeItem that is the previous sibling of the given TreeItem,
 696      *      or null if no such sibling can be found.
 697      */
 698     public TreeItem<T> previousSibling(final TreeItem<T> beforeNode) {
 699         if (getParent() == null || beforeNode == null) {
 700             return null;
 701         }
 702 
 703         List<TreeItem<T>> parentChildren = getParent().getChildren();
 704         final int childCount = parentChildren.size();
 705         int pos = -1;
 706         for (int i = 0; i < childCount; i++) {
 707             if (beforeNode.equals(parentChildren.get(i))) {
 708                 pos = i - 1;
 709                 return pos < 0 ? null : parentChildren.get(pos);
 710             }
 711         }
 712         return null;
 713     }
 714 
 715     /**
 716      * Returns the next sibling of the TreeItem. Ordering is based on the
 717      * position of the TreeItem relative to its siblings in the children
 718      * list belonging to the parent of the TreeItem.
 719      *
 720      * @return A TreeItem that is the next sibling of the current TreeItem,
 721      *      or null if no such sibling can be found.
 722      */
 723     public TreeItem<T> nextSibling() {
 724         return nextSibling(this);
 725     }
 726 
 727     /**
 728      * Returns the next sibling after the given node. Ordering is based on the
 729      * position of the given TreeItem relative to its siblings in the children
 730      * list belonging to the parent of the TreeItem.
 731      *
 732      * @param afterNode The TreeItem for which the next sibling is being
 733      *      sought.
 734      * @return A TreeItem that is the next sibling of the given TreeItem,
 735      *      or null if no such sibling can be found.
 736      */
 737     public TreeItem<T> nextSibling(final TreeItem<T> afterNode) {
 738         if (getParent() == null || afterNode == null) {
 739             return null;
 740         }
 741 
 742         List<TreeItem<T>> parentChildren = getParent().getChildren();
 743         final int childCount = parentChildren.size();
 744         int pos = -1;
 745         for (int i = 0; i < childCount; i++) {
 746             if (afterNode.equals(parentChildren.get(i))) {
 747                 pos = i + 1;
 748                 return pos >= childCount ? null : parentChildren.get(pos);
 749             }
 750         }
 751         return null;
 752     }
 753 
 754     /**
 755      * Returns a string representation of this {@code TreeItem} object.
 756      * @return a string representation of this {@code TreeItem} object.
 757      */
 758     @Override public String toString() {
 759         return "TreeItem [ value: " + getValue() + " ]";
 760     }
 761 
 762     private void fireEvent(TreeModificationEvent<T> evt) {
 763         Event.fireEvent(this, evt);
 764     }
 765 
 766 
 767 
 768 
 769     /***************************************************************************
 770      *                                                                         *
 771      * Event Target Implementation / API                                       *
 772      *                                                                         *
 773      **************************************************************************/
 774 
 775     /** {@inheritDoc} */
 776     @Override public EventDispatchChain buildEventDispatchChain(EventDispatchChain tail) {
 777         // To allow for a TreeView (and its skin) to be notified of changes in the
 778         // tree, this method recursively calls up to the root node, at which point
 779         // it fires a ROOT_NOTIFICATION_EVENT, which the TreeView may be watching for.
 780         if (getParent() != null) {
 781             getParent().buildEventDispatchChain(tail);
 782         }
 783         return tail.append(eventHandlerManager);
 784     }
 785 
 786     /**
 787      * Registers an event handler to this TreeItem. The TreeItem class allows
 788      * registration of listeners which will be notified as the
 789      * number of items changes, their position or if the values themselves change.
 790      * Note however that a TreeItem is <b>not</b> a Node, and therefore no visual
 791      * events will be fired on the TreeItem. To get these events, it is necessary to
 792      * add relevant observers to the TreeCell instances (via a custom cell factory -
 793      * see the {@link Cell} class documentation for more details).
 794      *
 795      * @param <E> The event
 796      * @param eventType the type of the events to receive by the handler
 797      * @param eventHandler the handler to register
 798      * @throws NullPointerException if the event type or handler is null
 799      */
 800     public <E extends Event> void addEventHandler(EventType<E> eventType, EventHandler<E> eventHandler) {
 801         eventHandlerManager.addEventHandler(eventType, eventHandler);
 802     }
 803 
 804     /**
 805      * Unregisters a previously registered event handler from this TreeItem. One
 806      * handler might have been registered for different event types, so the
 807      * caller needs to specify the particular event type from which to
 808      * unregister the handler.
 809      *
 810      * @param <E> The event
 811      * @param eventType the event type from which to unregister
 812      * @param eventHandler the handler to unregister
 813      * @throws NullPointerException if the event type or handler is null
 814      */
 815     public <E extends Event> void removeEventHandler(EventType<E> eventType, EventHandler<E> eventHandler) {
 816         eventHandlerManager.removeEventHandler(eventType, eventHandler);
 817     }
 818 
 819 
 820 
 821     /***************************************************************************
 822      *                                                                         *
 823      * private methods                                                         *
 824      *                                                                         *
 825      **************************************************************************/
 826 
 827     void sort() {
 828         sort(children, lastComparator, lastSortMode);
 829     }
 830 
 831     private void sort(final ObservableList<TreeItem<T>> children,
 832                          final Comparator<TreeItem<T>> comparator,
 833                          final TreeSortMode sortMode) {
 834 
 835         if (comparator == null) return;
 836 
 837         runSort(children, comparator, sortMode);
 838 
 839         // if we're at the root node, we'll fire an event so that the control
 840         // can update its display
 841         if (getParent() == null) {
 842             TreeModificationEvent<T> e = new TreeModificationEvent<T>(TreeItem.childrenModificationEvent(), this);
 843             e.wasPermutated = true;
 844             fireEvent(e);
 845         }
 846     }
 847 
 848     private void checkSortState() {
 849         TreeItem<T> rootNode = getRoot();
 850 
 851         TreeSortMode sortMode = rootNode.lastSortMode;
 852         Comparator<TreeItem<T>> comparator = rootNode.lastComparator;
 853 
 854         if (comparator != null && comparator != lastComparator) {
 855             lastComparator = comparator;
 856             runSort(children, comparator, sortMode);
 857         }
 858     }
 859 
 860     private void runSort(ObservableList<TreeItem<T>> children, Comparator<TreeItem<T>> comparator, TreeSortMode sortMode) {
 861         if (sortMode == ALL_DESCENDANTS) {
 862             doSort(children, comparator);
 863         } else if (sortMode == ONLY_FIRST_LEVEL) {
 864             // if we are here we presume that the current node is the root node
 865             // (but we can test to see if getParent() returns null to be sure).
 866             // We also know that ONLY_FIRST_LEVEL only applies to the children
 867             // of the root, so we return straight after we sort these children.
 868             if (getParent() == null) {
 869                 doSort(children, comparator);
 870             }
 871 //        } else if (sortMode == ONLY_LEAVES) {
 872 //            if (isLeaf()) {
 873 //                // sort the parent once
 874 //            }
 875 //        } else if (sortMode == ALL_BUT_LEAVES) {
 876 //
 877         } else {
 878             // Unknown sort mode
 879         }
 880     }
 881 
 882     private TreeItem<T> getRoot() {
 883         TreeItem<T> parent = getParent();
 884         if (parent == null) return this;
 885 
 886         while (true) {
 887             TreeItem<T> newParent = parent.getParent();
 888             if (newParent == null) return parent;
 889             parent = newParent;
 890         }
 891     }
 892 
 893     private void doSort(ObservableList<TreeItem<T>> children, final Comparator<TreeItem<T>> comparator) {
 894         if (!isLeaf() && isExpanded()) {
 895             FXCollections.sort(children, comparator);
 896         }
 897     }
 898 
 899     // This value is package accessible so that it may be retrieved from TreeView.
 900     int getExpandedDescendentCount(boolean reset) {
 901         if (reset || expandedDescendentCountDirty) {
 902             updateExpandedDescendentCount(reset);
 903             expandedDescendentCountDirty = false;
 904         }
 905         return expandedDescendentCount;
 906     }
 907 
 908     private void updateExpandedDescendentCount(boolean reset) {
 909         previousExpandedDescendentCount = expandedDescendentCount;
 910         expandedDescendentCount = 1;
 911 
 912         ignoreSortUpdate = true;
 913         if (!isLeaf() && isExpanded()) {
 914             for (TreeItem<T> child : getChildren()) {
 915                 if (child == null) continue;
 916                 expandedDescendentCount += child.isExpanded() ? child.getExpandedDescendentCount(reset) : 1;
 917             }
 918         }
 919         ignoreSortUpdate = false;
 920     }
 921 
 922     private void updateChildren(ListChangeListener.Change<? extends TreeItem<T>> c) {
 923         setLeaf(children.isEmpty());
 924 
 925         final List<TreeItem<T>> added = new ArrayList<>();
 926         final List<TreeItem<T>> removed = new ArrayList<>();
 927 
 928         while (c.next()) {
 929             added.addAll(c.getAddedSubList());
 930             removed.addAll(c.getRemoved());
 931         }
 932 
 933         // update the relationships such that all added children point to
 934         // this node as the parent (and all removed children point to null)
 935         updateChildrenParent(removed, null);
 936         updateChildrenParent(added, this);
 937 
 938         c.reset();
 939 
 940         // fire an event up the parent hierarchy such that any listening
 941         // TreeViews (which only listen to their root node) can redraw
 942         fireEvent(new TreeModificationEvent<T>(
 943                 CHILDREN_MODIFICATION_EVENT, this, added, removed, c));
 944     }
 945 
 946     // Convenience method to set the parent of all children in the given list to
 947     // the given parent TreeItem
 948     private static <T> void updateChildrenParent(List<? extends TreeItem<T>> treeItems, final TreeItem<T> newParent) {
 949         if (treeItems == null) return;
 950         for (final TreeItem<T> treeItem : treeItems) {
 951             if (treeItem == null) continue;
 952 
 953             TreeItem<T> currentParent = treeItem.getParent();
 954 
 955             // We only replace the parent if the parentLinkCount of the given
 956             // TreeItem is zero (which indicates that this TreeItem has not been
 957             // 'linked' to its parent multiple times). This can happen in
 958             // situations such as what is shown in RT-28668 (and tested for in
 959             // TreeViewTest.test_rt28556()). Specifically, when a sort is applied
 960             // to the children of a TreeItem, it is possible for them to be
 961             // sorted in such a way that the element is considered to be
 962             // added in multiple places in the child list and then removed from
 963             // one of those places subsequently. In doing this final removal,
 964             // the parent of that TreeItem is set to null when it should in fact
 965             // remain with the parent that it belongs to.
 966             if (treeItem.parentLinkCount == 0) {
 967                 treeItem.setParent(newParent);
 968             }
 969 
 970             boolean parentMatch = currentParent != null && currentParent.equals(newParent);
 971             if (parentMatch) {
 972                 if (newParent == null) {
 973                     treeItem.parentLinkCount--;
 974                 } else {
 975                     treeItem.parentLinkCount++;
 976                 }
 977             }
 978          }
 979     }
 980 
 981     /**
 982      * An {@link Event} that contains relevant information for all forms of
 983      * TreeItem modifications.
 984      * @param <T> The TreeModificationEvent
 985      * @since JavaFX 2.0
 986      */
 987     public static class TreeModificationEvent<T> extends Event {
 988         private static final long serialVersionUID = 4741889985221719579L;
 989 
 990         /**
 991          * Common supertype for all tree modification event types.
 992          * @since JavaFX 8.0
 993          */
 994         public static final EventType<?> ANY = TREE_NOTIFICATION_EVENT;
 995 
 996         private transient final TreeItem<T> treeItem;
 997         private final T newValue;
 998 
 999         private final List<? extends TreeItem<T>> added;
1000         private final List<? extends TreeItem<T>> removed;
1001         private final ListChangeListener.Change<? extends TreeItem<T>> change;
1002 
1003         private final boolean wasExpanded;
1004         private final boolean wasCollapsed;
1005         private boolean wasPermutated;
1006 
1007         /**
1008          * Constructs a basic TreeModificationEvent - this is useful in situations
1009          * where the tree item has not received a new value, has not changed
1010          * between expanded/collapsed states, and whose children has not changed.
1011          * An example of when this constructor is used is when the TreeItem
1012          * graphic property changes.
1013          *
1014          * @param eventType The type of the event that has occurred.
1015          * @param treeItem The TreeItem on which this event occurred.
1016          */
1017         public TreeModificationEvent(EventType<? extends Event> eventType, TreeItem<T> treeItem) {
1018             this (eventType, treeItem, null);
1019         }
1020 
1021         /**
1022          * Constructs a TreeModificationEvent for when the TreeItem has had its
1023          * {@link TreeItem#valueProperty()} changed.
1024          *
1025          * @param eventType The type of the event that has occurred.
1026          * @param treeItem The TreeItem on which this event occurred.
1027          * @param newValue The new value that has been put into the
1028          *      {@link TreeItem#valueProperty()}.
1029          */
1030         public TreeModificationEvent(EventType<? extends Event> eventType,
1031                 TreeItem<T> treeItem, T newValue) {
1032             super(eventType);
1033             this.treeItem = treeItem;
1034             this.newValue = newValue;
1035             this.added = null;
1036             this.removed = null;
1037             this.change = null;
1038             this.wasExpanded = false;
1039             this.wasCollapsed = false;
1040         }
1041 
1042         /**
1043          * Constructs a TreeModificationEvent for when the TreeItem has had its
1044          * {@link TreeItem#expandedProperty()} changed.
1045          *
1046          * @param eventType The type of the event that has occurred.
1047          * @param treeItem The TreeItem on which this event occurred.
1048          * @param expanded A boolean to represent the current expanded
1049          *      state of the TreeItem.
1050          */
1051         public TreeModificationEvent(EventType<? extends Event> eventType,
1052                 TreeItem<T> treeItem, boolean expanded) {
1053             super(eventType);
1054             this.treeItem = treeItem;
1055             this.newValue = null;
1056             this.added = null;
1057             this.removed = null;
1058             this.change = null;
1059             this.wasExpanded = expanded;
1060             this.wasCollapsed = ! expanded;
1061         }
1062 
1063         /**
1064          * Constructs a TreeModificationEvent for when the TreeItem has had its
1065          * children list changed.
1066          *
1067          * @param eventType The type of the event that has occurred.
1068          * @param treeItem The TreeItem on which this event occurred.
1069          * @param added A list of the items added to the children list of the
1070          *      given TreeItem.
1071          * @param removed A list of the items removed from the children list of
1072          *      the given TreeItem.
1073          */
1074         public TreeModificationEvent(EventType<? extends Event> eventType,
1075                                      TreeItem<T> treeItem,
1076                                      List<? extends TreeItem<T>> added,
1077                                      List<? extends TreeItem<T>> removed) {
1078             this(eventType, treeItem, added, removed, null);
1079         }
1080 
1081         /**
1082          * Constructs a TreeModificationEvent for when the TreeItem has had its
1083          * children list changed, including the
1084          * {@link javafx.collections.ListChangeListener.Change} that has taken place.
1085          *
1086          * @param eventType The type of the event that has occurred.
1087          * @param treeItem The TreeItem on which this event occurred.
1088          * @param added A list of the items added to the children list of the
1089          *      given TreeItem.
1090          * @param removed A list of the items removed from the children list of
1091          *      the given TreeItem.
1092          * @param change The actual change that has taken place on the children list.
1093          */
1094         private TreeModificationEvent(EventType<? extends Event> eventType,
1095                                      TreeItem<T> treeItem,
1096                                      List<? extends TreeItem<T>> added,
1097                                      List<? extends TreeItem<T>> removed,
1098                                      ListChangeListener.Change<? extends TreeItem<T>> change) {
1099             super(eventType);
1100             this.treeItem = treeItem;
1101             this.newValue = null;
1102             this.added = added;
1103             this.removed = removed;
1104             this.change = change;
1105             this.wasExpanded = false;
1106             this.wasCollapsed = false;
1107 
1108             this.wasPermutated = added != null && removed != null &&
1109                                  added.size() == removed.size() &&
1110                                  added.containsAll(removed);
1111         }
1112 
1113         /**
1114          * Returns the TreeItem upon which this event occurred.
1115          * @since JavaFX 2.1
1116          */
1117         @Override public TreeItem<T> getSource() {
1118             return this.treeItem;
1119         }
1120 
1121         /**
1122          * Returns the TreeItem that this event occurred upon.
1123          * @return The TreeItem that this event occurred upon.
1124          */
1125         public TreeItem<T> getTreeItem() {
1126             return treeItem;
1127         }
1128 
1129         /**
1130          * If the value of the TreeItem changed, this method will return the new
1131          * value. If it did not change, this method will return null.
1132          * @return The new value of the TreeItem if it changed, null otherwise.
1133          */
1134         public T getNewValue() {
1135             return newValue;
1136         }
1137 
1138         /**
1139          * Returns the children added to the TreeItem in this event, or an empty
1140          * list if no children were added.
1141          * @return The newly added children, or an empty list if no children
1142          *      were added.
1143          */
1144         public List<? extends TreeItem<T>> getAddedChildren() {
1145             return added == null ? Collections.<TreeItem<T>>emptyList() : added;
1146         }
1147 
1148         /**
1149          * Returns the children removed from the TreeItem in this event, or an
1150          * empty list if no children were added.
1151          * @return The removed children, or an empty list if no children
1152          *      were removed.
1153          */
1154         public List<? extends TreeItem<T>> getRemovedChildren() {
1155             return removed == null ? Collections.<TreeItem<T>>emptyList() : removed;
1156         }
1157 
1158         /**
1159          * Returns the number of children items that were removed in this event,
1160          * or zero if no children were removed.
1161          * @return The number of removed children items, or zero if no children
1162          *      were removed.
1163          */
1164         public int getRemovedSize() {
1165             return getRemovedChildren().size();
1166         }
1167 
1168         /**
1169          * Returns the number of children items that were added in this event,
1170          * or zero if no children were added.
1171          * @return The number of added children items, or zero if no children
1172          *      were added.
1173          */
1174         public int getAddedSize() {
1175             return getAddedChildren().size();
1176         }
1177 
1178         /**
1179          * Returns true if this event represents a TreeItem expansion event,
1180          * and false if the TreeItem was not expanded.
1181          * @return true if this event represents a TreeItem expansion event,
1182          * and false if the TreeItem was not expanded
1183          */
1184         public boolean wasExpanded() { return wasExpanded; }
1185 
1186         /**
1187          * Returns true if this event represents a TreeItem collapse event,
1188          * and false if the TreeItem was not collapsed.
1189          * @return true if this event represents a TreeItem collapse event,
1190          * and false if the TreeItem was not collapsed
1191          */
1192         public boolean wasCollapsed() { return wasCollapsed; }
1193 
1194         /**
1195          * Returns true if this event represents a TreeItem event where children
1196          * TreeItems were added.
1197          * @return true if this event represents a TreeItem event where children
1198          * TreeItems were added
1199          */
1200         public boolean wasAdded() { return getAddedSize() > 0; }
1201 
1202         /**
1203          * Returns true if this event represents a TreeItem event where children
1204          * TreeItems were removed.
1205          * @return true if this event represents a TreeItem event where children
1206          * TreeItems were removed
1207          */
1208         public boolean wasRemoved() { return getRemovedSize() > 0; }
1209 
1210         /**
1211          * Returns true if the order of the TreeItem children list has changed,
1212          * but that there have been no additions or removals.
1213          * @return true if the order of the TreeItem children list has changed,
1214          * but that there have been no additions or removals
1215          */
1216         public boolean wasPermutated() { return wasPermutated; }
1217 
1218         int getFrom() { return change == null ? -1 : change.getFrom(); }
1219         int getTo() { return change == null ? -1 : change.getTo(); }
1220         ListChangeListener.Change<? extends TreeItem<T>> getChange() { return change; }
1221     }
1222 }