Merge with trunk as of Jul 9, 2008
authorJaroslav Tulach <jtulach@netbeans.org>
Wed Jul 09 13:17:17 2008 +0200 (16 months ago)
changeset 8845820b09f436dc2
parent 884572a7dd649be13
parent 87926077acc4ea2ad
child 8846004b456446ca2
Merge with trunk as of Jul 9, 2008
     1.1 --- a/openide.explorer/src/org/openide/explorer/view/NodeListModel.java
     1.2 +++ b/openide.explorer/src/org/openide/explorer/view/NodeListModel.java
     1.3 @@ -158,7 +158,6 @@
     1.4          if (listener == null) {
     1.5              listener = new Listener(this);
     1.6          }
     1.7 -
     1.8          return listener;
     1.9      }
    1.10  
    1.11 @@ -170,7 +169,6 @@
    1.12      */
    1.13      public int getSize() {
    1.14          int s = findSize(parent, -1, depth);
    1.15 -
    1.16          return s;
    1.17      }
    1.18  
    1.19 @@ -186,9 +184,7 @@
    1.20      */
    1.21      public int getIndex(Object o) {
    1.22          getSize();
    1.23 -
    1.24          Info i = childrenCount.get(o);
    1.25 -
    1.26          return (i == null) ? (-1) : i.index;
    1.27      }
    1.28  
    1.29 @@ -221,7 +217,6 @@
    1.30      */
    1.31      private int findSize(VisualizerNode vis, int index, int depth) {
    1.32          Info info = childrenCount.get(vis);
    1.33 -
    1.34          if (info != null) {
    1.35              return info.childrenCount;
    1.36          }
    1.37 @@ -233,15 +228,16 @@
    1.38          info.depth = depth;
    1.39          info.index = index;
    1.40  
    1.41 +        /*if (depth == 1) {
    1.42 +            // enough to know the number of children
    1.43 +            size += vis.getChildren().getChildCount();
    1.44 +        } else */
    1.45          if (depth-- > 0) {
    1.46 -            Iterator it = vis.getChildren().iterator();
    1.47 -
    1.48 -            while (it.hasNext()) {
    1.49 -                VisualizerNode v = (VisualizerNode) it.next();
    1.50 -
    1.51 +            Enumeration it = vis.getChildren().children();
    1.52 +            while (it.hasMoreElements()) {
    1.53 +                VisualizerNode v = (VisualizerNode) it.nextElement();
    1.54                  // count node v
    1.55                  size++;
    1.56 -
    1.57                  // now count children of node v
    1.58                  size += findSize(v, index + size, depth);
    1.59              }
    1.60 @@ -249,7 +245,6 @@
    1.61  
    1.62          info.childrenCount = size;
    1.63          childrenCount.put(vis, info);
    1.64 -
    1.65          return size;
    1.66      }
    1.67  
    1.68 @@ -266,10 +261,9 @@
    1.69              return (VisualizerNode) vis.getChildAt(indx);
    1.70          }
    1.71  
    1.72 -        Iterator it = vis.getChildren().iterator();
    1.73 -
    1.74 -        while (it.hasNext()) {
    1.75 -            VisualizerNode v = (VisualizerNode) it.next();
    1.76 +        Enumeration it = vis.getChildren().children();
    1.77 +        while (it.hasMoreElements()) {
    1.78 +            VisualizerNode v = (VisualizerNode) it.nextElement();
    1.79  
    1.80              if (indx-- == 0) {
    1.81                  return v;
     2.1 --- a/openide.explorer/src/org/openide/explorer/view/TreeView.java
     2.2 +++ b/openide.explorer/src/org/openide/explorer/view/TreeView.java
     2.3 @@ -296,6 +296,7 @@
     2.4          // Init of the editor
     2.5          tree.setCellEditor(new TreeViewCellEditor(tree));
     2.6          tree.setEditable(true);
     2.7 +        tree.setLargeModel(true);
     2.8  
     2.9          // set selection mode to DISCONTIGUOUS_TREE_SELECTION as default
    2.10          setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
    2.11 @@ -842,21 +843,18 @@
    2.12          showWaitCursor();
    2.13          RequestProcessor.getDefault().post(new Runnable() {
    2.14  
    2.15 -                                               public void run() {
    2.16 -                                                   try {
    2.17 -                                                       node.getChildren().getNodes(true);
    2.18 -                                                   }
    2.19 -                                                   catch (Exception e) {
    2.20 -                                                       // log a exception
    2.21 -                                                       Logger.getLogger(TreeView.class.getName()).log(Level.WARNING,
    2.22 -                                                                         null, e);
    2.23 -                                                   }
    2.24 -                                                   finally {
    2.25 -                                                       // show normal cursor above all
    2.26 -                                                       showNormalCursor();
    2.27 -                                                   }
    2.28 -                                               }
    2.29 -                                           });
    2.30 +            public void run() {
    2.31 +                try {
    2.32 +                    node.getChildren().getNodesCount(true);
    2.33 +                } catch (Exception e) {
    2.34 +                    // log a exception
    2.35 +                    Logger.getLogger(TreeView.class.getName()).log(Level.WARNING, null, e);
    2.36 +                } finally {
    2.37 +                    // show normal cursor above all
    2.38 +                    showNormalCursor();
    2.39 +                }
    2.40 +            }
    2.41 +        });
    2.42      }
    2.43  
    2.44      /** Synchronize the selected nodes from the manager of this Explorer.
     3.1 --- a/openide.explorer/src/org/openide/explorer/view/VisualizerChildren.java
     3.2 +++ b/openide.explorer/src/org/openide/explorer/view/VisualizerChildren.java
     3.3 @@ -41,7 +41,6 @@
     3.4  package org.openide.explorer.view;
     3.5  
     3.6  import org.openide.nodes.*;
     3.7 -
     3.8  import java.util.*;
     3.9  
    3.10  
    3.11 @@ -51,35 +50,118 @@
    3.12  * @author Jaroslav Tulach
    3.13  */
    3.14  final class VisualizerChildren extends Object {
    3.15 +    /** empty visualizer children for any leaf */
    3.16 +    public static final VisualizerChildren EMPTY = new VisualizerChildren();   
    3.17 +    
    3.18      /** parent visualizer */
    3.19      public final VisualizerNode parent;
    3.20  
    3.21 -    /** list of all objects here (VisualizerNode) */
    3.22 -public final List<VisualizerNode> list = new ArrayList<VisualizerNode>();
    3.23 +    /** visualizer nodes (children) */
    3.24 +    private final List<VisualizerNode> visNodes;
    3.25  
    3.26 +    /** Empty VisualizerChildren. */
    3.27 +    private VisualizerChildren () {
    3.28 +        visNodes = Collections.EMPTY_LIST;
    3.29 +        parent = null;
    3.30 +    }    
    3.31 +    
    3.32      /** Creates new VisualizerChildren.
    3.33 -    * Can be called only from EventQueue.
    3.34 -    */
    3.35 -    public VisualizerChildren(VisualizerNode parent, Node[] nodes) {
    3.36 +     * Can be called only from EventQueue.
    3.37 +     */
    3.38 +    public VisualizerChildren(VisualizerNode parent, int size) {
    3.39          this.parent = parent;
    3.40 -
    3.41 -        int s = nodes.length;
    3.42 -
    3.43 -        for (int i = 0; i < s; i++) {
    3.44 -            VisualizerNode v = VisualizerNode.getVisualizer(this, nodes[i]);
    3.45 -            list.add(v);
    3.46 +        visNodes = new ArrayList<VisualizerNode>(size);
    3.47 +        for (int i = 0; i < size; i++) {
    3.48 +            visNodes.add(null);
    3.49          }
    3.50      }
    3.51  
    3.52 +    /** recomputes indexes for all nodes.
    3.53 +     * @param tn tree node that we are looking for
    3.54 +     * @return true if there is non-null object inside
    3.55 +     */
    3.56 +    private final boolean recomputeIndexes(VisualizerNode tn) {
    3.57 +        assert tn == null || this.parent == tn.getParent() : "tn must be our child!"; // NOI18N
    3.58 +
    3.59 +        boolean isNonNull = false;
    3.60 +        for (int i = 0; i < visNodes.size(); i++) {
    3.61 +            VisualizerNode node = (VisualizerNode) visNodes.get(i);
    3.62 +            if (node != null) {
    3.63 +                node.indexOf = i;
    3.64 +                isNonNull = true;
    3.65 +            }
    3.66 +        }
    3.67 +
    3.68 +        if (tn != null && tn.indexOf == -1) {
    3.69 +            // not computed => force computation
    3.70 +            for (int i = 0; i < visNodes.size(); i++) {
    3.71 +                VisualizerNode visNode = (VisualizerNode) getChildAt(i);
    3.72 +                visNode.indexOf = i;
    3.73 +                if (visNode == tn) {
    3.74 +                    return isNonNull;
    3.75 +                }
    3.76 +            }
    3.77 +        }
    3.78 +        return isNonNull;
    3.79 +    }  
    3.80 +    
    3.81 +    public javax.swing.tree.TreeNode getChildAt(int pos) {
    3.82 +        VisualizerNode visNode = visNodes.get(pos);
    3.83 +        if (visNode == null) {
    3.84 +            Node node = parent.node.getChildren().getNodeAt(pos);
    3.85 +            if (node == null) {
    3.86 +                return VisualizerNode.EMPTY;
    3.87 +            }
    3.88 +            visNode = VisualizerNode.getVisualizer(this, node);
    3.89 +            visNode.indexOf = pos;
    3.90 +            visNodes.set(pos, visNode);
    3.91 +        }
    3.92 +        return visNode;
    3.93 +    }
    3.94 +    
    3.95 +    public int getChildCount() {
    3.96 +        return visNodes.size();
    3.97 +    }
    3.98 +
    3.99 +    public java.util.Enumeration children() {
   3.100 +        return new java.util.Enumeration() {
   3.101 +
   3.102 +            private int index;
   3.103 +
   3.104 +            public boolean hasMoreElements() {
   3.105 +                return index < visNodes.size();
   3.106 +            }
   3.107 +
   3.108 +            public Object nextElement() {
   3.109 +                return getChildAt(index++);
   3.110 +            }
   3.111 +        };
   3.112 +    }
   3.113 +
   3.114 +    /** Delegated to us from VisualizerNode
   3.115 +     * 
   3.116 +     */
   3.117 +    public int getIndex(final javax.swing.tree.TreeNode p1) {
   3.118 +        VisualizerNode visNode = (VisualizerNode) p1;
   3.119 +        if (visNode.getParent() != this.parent) {
   3.120 +            return -1;
   3.121 +        }
   3.122 +
   3.123 +        if (visNode.indexOf == -1) {
   3.124 +            recomputeIndexes(visNode);
   3.125 +        }
   3.126 +        assert visNode.indexOf != -1 : "Index of v has been computed by recomputeIndexes"; // NOI18N
   3.127 +        return visNode.indexOf;
   3.128 +    }  
   3.129 +    
   3.130      /** Notification of children addded event. Modifies the list of nodes
   3.131 -    * and fires info to all listeners.
   3.132 -    */
   3.133 +     * and fires info to all listeners.
   3.134 +     */
   3.135      public void added(VisualizerEvent.Added ev) {
   3.136 -        ListIterator<VisualizerNode> it = list.listIterator();
   3.137 +        ListIterator<VisualizerNode> it = visNodes.listIterator();
   3.138          boolean empty = !it.hasNext();
   3.139  
   3.140          int[] indxs = ev.getArray();
   3.141 -        Node[] nodes = ev.getAdded();
   3.142  
   3.143          int current = 0;
   3.144          int inIndxs = 0;
   3.145 @@ -88,47 +170,33 @@
   3.146              while (current++ < indxs[inIndxs]) {
   3.147                  it.next();
   3.148              }
   3.149 -
   3.150 -            it.add(VisualizerNode.getVisualizer(this, nodes[inIndxs]));
   3.151 +            it.add(null);
   3.152              inIndxs++;
   3.153          }
   3.154  
   3.155 +        boolean isNonNull = recomputeIndexes(null);
   3.156 +
   3.157          VisualizerNode parent = this.parent;
   3.158 -
   3.159          while (parent != null) {
   3.160              Object[] listeners = parent.getListenerList();
   3.161 -
   3.162              for (int i = listeners.length - 1; i >= 0; i -= 2) {
   3.163                  ((NodeModel) listeners[i]).added(ev);
   3.164              }
   3.165 -
   3.166              parent = (VisualizerNode) parent.getParent();
   3.167          }
   3.168 -
   3.169          if (empty) {
   3.170              // change of state
   3.171 -            this.parent.notifyVisualizerChildrenChange(list.size(), this);
   3.172 +            this.parent.notifyVisualizerChildrenChange(isNonNull, this);
   3.173          }
   3.174      }
   3.175  
   3.176 -    private static boolean sameContains(List l, Object elem) {
   3.177 -        for (Iterator it = l.iterator(); it.hasNext();) {
   3.178 -            if (it.next() == elem) {
   3.179 -                return true;
   3.180 -            }
   3.181 -        }
   3.182 -
   3.183 -        return false;
   3.184 -    }
   3.185 -
   3.186      /** Notification that children has been removed. Modifies the list of nodes
   3.187 -    * and fires info to all listeners.
   3.188 -    */
   3.189 +     * and fires info to all listeners.
   3.190 +     */
   3.191      public void removed(VisualizerEvent.Removed ev) {
   3.192 +        /*
   3.193          List remList = Arrays.asList(ev.getRemovedNodes());
   3.194 -
   3.195 -        Iterator it = list.iterator();
   3.196 -
   3.197 +        Iterator it = visNodes.iterator();
   3.198          VisualizerNode vis;
   3.199  
   3.200          int[] indx = new int[remList.size()];
   3.201 @@ -140,37 +208,38 @@
   3.202              vis = (VisualizerNode) it.next();
   3.203  
   3.204              // check if it will removed
   3.205 -            if (sameContains(remList, vis.node)) {
   3.206 +            if (remList.contains(vis.node)) {
   3.207                  indx[remSize++] = count;
   3.208 -
   3.209                  // remove this VisualizerNode from children
   3.210                  it.remove();
   3.211 -
   3.212                  // bugfix #36389, add the removed node to VisualizerEvent
   3.213                  ev.removed.add(vis);
   3.214              }
   3.215 -
   3.216              count++;
   3.217          }
   3.218 +        ev.setRemovedIndicies(indx);*/
   3.219 +        
   3.220 +        int[] idxs = ev.getArray();
   3.221 +        for (int i = idxs.length - 1; i >= 0; i--) {
   3.222 +            VisualizerNode visNode = visNodes.remove(idxs[i]);
   3.223 +            ev.removed.add(visNode);
   3.224 +        }     
   3.225  
   3.226          // notify event about changed indexes
   3.227 -        ev.setRemovedIndicies(indx);
   3.228 +        recomputeIndexes(null);
   3.229  
   3.230          VisualizerNode parent = this.parent;
   3.231 -
   3.232          while (parent != null) {
   3.233              Object[] listeners = parent.getListenerList();
   3.234 -
   3.235              for (int i = listeners.length - 1; i >= 0; i -= 2) {
   3.236                  ((NodeModel) listeners[i]).removed(ev);
   3.237              }
   3.238 -
   3.239              parent = (VisualizerNode) parent.getParent();
   3.240          }
   3.241  
   3.242 -        if (list.isEmpty()) {
   3.243 +        if (visNodes.isEmpty()) {
   3.244              // now is empty
   3.245 -            this.parent.notifyVisualizerChildrenChange(0, this);
   3.246 +            this.parent.notifyVisualizerChildrenChange(true, this);
   3.247          }
   3.248      }
   3.249  
   3.250 @@ -180,35 +249,31 @@
   3.251       * which may be in an inconsistent state.
   3.252       */
   3.253      private int[] reorderByComparator(Comparator<VisualizerNode> c) {
   3.254 -        VisualizerNode[] old = list.toArray(new VisualizerNode[list.size()]);
   3.255 +        VisualizerNode[] old = visNodes.toArray(new VisualizerNode[visNodes.size()]);
   3.256          Arrays.sort(old, c);
   3.257  
   3.258          int[] idxs = new int[old.length];
   3.259 -
   3.260          for (int i = 0; i < idxs.length; i++) {
   3.261 -            idxs[i] = list.indexOf(old[i]);
   3.262 +            idxs[i] = visNodes.indexOf(old[i]);
   3.263          }
   3.264  
   3.265 -        list.clear();
   3.266 -        list.addAll(Arrays.asList(old));
   3.267 -
   3.268 +        visNodes.clear();
   3.269 +        visNodes.addAll(Arrays.asList(old));
   3.270          return idxs;
   3.271      }
   3.272  
   3.273      /** Notification that children has been reordered. Modifies the list of nodes
   3.274 -    * and fires info to all listeners.
   3.275 -    */
   3.276 +     * and fires info to all listeners.
   3.277 +     */
   3.278      public void reordered(VisualizerEvent.Reordered ev) {
   3.279          if (ev.getComparator() != null) {
   3.280              //#37802
   3.281              ev.array = reorderByComparator(ev.getComparator());
   3.282          } else {
   3.283              int[] indxs = ev.getArray();
   3.284 -            VisualizerNode[] old = list.toArray(new VisualizerNode[list.size()]);
   3.285 +            VisualizerNode[] old = visNodes.toArray(new VisualizerNode[visNodes.size()]);
   3.286              VisualizerNode[] arr = new VisualizerNode[old.length];
   3.287 -
   3.288              int s = indxs.length;
   3.289 -
   3.290              try {
   3.291                  for (int i = 0; i < s; i++) {
   3.292                      // arr[indxs[i]] = old[i];
   3.293 @@ -244,21 +309,20 @@
   3.294              }
   3.295  
   3.296              assert !Arrays.asList(arr).contains(null) : "Null element in reorderer list " + Arrays.asList(arr) +
   3.297 -            "; list=" + list + " indxs=" + Arrays.asList(org.openide.util.Utilities.toObjectArray(indxs));
   3.298 -            list.clear();
   3.299 -            list.addAll(Arrays.asList(arr));
   3.300 -            assert !list.contains(null);
   3.301 +            "; list=" + visNodes + " indxs=" + Arrays.asList(org.openide.util.Utilities.toObjectArray(indxs));
   3.302 +            visNodes.clear();
   3.303 +            visNodes.addAll(Arrays.asList(arr));
   3.304 +            assert !visNodes.contains(null);
   3.305          }
   3.306 +        recomputeIndexes(null);
   3.307  
   3.308          VisualizerNode parent = this.parent;
   3.309  
   3.310          while (parent != null) {
   3.311              Object[] listeners = parent.getListenerList();
   3.312 -
   3.313              for (int i = listeners.length - 1; i >= 0; i -= 2) {
   3.314                  ((NodeModel) listeners[i]).reordered(ev);
   3.315              }
   3.316 -
   3.317              parent = (VisualizerNode) parent.getParent();
   3.318          }
   3.319      }
     4.1 --- a/openide.explorer/src/org/openide/explorer/view/VisualizerEvent.java
     4.2 +++ b/openide.explorer/src/org/openide/explorer/view/VisualizerEvent.java
     4.3 @@ -53,7 +53,7 @@
     4.4  * @author Jaroslav Tulach
     4.5  */
     4.6  abstract class VisualizerEvent extends EventObject {
     4.7 -    /** indicies */
     4.8 +    /** indices */
     4.9      int[] array;
    4.10  
    4.11      public VisualizerEvent(VisualizerChildren ch, int[] array) {
    4.12 @@ -84,23 +84,12 @@
    4.13      static final class Added extends VisualizerEvent implements Runnable {
    4.14          static final long serialVersionUID = 5906423476285962043L;
    4.15  
    4.16 -        /** array of newly added nodes */
    4.17 -        private Node[] added;
    4.18 -
    4.19 -        /** Constructor for add of nodes notification.
    4.20 +        /** Constructor for nodes adding notification.
    4.21          * @param ch children
    4.22 -        * @param n array of added nodes
    4.23 -        * @param indx indicies of added nodes
    4.24 +        * @param idxs indicies of added nodes
    4.25          */
    4.26 -        public Added(VisualizerChildren ch, Node[] n, int[] indx) {
    4.27 -            super(ch, indx);
    4.28 -            added = n;
    4.29 -        }
    4.30 -
    4.31 -        /** Getter for added nodes.
    4.32 -        */
    4.33 -        public Node[] getAdded() {
    4.34 -            return added;
    4.35 +        public Added(VisualizerChildren ch, int[] idxs) {
    4.36 +            super(ch, idxs);
    4.37          }
    4.38  
    4.39          /** Process the event
    4.40 @@ -119,24 +108,13 @@
    4.41          /** linked list of removed nodes, that is filled in getChildren ().removed () method
    4.42          */
    4.43          public LinkedList<VisualizerNode> removed = new LinkedList<VisualizerNode>();
    4.44 -        private Node[] removedNodes;
    4.45  
    4.46 -        /** Constructor for add of nodes notification.
    4.47 +        /** Constructor for nodes removal notification.
    4.48          * @param ch children
    4.49 -        * @param n array of added nodes
    4.50 -        * @param indx indicies of added nodes
    4.51 +        * @param idxs indicies of added nodes
    4.52          */
    4.53 -        public Removed(VisualizerChildren ch, Node[] removedNodes) {
    4.54 -            super(ch, null);
    4.55 -            this.removedNodes = removedNodes;
    4.56 -        }
    4.57 -
    4.58 -        public Node[] getRemovedNodes() {
    4.59 -            return removedNodes;
    4.60 -        }
    4.61 -
    4.62 -        public void setRemovedIndicies(int[] arr) {
    4.63 -            super.array = arr;
    4.64 +        public Removed(VisualizerChildren ch, int[] idxs) {
    4.65 +            super(ch, idxs);
    4.66          }
    4.67  
    4.68          /** Process the event
    4.69 @@ -153,13 +131,12 @@
    4.70          static final long serialVersionUID = -4572356079752325870L;
    4.71          private Comparator<VisualizerNode> comparator = null;
    4.72  
    4.73 -        /** Constructor for add of nodes notification.
    4.74 +        /** Constructor for nodes reordering notification.
    4.75          * @param ch children
    4.76 -        * @param n array of added nodes
    4.77          * @param indx indicies of added nodes
    4.78          */
    4.79 -        public Reordered(VisualizerChildren ch, int[] indx) {
    4.80 -            super(ch, indx);
    4.81 +        public Reordered(VisualizerChildren ch, int[] idxs) {
    4.82 +            super(ch, idxs);
    4.83          }
    4.84  
    4.85          //#37802 - provide a way to just send a comparator along to do the 
     5.1 --- a/openide.explorer/src/org/openide/explorer/view/VisualizerNode.java
     5.2 +++ b/openide.explorer/src/org/openide/explorer/view/VisualizerNode.java
     5.3 @@ -118,6 +118,9 @@
     5.4      /** the VisualizerChildren that contains this VisualizerNode or null */
     5.5      private VisualizerChildren parent;
     5.6  
     5.7 +    /** index in parent */
     5.8 +    int indexOf = -1;
     5.9 +    
    5.10      /** cached name */
    5.11      private String name;
    5.12  
    5.13 @@ -242,13 +245,13 @@
    5.14      /** Getter for list of children of this visualizer.
    5.15      * @return list of VisualizerNode objects
    5.16      */
    5.17 -    public List<VisualizerNode> getChildren() {
    5.18 +    public VisualizerChildren getChildren() {
    5.19          VisualizerChildren ch = children.get();
    5.20  
    5.21          if ((ch == null) && !node.isLeaf()) {
    5.22 -            // initialize the nodes children before we enter
    5.23 -            // the readAccess section
    5.24 -            Node[] tmpInit = node.getChildren().getNodes();
    5.25 +            // initialize the nodes children before we enter the readAccess section 
    5.26 +            // (otherwise we could receive invalid node count (under lock))
    5.27 +            final int count = node.getChildren().getNodesCount();
    5.28  
    5.29              // go into lock to ensure that no childrenAdded, childrenRemoved,
    5.30              // childrenReordered notifications occures and that is why we do
    5.31 @@ -256,29 +259,22 @@
    5.32              ch = Children.MUTEX.readAccess(
    5.33                      new Mutex.Action<VisualizerChildren>() {
    5.34                          public VisualizerChildren run() {
    5.35 -                            Node[] nodes = node.getChildren().getNodes();
    5.36 -                            VisualizerChildren vc = new VisualizerChildren(VisualizerNode.this, nodes);
    5.37 -                            notifyVisualizerChildrenChange(nodes.length, vc);
    5.38 -
    5.39 +                            int nodesCount = node.getChildren().getNodesCount();
    5.40 +                            VisualizerChildren vc = new VisualizerChildren(VisualizerNode.this, nodesCount);
    5.41 +                            notifyVisualizerChildrenChange(true, vc);
    5.42                              return vc;
    5.43                          }
    5.44                      }
    5.45                  );
    5.46          }
    5.47 -
    5.48 -        if (LOG.isLoggable(Level.FINER)) {
    5.49 -            // this assert is too expensive during the performance tests:
    5.50 -            assert (ch == null) || !ch.list.contains(null) : ch.list + " from " + node; 
    5.51 -        }
    5.52 -
    5.53 -        return (ch == null) ? Collections.<VisualizerNode>emptyList() : ch.list;
    5.54 +        return ch == null ? VisualizerChildren.EMPTY : ch;
    5.55      }
    5.56  
    5.57      //
    5.58      // TreeNode interface (callable only from AWT-Event-Queue)
    5.59      //
    5.60      public int getIndex(final javax.swing.tree.TreeNode p1) {
    5.61 -        return getChildren().indexOf(p1);
    5.62 +        return getChildren().getIndex(p1);
    5.63      }
    5.64  
    5.65      public boolean getAllowsChildren() {
    5.66 @@ -286,22 +282,15 @@
    5.67      }
    5.68  
    5.69      public javax.swing.tree.TreeNode getChildAt(int p1) {
    5.70 -        List ch = getChildren();
    5.71 -        VisualizerNode vn = (VisualizerNode) ch.get(p1);
    5.72 -        assert vn != null : "Null child in " + ch + " from " + node;
    5.73 -
    5.74 -        return vn;
    5.75 +        return getChildren().getChildAt(p1);
    5.76      }
    5.77  
    5.78      public int getChildCount() {
    5.79 -        return getChildren().size();
    5.80 +        return getChildren().getChildCount();
    5.81      }
    5.82  
    5.83      public java.util.Enumeration<VisualizerNode> children() {
    5.84 -        List<VisualizerNode> l = getChildren();
    5.85 -        assert !l.contains(null) : "Null child in " + l + " from " + node;
    5.86 -
    5.87 -        return java.util.Collections.enumeration(l);
    5.88 +        return getChildren().children();
    5.89      }
    5.90  
    5.91      public boolean isLeaf() {
    5.92 @@ -330,7 +319,7 @@
    5.93              return;
    5.94          }
    5.95  
    5.96 -        QUEUE.runSafe(new VisualizerEvent.Added(ch, ev.getDelta(), ev.getDeltaIndices()));
    5.97 +        QUEUE.runSafe(new VisualizerEvent.Added(ch, ev.getDeltaIndices()));
    5.98          LOG.log(Level.FINER, "childrenAdded - end"); // NOI18N
    5.99      }
   5.100  
   5.101 @@ -346,7 +335,7 @@
   5.102              return;
   5.103          }
   5.104  
   5.105 -        QUEUE.runSafe(new VisualizerEvent.Removed(ch, ev.getDelta()));
   5.106 +        QUEUE.runSafe(new VisualizerEvent.Removed(ch, ev.getDeltaIndices()));
   5.107          LOG.log(Level.FINER, "childrenRemoved - end"); // NOI18N
   5.108      }
   5.109  
   5.110 @@ -486,8 +475,8 @@
   5.111      * @param size amount of children
   5.112      * @param ch the children
   5.113      */
   5.114 -    void notifyVisualizerChildrenChange(int size, VisualizerChildren ch) {
   5.115 -        if (size == 0) {
   5.116 +    void notifyVisualizerChildrenChange(boolean strongly, VisualizerChildren ch) {
   5.117 +        if (strongly) {
   5.118              // hold the children hard
   5.119              children = new StrongReference<VisualizerChildren>(ch);
   5.120          } else {
   5.121 @@ -513,12 +502,14 @@
   5.122  
   5.123      /** Hash code
   5.124      */
   5.125 +    @Override
   5.126      public int hashCode() {
   5.127          return hashCode;
   5.128      }
   5.129  
   5.130      /** Equals two objects are equal if they have the same hash code
   5.131      */
   5.132 +    @Override
   5.133      public boolean equals(Object o) {
   5.134          if (!(o instanceof VisualizerNode)) {
   5.135              return false;
   5.136 @@ -531,6 +522,7 @@
   5.137  
   5.138      /** String name is taken from the node.
   5.139      */
   5.140 +    @Override
   5.141      public String toString() {
   5.142          return getDisplayName();
   5.143      }
   5.144 @@ -603,6 +595,7 @@
   5.145              this.o = o;
   5.146          }
   5.147  
   5.148 +        @Override
   5.149          public T get() {
   5.150              return o;
   5.151          }
     6.1 --- a/openide.explorer/test/unit/src/org/openide/explorer/view/VisualizerNodeTest.java
     6.2 +++ b/openide.explorer/test/unit/src/org/openide/explorer/view/VisualizerNodeTest.java
     6.3 @@ -47,6 +47,7 @@
     6.4  import org.netbeans.junit.NbTestCase;
     6.5  import org.openide.nodes.AbstractNode;
     6.6  import org.openide.nodes.Children;
     6.7 +import org.openide.nodes.FilterNode;
     6.8  import org.openide.nodes.Node;
     6.9  
    6.10  /** VisualizerNode tests, mostly based on reported bugs.
    6.11 @@ -101,4 +102,40 @@
    6.12          
    6.13          assertSame("Icon instances should be same", icon1, icon2);
    6.14      }
    6.15 +    
    6.16 +    public void testLazyVisGet() throws Exception {
    6.17 +        LazyChildren lch = new LazyChildren();
    6.18 +        AbstractNode a = new AbstractNode(lch);
    6.19 +        
    6.20 +        TreeNode ta = Visualizer.findVisualizer(a);
    6.21 +        
    6.22 +        assertEquals("Child check", "c", ta.getChildAt(2).toString());
    6.23 +        assertEquals("Counter should be 1", 1, lch.cnt);
    6.24 +    }
    6.25 +    
    6.26 +    public void testLazyFilterGet() throws Exception {
    6.27 +        LazyChildren lch = new LazyChildren();
    6.28 +        AbstractNode a = new AbstractNode(lch);
    6.29 +        FilterNode fnode = new FilterNode(a);
    6.30 +        
    6.31 +        TreeNode ta = Visualizer.findVisualizer(fnode);
    6.32 +        
    6.33 +        assertEquals("Child check", "c", ta.getChildAt(2).toString());
    6.34 +        assertEquals("Counter should be 1", 1, lch.cnt);
    6.35 +    }
    6.36 +    
    6.37 +    static class LazyChildren extends Children.Keys<String> {
    6.38 +        public LazyChildren() {
    6.39 +            super(true);
    6.40 +            setKeys(new String[] {"a", "b", "c"});
    6.41 +        }
    6.42 +        int cnt;
    6.43 +        @Override
    6.44 +        protected Node[] createNodes(String key) {
    6.45 +            AbstractNode node = new AbstractNode(LEAF);
    6.46 +            node.setName(key);
    6.47 +            cnt++;
    6.48 +            return new Node[] {node};
    6.49 +        }
    6.50 +    }
    6.51  }
     7.1 --- a/openide.loaders/src/org/openide/loaders/DataFolder.java
     7.2 +++ b/openide.loaders/src/org/openide/loaders/DataFolder.java
     7.3 @@ -893,7 +893,7 @@
     7.4          /* Returns count of the nodes.
     7.5          */
     7.6          public int getNodesCount () {
     7.7 -            return node.getChildren().getNodes(FolderChildren.checkChildrenMutex()).length;
     7.8 +            return node.getChildren().getNodesCount(FolderChildren.checkChildrenMutex());
     7.9          }
    7.10  
    7.11          /* Returns array of subnodes
     8.1 --- a/openide.loaders/src/org/openide/loaders/DataObjectPool.java
     8.2 +++ b/openide.loaders/src/org/openide/loaders/DataObjectPool.java
     8.3 @@ -591,26 +591,8 @@
     8.4       */
     8.5      private final class FSListener extends FileChangeAdapter {
     8.6          FSListener() {}
     8.7 -        private Collection<Item> getTargets(FileEvent fe) {
     8.8 -            FileObject fo = fe.getFile();
     8.9 -            List<Item> toNotify = new LinkedList<Item>();
    8.10 -            // The FileSystem notifying us about the changes should
    8.11 -            // not hold any lock so we're safe here
    8.12 -            synchronized (DataObjectPool.this) {
    8.13 -                Item itm = map.get(fo);
    8.14 -                if (itm != null) { // the file was someones' primary
    8.15 -                    return Collections.singleton(itm); // so notify only owner
    8.16 -                } else { // unknown file or someone secondary
    8.17 -                    List<Item> arr = children.get(fo.getParent());
    8.18 -                    if (arr != null) {
    8.19 -                        return new ArrayList<Item>(arr);
    8.20 -                    } else {
    8.21 -                        return Collections.emptyList();
    8.22 -                    }
    8.23 -                }
    8.24 -            }
    8.25 -        }
    8.26  
    8.27 +        @Override
    8.28          public void fileChanged(FileEvent fe) {
    8.29              if (LISTENER.isLoggable(Level.FINE)) {
    8.30                  LISTENER.fine("fileChanged: " + fe); // NOI18N
    8.31 @@ -624,6 +606,7 @@
    8.32              }
    8.33          }
    8.34  
    8.35 +        @Override
    8.36          public void fileRenamed (FileRenameEvent fe) {
    8.37              if (LISTENER.isLoggable(Level.FINE)) {
    8.38                  LISTENER.fine("fileRenamed: " + fe); // NOI18N
    8.39 @@ -637,6 +620,7 @@
    8.40              }
    8.41          }
    8.42  
    8.43 +        @Override
    8.44          public void fileDeleted (FileEvent fe) {
    8.45              if (LISTENER.isLoggable(Level.FINE)) {
    8.46                  LISTENER.fine("fileDeleted: " + fe); // NOI18N
    8.47 @@ -650,6 +634,7 @@
    8.48              }
    8.49          }
    8.50  
    8.51 +        @Override
    8.52          public void fileDataCreated (FileEvent fe) {
    8.53              if (LISTENER.isLoggable(Level.FINE)) {
    8.54                  LISTENER.fine("fileDataCreated: " + fe); // NOI18N
    8.55 @@ -664,19 +649,12 @@
    8.56              ShadowChangeAdapter.checkBrokenDataShadows(fe);
    8.57          }
    8.58          
    8.59 +        @Override
    8.60          public void fileAttributeChanged (FileAttributeEvent fe) {
    8.61 -            if (LISTENER.isLoggable(Level.FINE)) {
    8.62 -                LISTENER.fine("fileAttributeChanged: " + fe); // NOI18N
    8.63 -            }
    8.64 -            for (Item item : getTargets(fe)) {
    8.65 -                DataObject dobj = item.getDataObjectOrNull();
    8.66 -                if (LISTENER.isLoggable(Level.FINE)) {
    8.67 -                    LISTENER.fine("  to: " + dobj); // NOI18N
    8.68 -                }
    8.69 -                if (dobj != null) dobj.notifyAttributeChanged(fe);
    8.70 -            }
    8.71 +            checkAttributeChanged(fe);
    8.72          }
    8.73  
    8.74 +        @Override
    8.75          public void fileFolderCreated(FileEvent fe) {
    8.76              if (LISTENER.isLoggable(Level.FINE)) {
    8.77                  LISTENER.fine("fileFolderCreated: " + fe); // NOI18N
    8.78 @@ -685,6 +663,55 @@
    8.79          }
    8.80      }
    8.81      
    8.82 +    static private Collection<Item> getTargets(FileEvent fe) {
    8.83 +        FileObject fo = fe.getFile();
    8.84 +        // The FileSystem notifying us about the changes should
    8.85 +        // not hold any lock so we're safe here
    8.86 +        synchronized (DataObjectPool.POOL) {
    8.87 +            Item itm = DataObjectPool.POOL.map.get(fo);
    8.88 +            if (itm != null) { // the file was someones' primary
    8.89 +                return Collections.singleton(itm); // so notify only owner
    8.90 +            } else { // unknown file or someone secondary
    8.91 +                List<Item> arr = DataObjectPool.POOL.children.get(fo.getParent());
    8.92 +                if (arr != null) {
    8.93 +                    return new ArrayList<Item>(arr);
    8.94 +                } else {
    8.95 +                    return Collections.emptyList();
    8.96 +                }
    8.97 +                /*List<Item> toNotify = new LinkedList<Item>();
    8.98 +                FileObject parent = fo.getParent();
    8.99 +                if (parent != null) { // the fo is not root
   8.100 +                    FileObject[] siblings = parent.getChildren();
   8.101 +                    // notify all in folder
   8.102 +                    for (int i = 0; i < siblings.length; i++) {
   8.103 +                        itm = (Item) DataObjectPool.POOL.map.get(siblings[i]);
   8.104 +                        if (itm != null) {
   8.105 +                            toNotify.add(itm);
   8.106 +                        }
   8.107 +                    }
   8.108 +                }
   8.109 +                return toNotify;*/
   8.110 +            }
   8.111 +        }
   8.112 +    }
   8.113 +
   8.114 +    /** Checks for attribute changes.
   8.115 +     */
   8.116 +    public static void checkAttributeChanged(FileAttributeEvent fe) {
   8.117 +        if (LISTENER.isLoggable(Level.FINE)) {
   8.118 +            LISTENER.fine("fileAttributeChanged: " + fe); // NOI18N
   8.119 +        }
   8.120 +        for (Item item : getTargets(fe)) {
   8.121 +            DataObject dobj = item.getDataObjectOrNull();
   8.122 +            if (LISTENER.isLoggable(Level.FINE)) {
   8.123 +                LISTENER.fine("  to: " + dobj); // NOI18N
   8.124 +            }
   8.125 +            if (dobj != null) {
   8.126 +                dobj.notifyAttributeChanged(fe);
   8.127 +            }
   8.128 +        }
   8.129 +    }   
   8.130 +    
   8.131      /** Registers new DataObject instance.
   8.132      * @param fo primary file for obj
   8.133      * @param loader the loader of the object to be created
     9.1 --- a/openide.loaders/src/org/openide/loaders/FolderChildren.java
     9.2 +++ b/openide.loaders/src/org/openide/loaders/FolderChildren.java
     9.3 @@ -47,35 +47,31 @@
     9.4  import java.util.logging.Level;
     9.5  import java.util.logging.Logger;
     9.6  import javax.swing.event.*;
     9.7 +import org.openide.filesystems.FileAttributeEvent;
     9.8 +import org.openide.filesystems.FileChangeListener;
     9.9 +import org.openide.filesystems.FileEvent;
    9.10  import org.openide.filesystems.FileObject;
    9.11 +import org.openide.filesystems.FileRenameEvent;
    9.12  import org.openide.nodes.*;
    9.13 -import org.openide.util.RequestProcessor;
    9.14  
    9.15  /** Watches over a folder and represents its
    9.16  * child data objects by nodes.
    9.17  *
    9.18  * @author Jaroslav Tulach
    9.19  */
    9.20 -final class FolderChildren extends Children.Keys<FolderChildren.Pair>
    9.21 -implements PropertyChangeListener, ChangeListener {
    9.22 +final class FolderChildren extends Children.Keys<FileObject>
    9.23 +        implements PropertyChangeListener, ChangeListener, FileChangeListener {
    9.24 +
    9.25      /** the folder */
    9.26      private DataFolder folder;
    9.27      /** filter of objects */
    9.28      private final DataFilter filter;
    9.29      /** listener on changes in nodes */
    9.30      private PropertyChangeListener listener;
    9.31 +    /** file change listener */
    9.32 +    private FileChangeListener fcListener;    
    9.33      /** logging, if needed */
    9.34      private Logger err;
    9.35 -    /** true if the refrersh is done after DataFilter change */
    9.36 -    private boolean refresh;        
    9.37 -    /**  we wait for this task finished in getNodes(true) */
    9.38 -    private RequestProcessor.Task refreshTask;
    9.39 -    /** Runnable scheduled to refRP */
    9.40 -    private ChildrenRefreshRunnable refreshRunnable;
    9.41 -    
    9.42 -    /** Private req processor for the refresh tasks */
    9.43 -    private static RequestProcessor refRP = 
    9.44 -        new RequestProcessor("FolderChildren_Refresh"); // NOI18N
    9.45      
    9.46      /**
    9.47      * @param f folder to display content of
    9.48 @@ -90,11 +86,11 @@
    9.49      * @param filter filter of objects
    9.50      */
    9.51      public FolderChildren(DataFolder f, DataFilter filter) {
    9.52 +        super(true);
    9.53          this.folder = f;
    9.54          this.filter = filter;
    9.55 -        this.refreshRunnable = new ChildrenRefreshRunnable();
    9.56 -        this.refreshTask = refRP.create(refreshRunnable);
    9.57          this.listener = org.openide.util.WeakListeners.propertyChange(this, folder);
    9.58 +        this.fcListener = org.openide.filesystems.FileUtil.weakFileChangeListener(this, folder.getPrimaryFile());
    9.59          String log;
    9.60          if (f.getPrimaryFile().isRoot()) {
    9.61              log = "org.openide.loaders.FolderChildren"; // NOI18N
    9.62 @@ -110,58 +106,51 @@
    9.63      }
    9.64  
    9.65      /** If the folder changed its children we change our nodes.
    9.66 -    */
    9.67 -    public void propertyChange (final PropertyChangeEvent ev) {
    9.68 -        if (DataFolder.PROP_CHILDREN.equals (ev.getPropertyName ())) {
    9.69 +     */
    9.70 +    public void propertyChange(final PropertyChangeEvent ev) {
    9.71 +        if (DataFolder.PROP_CHILDREN.equals(ev.getPropertyName())) {
    9.72              err.fine("Got PROP_CHILDREN");
    9.73 -            refreshChildren().schedule (0);
    9.74 -            postClearTask();
    9.75 +            refreshChildren(false);
    9.76              return;
    9.77          }
    9.78 -        if (
    9.79 -            DataFolder.PROP_SORT_MODE.equals (ev.getPropertyName ()) ||
    9.80 -            DataFolder.PROP_ORDER.equals (ev.getPropertyName ())
    9.81 -        ) {
    9.82 +        if (DataFolder.PROP_SORT_MODE.equals(ev.getPropertyName()) ||
    9.83 +                DataFolder.PROP_ORDER.equals(ev.getPropertyName())) {
    9.84              err.fine("Got PROP_SORT_MODE or PROP_ORDER");
    9.85 -            refreshChildren().schedule (0);
    9.86 -            postClearTask();
    9.87 -            return;
    9.88 +            refreshChildren(false);
    9.89          }
    9.90      }
    9.91      
    9.92 -    public void stateChanged( ChangeEvent e ) {
    9.93 +    public void stateChanged(ChangeEvent e) {
    9.94          // Filtering changed need to recompute children
    9.95 -        refresh = true;
    9.96 -        refreshChildren().schedule(0);
    9.97 -        postClearTask();
    9.98 -        return;
    9.99 -    }
   9.100 -
   9.101 -    /**
   9.102 -     * refreshRunnable holds references to the data object
   9.103 -     * to prevent GC. This method post a task to the same request processor
   9.104 -     * (refRP) to clear this references after they are no longer needed.
   9.105 -     */ 
   9.106 -    private void postClearTask() {
   9.107 -        refRP.post(new Runnable() {
   9.108 -            public void run() {
   9.109 -                refreshRunnable.clear();
   9.110 -            }
   9.111 -        });
   9.112 +        refreshChildren(true);
   9.113      }
   9.114      
   9.115 -    /** Refreshes the children.
   9.116 -    */
   9.117 -    RequestProcessor.Task refreshChildren() {
   9.118 -        return refreshTask;    
   9.119 +    /** Deep refresh or not. */
   9.120 +    final void refreshChildren(boolean deep) {
   9.121 +        final FileObject[] arr = folder.getPrimaryFile().getChildren();
   9.122 +        FolderOrder order = FolderOrder.findFor(folder.getPrimaryFile());
   9.123 +        Arrays.sort(arr, order);
   9.124 +
   9.125 +        if (deep) {
   9.126 +            MUTEX.postWriteRequest(new Runnable() {
   9.127 +
   9.128 +                public void run() {
   9.129 +                    List<FileObject> emptyList = Collections.emptyList();
   9.130 +                    setKeys(emptyList);
   9.131 +                    setKeys(arr);
   9.132 +                }
   9.133 +            });
   9.134 +            return;
   9.135 +        } else {
   9.136 +            setKeys(arr);
   9.137 +        }
   9.138      }
   9.139  
   9.140      /** Create a node for one data object.
   9.141      * @param key DataObject
   9.142      */
   9.143 -    protected Node[] createNodes(Pair key) {
   9.144 -        err.fine("createNodes: " + key);
   9.145 -        FileObject fo = key.primaryFile;
   9.146 +    protected Node[] createNodes(FileObject fo) {
   9.147 +        err.fine("createNodes: " + fo);
   9.148          DataObject obj;
   9.149          try {
   9.150              obj = DataObject.find (fo);
   9.151 @@ -176,41 +165,47 @@
   9.152          }
   9.153      }
   9.154    
   9.155 +    @Override
   9.156      public Node[] getNodes(boolean optimalResult) {
   9.157 -        Node[] res;
   9.158 -        Object hold;
   9.159 -
   9.160          if (optimalResult) {
   9.161              if (checkChildrenMutex()) {
   9.162                  err.fine("getNodes(true)"); // NOI18N
   9.163                  FolderList.find(folder.getPrimaryFile(), true).waitProcessingFinished();
   9.164                  err.fine("getNodes(true): waitProcessingFinished"); // NOI18N
   9.165 -                RequestProcessor.Task task = refreshChildren();
   9.166 -                res = getNodes();
   9.167 -                err.fine("getNodes(true): getNodes: " + res.length); // NOI18N
   9.168 -                task.schedule(0);
   9.169 -                task.waitFinished();
   9.170 -                err.fine("getNodes(true): waitFinished"); // NOI18N
   9.171 +                //refreshChildren(false);
   9.172              } else {
   9.173                  Logger.getLogger(FolderChildren.class.getName()).log(Level.WARNING, null,
   9.174                                    new java.lang.IllegalStateException("getNodes(true) called while holding the Children.MUTEX"));
   9.175              }
   9.176          }
   9.177 -        res = getNodes();
   9.178 -        err.fine("getNodes(boolean): post clear task"); // NOI18N
   9.179 -        postClearTask();         // we can clean the references to data objects now
   9.180 -                                 // they are no longer needed
   9.181 +        Node[] res = getNodes();
   9.182          return res;
   9.183      }
   9.184      
   9.185 +    @Override
   9.186      public Node findChild(String name) {
   9.187          if (checkChildrenMutex()) {
   9.188 -            getNodes(true);
   9.189 +            getNodesCount(true);
   9.190          }
   9.191          return super.findChild(name);
   9.192      }
   9.193  
   9.194 -
   9.195 +    @Override
   9.196 +    public int getNodesCount(boolean optimalResult) {
   9.197 +        if (optimalResult) {
   9.198 +            if (checkChildrenMutex()) {
   9.199 +                err.fine("getNodesCount(true)"); // NOI18N
   9.200 +                FolderList.find(folder.getPrimaryFile(), true).waitProcessingFinished();
   9.201 +                err.fine("getNodesCount(true): waitProcessingFinished"); // NOI18N
   9.202 +                //refreshChildren(false);
   9.203 +            } else {
   9.204 +                Logger.getLogger(FolderChildren.class.getName()).log(Level.WARNING, null,
   9.205 +                        new java.lang.IllegalStateException("getNodes(true) called while holding the Children.MUTEX"));
   9.206 +            }
   9.207 +        }
   9.208 +        int count = getNodesCount();
   9.209 +        return count;
   9.210 +    }
   9.211  
   9.212      /**
   9.213       * @return true if it is safe to wait (our thread is
   9.214 @@ -222,120 +217,69 @@
   9.215      
   9.216      /** Initializes the children.
   9.217      */
   9.218 +    @Override
   9.219      protected void addNotify () {
   9.220          err.fine("addNotify begin");
   9.221          // add as a listener for changes on nodes
   9.222 -        folder.addPropertyChangeListener (listener);
   9.223 +        folder.addPropertyChangeListener(listener);
   9.224 +        folder.getPrimaryFile().addFileChangeListener(fcListener);
   9.225          // add listener to the filter
   9.226          if ( filter instanceof ChangeableDataFilter ) {
   9.227              ((ChangeableDataFilter)filter).addChangeListener( this );
   9.228          }
   9.229          // start the refresh task to compute the children
   9.230 -        refreshChildren().schedule(0);
   9.231 +        refreshChildren(false);
   9.232          err.fine("addNotify end");
   9.233      }
   9.234  
   9.235      /** Deinitializes the children.
   9.236      */
   9.237 +    @Override
   9.238      protected void removeNotify () {
   9.239          err.fine("removeNotify begin");
   9.240 -        // removes the listener
   9.241 -        folder.removePropertyChangeListener (listener);
   9.242 +        // removes the listeners
   9.243 +        folder.getPrimaryFile().removeFileChangeListener(fcListener);
   9.244 +        folder.removePropertyChangeListener(listener);
   9.245          // remove listener from filter
   9.246          if ( filter instanceof ChangeableDataFilter ) {
   9.247              ((ChangeableDataFilter)filter).removeChangeListener( this );
   9.248          }
   9.249          
   9.250          // we need to clear the children now
   9.251 -        setKeys(Collections.<Pair>emptySet());
   9.252 +        List<FileObject> emptyList = Collections.emptyList();
   9.253 +        setKeys(emptyList);
   9.254          err.fine("removeNotify end");
   9.255      }
   9.256  
   9.257      /** Display name */
   9.258 +    @Override
   9.259      public String toString () {
   9.260          return (folder != null) ? folder.getPrimaryFile ().toString () : super.toString();
   9.261      }
   9.262 -    
   9.263 -    /**
   9.264 -     * Instances of this class are posted to the request processor refRP
   9.265 -     * (FolderChildren_refresher). We do this because we do not want
   9.266 -     * to call setKeys synchronously.
   9.267 -     */
   9.268 -    private final class ChildrenRefreshRunnable implements Runnable {
   9.269 -        /** store the referneces to the data objects to
   9.270 -         * prevent GC.
   9.271 -         */
   9.272 -        private DataObject[] ch;
   9.273 -                
   9.274 -        /** calls setKeys with the folder children 
   9.275 -         * or with empty collection if active is false
   9.276 -         */
   9.277 -        public void run() {
   9.278 -            // this can be run only on the refRP thread
   9.279 -            assert refRP.isRequestProcessorThread();
   9.280  
   9.281 -            FolderList.find(folder.getPrimaryFile(), true).waitProcessingFinished();
   9.282 -            
   9.283 -            ch = folder.getChildren();
   9.284 -            err.fine("Children computed");
   9.285 -            Pair[] keys = new Pair[ch.length];
   9.286 -            for (int i = 0; i < keys.length; i++) {
   9.287 -                keys[i] = new Pair(ch[i].getPrimaryFile());
   9.288 -            }
   9.289 -            setKeys(Arrays.asList(keys));
   9.290 -            
   9.291 -            if ( refresh ) {
   9.292 -                refresh = false;
   9.293 -                for (Pair key : keys) {
   9.294 -                    refreshKey(key);
   9.295 -                }
   9.296 -            }
   9.297 -            
   9.298 -            if (!isInitialized()) {
   9.299 -                clear();
   9.300 -            }
   9.301 -        }
   9.302 -        
   9.303 -        /** stop holding the references to the data objects. After
   9.304 -         * calling this they can be GCed again.
   9.305 -         */
   9.306 -        public void clear() {
   9.307 -            // this can be run only on the refRP thread
   9.308 -            assert refRP.isRequestProcessorThread();
   9.309 -            err.fine("Clearing the reference to children"); // NOI18N
   9.310 -            ch = null;
   9.311 +    public void fileAttributeChanged(FileAttributeEvent fe) {
   9.312 +        if (DataObject.EA_ASSIGNED_LOADER.equals(fe.getName())) {
   9.313 +            // make sure this event is processed by the data system
   9.314 +            DataObjectPool.checkAttributeChanged(fe);
   9.315 +            refreshKey(fe.getFile());
   9.316          }
   9.317      }
   9.318 -    
   9.319 -    /** Pair of dataobject invalidation sequence # and primary file.
   9.320 -     * It serves as a key for the given data object.
   9.321 -     * It is here to create something different then data object,
   9.322 -     * because the data object should be finalized when not needed and
   9.323 -     * that is why it should not be used as a key.
   9.324 -     */
   9.325 -    static final class Pair extends Object {
   9.326 -        public FileObject primaryFile;
   9.327 -        public int seq;
   9.328  
   9.329 -        public Pair (FileObject primaryFile) {
   9.330 -            this.primaryFile = primaryFile;
   9.331 -            this.seq = DataObjectPool.getPOOL().registrationCount(primaryFile);
   9.332 -        }
   9.333 +    public void fileChanged(FileEvent fe) {
   9.334 +    }
   9.335  
   9.336 -        public int hashCode () {
   9.337 -            return primaryFile.hashCode () ^ seq;
   9.338 -        }
   9.339 +    public void fileDataCreated(FileEvent fe) {
   9.340 +         refreshChildren(false);
   9.341 +    }
   9.342  
   9.343 -        public boolean equals (Object o) {
   9.344 -            if (o instanceof Pair) {
   9.345 -                Pair p = (Pair)o;
   9.346 -                return primaryFile.equals (p.primaryFile) && seq == p.seq;
   9.347 -            }
   9.348 -            return false;
   9.349 -        }
   9.350 -        
   9.351 -        public String toString() {
   9.352 -            return "FolderChildren.Pair[" + primaryFile + "," + seq + "]"; // NOI18N
   9.353 -        }
   9.354 +    public void fileDeleted(FileEvent fe) {
   9.355 +        refreshChildren(false);
   9.356 +    }
   9.357 +
   9.358 +    public void fileFolderCreated(FileEvent fe) {
   9.359 +        refreshChildren(false);
   9.360 +    }
   9.361 +
   9.362 +    public void fileRenamed(FileRenameEvent fe) {
   9.363      }
   9.364  }
    10.1 --- a/openide.loaders/src/org/openide/loaders/FolderComparator.java
    10.2 +++ b/openide.loaders/src/org/openide/loaders/FolderComparator.java
    10.3 @@ -41,9 +41,11 @@
    10.4  
    10.5  package org.openide.loaders;
    10.6  
    10.7 +import java.util.Comparator;
    10.8  import java.util.Date;
    10.9  import java.util.Enumeration;
   10.10  import org.openide.filesystems.FileObject;
   10.11 +import org.openide.nodes.Node;
   10.12  
   10.13  /**
   10.14   * Compares objects in a folder.
   10.15 @@ -80,10 +82,14 @@
   10.16          this.mode = mode;
   10.17      }
   10.18  
   10.19 +    public int compare(DataObject o1, DataObject o2) {
   10.20 +        return doCompare((Object) o1, (Object) o2);
   10.21 +    }
   10.22 +
   10.23      /** Comparing method. Can compare two DataObjects
   10.24 -    * or two Nodes (if they have data object cookie)
   10.25 +    * or two Nodes (if they have data object cookie) or two FileObjects
   10.26      */
   10.27 -    public int compare(DataObject obj1, DataObject obj2) {
   10.28 +    int doCompare(Object obj1, Object obj2) {
   10.29          switch (mode) {
   10.30          case NONE:
   10.31              return 0;
   10.32 @@ -103,34 +109,57 @@
   10.33          }
   10.34      }
   10.35  
   10.36 +    static FileObject findFileObject(Object o) {
   10.37 +        if (o instanceof FileObject) {
   10.38 +            return (FileObject) o;
   10.39 +        }
   10.40 +        if (o instanceof DataObject) {
   10.41 +            return ((DataObject) o).getPrimaryFile();
   10.42 +        }
   10.43 +        Node n = (Node) o;
   10.44 +        DataObject obj = (DataObject) n.getCookie(DataObject.class);
   10.45 +        return obj.getPrimaryFile();
   10.46 +    }
   10.47 +
   10.48 +    private static DataObject findDataObject(Object o) {
   10.49 +        if (o instanceof DataObject) {
   10.50 +            return (DataObject) o;
   10.51 +        }
   10.52 +        if (o instanceof FileObject) {
   10.53 +            try {
   10.54 +                return DataObject.find((FileObject) o);
   10.55 +            } catch (DataObjectNotFoundException ex) {
   10.56 +                return null;
   10.57 +            }
   10.58 +        }
   10.59 +        Node n = (Node) o;
   10.60 +        DataObject obj = (DataObject) n.getCookie(DataObject.class);
   10.61 +        return obj;
   10.62 +    }
   10.63  
   10.64      /** for sorting data objects by names */
   10.65 -    private int compareNames (DataObject obj1, DataObject obj2) {
   10.66 -        // #35069 - use extension for sorting if displayname is same.
   10.67 -        //  Otherwise the order of files is random.
   10.68 -        int part = obj1.getName().compareTo(obj2.getName());
   10.69 -        return part != 0 ? part :
   10.70 -            obj1.getPrimaryFile().getExt().compareTo(obj2.getPrimaryFile().getExt());
   10.71 +    private int compareNames(Object o1, Object o2) {     
   10.72 +        return findFileObject(o1).getNameExt().compareTo(findFileObject(o2).getNameExt());
   10.73      }
   10.74  
   10.75      /** for sorting folders first and then by names */
   10.76 -    private int compareFoldersFirst (DataObject obj1, DataObject obj2) {
   10.77 -        if (obj1.getClass () != obj2.getClass ()) {
   10.78 -            // if classes are different than the folder goes first
   10.79 -            if (obj1 instanceof DataFolder) {
   10.80 -                return -1;
   10.81 -            }
   10.82 -            if (obj2 instanceof DataFolder) {
   10.83 -                return 1;
   10.84 -            }
   10.85 +    private int compareFoldersFirst(Object o1, Object o2) {
   10.86 +        boolean f1 = findFileObject(o1).isFolder();
   10.87 +        boolean f2 = findFileObject(o2).isFolder();
   10.88 +
   10.89 +        if (f1 != f2) {
   10.90 +            return f1 ? -1 : 1;
   10.91          }
   10.92  
   10.93          // otherwise compare by names
   10.94 -        return compareNames(obj1, obj2);
   10.95 +        return compareNames(o1, o2);
   10.96      }
   10.97  
   10.98      /** for sorting data objects by their classes */
   10.99 -    private int compareClass (DataObject obj1, DataObject obj2) {
  10.100 +    private int compareClass(Object o1, Object o2) {
  10.101 +        DataObject obj1 = findDataObject(o1);
  10.102 +        DataObject obj2 = findDataObject(o2);
  10.103 +
  10.104          Class<?> c1 = obj1.getClass ();
  10.105          Class<?> c2 = obj2.getClass ();
  10.106  
  10.107 @@ -165,59 +194,48 @@
  10.108      /**
  10.109       * Sort folders alphabetically first. Then files, newest to oldest.
  10.110       */
  10.111 -    private static int compareLastModified(DataObject obj1, DataObject obj2) {
  10.112 -        if (obj1 instanceof DataFolder) {
  10.113 -            if (obj2 instanceof DataFolder) {
  10.114 -                return obj1.getName().compareTo(obj2.getName());
  10.115 -            } else {
  10.116 -                return -1;
  10.117 -            }
  10.118 +    private static int compareLastModified(Object o1, Object o2) {
  10.119 +        boolean f1 = findFileObject(o1).isFolder();
  10.120 +        boolean f2 = findFileObject(o2).isFolder();
  10.121 +
  10.122 +        if (f1 != f2) {
  10.123 +            return f1 ? -1 : 1;
  10.124 +        }
  10.125 +
  10.126 +        FileObject fo1 = findFileObject(o1);
  10.127 +        FileObject fo2 = findFileObject(o2);
  10.128 +        Date d1 = fo1.lastModified();
  10.129 +        Date d2 = fo2.lastModified();
  10.130 +        if (d1.after(d2)) {
  10.131 +            return -1;
  10.132 +        } else if (d2.after(d1)) {
  10.133 +            return 1;
  10.134          } else {
  10.135 -            if (obj2 instanceof DataFolder) {
  10.136 -                return 1;
  10.137 -            } else {
  10.138 -                FileObject fo1 = obj1.getPrimaryFile();
  10.139 -                FileObject fo2 = obj2.getPrimaryFile();
  10.140 -                Date d1 = fo1.lastModified();
  10.141 -                Date d2 = fo2.lastModified();
  10.142 -                if (d1.after(d2)) {
  10.143 -                    return -1;
  10.144 -                } else if (d2.after(d1)) {
  10.145 -                    return 1;
  10.146 -                } else {
  10.147 -                    return fo1.getNameExt().compareTo(fo2.getNameExt());
  10.148 -                }
  10.149 -            }
  10.150 +            return fo1.getNameExt().compareTo(fo2.getNameExt());
  10.151          }
  10.152      }
  10.153  
  10.154      /**
  10.155       * Sort folders alphabetically first. Then files, biggest to smallest.
  10.156       */
  10.157 -    private static int compareSize(DataObject obj1, DataObject obj2) {
  10.158 -        if (obj1 instanceof DataFolder) {
  10.159 -            if (obj2 instanceof DataFolder) {
  10.160 -                return obj1.getName().compareTo(obj2.getName());
  10.161 -            } else {
  10.162 -                return -1;
  10.163 -            }
  10.164 +    private static int compareSize(Object o1, Object o2) {
  10.165 +        boolean f1 = findFileObject(o1).isFolder();
  10.166 +        boolean f2 = findFileObject(o2).isFolder();
  10.167 +
  10.168 +        if (f1 != f2) {
  10.169 +            return f1 ? -1 : 1;
  10.170 +        }
  10.171 +
  10.172 +        FileObject fo1 = findFileObject(o1);
  10.173 +        FileObject fo2 = findFileObject(o2);
  10.174 +        long s1 = fo1.getSize();
  10.175 +        long s2 = fo2.getSize();
  10.176 +        if (s1 > s2) {
  10.177 +            return -1;
  10.178 +        } else if (s2 > s1) {
  10.179 +            return 1;
  10.180          } else {
  10.181 -            if (obj2 instanceof DataFolder) {
  10.182 -                return 1;
  10.183 -            } else {
  10.184 -                FileObject fo1 = obj1.getPrimaryFile();
  10.185 -                FileObject fo2 = obj2.getPrimaryFile();
  10.186 -                long s1 = fo1.getSize();
  10.187 -                long s2 = fo2.getSize();
  10.188 -                if (s1 > s2) {
  10.189 -                    return -1;
  10.190 -                } else if (s2 > s1) {
  10.191 -                    return 1;
  10.192 -                } else {
  10.193 -                    return fo1.getNameExt().compareTo(fo2.getNameExt());
  10.194 -                }
  10.195 -            }
  10.196 +            return fo1.getNameExt().compareTo(fo2.getNameExt());
  10.197          }
  10.198      }
  10.199 -
  10.200  }
    11.1 --- a/openide.loaders/src/org/openide/loaders/FolderOrder.java
    11.2 +++ b/openide.loaders/src/org/openide/loaders/FolderOrder.java
    11.3 @@ -55,7 +55,7 @@
    11.4   *
    11.5   * @author  Jaroslav Tulach
    11.6   */
    11.7 -final class FolderOrder extends Object implements Comparator<DataObject> {
    11.8 +final class FolderOrder extends Object implements Comparator<Object> {
    11.9      
   11.10      /** a static map with (FileObject, Reference (Folder))
   11.11       */
   11.12 @@ -142,15 +142,15 @@
   11.13  
   11.14      /** Compares two data object or two nodes.
   11.15      */
   11.16 -    public int compare (DataObject obj1, DataObject obj2) {
   11.17 -        Integer i1 = (order == null) ? null : order.get (obj1.getPrimaryFile ().getNameExt ());
   11.18 -        Integer i2 = (order == null) ? null : order.get (obj2.getPrimaryFile ().getNameExt ());
   11.19 +    public int compare (Object obj1, Object obj2) {
   11.20 +        Integer i1 = (order == null) ? null : (Integer) order.get(FolderComparator.findFileObject(obj1).getNameExt());
   11.21 +        Integer i2 = (order == null) ? null : (Integer) order.get(FolderComparator.findFileObject(obj2).getNameExt());
   11.22  
   11.23          if (i1 == null) {
   11.24              if (i2 != null) return 1;
   11.25  
   11.26              // compare by the provided comparator
   11.27 -            return getSortMode ().compare (obj1, obj2);
   11.28 +            return ((FolderComparator)(getSortMode())).doCompare(obj1, obj2);
   11.29          } else {
   11.30              if (i2 == null) return -1;
   11.31              // compare integers
    12.1 --- a/openide.nodes/src/org/openide/nodes/Children.java
    12.2 +++ b/openide.nodes/src/org/openide/nodes/Children.java
    12.3 @@ -123,6 +123,11 @@
    12.4      /** Constructor.
    12.5      */
    12.6      public Children() {
    12.7 +        this(false);
    12.8 +    }
    12.9 +
   12.10 +    public Children(boolean lazy) {
   12.11 +        lazySupport = lazy;
   12.12      }
   12.13  
   12.14      /**
   12.15 @@ -137,13 +142,15 @@
   12.16              return entrySupport;
   12.17          }
   12.18      }
   12.19 -
   12.20 +    
   12.21 +    final boolean lazySupport;
   12.22      /**
   12.23       * Creates appropriate entry support for this children.
   12.24       * Overriden in Children.Keys to sometimes make lazy support.
   12.25       */
   12.26      EntrySupport createEntrySource() {
   12.27 -        return new EntrySupport.Default(this);
   12.28 +        return lazySupport ? new EntrySupport.Lazy(this) : new EntrySupport.Default(this);
   12.29 +        //return new EntrySupport.Lazy(this);
   12.30      }
   12.31  
   12.32      /**
   12.33 @@ -310,6 +317,7 @@
   12.34      *   a parent node
   12.35      * *exception CloneNotSupportedException if <code>Cloneable</code> interface is not implemented
   12.36      */
   12.37 +    @Override
   12.38      protected Object clone() throws CloneNotSupportedException {
   12.39          Children ch = (Children) super.clone();
   12.40          ch.parent = null;
   12.41 @@ -452,7 +460,14 @@
   12.42      * @return the count
   12.43      */
   12.44      public final int getNodesCount() {
   12.45 -        return entrySupport().getNodesCount();
   12.46 +        return entrySupport().getNodesCount(false);
   12.47 +    }
   12.48 +
   12.49 +    /** Get the number of nodes in the list
   12.50 +    * @return the count
   12.51 +    */
   12.52 +    public int getNodesCount(boolean optimalResult) {
   12.53 +        return entrySupport().getNodesCount(optimalResult);
   12.54      }
   12.55  
   12.56      //
   12.57 @@ -485,7 +500,21 @@
   12.58      * do additional work and then call addNotify.
   12.59      */
   12.60      void callAddNotify() {
   12.61 +        //System.err.println("Thread: " + Thread.currentThread().getName() + ", N: " + getNode());
   12.62 +        //System.err.println("Children: " + this);
   12.63          addNotify();
   12.64 +        //System.err.println("Finished: " + this);
   12.65 +    }
   12.66 +
   12.67 +    /** Called when the nodes have been removed from the children.
   12.68 +     * This method should allow subclasses to clean the nodes somehow.
   12.69 +     * <p>
   12.70 +     * Current implementation notifies all listeners on the nodes
   12.71 +     * that nodes have been deleted.
   12.72 +     *
   12.73 +     * @param arr array of deleted nodes
   12.74 +     */
   12.75 +    void destroyNodes(Node[] arr) {
   12.76      }
   12.77  
   12.78      /** @return either nodes associated with this children or null if
   12.79 @@ -495,68 +524,6 @@
   12.80          return entrySupport == null ? null : entrySupport().testNodes();
   12.81      }
   12.82  
   12.83 -    /** Notifies that a set of nodes has been removed from
   12.84 -     * children. It is necessary that the system is already
   12.85 -     * in consistent state, so any callbacks will return
   12.86 -     * valid values.
   12.87 -     *
   12.88 -     * @param nodes list of removed nodes
   12.89 -     * @param current state of nodes
   12.90 -     * @return array of nodes that were deleted
   12.91 -     */
   12.92 -    Node[] notifyRemove(Collection<Node> nodes, Node[] current) {
   12.93 -        //System.err.println("notifyRemove from: " + getNode ());
   12.94 -        //System.err.println("notifyRemove: " + nodes);
   12.95 -        //System.err.println("Current     : " + Arrays.asList (current));
   12.96 -        //Thread.dumpStack();
   12.97 -        //Keys.last.printStackTrace();
   12.98 -        // [TODO] Children do not have always a parent
   12.99 -        // see Services->FIRST ($SubLevel.class)
  12.100 -        // during a deserialization it may have parent == null
  12.101 -        Node[] arr = nodes.toArray(new Node[nodes.size()]);
  12.102 -
  12.103 -        if (parent == null) {
  12.104 -            return arr;
  12.105 -        }
  12.106 -
  12.107 -        // fire change of nodes
  12.108 -        parent.fireSubNodesChange(false, // remove
  12.109 -                arr, current);
  12.110 -
  12.111 -        // fire change of parent
  12.112 -        Iterator it = nodes.iterator();
  12.113 -
  12.114 -        while (it.hasNext()) {
  12.115 -            Node n = (Node) it.next();
  12.116 -            n.deassignFrom(this);
  12.117 -            n.fireParentNodeChange(parent, null);
  12.118 -        }
  12.119 -
  12.120 -        return arr;
  12.121 -    }
  12.122 -
  12.123 -    /** Notifies that a set of nodes has been add to
  12.124 -     * children. It is necessary that the system is already
  12.125 -     * in consistent state, so any callbacks will return
  12.126 -     * valid values.
  12.127 -     *
  12.128 -     * @param nodes list of removed nodes
  12.129 -     */
  12.130 -    void notifyAdd(Collection<Node> nodes) {
  12.131 -        // notify about parent change
  12.132 -        for (Node n : nodes) {
  12.133 -            n.assignTo(this, -1);
  12.134 -            n.fireParentNodeChange(null, parent);
  12.135 -        }
  12.136 -
  12.137 -        Node[] arr = nodes.toArray(new Node[nodes.size()]);
  12.138 -
  12.139 -        Node n = parent;
  12.140 -
  12.141 -        if (n != null) {
  12.142 -            n.fireSubNodesChange(true, arr, null);
  12.143 -        }
  12.144 -    }
  12.145  
  12.146      /** Interface that provides a set of nodes.
  12.147      */
  12.148 @@ -622,8 +589,15 @@
  12.149          * first time, children will be used.
  12.150          */
  12.151          public Array() {
  12.152 -            nodesEntry = createNodesEntry();
  12.153 -            entrySupport().setEntries(Collections.singleton(getNodesEntry()));
  12.154 +            this(false);
  12.155 +        }
  12.156 +
  12.157 +        Array(boolean lazy) {
  12.158 +            super(lazy);
  12.159 +            if (!lazy) {
  12.160 +                nodesEntry = createNodesEntry();
  12.161 +                entrySupport().setEntries(Collections.singleton(getNodesEntry()));
  12.162 +            }
  12.163          }
  12.164  
  12.165          /** Clones all nodes that are contained in the children list.
  12.166 @@ -741,12 +715,8 @@
  12.167                      // no change to the collection
  12.168                      return false;
  12.169                  }
  12.170 -
  12.171 -                ;
  12.172              }
  12.173 -
  12.174              refresh();
  12.175 -
  12.176              return true;
  12.177          }
  12.178  
  12.179 @@ -1020,12 +990,14 @@
  12.180  
  12.181              /** Hash code.
  12.182              */
  12.183 +            @Override
  12.184              public int hashCode() {
  12.185                  return key.hashCode();
  12.186              }
  12.187  
  12.188              /** Equals.
  12.189              */
  12.190 +            @Override
  12.191              public boolean equals(Object o) {
  12.192                  if (o instanceof ME) {
  12.193                      ME me = (ME) o;
  12.194 @@ -1036,6 +1008,7 @@
  12.195                  return false;
  12.196              }
  12.197  
  12.198 +            @Override
  12.199              public String toString() {
  12.200                  return "Key (" + key + ")"; // NOI18N
  12.201              }
  12.202 @@ -1090,6 +1063,7 @@
  12.203          /** This method allows subclasses (only in this package) to
  12.204          * provide own version of entry. Useful for SortedArray.
  12.205          */
  12.206 +        @Override
  12.207          Entry createNodesEntry() {
  12.208              return new SAE();
  12.209          }
  12.210 @@ -1164,6 +1138,7 @@
  12.211          * @param map the map (Object, Node)
  12.212          * @return collection of (Entry)
  12.213          */
  12.214 +        @Override
  12.215          Collection<? extends Entry> createEntries(java.util.Map<T,Node> map) {
  12.216              // SME objects use natural ordering
  12.217              Set<ME> l = new TreeSet<ME>(new SMComparator());
  12.218 @@ -1237,14 +1212,19 @@
  12.219          private static java.util.Map<Keys<?>,Runnable> lastRuns = new HashMap<Keys<?>,Runnable>(11);
  12.220  
  12.221          /** add array children before or after keys ones */
  12.222 -        private boolean before;
  12.223 -
  12.224 +        boolean before;
  12.225 +        
  12.226          public Keys() {
  12.227 -            super();
  12.228 +            this(false);
  12.229          }
  12.230 -
  12.231 +        
  12.232 +        public Keys(boolean lazy) {
  12.233 +            super(lazy);
  12.234 +        }
  12.235 +        
  12.236          /** Special handling for clonning.
  12.237          */
  12.238 +        @Override
  12.239          public Object clone() {
  12.240              Keys<?> k = (Keys<?>) super.clone();
  12.241  
  12.242 @@ -1255,6 +1235,7 @@
  12.243           * @deprecated Do not use! Just call {@link #setKeys(Collection)} with a larger set.
  12.244           */
  12.245          @Deprecated
  12.246 +        @Override
  12.247          public boolean add(Node[] arr) {
  12.248              return super.add(arr);
  12.249          }
  12.250 @@ -1263,6 +1244,7 @@
  12.251           * @deprecated Do not use! Just call {@link #setKeys(Collection)} with a smaller set.
  12.252           */
  12.253          @Deprecated
  12.254 +        @Override
  12.255          public boolean remove(final Node[] arr) {
  12.256              try {
  12.257                  PR.enterWriteAccess();
  12.258 @@ -1322,16 +1304,18 @@
  12.259              }
  12.260  
  12.261              final List<Entry> l = new ArrayList<Entry>(keysSet.size() + 1);
  12.262 +            KE updator = new KE();
  12.263  
  12.264 -            if (before) {
  12.265 -                l.add(getNodesEntry());
  12.266 -            }
  12.267 -
  12.268 -            KE updator = new KE();
  12.269 -            updator.updateList(keysSet, l);
  12.270 -
  12.271 -            if (!before) {
  12.272 -                l.add(getNodesEntry());
  12.273 +            if (lazySupport) {
  12.274 +                updator.updateList(keysSet, l);
  12.275 +            } else {
  12.276 +                if (before) {
  12.277 +                    l.add(getNodesEntry());
  12.278 +                }
  12.279 +                updator.updateList(keysSet, l);
  12.280 +                if (!before) {
  12.281 +                    l.add(getNodesEntry());
  12.282 +                }
  12.283              }
  12.284  
  12.285              applyKeys(l);
  12.286 @@ -1357,17 +1341,18 @@
  12.287              }
  12.288  
  12.289              final List<Entry> l = new ArrayList<Entry>(keys.length + 1);
  12.290 -
  12.291              KE updator = new KE();
  12.292  
  12.293 -            if (before) {
  12.294 -                l.add(getNodesEntry());
  12.295 -            }
  12.296 -
  12.297 -            updator.updateList(keys, l);
  12.298 -
  12.299 -            if (!before) {
  12.300 -                l.add(getNodesEntry());
  12.301 +            if (lazySupport) {
  12.302 +                updator.updateList(keys, l);
  12.303 +            } else {
  12.304 +                if (before) {
  12.305 +                    l.add(getNodesEntry());
  12.306 +                }
  12.307 +                updator.updateList(keys, l);
  12.308 +                if (!before) {
  12.309 +                    l.add(getNodesEntry());
  12.310 +                }
  12.311              }
  12.312  
  12.313              applyKeys(l);
  12.314 @@ -1400,7 +1385,7 @@
  12.315              try {
  12.316                  PR.enterWriteAccess();
  12.317  
  12.318 -                if (before != b) {
  12.319 +                if (before != b && !lazySupport) {
  12.320                      List<Entry> l = entrySupport().getEntries();
  12.321                      l.remove(getNodesEntry());
  12.322                      before = b;
  12.323 @@ -1417,7 +1402,7 @@
  12.324                  PR.exitWriteAccess();
  12.325              }
  12.326          }
  12.327 -
  12.328 +        
  12.329          /** Create nodes for a given key.
  12.330          * @param key the key
  12.331          * @return child nodes for this key or null if there should be no
  12.332 @@ -1433,21 +1418,13 @@
  12.333          *
  12.334          * @param arr array of deleted nodes
  12.335          */
  12.336 +        @Override
  12.337          protected void destroyNodes(Node[] arr) {
  12.338              for (int i = 0; i < arr.length; i++) {
  12.339                  arr[i].fireNodeDestroyed();
  12.340              }
  12.341          }
  12.342  
  12.343 -        /** Notifies the children class that nodes has been released.
  12.344 -        */
  12.345 -        Node[] notifyRemove(Collection<Node> nodes, Node[] current) {
  12.346 -            Node[] arr = super.notifyRemove(nodes, current);
  12.347 -            destroyNodes(arr);
  12.348 -
  12.349 -            return arr;
  12.350 -        }
  12.351 -
  12.352          /** Enter of setKeys.
  12.353           * @param ch the children
  12.354           * @param run runnable
  12.355 @@ -1478,7 +1455,7 @@
  12.356  
  12.357          /** Entry for a key
  12.358          */
  12.359 -        private final class KE extends Dupl<T> {
  12.360 +        class KE extends Dupl<T> {
  12.361              /** Has default constructor that allows to create a factory
  12.362              * of KE objects for use with updateList
  12.363              */
  12.364 @@ -1590,7 +1567,7 @@
  12.365          * @return the key
  12.366          */
  12.367          @SuppressWarnings("unchecked") // data structure really weird
  12.368 -        public final T getKey() {
  12.369 +        public T getKey() {
  12.370              if (key instanceof Dupl) {
  12.371                  return (T) ((Dupl) key).getKey();
  12.372              } else {
  12.373 @@ -1601,7 +1578,7 @@
  12.374          /** Counts the index of this key.
  12.375          * @return integer
  12.376          */
  12.377 -        public final int getCnt() {
  12.378 +        public int getCnt() {
  12.379              int cnt = 0;
  12.380              Dupl d = this;
  12.381  
    13.1 --- a/openide.nodes/src/org/openide/nodes/EntrySupport.java
    13.2 +++ b/openide.nodes/src/org/openide/nodes/EntrySupport.java
    13.3 @@ -49,7 +49,6 @@
    13.4  import java.util.Iterator;
    13.5  import java.util.LinkedList;
    13.6  import java.util.List;
    13.7 -import java.util.ListIterator;
    13.8  import java.util.Map;
    13.9  import java.util.Set;
   13.10  import java.util.logging.Level;
   13.11 @@ -69,7 +68,7 @@
   13.12      Reference<ChildrenArray> array = new WeakReference<ChildrenArray>(null);
   13.13      
   13.14      /** collection of all entries */
   13.15 -    protected List<? extends Entry> entries = Collections.emptyList();
   13.16 +    protected List<Entry> entries = Collections.emptyList();
   13.17  
   13.18      /** Creates a new instance of EntrySupport */
   13.19      protected EntrySupport(Children children) {
   13.20 @@ -79,7 +78,7 @@
   13.21      //
   13.22      // API methods to be called from Children
   13.23      //
   13.24 -    public abstract int getNodesCount();
   13.25 +    public abstract int getNodesCount(boolean optimalResult);
   13.26  
   13.27      public abstract Node[] getNodes(boolean optimalResult);
   13.28  
   13.29 @@ -92,7 +91,7 @@
   13.30      public abstract boolean isInitialized();
   13.31  
   13.32      abstract void notifySetEntries();
   13.33 -    
   13.34 +
   13.35      abstract void setEntries(Collection<? extends Entry> entries);
   13.36      
   13.37      /** Access to copy of current entries.
   13.38 @@ -102,6 +101,8 @@
   13.39          return new ArrayList<Entry>(this.entries);
   13.40      }
   13.41      
   13.42 +    abstract Collection<Node> getEntryNodes(Entry entry);
   13.43 +    
   13.44      /** Refreshes content of one entry. Updates the state of children appropriately. */
   13.45      abstract void refreshEntry(Entry entry);
   13.46  
   13.47 @@ -195,8 +196,8 @@
   13.48              return getNodes();
   13.49          }
   13.50  
   13.51 -        public final int getNodesCount() {
   13.52 -            return getNodes().length;
   13.53 +        public final int getNodesCount(boolean optimalResult) {
   13.54 +            return getNodes(optimalResult).length;
   13.55          }
   13.56  
   13.57          @Override
   13.58 @@ -261,7 +262,7 @@
   13.59          //
   13.60          // Entries
   13.61          //
   13.62 -        
   13.63 +
   13.64          private boolean mustNotifySetEnties = false;
   13.65  
   13.66          void notifySetEntries() {
   13.67 @@ -281,7 +282,7 @@
   13.68                  LOG_GET_ARRAY.fine("       holder: " + holder); // NOI18N
   13.69  
   13.70              }
   13.71 -            
   13.72 +
   13.73              Node[] current = holder == null ? null : holder.nodes();
   13.74              if (mustNotifySetEnties) {
   13.75                  if (holder == null) {
   13.76 @@ -362,8 +363,7 @@
   13.77              //debug.append ("New     : " + this.entries + '\n'); // NOI18N
   13.78              //printStackTrace();
   13.79              clearNodes();
   13.80 -
   13.81 -            children.notifyRemove(nodes, current);
   13.82 +            notifyRemove(nodes, current, null);
   13.83          }
   13.84  
   13.85          /** Updates the order of entries.
   13.86 @@ -487,7 +487,7 @@
   13.87           * @param infos list of Info objects to add
   13.88           * @param entries the final state of entries that should occur
   13.89           */
   13.90 -        private void updateAdd(Collection<Info> infos, List<? extends Entry> entries) {
   13.91 +        private void updateAdd(Collection<Info> infos, List<Entry> entries) {
   13.92              List<Node> nodes = new LinkedList<Node>();
   13.93              for (Info info : infos) {
   13.94                  nodes.addAll(info.nodes());
   13.95 @@ -502,8 +502,7 @@
   13.96              //      debug.append ("Set4: " + entries); // NOI18N
   13.97              //      printStackTrace();
   13.98              clearNodes();
   13.99 -
  13.100 -            children.notifyAdd(nodes);
  13.101 +            notifyAdd(nodes, null);
  13.102          }
  13.103  
  13.104          /** Refreshes content of one entry. Updates the state of children
  13.105 @@ -556,8 +555,7 @@
  13.106                  clearNodes();
  13.107  
  13.108                  // now everything should be consistent => notify the remove
  13.109 -                children.notifyRemove(toRemove, current);
  13.110 -
  13.111 +                notifyRemove(toRemove, current, entry);
  13.112                  current = holder.nodes();
  13.113              }
  13.114  
  13.115 @@ -567,7 +565,7 @@
  13.116              if (!toAdd.isEmpty()) {
  13.117                  // modifies the list associated with the info
  13.118                  clearNodes();
  13.119 -                children.notifyAdd(toAdd);
  13.120 +                notifyAdd(toAdd, entry);
  13.121              }
  13.122          }
  13.123  
  13.124 @@ -579,23 +577,6 @@
  13.125           */
  13.126          private List<Node> refreshOrder(Entry entry, Collection<Node> oldNodes, Collection<Node> newNodes) {
  13.127              List<Node> toAdd = new LinkedList<Node>();
  13.128 -
  13.129 -            int currentPos = 0;
  13.130 -
  13.131 -            // cycle thru all entries to find index of the entry
  13.132 -            Iterator<? extends Entry> it1 = this.entries.iterator();
  13.133 -
  13.134 -            for (;;) {
  13.135 -                Entry e = it1.next();
  13.136 -
  13.137 -                if (e.equals(entry)) {
  13.138 -                    break;
  13.139 -                }
  13.140 -
  13.141 -                Info info = findInfo(e);
  13.142 -                currentPos += info.length();
  13.143 -            }
  13.144 -
  13.145              Set<Node> oldNodesSet = new HashSet<Node>(oldNodes);
  13.146              Set<Node> toProcess = new HashSet<Node>(oldNodesSet);
  13.147  
  13.148 @@ -636,10 +617,68 @@
  13.149                      p.fireReorderChange(perm);
  13.150                  }
  13.151              }
  13.152 -
  13.153              return toAdd;
  13.154          }
  13.155  
  13.156 +        /** Notifies that a set of nodes has been removed from
  13.157 +         * children. It is necessary that the system is already
  13.158 +         * in consistent state, so any callbacks will return
  13.159 +         * valid values.
  13.160 +         *
  13.161 +         * @param nodes list of removed nodes
  13.162 +         * @param current state of nodes
  13.163 +         * @return array of nodes that were deleted
  13.164 +         */
  13.165 +        Node[] notifyRemove(Collection<Node> nodes, Node[] current, Entry sourceEntry) {
  13.166 +            //System.err.println("notifyRemove from: " + getNode ());
  13.167 +            //System.err.println("notifyRemove: " + nodes);
  13.168 +            //System.err.println("Current     : " + Arrays.asList (current));
  13.169 +            //Thread.dumpStack();
  13.170 +            //Keys.last.printStackTrace();
  13.171 +            // [TODO] Children do not have always a parent
  13.172 +            // see Services->FIRST ($SubLevel.class)
  13.173 +            // during a deserialization it may have parent == null
  13.174 +            Node[] arr = nodes.toArray(new Node[nodes.size()]);
  13.175 +
  13.176 +            if (children.parent != null) {
  13.177 +                // fire change of nodes
  13.178 +                children.parent.fireSubNodesChange(false, arr, current, sourceEntry);
  13.179 +
  13.180 +                // fire change of parent
  13.181 +                Iterator it = nodes.iterator();
  13.182 +
  13.183 +                while (it.hasNext()) {
  13.184 +                    Node n = (Node) it.next();
  13.185 +                    n.deassignFrom(children);
  13.186 +                    n.fireParentNodeChange(children.parent, null);
  13.187 +                }
  13.188 +            }
  13.189 +            children.destroyNodes(arr);
  13.190 +            return arr;
  13.191 +        }
  13.192 +
  13.193 +        /** Notifies that a set of nodes has been add to
  13.194 +         * children. It is necessary that the system is already
  13.195 +         * in consistent state, so any callbacks will return
  13.196 +         * valid values.
  13.197 +         *
  13.198 +         * @param nodes list of removed nodes
  13.199 +         */
  13.200 +        void notifyAdd(Collection<Node> nodes, Entry sourceEntry) {
  13.201 +            // notify about parent change
  13.202 +            for (Node n : nodes) {
  13.203 +                n.assignTo(children, -1);
  13.204 +                n.fireParentNodeChange(null, children.parent);
  13.205 +            }
  13.206 +
  13.207 +            Node[] arr = nodes.toArray(new Node[nodes.size()]);
  13.208 +
  13.209 +            Node n = children.parent;
  13.210 +
  13.211 +            if (n != null) {
  13.212 +                n.fireSubNodesChange(true, arr, null, sourceEntry);
  13.213 +            }
  13.214 +        }    
  13.215          //
  13.216          // ChildrenArray operations call only under lock
  13.217          //
  13.218 @@ -714,13 +753,9 @@
  13.219                                  initThread = null;
  13.220                                  LOCK.notifyAll();
  13.221                              }
  13.222 -
  13.223                              if (IS_LOG_GET_ARRAY) {
  13.224 -                                LOG_GET_ARRAY.fine(
  13.225 -                                        "notifyAll done"); // NOI18N
  13.226 -
  13.227 +                                LOG_GET_ARRAY.fine("notifyAll done"); // NOI18N
  13.228                              }
  13.229 -
  13.230                          }
  13.231                      }
  13.232  
  13.233 @@ -865,6 +900,19 @@
  13.234                  arr.finalizeNodes();
  13.235              }
  13.236          }
  13.237 +        
  13.238 +        Collection<Node> getEntryNodes(Entry entry) {
  13.239 +            try {
  13.240 +                //Children.PR.enterReadAccess();
  13.241 +                if (map == null) {
  13.242 +                    map = Collections.synchronizedMap(new HashMap<Entry, Info>(17));
  13.243 +                }
  13.244 +                Info info = findInfo(entry);
  13.245 +                return info.nodes();
  13.246 +            } finally {
  13.247 +                //Children.PR.exitReadAccess();
  13.248 +            }
  13.249 +        }
  13.250  
  13.251          /** Information about an entry. Contains number of nodes,
  13.252           * position in the array of nodes, etc.
  13.253 @@ -916,82 +964,198 @@
  13.254      
  13.255      static final class Lazy extends EntrySupport {
  13.256          private Map<Entry, EntryInfo> entryToInfo = new HashMap<Entry, EntryInfo>();
  13.257 -        private List<WeakReference<Node>> childrenNodes = new ArrayList<WeakReference<Node>>();
  13.258 -        /** Computed size of nodes in this support or -1 if the size
  13.259 -         * needs to be recomputed once again. Clear to -1 if you do some
  13.260 -         * changes in nodes count.
  13.261 -         */
  13.262 -        private int nodesCount = -1;
  13.263 +        private static final Logger LAZY_LOG = Logger.getLogger("org.openide.nodes.Children.getArray"); // NOI18N
  13.264 +        
  13.265 +        static final Node NONEXISTING_NODE = new NonexistingNode();
  13.266  
  13.267          public Lazy(Children ch) {
  13.268              super(ch);
  13.269          }
  13.270  
  13.271 +        private final Object LOCK = new Object();
  13.272 +        private boolean initInProgress = false;
  13.273 +        private boolean inited = false;
  13.274 +        private Thread initThread; 
  13.275 +        public boolean checkInit() {
  13.276 +            if (inited) {
  13.277 +                return true;
  13.278 +            }
  13.279 +            boolean doInit = false;
  13.280 +            synchronized (LOCK) {
  13.281 +                if (!initInProgress) {
  13.282 +                    doInit = true;
  13.283 +                    initInProgress = true;
  13.284 +                    initThread = Thread.currentThread();
  13.285 +                }
  13.286 +            }
  13.287 +
  13.288 +            if (doInit) {
  13.289 +
  13.290 +                try {
  13.291 +                    children.callAddNotify();
  13.292 +                } finally {
  13.293 +                    class Notify implements Runnable {
  13.294 +                        public void run() {
  13.295 +                            synchronized (LOCK) {
  13.296 +                                inited = true;
  13.297 +                                initThread = null;
  13.298 +                                LOCK.notifyAll();
  13.299 +                            }
  13.300 +                        }
  13.301 +                    }
  13.302 +                    Notify notify = new Notify();
  13.303 +                    if (Children.MUTEX.isReadAccess()) {
  13.304 +                        Children.MUTEX.postWriteRequest(notify);
  13.305 +                    } else {
  13.306 +                        notify.run();
  13.307 +                    }
  13.308 +                }
  13.309 +            } else {
  13.310 +                if (Children.MUTEX.isReadAccess() || Children.MUTEX.isWriteAccess() || (initThread == Thread.currentThread())) {
  13.311 +                    // we cannot wait
  13.312 +                    return false;
  13.313 +                }
  13.314 +
  13.315 +                // otherwise we can wait
  13.316 +                synchronized (LOCK) {
  13.317 +                    while (initThread != null) {
  13.318 +                        try {
  13.319 +                            LOCK.wait();
  13.320 +                        } catch (InterruptedException ex) {
  13.321 +                        }
  13.322 +                    }
  13.323 +                }
  13.324 +            }
  13.325 +            return true;
  13.326 +    }
  13.327 +
  13.328          @Override
  13.329          public Node getNodeAt(int index) {
  13.330 -            try {
  13.331 -                Children.PR.enterReadAccess();
  13.332 -                if (childrenNodes != null) {
  13.333 -                    if (index >= childrenNodes.size()) {
  13.334 +            if (!checkInit()) {
  13.335 +                return null;
  13.336 +            }
  13.337 +            while (true) {
  13.338 +                Node node;
  13.339 +                try {
  13.340 +                    Children.PR.enterReadAccess();
  13.341 +                    if (index >= entries.size()) {
  13.342                          return null;
  13.343                      }
  13.344 -                    Node node = childrenNodes.get(index).get();
  13.345 -                    if (node != null) {
  13.346 +                    Entry entry = entries.get(index);
  13.347 +                    EntryInfo info = entryToInfo.get(entry);
  13.348 +                    node = info.getNode();
  13.349 +                    if (node != NONEXISTING_NODE) {
  13.350                          return node;
  13.351                      }
  13.352 +                    removeEmptyEntry(entry);
  13.353 +                } finally {
  13.354 +                    Children.PR.exitReadAccess();
  13.355                  }
  13.356 -            } finally {
  13.357 -                Children.PR.exitReadAccess();
  13.358 +                if (Children.MUTEX.isReadAccess()) {
  13.359 +                    return node;
  13.360 +                }
  13.361              }
  13.362 -            return computeAt(index);
  13.363 -        }
  13.364 -        
  13.365 -        final Node computeAt(final int index) {
  13.366 -            int low = 0;
  13.367 -            int high = entries.size() - 1;
  13.368 -            while (low <= high) {
  13.369 -                int mid = (low + high) / 2;
  13.370 -                Entry e = entries.get(mid);
  13.371 -                EntryInfo info = entryToInfo.get(e);
  13.372 -                if (info.getIndex() > index) {
  13.373 -                    high = mid - 1;
  13.374 -                    continue;
  13.375 -                }
  13.376 -                int above = info.getIndex() + info.size();
  13.377 -                if (above > index) {
  13.378 -                    List<Node> list = info.getNodes();
  13.379 -                    int size = info.getIndex();
  13.380 -
  13.381 -                    /*if (list.size() <= index - size) {
  13.382 -                        return NO_NODE;
  13.383 -                    }*/
  13.384 -                    Node n = (Node) list.get(index - size);
  13.385 -                    n.assignTo(children, index);
  13.386 -                    return n;
  13.387 -                }
  13.388 -                low = mid + 1;
  13.389 -            }
  13.390 -            return null;
  13.391          }
  13.392  
  13.393          @Override
  13.394          public Node[] getNodes(boolean optimalResult) {
  13.395 -            throw new UnsupportedOperationException("Not supported yet.");
  13.396 +            if (!checkInit()) {
  13.397 +                return new Node[0];
  13.398 +            }            
  13.399 +            if (optimalResult) {
  13.400 +                children.findChild(null);
  13.401 +            }
  13.402 +            while (true) {
  13.403 +                HashSet<Entry> invalidEntries = null;
  13.404 +                Node[] nodes = null;
  13.405 +                try {
  13.406 +                    Children.PR.enterReadAccess();
  13.407 +
  13.408 +                    nodes = new Node[entries.size()];
  13.409 +                    for (int i = 0; i < nodes.length; i++) {
  13.410 +                        Entry entry = entries.get(i);
  13.411 +                        EntryInfo info = entryToInfo.get(entry);
  13.412 +                        Node node = info.getNode();
  13.413 +                        if (node == NONEXISTING_NODE) {
  13.414 +                            if (invalidEntries == null) {
  13.415 +                                invalidEntries = new HashSet<Entry>();
  13.416 +                            }
  13.417 +                            invalidEntries.add(entry);
  13.418 +                        }
  13.419 +                        nodes[i] = node;
  13.420 +                    }
  13.421 +                    nodesCreated = true;
  13.422 +                    if (invalidEntries == null) {
  13.423 +                        return nodes;
  13.424 +                    }
  13.425 +                    removeEmptyEntries(invalidEntries);
  13.426 +                } finally {
  13.427 +                    Children.PR.exitReadAccess();
  13.428 +                }
  13.429 +                
  13.430 +                if (Children.MUTEX.isReadAccess()) {
  13.431 +                    return nodes;
  13.432 +                }
  13.433 +            }
  13.434 +        }
  13.435 +
  13.436 +        boolean nodesCreated = false;
  13.437 +        @Override
  13.438 +        public Node[] testNodes() {
  13.439 +            return nodesCreated ? getNodes(false) : null;
  13.440          }
  13.441  
  13.442          @Override
  13.443 -        public int getNodesCount() {
  13.444 -            throw new UnsupportedOperationException("Not supported yet.");
  13.445 +        public synchronized int getNodesCount(boolean optimalResult) {
  13.446 +            if (!checkInit()) {
  13.447 +                return 0;
  13.448 +            }
  13.449 +            try {
  13.450 +                Children.PR.enterReadAccess();
  13.451 +                return entries.size();
  13.452 +            } finally {
  13.453 +                Children.PR.exitReadAccess();
  13.454 +            }
  13.455          }
  13.456  
  13.457          @Override
  13.458          public boolean isInitialized() {
  13.459 -            throw new UnsupportedOperationException("Not supported yet.");
  13.460 +            return inited;
  13.461          }
  13.462  
  13.463          @Override
  13.464          void refreshEntry(Entry entry) {
  13.465 -            throw new UnsupportedOperationException("Not supported yet.");
  13.466 +            if (!inited) {
  13.467 +                return;
  13.468 +            }
  13.469 +            EntryInfo info = entryToInfo.get(entry);
  13.470 +
  13.471 +            if (info == null) {
  13.472 +                // no such entry
  13.473 +                return;
  13.474 +            }
  13.475 +            
  13.476 +            Node oldNode = info.currentNode();
  13.477 +            Node newNode = info.refreshNode();
  13.478 +            
  13.479 +            if (newNode == NONEXISTING_NODE) {
  13.480 +                removeEmptyEntry(entry);
  13.481 +            }
  13.482 +
  13.483 +            if (newNode.equals(oldNode)) {
  13.484 +                // same node =>
  13.485 +                return;
  13.486 +            }
  13.487 +            
  13.488 +            if (oldNode != null) {
  13.489 +                oldNode.deassignFrom(children);
  13.490 +                info.useNode(oldNode);
  13.491 +                fireSubNodesChangeIdx(false, new int[]{info.getIndex()});
  13.492 +                children.destroyNodes(new Node[]{oldNode});
  13.493 +            }
  13.494 +
  13.495 +            info.useNode(newNode);
  13.496 +            fireSubNodesChangeIdx(true, new int[]{info.getIndex()});
  13.497          }
  13.498          
  13.499          /** Gets info for given entry, or create one if not registered yet. */
  13.500 @@ -1004,46 +1168,52 @@
  13.501                  }
  13.502                  return info;
  13.503              }
  13.504 -        }        
  13.505 +        }
  13.506 +
  13.507 +        private boolean mustNotifySetEnties = false;
  13.508 +
  13.509 +        void notifySetEntries() {
  13.510 +            mustNotifySetEnties = true;
  13.511 +        }
  13.512  
  13.513          @Override
  13.514 -        void setEntries(Collection<? extends Entry> entries) {
  13.515 -            entries = new ArrayList(entries);
  13.516 -            nodesCount = -1;
  13.517 +        void setEntries(Collection<? extends Entry> newEntries) {
  13.518 +            assert entries.size() == entryToInfo.size();
  13.519  
  13.520 -            assert this.entries.size() == entryToInfo.size();
  13.521 +            if (!mustNotifySetEnties && !inited) {
  13.522 +                entries = new ArrayList<Entry>(newEntries);
  13.523 +                for (int i = 0; i < entries.size(); i++) {
  13.524 +                    Entry entry = entries.get(i);
  13.525 +                    EntryInfo info = new EntryInfo(entry);
  13.526 +                    info.setIndex(i);
  13.527 +                    entryToInfo.put(entry, info);
  13.528 +                }
  13.529 +                return;
  13.530 +            }
  13.531  
  13.532 -            HashSet<Entry> retain = new HashSet<Entry>(entries);
  13.533 -            Iterator<? extends Entry> it = this.entries.iterator();
  13.534 +            HashSet<Entry> retain = new HashSet<Entry>(newEntries);
  13.535 +            Iterator<Entry> it = this.entries.iterator();
  13.536 +            int newIndex = 0;
  13.537              int index = 0;
  13.538              ArrayList<Integer> removedIdxs = new ArrayList<Integer>();
  13.539              ArrayList<Node> removedNodes = new ArrayList<Node>();
  13.540              while (it.hasNext()) {
  13.541                  EntryInfo info = entryToInfo.get(it.next());
  13.542 -                int size = info.size();
  13.543                  if (!retain.contains(info.entry)) {
  13.544 -                    for (int i = 0; i < size; i++) {
  13.545 -                        removedIdxs.add(new Integer(index + i));
  13.546 -                    }
  13.547 +                    removedIdxs.add(new Integer(index));
  13.548                      // unassign from parent
  13.549 -                    Collection<Node> nodes = info.currentNodes(null);
  13.550 -                    if (nodes != null) {
  13.551 -                        removedNodes = new ArrayList<Node>(nodes.size());
  13.552 -                        Iterator nodeIt = nodes.iterator();
  13.553 -                        while (nodeIt.hasNext()) {
  13.554 -                            Node n = (Node) nodeIt.next();
  13.555 -                            if (n != null) {
  13.556 -                                n.deassignFrom(children);
  13.557 -                                removedNodes.add(n);
  13.558 -                            }
  13.559 -                        }
  13.560 +                    Node node = info.currentNode();
  13.561 +                    if (node != null && node != NONEXISTING_NODE) {
  13.562 +                        node.deassignFrom(children);
  13.563 +                        removedNodes.add(node);
  13.564                      }
  13.565                      // remove the entry from collection
  13.566                      it.remove();
  13.567                      entryToInfo.remove(info.entry);
  13.568 +                } else {
  13.569 +                    info.setIndex(newIndex++);
  13.570                  }
  13.571 -                info.setIndex(index);
  13.572 -                index += size;
  13.573 +                index++;
  13.574              }
  13.575  
  13.576              if (!removedIdxs.isEmpty()) {
  13.577 @@ -1051,76 +1221,39 @@
  13.578                  for (int i = 0; i < idxs.length; i++) {
  13.579                      idxs[i] = ((Integer) removedIdxs.get(i)).intValue();
  13.580                  }
  13.581 -                childrenNodes.clear();
  13.582 -                //fireIndexesAddedOrRemoved(false, idxs);
  13.583 -                //children.destroyNodes(removedNodes.toArray(new Node[removedNodes.size()]));
  13.584 +                fireSubNodesChangeIdx(false, idxs);
  13.585 +                children.destroyNodes(removedNodes.toArray(new Node[removedNodes.size()]));
  13.586              }
  13.587  
  13.588              // change the order of entries, notifies
  13.589              // it and again brings children to up-to-date state, recomputes indexes
  13.590 -            Collection<EntryInfo> toAdd = updateOrder(entries);
  13.591 -
  13.592 +            Collection<Entry> toAdd = updateOrder(newEntries);
  13.593              if (!toAdd.isEmpty()) {
  13.594 -                // now we know that this.entries are subset of entries and
  13.595 -                // are also properly sorted. So we can just iterate over 
  13.596 -                // entries and whenever there is a different, just add once
  13.597 -                ArrayList addedIndixes = new ArrayList(toAdd.size());
  13.598 -                for (EntryInfo info : toAdd) {
  13.599 -                    final int size = info.size();
  13.600 -                    final int idx = info.getIndex();
  13.601 -                    Collection<Node> nodes = info.currentNodes(null);
  13.602 -                    Iterator nodeIt = nodes == null ? null : nodes.iterator();
  13.603 -                    for (int i = 0; i < size; i++) {
  13.604 -                        addedIndixes.add(new Integer(idx + i));
  13.605 -                        if (nodeIt != null) {
  13.606 -                            // assign to new parent
  13.607 -                            Node n = (Node) nodeIt.next();
  13.608 -                            if (n != null) {
  13.609 -                                n.assignTo(children, i);
  13.610 -                            }
  13.611 -                        }                     
  13.612 +                entries = new ArrayList<Entry>(newEntries);
  13.613 +                int[] idxs = new int[toAdd.size()];
  13.614 +                int addIdx = 0;
  13.615 +                for (int i = 0; i < entries.size(); i++) {
  13.616 +                    Entry entry = entries.get(i);
  13.617 +                    EntryInfo info = entryToInfo.get(entry);
  13.618 +                    if (info == null) {
  13.619 +                        info = new EntryInfo(entry);
  13.620 +                        entryToInfo.put(entry, info);
  13.621 +                        idxs[addIdx++] = i;
  13.622                      }
  13.623 +                    info.setIndex(i);
  13.624                  }
  13.625 -                if (!addedIndixes.isEmpty()) {
  13.626 -                    int[] idxs = new int[addedIndixes.size()];
  13.627 -                    for (int i = 0; i < idxs.length; i++) {
  13.628 -                        idxs[i] = ((Integer) addedIndixes.get(i)).intValue();
  13.629 -                    }
  13.630 -                    childrenNodes.clear();
  13.631 -                    //fireIndexesAddedOrRemoved(true, idxs);
  13.632 -                }
  13.633 +                fireSubNodesChangeIdx(true, idxs);
  13.634              }
  13.635          }
  13.636  
  13.637 -        @Override
  13.638 -        void notifySetEntries() {
  13.639 -            throw new UnsupportedOperationException("Not supported yet.");
  13.640 -        }
  13.641 -
  13.642          /** Updates the order of entries.
  13.643           * @param current current state of nodes
  13.644           * @param entries new set of entries
  13.645           * @return list of infos that should be added
  13.646           */
  13.647 -        private List<EntryInfo> updateOrder(Collection<? extends Entry> newEntries) {
  13.648 -            List<EntryInfo> toAdd = new LinkedList<EntryInfo>();
  13.649 -
  13.650 -            // that assignes entries their begining position in the array of nodes
  13.651 -            Map<EntryInfo, Integer> offsets = new HashMap<EntryInfo, Integer>();
  13.652 -            int previousPos = 0;
  13.653 -            for (Entry entry : entries) {
  13.654 -                EntryInfo info = entryToInfo.get(entry);
  13.655 -                offsets.put(info, previousPos);
  13.656 -                previousPos += info.size();
  13.657 -            }
  13.658 -
  13.659 -            // because map can contain some additional items,
  13.660 -            // that has not been garbage collected yet,
  13.661 -            // retain only those that are in current list of
  13.662 -            // entries
  13.663 -            entryToInfo.keySet().retainAll(new HashSet<Entry>(entries));
  13.664 -
  13.665 -            int[] perm = new int[previousPos];
  13.666 +        private List<Entry> updateOrder(Collection<? extends Entry> newEntries) {
  13.667 +            List<Entry> toAdd = new LinkedList<Entry>();
  13.668 +            int[] perm = new int[entries.size()];
  13.669              int currentPos = 0;
  13.670              int permSize = 0;
  13.671              List<Entry> reorderedEntries = null;
  13.672 @@ -1129,33 +1262,22 @@
  13.673                  EntryInfo info = entryToInfo.get(entry);
  13.674  
  13.675                  if (info == null) {
  13.676 -                    // this info has to be added
  13.677 -                    info = new EntryInfo(entry);
  13.678 -                    info.setIndex(currentPos);
  13.679 -                    entryToInfo.put(entry, info);
  13.680 -                    toAdd.add(info);
  13.681 +                    // this entry has to be added
  13.682 +                    toAdd.add(entry);
  13.683                  } else {
  13.684 -                    int len = info.size();
  13.685 -
  13.686                      if (reorderedEntries == null) {
  13.687                          reorderedEntries = new LinkedList<Entry>();
  13.688                      }
  13.689 -
  13.690                      reorderedEntries.add(entry);
  13.691 -                    info.setIndex(currentPos);
  13.692 -
  13.693 +                    int oldPos = info.getIndex();
  13.694                      // already there => test if it should not be reordered
  13.695 -                    Integer previousInt = offsets.get(info);
  13.696 -                    previousPos = previousInt;
  13.697 -
  13.698 -                    if (currentPos != previousPos) {
  13.699 -                        for (int i = 0; i < len; i++) {
  13.700 -                            perm[previousPos + i] = 1 + currentPos + i;
  13.701 -                        }
  13.702 -                        permSize += len;
  13.703 +                    if (currentPos != oldPos) {
  13.704 +                        info.setIndex(currentPos);
  13.705 +                        perm[oldPos] = 1 + currentPos;
  13.706 +                        permSize++;
  13.707                      }
  13.708 +                    currentPos++;
  13.709                  }
  13.710 -                currentPos += info.size();
  13.711              }
  13.712  
  13.713              if (permSize > 0) {
  13.714 @@ -1174,11 +1296,8 @@
  13.715  
  13.716                  // reorderedEntries are not null
  13.717                  this.entries = reorderedEntries;
  13.718 -                childrenNodes.clear();
  13.719  
  13.720 -                //System.err.println("Paremutaiton! " + getNode ());
  13.721                  Node p = children.parent;
  13.722 -
  13.723                  if (p != null) {
  13.724                      p.fireReorderChange(perm);
  13.725                  }
  13.726 @@ -1187,22 +1306,31 @@
  13.727          }
  13.728  
  13.729          @Override
  13.730 -        public Node[] testNodes() {
  13.731 -            throw new UnsupportedOperationException("Not supported yet.");
  13.732 +        Collection<Node> getEntryNodes(Entry entry) {
  13.733 +            EntryInfo info = entryToInfo.get(entry);
  13.734 +            if (info == null) {
  13.735 +                return Collections.emptyList();
  13.736 +            }
  13.737 +            return Arrays.asList(info.getNode());
  13.738 +        }
  13.739 +
  13.740 +        /** @param added added or removed
  13.741 +         *  @param indices list of integers with indexes that changed
  13.742 +         */
  13.743 +        protected void fireSubNodesChangeIdx(boolean added, int[] idxs) {
  13.744 +            if (children.parent != null) {
  13.745 +                children.parent.fireSubNodesChangeIdx(added, idxs);
  13.746 +            }
  13.747          }
  13.748          
  13.749 -      
  13.750 +        /** holds node for entry; 1:1 mapping */
  13.751          final class EntryInfo {
  13.752 -            
  13.753              /** corresponding entry */
  13.754              final Entry entry;
  13.755  
  13.756 -            /** my length, -1 means uninitialized */
  13.757 -            private int length = -1;
  13.758 -
  13.759 -            /** cached nodes for this entry */
  13.760 -            private List<WeakReference<Node>> nodesCache;
  13.761 -
  13.762 +            /** cached node for this entry */
  13.763 +            private WeakReference<Node> refNode;
  13.764 +            
  13.765              /** my index (including sizes) in list of entries */
  13.766              private int index = -1;
  13.767              
  13.768 @@ -1211,56 +1339,50 @@
  13.769              }
  13.770  
  13.771              /** Returns size of this entry */
  13.772 -            public final int size() {
  13.773 -                if (length < 0) {
  13.774 -                    length = getNodes().size();
  13.775 -                }
  13.776 -                return length;
  13.777 -            }
  13.778 +            /*public final int size() {
  13.779 +                return 1;
  13.780 +            }*/
  13.781  
  13.782              /** Gets or computes the nodes. It holds them using weak reference
  13.783               * so they can get garbage collected.
  13.784               */
  13.785 -            public final synchronized List<Node> getNodes() {
  13.786 -                boolean[] containsNulls = new boolean[1];
  13.787 -                List<Node> curNodes = currentNodes(containsNulls);
  13.788 -                if (curNodes != null && containsNulls[0] == false) {
  13.789 -                    return curNodes;
  13.790 +            public final synchronized Node getNode() {
  13.791 +                Node n = null;
  13.792 +                if (refNode != null) {
  13.793 +                    n = refNode.get();
  13.794                  }
  13.795 -                Collection<Node> nodes = entry.nodes();
  13.796 -                useNodes(nodes);
  13.797 -                return new ArrayList<Node>(nodes);
  13.798 +                if (n == null) {
  13.799 +                    n = refreshNode();
  13.800 +                }
  13.801 +                return n;
  13.802              }
  13.803  
  13.804 -            /** extract current nodes */
  13.805 -            synchronized List<Node> currentNodes(boolean[] containsNulls) {
  13.806 -                if (nodesCache == null) {
  13.807 -                    return null;
  13.808 +            /** extract current node (if was already created) */
  13.809 +            synchronized Node currentNode() {
  13.810 +                return refNode == null ? null : refNode.get();
  13.811 +            }
  13.812 +            
  13.813 +            synchronized Node refreshNode() {
  13.814 +                Collection<Node> nodes = entry.nodes();
  13.815 +                if (nodes.size() != 1) {
  13.816 +                    LAZY_LOG.fine("Number of nodes for Entry: " + entry + " is " + nodes.size() + " instead of 1");
  13.817 +                    if (nodes.size() == 0) {
  13.818 +                        return useNode(NONEXISTING_NODE);
  13.819 +                    }
  13.820                  }
  13.821 -                ArrayList<Node> arr = new ArrayList<Node>(nodesCache.size());
  13.822 -                for (int i = 0; i < nodesCache.size(); i++) {
  13.823 -                    Node n = nodesCache.get(i).get();
  13.824 -                    if (n == null && containsNulls != null) {
  13.825 -                        containsNulls[0] = true;
  13.826 -                    }
  13.827 -                    arr.add(n);
  13.828 -                }
  13.829 -                return arr;
  13.830 +                return useNode(nodes.iterator().next());
  13.831              }
  13.832  
  13.833              /** Assignes new set of nodes to this entry. */
  13.834 -            public final synchronized void useNodes(Collection<Node> nodes) {
  13.835 -                nodesCache = new ArrayList<WeakReference<Node>>(nodes.size());
  13.836 -                for (Node n : nodes) {
  13.837 -                    nodesCache.add(new WeakReference<Node>(n));
  13.838 +            public final synchronized Node useNode(Node node) {
  13.839 +                refNode = new WeakReference<Node>(node);
  13.840 +                
  13.841 +                // assign node to the new children
  13.842 +                if (node != NONEXISTING_NODE) {
  13.843 +                    node.assignTo(children, -1);
  13.844 +                    node.fireParentNodeChange(null, children.parent);
  13.845                  }
  13.846 -                length = nodes.size();
  13.847 -                /*
  13.848 -                // assign all there nodes the new children
  13.849 -                for (Node n : list) {
  13.850 -                n.assignTo(Children.this, -1);
  13.851 -                n.fireParentNodeChange(null, parent);
  13.852 -                }*/
  13.853 +                return node;
  13.854              }
  13.855  
  13.856              /** Sets the index of the entry. */
  13.857 @@ -1276,13 +1398,58 @@
  13.858  
  13.859              @Override
  13.860              public String toString() {
  13.861 -                String clazz = super.toString();
  13.862 -                int in = clazz.lastIndexOf('$');
  13.863 -                if (in >= 0) {
  13.864 -                    clazz = clazz.substring(in + 1);
  13.865 +                return "EntryInfo for entry: " + entry + ", node: " + (refNode == null ? null : refNode.get()); // NOI18N
  13.866 +            }
  13.867 +        }
  13.868 +
  13.869 +        /** Dummy node for nonexisting Node */
  13.870 +        private static final class NonexistingNode extends AbstractNode {
  13.871 +
  13.872 +            public NonexistingNode() {
  13.873 +                super(Children.LEAF);
  13.874 +                setName("Nonexisting node"); // NOI18N
  13.875 +            }
  13.876 +        }
  13.877 +        
  13.878 +        private void removeEmptyEntry(Entry entry) {
  13.879 +            Children.MUTEX.postWriteRequest(new RemoveEmptyEntries(entry));
  13.880 +        }
  13.881 +
  13.882 +        private void removeEmptyEntries(HashSet<Entry> entries) {
  13.883 +            Children.MUTEX.postWriteRequest(new RemoveEmptyEntries(entries));
  13.884 +        }
  13.885 +        
  13.886 +        private final class RemoveEmptyEntries implements Runnable {
  13.887 +            private HashSet<Entry> emptyEntries;
  13.888 +
  13.889 +            public RemoveEmptyEntries(Entry entry) {
  13.890 +                emptyEntries = new HashSet<Entry>(1);
  13.891 +                emptyEntries.add(entry);
  13.892 +            }
  13.893 +
  13.894 +            public RemoveEmptyEntries(HashSet<Entry> entries) {
  13.895 +                this.emptyEntries = entries;
  13.896 +            }
  13.897 +
  13.898 +            public void run() {
  13.899 +                ArrayList<Entry> updatedEntries = new ArrayList<Entry>(entries.size() - emptyEntries.size());
  13.900 +                int index = 0;
  13.901 +                int removedIdx = 0;
  13.902 +                int[] idxs = new int[emptyEntries.size()];
  13.903 +                for (Entry entry : entries) {
  13.904 +                    if (emptyEntries.contains(entry)) {
  13.905 +                        emptyEntries.remove(entry);
  13.906 +                        EntryInfo info = entryToInfo.remove(entry);
  13.907 +                        idxs[removedIdx++] = info.getIndex();
  13.908 +                    } else {
  13.909 +                        updatedEntries.add(entry);
  13.910 +                        EntryInfo info = entryToInfo.get(entry);
  13.911 +                        info.setIndex(index++);
  13.912 +                    }
  13.913                  }
  13.914 -                return clazz + "[index: " + index + ",length:" + length + "]"; // NOI18N
  13.915 +                entries = updatedEntries;
  13.916 +                fireSubNodesChangeIdx(false, idxs);                
  13.917              }
  13.918 -        }        
  13.919 +        }
  13.920      }
  13.921  }
    14.1 --- a/openide.nodes/src/org/openide/nodes/FilterNode.java
    14.2 +++ b/openide.nodes/src/org/openide/nodes/FilterNode.java
    14.3 @@ -62,6 +62,7 @@
    14.4  import java.lang.ref.WeakReference;
    14.5  
    14.6  import java.util.*;
    14.7 +import java.util.ArrayList;
    14.8  import java.util.logging.Level;
    14.9  import java.util.logging.Logger;
   14.10  import org.openide.nodes.Node.PropertySet;
   14.11 @@ -216,6 +217,7 @@
   14.12       * @param lookup
   14.13       * @return lookup or null
   14.14       */
   14.15 +    @Override
   14.16      final Lookup replaceProvidedLookup(Lookup lookup) {
   14.17          synchronized (replaceProvidedLookupCache) {
   14.18              Boolean b = replaceProvidedLookupCache.get(getClass());
   14.19 @@ -257,6 +259,7 @@
   14.20          delegateMask = DELEGATE_ALL;
   14.21      }
   14.22  
   14.23 +    @Override
   14.24      void notifyPropertyChangeListenerAdded(PropertyChangeListener l) {
   14.25          if (!pchlAttached) {
   14.26              original.addPropertyChangeListener(getPropertyChangeListener());
   14.27 @@ -264,6 +267,7 @@
   14.28          }
   14.29      }
   14.30  
   14.31 +    @Override
   14.32      void notifyPropertyChangeListenerRemoved(PropertyChangeListener l) {
   14.33          if (getPropertyChangeListenersCount() == 0) {
   14.34              original.removePropertyChangeListener(getPropertyChangeListener());
   14.35 @@ -905,6 +909,7 @@
   14.36      * the hash reverts to the identity hash code.
   14.37      * @return the delegated hash code
   14.38      */
   14.39 +    @Override
   14.40      public int hashCode() {
   14.41          try {
   14.42              assert hashCodeLogging(true) : ""; // NOI18N
   14.43 @@ -1000,6 +1005,7 @@
   14.44      /** Notified from Node that a listener has been added.
   14.45       * Thus we force initialization of listeners.
   14.46       */
   14.47 +    @Override
   14.48      final void listenerAdded() {
   14.49          getNodeListener();
   14.50      }
   14.51 @@ -1023,6 +1029,7 @@
   14.52       * typically used to when there is a setChildren() on the original node
   14.53       * setChildren will fire the appropriate events
   14.54       */
   14.55 +    @Override
   14.56      final void updateChildren() {
   14.57          if (isDefault()) {
   14.58              org.openide.nodes.Children newChildren = null;
   14.59 @@ -1095,20 +1102,20 @@
   14.60      * prevent the node from being finalized.
   14.61      */
   14.62      protected static class PropertyChangeAdapter extends Object implements PropertyChangeListener {
   14.63 -        private Reference<FilterNode> fn;
   14.64 +        private Reference<FilterNode> fnRef;
   14.65  
   14.66          /** Create a new adapter.
   14.67          * @param fn the proxy
   14.68          */
   14.69          public PropertyChangeAdapter(FilterNode fn) {
   14.70 -            this.fn = new WeakReference<FilterNode>(fn);
   14.71 +            this.fnRef = new WeakReference<FilterNode>(fn);
   14.72          }
   14.73  
   14.74          /* Find the node we are attached to. If it is not null call property
   14.75          * change method with two arguments.
   14.76          */
   14.77          public final void propertyChange(PropertyChangeEvent ev) {
   14.78 -            FilterNode fn = this.fn.get();
   14.79 +            FilterNode fn = this.fnRef.get();
   14.80  
   14.81              if (fn == null) {
   14.82                  return;
   14.83 @@ -1132,20 +1139,20 @@
   14.84      * @see FilterNode.PropertyChangeAdapter
   14.85      */
   14.86      protected static class NodeAdapter extends Object implements NodeListener {
   14.87 -        private Reference<FilterNode> fn;
   14.88 +        private Reference<FilterNode> fnRef;
   14.89  
   14.90          /** Create an adapter.
   14.91          * @param fn the proxy
   14.92          */
   14.93          public NodeAdapter(FilterNode fn) {
   14.94 -            this.fn = new WeakReference<FilterNode>(fn);
   14.95 +            this.fnRef = new WeakReference<FilterNode>(fn);
   14.96          }
   14.97  
   14.98          /* Tests if the reference to the node provided in costructor is
   14.99          * still valid (it has not been finalized) and if so, calls propertyChange (Node, ev).
  14.100          */
  14.101          public final void propertyChange(PropertyChangeEvent ev) {
  14.102 -            FilterNode fn = this.fn.get();
  14.103 +            FilterNode fn = this.fnRef.get();
  14.104  
  14.105              if (fn == null) {
  14.106                  return;
  14.107 @@ -1244,7 +1251,7 @@
  14.108          * @param ev event describing the node
  14.109          */
  14.110          public final void nodeDestroyed(NodeEvent ev) {
  14.111 -            FilterNode fn = this.fn.get();
  14.112 +            FilterNode fn = this.fnRef.get();
  14.113  
  14.114              if (fn == null) {
  14.115                  return;
  14.116 @@ -1279,10 +1286,19 @@
  14.117          /** node listener on original */
  14.118          private ChildrenAdapter nodeL;
  14.119  
  14.120 +        /** default or lazy implementation */
  14.121 +        private ChildrenSupport support;
  14.122 +
  14.123          /** Create children.
  14.124           * @param or original node to take children from */
  14.125          public Children(Node or) {
  14.126 +            this(or, or.getChildren().entrySupport() instanceof EntrySupport.Lazy);
  14.127 +        }
  14.128 +        
  14.129 +        private Children(Node or, boolean lazy) {
  14.130 +            super(lazy);
  14.131              original = or;
  14.132 +            support = lazy ? new LazySupport() : new DefaultSupport();            
  14.133          }
  14.134  
  14.135          /** Sets the original children for this children. 
  14.136 @@ -1344,8 +1360,7 @@
  14.137              // add itself to reflect to changes children of original node
  14.138              nodeL = new ChildrenAdapter(this);
  14.139              original.addNodeListener(nodeL);
  14.140 -
  14.141 -            updateKeys();
  14.142 +            support.update();
  14.143          }
  14.144  
  14.145          /** Clears current keys, because all mirrored nodes disappeared.
  14.146 @@ -1378,10 +1393,9 @@
  14.147          */
  14.148          @Override
  14.149          public Node findChild(String name) {
  14.150 -            original.getChildren().findChild(name);
  14.151 -
  14.152 -            return super.findChild(name);
  14.153 +            return support.findChild(name);
  14.154          }
  14.155 +               
  14.156  
  14.157          /** Create nodes representing copies of the original node's children.
  14.158          * The default implementation returns exactly one representative for each original node,
  14.159 @@ -1422,7 +1436,7 @@
  14.160          * @param ev info about the change
  14.161          */
  14.162          protected void filterChildrenAdded(NodeMemberEvent ev) {
  14.163 -            updateKeys();
  14.164 +            support.filterChildrenAdded(ev);
  14.165          }
  14.166  
  14.167          /** Called when the filter node removes a child.
  14.168 @@ -1430,7 +1444,7 @@
  14.169          * @param ev info about the change
  14.170          */
  14.171          protected void filterChildrenRemoved(NodeMemberEvent ev) {
  14.172 -            updateKeys();
  14.173 +            support.filterChildrenRemoved(ev);
  14.174          }
  14.175  
  14.176          /** Called when the filter node reorders its children.
  14.177 @@ -1438,25 +1452,7 @@
  14.178          * @param ev info about the change
  14.179          */
  14.180          protected void filterChildrenReordered(NodeReorderEvent ev) {
  14.181 -            updateKeys();
  14.182 -        }
  14.183 -
  14.184 -        /** variable to notify that there is a cyclic update.
  14.185 -        * Used only in updateKeys method
  14.186 -        */
  14.187 -
  14.188 -        //    private transient boolean cyclic;
  14.189 -
  14.190 -        /** Update keys from original nodes */
  14.191 -        private void updateKeys() {
  14.192 -            ChildrenAdapter runnable = nodeL;
  14.193 -
  14.194 -            if (runnable != null) {
  14.195 -                runnable.run();
  14.196 -                if (!original.getChildren().isInitialized()) {
  14.197 -                    original.getChildren().entrySupport().notifySetEntries();
  14.198 -                }
  14.199 -            }
  14.200 +            support.filterChildrenReordered(ev);
  14.201          }
  14.202  
  14.203          /**
  14.204 @@ -1469,11 +1465,220 @@
  14.205           */
  14.206          @Override
  14.207          public Node[] getNodes(boolean optimalResult) {
  14.208 -            if (optimalResult) {
  14.209 -                setKeys(original.getChildren().getNodes(true));
  14.210 +            return support.getNodes(optimalResult);
  14.211 +        }
  14.212 +
  14.213 +        @Override
  14.214 +        public int getNodesCount(boolean optimalResult) {
  14.215 +            return support.getNodesCount(optimalResult);
  14.216 +        }
  14.217 +        
  14.218 +
  14.219 +        abstract private class ChildrenSupport {
  14.220 +            abstract protected Node[] getNodes(boolean optimalResult);
  14.221 +
  14.222 +            abstract protected int getNodesCount(boolean optimalResult);
  14.223 +            
  14.224 +            abstract protected Node findChild(String name);
  14.225 +
  14.226 +            abstract protected void filterChildrenAdded(NodeMemberEvent ev);
  14.227 +
  14.228 +            abstract protected void filterChildrenRemoved(NodeMemberEvent ev);
  14.229 +
  14.230 +            abstract protected void filterChildrenReordered(NodeReorderEvent ev);
  14.231 +
  14.232 +            abstract protected void update();
  14.233 +        }
  14.234 +
  14.235 +        private class DefaultSupport extends ChildrenSupport {
  14.236 +
  14.237 +            @Override
  14.238 +            protected Node[] getNodes(boolean optimalResult) {
  14.239 +                Node[] hold;
  14.240 +                if (optimalResult) {
  14.241 +                    hold = original.getChildren().getNodes(true);
  14.242 +                }
  14.243 +                hold = Children.this.getNodes();
  14.244 +                return hold;
  14.245              }
  14.246  
  14.247 -            return getNodes();
  14.248 +            @Override
  14.249 +            protected int getNodesCount(boolean optimalResult) {
  14.250 +                Node[] hold;
  14.251 +                if (optimalResult) {
  14.252 +                    hold = original.getChildren().getNodes(optimalResult);
  14.253 +                }
  14.254 +                return Children.this.getNodesCount();
  14.255 +            }
  14.256 +
  14.257 +            @Override
  14.258 +            protected Node findChild(String name) {
  14.259 +                original.getChildren().findChild(name);
  14.260 +                return Children.super.findChild(name);
  14.261 +            }
  14.262 +
  14.263 +            @Override
  14.264 +            protected void filterChildrenAdded(NodeMemberEvent ev) {
  14.265 +                updateKeys();
  14.266 +            }
  14.267 +
  14.268 +            @Override
  14.269 +            protected void filterChildrenRemoved(NodeMemberEvent ev) {
  14.270 +                updateKeys();
  14.271 +            }
  14.272 +
  14.273 +            @Override
  14.274 +            protected void filterChildrenReordered(NodeReorderEvent ev) {
  14.275 +                updateKeys();
  14.276 +            }
  14.277 +
  14.278 +            @Override
  14.279 +            protected void update() {
  14.280 +                updateKeys();
  14.281 +            }
  14.282 +
  14.283 +            private void updateKeys() {
  14.284 +                ChildrenAdapter cha = nodeL;
  14.285 +                if (cha != null) {
  14.286 +                    Node[] arr = original.getChildren().getNodes();
  14.287 +                    setKeys(arr);
  14.288 +                    if (!original.getChildren().isInitialized()) {
  14.289 +                        original.getChildren().entrySupport().notifySetEntries();
  14.290 +                    }
  14.291 +                }
  14.292 +            }
  14.293 +        }
  14.294 +
  14.295 +        private class LazySupport extends ChildrenSupport {
  14.296 +
  14.297 +            @Override
  14.298 +            protected Node[] getNodes(boolean optimalResult) {
  14.299 +                return Children.this.getNodes();
  14.300 +            }
  14.301 +
  14.302 +            @Override
  14.303 +            protected int getNodesCount(boolean optimalResult) {
  14.304 +                return Children.this.getNodesCount();
  14.305 +            }
  14.306 +
  14.307 +            @Override
  14.308 +            protected Node findChild(String name) {
  14.309 +                original.getChildren().findChild(name);
  14.310 +                return Children.super.findChild(name);
  14.311 +            }
  14.312 +
  14.313 +            @Override
  14.314 +            protected void filterChildrenAdded(NodeMemberEvent ev) {
  14.315 +                if (ev.sourceEntry == null) {
  14.316 +                    updateEntries();
  14.317 +                } else {
  14.318 +                    refreshEntry(ev.sourceEntry);
  14.319 +                }
  14.320 +            }
  14.321 +
  14.322 +            @Override
  14.323 +            protected void filterChildrenRemoved(NodeMemberEvent ev) {
  14.324 +                if (ev.sourceEntry == null) {
  14.325 +                    updateEntries();
  14.326 +                } else {
  14.327 +                    refreshEntry(ev.sourceEntry);
  14.328 +                }
  14.329 +            }
  14.330 +
  14.331 +            @Override
  14.332 +            protected void filterChildrenReordered(NodeReorderEvent ev) {
  14.333 +                updateEntries();
  14.334 +            }
  14.335 +
  14.336 +            @Override
  14.337 +            protected void update() {
  14.338 +                updateEntries();
  14.339 +            }
  14.340 +
  14.341 +            private void updateEntries() {
  14.342 +                ChildrenAdapter cha = nodeL;
  14.343 +                if (cha != null) {
  14.344 +                    int count = original.getChildren().getNodesCount();
  14.345 +
  14.346 +                    List<Entry> entries = original.getChildren().entrySupport().getEntries();
  14.347 +
  14.348 +                    /*ArrayList<Entry> filtEntries = new ArrayList<Entry>(entries.size() + 1);
  14.349 +                    boolean b = before;
  14.350 +                    if (b) {
  14.351 +                        filtEntries.add(getNodesEntry());
  14.352 +                    }
  14.353 +                    for (Entry e : entries) {
  14.354 +                        filtEntries.add(new FilterNodeEntry(e));
  14.355 +                    }
  14.356 +                    if (!b) {
  14.357 +                        filtEntries.add(getNodesEntry());
  14.358 +                    }*/
  14.359 +                    
  14.360 +                    ArrayList<Entry> filtEntries = new ArrayList<Entry>(entries.size());
  14.361 +                    for (Entry e : entries) {
  14.362 +                        filtEntries.add(new FilterNodeEntry(e));
  14.363 +                    }
  14.364 +                    
  14.365 +                    entrySupport().setEntries(filtEntries);
  14.366 +                    
  14.367 +                    if (!original.getChildren().isInitialized()) {
  14.368 +                        original.getChildren().entrySupport().notifySetEntries();
  14.369 +                        return;
  14.370 +                    }                    
  14.371 +                }
  14.372 +            }
  14.373 +
  14.374 +            private void refreshEntry(Entry entry) {
  14.375 +                entrySupport().refreshEntry(new FilterNodeEntry(entry));
  14.376 +            }
  14.377 +
  14.378 +            /** Substitution for Node to use it as key instead of Node */
  14.379 +            private final class FilterNodeEntry extends Children.Keys<Object>.KE {
  14.380 +
  14.381 +                Entry origEntry;
  14.382 +
  14.383 +                public FilterNodeEntry(Entry origEntry) {
  14.384 +                    this.origEntry = origEntry;
  14.385 +                }
  14.386 +
  14.387 +                @Override
  14.388 +                public Object getKey() {
  14.389 +                    return this;
  14.390 +                }
  14.391 +
  14.392 +                @Override
  14.393 +                public int getCnt() {
  14.394 +                    return 0;
  14.395 +                }
  14.396 +
  14.397 +                @Override
  14.398 +                public Collection<Node> nodes() {
  14.399 +                    Collection<Node> cn = original.getChildren().entrySupport().getEntryNodes(origEntry);
  14.400 +                    ArrayList<Node> list = new ArrayList<Node>(cn.size());
  14.401 +                    for (Node n : cn) {
  14.402 +                        Node[] created = createNodes(n);
  14.403 +                        if (created != null) {
  14.404 +                            list.addAll(Arrays.asList(created));
  14.405 +                        }
  14.406 +                    }
  14.407 +                    return list;
  14.408 +                }
  14.409 +
  14.410 +                @Override
  14.411 +                public int hashCode() {
  14.412 +                    return origEntry.hashCode();
  14.413 +                }
  14.414 +
  14.415 +                @Override
  14.416 +                public boolean equals(Object o) {
  14.417 +                    return o instanceof FilterNodeEntry ? origEntry.equals(((FilterNodeEntry) o).origEntry) : false;
  14.418 +                }
  14.419 +
  14.420 +                @Override
  14.421 +                public String toString() {
  14.422 +                    return "FilterNodeEntry[" + origEntry + "]@" + Integer.toString(hashCode(), 16);
  14.423 +                }
  14.424 +            }
  14.425          }
  14.426      }
  14.427  
  14.428 @@ -1482,28 +1687,17 @@
  14.429      * Used as the default listener in {@link FilterNode.Children},
  14.430      * and is intended for refinement by its subclasses.
  14.431      */
  14.432 -    private static class ChildrenAdapter extends Object implements NodeListener, Runnable {
  14.433 +    private static class ChildrenAdapter extends Object implements NodeListener {
  14.434          /** children object to notify about addition of children.
  14.435          * Can be null. Set from Children's initNodes method.
  14.436          */
  14.437 -        private Reference<Children> children;
  14.438 +        private Reference<Children> childrenRef;
  14.439  
  14.440          /** Create a new adapter.
  14.441          * @param ch the children list
  14.442          */
  14.443          public ChildrenAdapter(Children ch) {
  14.444 -            this.children = new WeakReference<Children>(ch);
  14.445 -        }
  14.446 -
  14.447 -        /** Called to update the content of children.
  14.448 -         */
  14.449 -        public void run() {
  14.450 -            Children ch = children.get();
  14.451 -
  14.452 -            if (ch != null) {
  14.453 -                Node[] arr = ch.original.getChildren().getNodes();
  14.454 -                ch.setKeys(arr);
  14.455 -            }
  14.456 +            this.childrenRef = new WeakReference<Children>(ch);
  14.457          }
  14.458  
  14.459          /** Does nothing.
  14.460 @@ -1516,7 +1710,7 @@
  14.461          * @param ev event describing the action
  14.462          */
  14.463          public void childrenAdded(NodeMemberEvent ev) {
  14.464 -            Children children = this.children.get();
  14.465 +            Children children = this.childrenRef.get();
  14.466  
  14.467              if (children == null) {
  14.468                  return;
  14.469 @@ -1529,7 +1723,7 @@
  14.470          * @param ev event describing the action
  14.471          */
  14.472          public void childrenRemoved(NodeMemberEvent ev) {
  14.473 -            Children children = this.children.get();
  14.474 +            Children children = this.childrenRef.get();
  14.475  
  14.476              if (children == null) {
  14.477                  return;
  14.478 @@ -1542,7 +1736,7 @@
  14.479          * @param ev event describing the action
  14.480          */
  14.481          public void childrenReordered(NodeReorderEvent ev) {
  14.482 -            Children children = this.children.get();
  14.483 +            Children children = this.childrenRef.get();
  14.484  
  14.485              if (children == null) {
  14.486                  return;
    15.1 --- a/openide.nodes/src/org/openide/nodes/Node.java
    15.2 +++ b/openide.nodes/src/org/openide/nodes/Node.java
    15.3 @@ -455,10 +455,10 @@
    15.4              hierarchy.attachTo(Node.this);
    15.5  
    15.6              if ((oldNodes != null) && !wasLeaf) {
    15.7 -                fireSubNodesChange(false, oldNodes, oldNodes);
    15.8 +                fireSubNodesChange(false, oldNodes, oldNodes, null);
    15.9                  Node[] arr = hierarchy.getNodes();
   15.10                  if (arr.length > 0) {
   15.11 -                    fireSubNodesChange(true, arr, null);
   15.12 +                    fireSubNodesChange(true, arr, null, null);
   15.13                  }
   15.14              }
   15.15  
   15.16 @@ -1022,7 +1022,7 @@
   15.17      * @param from the array of nodes to take indices from.
   15.18      *   Can be null if one should find indices from current set of nodes
   15.19      */
   15.20 -    final void fireSubNodesChange(boolean addAction, Node[] delta, Node[] from) {
   15.21 +    final void fireSubNodesChange(boolean addAction, Node[] delta, Node[] from, org.openide.nodes.Children.Entry sourceEntry) {
   15.22          NodeMemberEvent ev = null;
   15.23  
   15.24          Object[] listeners = this.listeners.getListenerList();
   15.25 @@ -1034,6 +1034,7 @@
   15.26                  // Lazily create the event:
   15.27                  if (ev == null) {
   15.28                      ev = new NodeMemberEvent(this, addAction, delta, from);
   15.29 +                    ev.sourceEntry = sourceEntry;
   15.30                  }
   15.31  
   15.32                  if (addAction) {
   15.33 @@ -1044,6 +1045,31 @@
   15.34              }
   15.35          }
   15.36      }
   15.37 +    
   15.38 +    /** Fires that some indexes has been removed.
   15.39 +     *
   15.40 +     * @param indices removed indicies, 
   15.41 +     */
   15.42 +    final void fireSubNodesChangeIdx(boolean added, int[] idxs) {
   15.43 +        NodeMemberEvent ev = null;
   15.44 +
   15.45 +        Object[] listeners = this.listeners.getListenerList();
   15.46 +        // Process the listeners last to first, notifying
   15.47 +        // those that are interested in this event
   15.48 +        for (int i = listeners.length - 2; i >= 0; i -= 2) {
   15.49 +            if (listeners[i] == NodeListener.class) {
   15.50 +                // Lazily create the event:
   15.51 +                if (ev == null) {
   15.52 +                    ev = new NodeMemberEvent(this, added, idxs);
   15.53 +                }
   15.54 +                if (added) {
   15.55 +                    ((NodeListener) listeners[i + 1]).childrenAdded(ev);
   15.56 +                } else {
   15.57 +                    ((NodeListener) listeners[i + 1]).childrenRemoved(ev);
   15.58 +                }
   15.59 +            }
   15.60 +        }
   15.61 +    }     
   15.62  
   15.63      /** Fires info about reordering of some children.
   15.64      *
    16.1 --- a/openide.nodes/src/org/openide/nodes/NodeMemberEvent.java
    16.2 +++ b/openide.nodes/src/org/openide/nodes/NodeMemberEvent.java
    16.3 @@ -64,6 +64,8 @@
    16.4  
    16.5      /** list of nodes indexes, can be null if it should be computed lazily */
    16.6      private int[] indices;
    16.7 +    
    16.8 +    org.openide.nodes.Children.Entry sourceEntry;
    16.9  
   16.10      /** Package private constructor to allow construction only
   16.11      * @param n node that should fire change
   16.12 @@ -85,11 +87,30 @@
   16.13      public final boolean isAddEvent() {
   16.14          return add;
   16.15      }
   16.16 +    
   16.17 +    /** Fires when non-constructed nodes has been removed.
   16.18 +     * @param add is add or remove
   16.19 +     * @param indices the indicies that changed
   16.20 +     */
   16.21 +    NodeMemberEvent(Node n, boolean add, int[] indices) {
   16.22 +        super(n);
   16.23 +        this.add = add;
   16.24 +        this.indices = indices;
   16.25 +    }    
   16.26  
   16.27      /** Get a list of children that changed.
   16.28      * @return array of nodes that changed
   16.29      */
   16.30      public final Node[] getDelta() {
   16.31 +        if (delta == null) {
   16.32 +            assert indices != null : "Well, indices cannot be null now"; // NOI18N
   16.33 +
   16.34 +            Node[] arr = new Node[indices.length];
   16.35 +            for (int i = 0; i < arr.length; i++) {
   16.36 +                arr[i] = getNode().getChildren().getNodeAt(indices[i]);
   16.37 +            }
   16.38 +            delta = arr;
   16.39 +        }
   16.40          return delta;
   16.41      }
   16.42