1 19 20 package org.netbeans.modules.java.ui; 21 22 import java.util.BitSet ; 23 import javax.swing.*; 24 import javax.swing.event.*; 25 26 32 public final class LazyListModel extends Object 33 implements ListModel, Runnable , javax.swing.event.ListDataListener { 34 35 private static int NOT_TESTED = Short.MIN_VALUE - 1; 36 private static int EMPTY_VALUE = Short.MIN_VALUE - 2; 37 38 private static final boolean skipExpensiveAsserts = Boolean.getBoolean ("org.openide.explorer.view.LazyListModel.skipExpensiveAsserts"); 40 41 private boolean log; 42 private ListModel listModel; 43 private Filter filter; 44 45 private Object defaultValue; 46 47 private javax.swing.event.EventListenerList list = new javax.swing.event.EventListenerList (); 48 49 50 private int originalSize; 51 52 private int size; 53 57 private int[] external; 58 59 private BitSet checked; 60 61 private int refused; 62 63 private BitSet tested; 64 65 private boolean markDirty; 66 67 private LazyListModel (ListModel m, Filter f, double expectedRadio, Object defaultValue) { 68 this.listModel = m; 69 this.filter = f; 70 this.defaultValue = defaultValue; 71 72 m.addListDataListener (this); 74 } 75 76 final Filter getFilter () { 77 return filter; 78 } 79 80 final boolean isComputed (int index) { 81 return tested != null && tested.get (index); 82 } 83 84 86 private void markDirty () { 87 this.markDirty = true; 88 getFilter ().scheduleUpdate (this); 89 } 90 91 93 public void run () { 94 if (!markDirty) { 95 return; 96 } 97 98 markDirty = false; 99 if (log) { 100 System.err.println("updateYourAssumeptions ();"); } 102 updateYourAssumeptions (); 103 } 104 105 112 private void notifyRemoval (int from, int to) { 113 ListDataEvent ev = new ListDataEvent ( 114 this, ListDataEvent.INTERVAL_REMOVED, from, to - 1 115 ); 116 removeInterval (external, from, to); 117 int cnt = to - from; 118 size -= cnt; 119 120 regenerateCheckedBitSet (); 121 fireChange (ev); 122 } 123 124 private void regenerateCheckedBitSet () { 125 checked = new BitSet (size); 126 for (int i = 0; i < size; i++) { 127 if (external[i] >= 0) { 128 checked.set (i); 129 } 130 } 131 } 132 133 private int getExternal (int index) { 134 if (index == size) { 135 return originalSize; 136 } 137 if (index < 0) { 138 return -1; 139 } 140 return external[index]; 141 } 142 143 146 final void updateYourAssumeptions () { 147 if (external == null) { 148 return; 149 } 150 151 int sizeBefore = size; 152 int notifiedRemovals = 0; 153 154 int i = 0; 155 LOOP: while (i < size) { 156 while (getExternal (i) >= 0 && i < size) { 157 i++; 158 } 159 if (i == size) { 160 break; 161 } 162 163 if (getExternal (i) == NOT_TESTED) { 164 int minusOneIndex = i - 1; 165 while (i < size && getExternal (i) == NOT_TESTED) { 166 i++; 167 } 168 169 int count = i - minusOneIndex - 1; 170 int from = getExternal (minusOneIndex) + 1; 171 int to = getExternal (i); 172 173 assert from >= 0 : "Value at " + minusOneIndex + "(" + from + ") must be greater than minus one"; assert to >= 0 : "Value at " + i + "must be greater than minus one but was: " + to; assert to >= from : "Must be true: " + to + " >= " + from; 177 int howMuch = count - (to - from); 178 if (howMuch > 0) { 179 notifyRemoval (i - howMuch, i); 181 i -= howMuch; 182 } 183 } else { 184 int minusTwoIndex = i; 185 while (i < size && getExternal (i) == EMPTY_VALUE) { 186 i++; 187 } 188 notifyRemoval (minusTwoIndex, i); 189 i = minusTwoIndex; 190 } 191 } 192 193 assert externalContraints () : "Constraints failed"; } 195 196 private boolean externalContraints () { 197 assert external != null : "Not null"; assert external.length >= size : "Length " + external.length + " >= " + size; if (!skipExpensiveAsserts) { 200 for (int i = 1; i < size; i++) { 201 assert external[i - 1] != NOT_TESTED || external[i] != EMPTY_VALUE : "There cannot be empty value after not tested value"; assert external[i - 1] != EMPTY_VALUE || external[i] != NOT_TESTED : "Not tested cannot immediatelly follow empty value"; assert external[i] < 0 || external[i] > external[i - 1] : "If valid index it has to be greater: " + i; assert external[i] < 0 == !checked.get (i) : "external and checked must be consistent: " + i; } 206 } 207 return true; 208 } 209 210 211 private static void removeInterval (int[] array, int index0, int index1) { 212 assert index0 < index1 : "Index1 must be bigger than index0: " + index1 + " > " + index0; System.arraycopy (array, index1, array, index0, array.length - index1); 214 } 215 216 218 public static LazyListModel create (ListModel listModel, Filter f, double expectedRadio, Object defValue) { 219 return create (listModel, f, expectedRadio, defValue, false); 220 } 221 222 224 static LazyListModel create (ListModel listModel, Filter f, double expectedRadio, Object defValue, boolean log) { 225 LazyListModel lazy = new LazyListModel (listModel, f, expectedRadio, defValue); 226 lazy.log = log; 227 return lazy; 228 } 229 230 234 public static LazyListModel create (final org.openide.nodes.Children.Keys ch, ListModel model) { 235 class NodeF implements Filter { 236 private java.lang.reflect.Method m; 237 238 public boolean accept (Object obj) { 239 return obj instanceof org.openide.nodes.Node; 240 } 241 public void scheduleUpdate (Runnable run) { 242 try { 243 if (m == null) { 244 m = org.openide.nodes.Children.Keys.class.getDeclaredMethod ( 245 "updateMyAssumptions", new Class [] { Runnable .class } 247 ); 248 m.setAccessible (true); 249 } 250 m.invoke (ch, new Object [] { run }); 251 } catch (Exception ex) { 252 IllegalStateException i = new IllegalStateException (ex.getMessage ()); 253 i.initCause (ex); 254 throw i; 255 } 256 } 257 } 258 259 return create (model, new NodeF (), 1.0, null); 260 } 261 262 266 public void addListDataListener(ListDataListener l) { 267 list.add (ListDataListener.class, l); 268 } 269 270 public void removeListDataListener(ListDataListener l) { 271 list.remove (ListDataListener.class, l); 272 } 273 274 private void fireChange (ListDataEvent ev) { 275 if (list.getListenerCount () == 0) return ; 276 277 Object [] arr = list.getListenerList (); 278 for (int i = arr.length - 1; i >= 0; i -= 2) { 279 ListDataListener l = (ListDataListener)arr[i]; 280 switch (ev.getType ()) { 281 case ListDataEvent.CONTENTS_CHANGED: l.contentsChanged (ev); break; 282 case ListDataEvent.INTERVAL_ADDED: l.intervalAdded (ev); break; 283 case ListDataEvent.INTERVAL_REMOVED: l.intervalRemoved (ev); break; 284 default: 285 throw new IllegalArgumentException ("Unknown type: " + ev.getType ()); 286 } 287 } 288 } 289 290 292 private boolean accepted (int indx, Object [] result) { 293 Object v = listModel.getElementAt (indx); 294 tested.set (indx); 295 if (filter.accept (v)) { 296 result[0] = v; 297 return true; 298 } 299 300 markDirty (); 301 return false; 302 } 303 304 306 private void initialize () { 307 if (tested == null) { 308 originalSize = listModel.getSize (); 309 size = listModel.getSize (); 310 tested = new BitSet (size); 311 external = new int[size]; 312 for (int i = 0; i < size; i++) { 313 external[i] = NOT_TESTED; 314 } 315 checked = new BitSet (size); 316 } 317 assert externalContraints () : "Constraints failed"; } 319 320 323 static Boolean CREATE; 324 326 public Object getElementAt(int index) { 327 initialize (); 328 329 if (log) { 330 System.err.println("model.getElementAt (" + index + ");"); } 332 333 if (external[index] >= 0) { 334 return listModel.getElementAt (external[index]); 336 } 337 338 if (external[index] == EMPTY_VALUE) { 339 return defaultValue; 341 } 342 343 if (CREATE != null && !CREATE.booleanValue()) { 344 assert Thread.holdsLock(CREATE) : "Only one thread (from tests) can access this"; return defaultValue; 346 } 347 348 349 int minIndex = index; 351 while (minIndex >= 0 && getExternal (minIndex) < 0) { 352 minIndex--; 353 } 354 int maxIndex; 355 if (checked.get (index)) { 356 maxIndex = index; 357 } else { 358 maxIndex = checked.nextSetBit (index); 359 if (maxIndex == -1 || maxIndex > size) { 360 maxIndex = size; 361 } 362 } 363 364 int myMinIndex = getExternal (minIndex) + 1; int myMaxIndex = getExternal (maxIndex); 366 367 assert myMaxIndex >= myMaxIndex : "Must be greater"; if (myMaxIndex != myMinIndex) { 369 int myIndex = myMinIndex + (index - minIndex) - 1; 370 if (myIndex >= myMaxIndex) { 371 myIndex = myMaxIndex - 1; 372 } 373 374 Object [] result = new Object [1]; 375 if (accepted (myIndex, result)) { 376 assert external[index] == NOT_TESTED : "External index " + index + " still needs to be unset: " + external[index]; 377 external[index] = myIndex; 378 checked.set (index); 379 return result[0]; 380 } 381 382 boolean checkBefore = true; 383 boolean checkAfter = true; 384 for (int i = 1; checkAfter || checkBefore; i++) { 385 if (checkBefore) { 386 checkBefore = index - i >= minIndex && myIndex - i >= myMinIndex && getExternal (index - i) == NOT_TESTED; 387 if (checkBefore && accepted (myIndex - i, result)) { 388 external[index] = myIndex - i; 389 checked.set (index); 390 return result[0]; 391 } 392 } 393 if (checkAfter) { 394 checkAfter = index + i < maxIndex && myIndex + i < myMaxIndex && getExternal (index + i) == NOT_TESTED; 395 if (checkAfter && accepted (myIndex + i, result)) { 396 external[index] = myIndex + i; 397 checked.set (index); 398 return result[0]; 399 } 400 } 401 } 402 } 403 404 markDirty (); 405 406 for (int i = minIndex + 1; i < maxIndex; i++) { 408 assert external[i] == NOT_TESTED : i + " should not be set: " + external[i]; external[i] = EMPTY_VALUE; 410 } 411 checked.clear (minIndex + 1, maxIndex); 412 413 assert external[index] == EMPTY_VALUE : "Should be asigned in the cycle above"; return defaultValue; 415 } 416 417 public int getSize() { 418 initialize (); 419 return size; 420 } 421 422 426 428 private static BitSet insertAt (BitSet b, int at, int len, int size) { 429 BitSet before = b.get (0, at); 430 431 BitSet res = new BitSet (size); 432 res.or (before); 433 434 int max = b.length (); 435 while (at < max) { 436 res.set (at + len, b.get (at)); 437 at++; 438 } 439 return res; 440 } 441 443 private static BitSet removeAt (BitSet b, int at, int len, int newSize) { 444 BitSet clone = (BitSet )b.clone (); 445 446 int max = b.length (); 447 while (at < max) { 448 clone.set (at, b.get (at + len)); 449 at++; 450 } 451 clone.set (newSize, b.size (), false); 452 return clone; 453 } 454 455 public void contentsChanged (ListDataEvent listDataEvent) { 456 throw new java.lang.UnsupportedOperationException ("Not yet implemented"); 457 } 458 459 public void intervalAdded (ListDataEvent listDataEvent) { 460 if (external == null) { 461 return; 462 } 463 464 updateYourAssumeptions (); 465 466 int first = listDataEvent.getIndex0 (); 467 int end = listDataEvent.getIndex1 () + 1; 468 int len = end - first; 469 470 int newOriginalSize = originalSize + len; 471 int newSize = size + len; 472 473 tested = insertAt (tested, first, len, newOriginalSize); 474 475 int insert = findExternalIndex (first); 476 int[] newExternal = new int[newSize]; 477 System.arraycopy (external, 0, newExternal, 0, insert); 478 for (int i = 0; i < len; i++) { 479 newExternal[insert + i] = NOT_TESTED; 480 } 481 for (int i = insert + len; i < newExternal.length; i++) { 482 int v = external[i - len]; 483 newExternal[i] = v < 0 ? v : v + len; 484 } 485 external = newExternal; 486 size = newSize; 487 originalSize = newOriginalSize; 488 489 regenerateCheckedBitSet (); 490 491 fireChange (new ListDataEvent (this, ListDataEvent.INTERVAL_ADDED, insert, insert + len - 1)); 492 assert externalContraints () : "Constraints failed"; } 494 495 498 private int findExternalIndex (int myIndex) { 499 int outIndex = 0; 500 for (int i = -1; i < size; i++) { 501 if (getExternal (i) == NOT_TESTED) { 502 outIndex++; 503 } else { 504 outIndex = getExternal (i); 505 } 506 507 if (outIndex >= myIndex) { 508 return i; 509 } 510 } 511 return size; 512 } 513 514 public void intervalRemoved (ListDataEvent listDataEvent) { 515 if (external == null) { 516 return; 517 } 518 519 updateYourAssumeptions (); 520 521 int first = listDataEvent.getIndex0 (); 522 int end = listDataEvent.getIndex1 () + 1; 523 int len = end - first; 524 525 int newOriginalSize = originalSize - len; 526 527 int f = findExternalIndex (first); 528 int e = findExternalIndex (end); 529 530 assert f >= 0 : "First index must be above zero: " + f; assert e >= f : "End index must be above first: " + f + " <= " + e; 533 int outLen = e - f; 534 535 int[] newExternal = (int[])external.clone (); 536 for (int i = e; i < size; i++) { 537 int v = external[i]; 538 newExternal[i - outLen] = v < 0 ? v : v - len; 539 checked.set (i - outLen, v >= 0); 540 } 541 external = newExternal; 542 size -= outLen; 543 originalSize = newOriginalSize; 544 545 if (outLen != 0) { 546 fireChange (new ListDataEvent (this, ListDataEvent.INTERVAL_REMOVED, f, e - 1)); 547 } 548 assert externalContraints () : "Constraints failed"; } 550 551 552 556 public interface Filter { 557 public boolean accept (Object obj); 558 562 public void scheduleUpdate (Runnable run); 563 } 564 } 565 | Popular Tags |