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