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