1 19 20 package org.apache.cayenne.remote; 21 22 import java.util.ArrayList ; 23 import java.util.Collection ; 24 import java.util.Iterator ; 25 import java.util.List ; 26 import java.util.ListIterator ; 27 import java.util.Map ; 28 import java.util.NoSuchElementException ; 29 30 import org.apache.cayenne.CayenneRuntimeException; 31 import org.apache.cayenne.ObjectContext; 32 import org.apache.cayenne.Persistent; 33 import org.apache.cayenne.QueryResponse; 34 import org.apache.cayenne.query.Query; 35 import org.apache.cayenne.query.QueryMetadata; 36 import org.apache.cayenne.query.SelectQuery; 37 import org.apache.cayenne.util.IDUtil; 38 import org.apache.cayenne.util.IncrementalListResponse; 39 import org.apache.cayenne.util.Util; 40 41 62 public class RemoteIncrementalFaultList implements List { 63 64 static final Object PLACEHOLDER = new Object (); 65 66 protected List elements; 67 68 protected String cacheKey; 69 protected int pageSize; 70 protected int unfetchedObjects; 71 protected QueryMetadata metadata; 72 73 protected transient ObjectContext context; 74 75 79 protected int rowWidth; 80 81 private ListHelper helper; 82 83 public RemoteIncrementalFaultList(ObjectContext context, Query paginatedQuery) { 84 85 this.metadata = paginatedQuery.getMetaData(context.getEntityResolver()); 86 87 if (metadata.getPageSize() <= 0) { 88 throw new IllegalArgumentException ("Page size must be positive: " 89 + metadata.getPageSize()); 90 } 91 92 this.pageSize = metadata.getPageSize(); 93 this.helper = (metadata.isFetchingDataRows()) 94 ? (ListHelper) new DataRowListHelper() 95 : new PersistentListHelper(); 96 this.context = context; 97 98 this.cacheKey = metadata.getCacheKey(); 101 if (cacheKey == null) { 102 cacheKey = generateCacheKey(); 103 } 104 105 Query query = paginatedQuery; 106 if (metadata.getCacheKey() == null) { 107 108 if (query instanceof SelectQuery) { 112 query = new IncrementalSelectQuery((SelectQuery) paginatedQuery, cacheKey); 113 } 114 else { 115 query = new IncrementalQuery(paginatedQuery, cacheKey); 116 } 117 } 118 119 QueryResponse response = context.getChannel().onQuery(context, query); 122 123 List firstPage = response.firstList(); 124 125 if (firstPage.size() > pageSize) { 127 throw new IllegalArgumentException ("Returned page size (" 128 + firstPage.size() 129 + ") exceeds requested page size (" 130 + pageSize 131 + ")"); 132 } 133 else if (firstPage.size() < pageSize) { 135 this.elements = new ArrayList (firstPage); 136 unfetchedObjects = 0; 137 } 138 else { 139 140 if (response instanceof IncrementalListResponse) { 141 int fullListSize = ((IncrementalListResponse) response).getFullSize(); 142 143 this.unfetchedObjects = fullListSize - firstPage.size(); 144 this.elements = new ArrayList (fullListSize); 145 elements.addAll(firstPage); 146 147 for (int i = pageSize; i < fullListSize; i++) { 149 elements.add(PLACEHOLDER); 150 } 151 } 152 else { 154 this.elements = new ArrayList (firstPage); 155 unfetchedObjects = 0; 156 } 157 } 158 } 159 160 private String generateCacheKey() { 161 byte[] bytes = IDUtil.pseudoUniqueByteSequence8(); 162 StringBuffer buffer = new StringBuffer (17); 163 buffer.append("I"); 164 for (int i = 0; i < bytes.length; i++) { 165 IDUtil.appendFormattedByte(buffer, bytes[i]); 166 } 167 168 return buffer.toString(); 169 } 170 171 174 public void resolveAll() { 175 resolveInterval(0, size()); 176 } 177 178 183 private boolean isUnresolved(Object object) { 184 return object == PLACEHOLDER; 185 } 186 187 192 protected void resolveInterval(int fromIndex, int toIndex) { 193 if (fromIndex >= toIndex || elements.isEmpty()) { 194 return; 195 } 196 197 if (context == null) { 198 throw new CayenneRuntimeException( 199 "No ObjectContext set, can't resolve objects."); 200 } 201 202 204 if (fromIndex < 0) { 205 fromIndex = 0; 206 } 207 208 if (toIndex > elements.size()) { 209 toIndex = elements.size(); 210 } 211 212 214 int fromPage = pageIndex(fromIndex); 215 int toPage = pageIndex(toIndex - 1); 216 217 int rangeStartIndex = -1; 218 for (int i = fromPage; i <= toPage; i++) { 219 220 int pageStartIndex = i * pageSize; 221 Object firstPageObject = elements.get(pageStartIndex); 222 if (isUnresolved(firstPageObject)) { 223 224 if (rangeStartIndex < 0) { 226 rangeStartIndex = pageStartIndex; 227 } 228 } 229 else { 230 231 if (rangeStartIndex >= 0) { 233 forceResolveInterval(rangeStartIndex, pageStartIndex); 234 rangeStartIndex = -1; 235 } 236 } 237 } 238 239 if (rangeStartIndex >= 0) { 241 forceResolveInterval(rangeStartIndex, toIndex); 242 } 243 } 244 245 void forceResolveInterval(int fromIndex, int toIndex) { 246 247 int pastEnd = toIndex - size(); 248 if (pastEnd > 0) { 249 toIndex = size(); 250 } 251 252 int fetchLimit = toIndex - fromIndex; 253 254 RangeQuery query = new RangeQuery(cacheKey, fromIndex, fetchLimit, metadata); 255 256 List sublist = context.performQuery(query); 257 258 if (sublist.size() != fetchLimit) { 260 throw new CayenneRuntimeException("Resolved range size '" 261 + sublist.size() 262 + "' is not the same as expected: " 263 + fetchLimit); 264 } 265 266 for (int i = 0; i < fetchLimit; i++) { 267 elements.set(fromIndex + i, sublist.get(i)); 268 } 269 270 unfetchedObjects -= sublist.size(); 271 } 272 273 276 int pageIndex(int elementIndex) { 277 if (elementIndex < 0 || elementIndex > size()) { 278 throw new IndexOutOfBoundsException ("Index: " + elementIndex); 279 } 280 281 if (pageSize <= 0 || elementIndex < 0) { 282 return -1; 283 } 284 285 return elementIndex / pageSize; 286 } 287 288 291 public ObjectContext getContext() { 292 return context; 293 } 294 295 300 public int getPageSize() { 301 return pageSize; 302 } 303 304 309 public ListIterator listIterator() { 310 return new ListIteratorHelper(0); 311 } 312 313 321 public ListIterator listIterator(int index) { 322 if (index < 0 || index > size()) { 323 throw new IndexOutOfBoundsException ("Index: " + index); 324 } 325 326 return new ListIteratorHelper(index); 327 } 328 329 333 public Iterator iterator() { 334 return new Iterator () { 337 338 int listIndex = 0; 339 340 public boolean hasNext() { 341 return (listIndex < elements.size()); 342 } 343 344 public Object next() { 345 if (listIndex >= elements.size()) 346 throw new NoSuchElementException ("no more elements"); 347 348 return get(listIndex++); 349 } 350 351 public void remove() { 352 throw new UnsupportedOperationException ("remove not supported."); 353 } 354 }; 355 } 356 357 360 public void add(int index, Object element) { 361 helper.validateListObject(element); 362 elements.add(index, element); 363 364 } 365 366 369 public boolean add(Object o) { 370 helper.validateListObject(o); 371 return elements.add(o); 372 } 373 374 377 public boolean addAll(Collection c) { 378 379 return elements.addAll(c); 380 381 } 382 383 386 public boolean addAll(int index, Collection c) { 387 388 return elements.addAll(index, c); 389 390 } 391 392 395 public void clear() { 396 elements.clear(); 397 } 398 399 402 public boolean contains(Object o) { 403 return indexOf(o) >= 0; 404 } 405 406 409 public boolean containsAll(Collection c) { 410 Iterator it = c.iterator(); 411 while (it.hasNext()) { 412 if (!contains(it.next())) { 413 return false; 414 } 415 } 416 417 return true; 418 } 419 420 423 public Object get(int index) { 424 425 Object o = elements.get(index); 426 427 if (isUnresolved(o)) { 428 int pageStart = pageIndex(index) * pageSize; 430 resolveInterval(pageStart, pageStart + pageSize); 431 432 return elements.get(index); 433 } 434 else { 435 return o; 436 } 437 438 } 439 440 443 public int indexOf(Object o) { 444 return helper.indexOfObject(o); 445 } 446 447 450 public boolean isEmpty() { 451 452 return elements.isEmpty(); 453 454 } 455 456 459 public int lastIndexOf(Object o) { 460 return helper.lastIndexOfObject(o); 461 } 462 463 466 public Object remove(int index) { 467 468 return elements.remove(index); 469 470 } 471 472 475 public boolean remove(Object o) { 476 477 return elements.remove(o); 478 479 } 480 481 484 public boolean removeAll(Collection c) { 485 486 return elements.removeAll(c); 487 488 } 489 490 493 public boolean retainAll(Collection c) { 494 495 return elements.retainAll(c); 496 497 } 498 499 502 public Object set(int index, Object element) { 503 helper.validateListObject(element); 504 505 return elements.set(index, element); 506 507 } 508 509 512 public int size() { 513 return elements.size(); 514 } 515 516 public List subList(int fromIndex, int toIndex) { 517 resolveInterval(fromIndex, toIndex); 518 return elements.subList(fromIndex, toIndex); 519 } 520 521 public Object [] toArray() { 522 resolveAll(); 523 524 return elements.toArray(); 525 } 526 527 530 public Object [] toArray(Object [] a) { 531 resolveAll(); 532 533 return elements.toArray(a); 534 } 535 536 539 public int getUnfetchedObjects() { 540 return unfetchedObjects; 541 } 542 543 abstract class ListHelper { 544 545 int indexOfObject(Object object) { 546 if (incorrectObjectType(object)) { 547 return -1; 548 } 549 550 for (int i = 0; i < elements.size(); i++) { 551 552 if (Util.nullSafeEquals(object, get(i))) { 553 return i; 554 } 555 } 556 557 return -1; 558 } 559 560 int lastIndexOfObject(Object object) { 561 if (incorrectObjectType(object)) { 562 return -1; 563 } 564 565 for (int i = elements.size() - 1; i >= 0; i--) { 566 if (Util.nullSafeEquals(object, get(i))) { 567 return i; 568 } 569 } 570 571 return -1; 572 } 573 574 abstract boolean incorrectObjectType(Object object); 575 576 void validateListObject(Object object) throws IllegalArgumentException { 577 if (incorrectObjectType(object)) { 578 throw new IllegalArgumentException ("Can't store this object: " + object); 579 } 580 } 581 } 582 583 class PersistentListHelper extends ListHelper { 584 585 boolean incorrectObjectType(Object object) { 586 if (!(object instanceof Persistent)) { 587 return true; 588 } 589 590 Persistent persistent = (Persistent) object; 591 if (persistent.getObjectContext() != context) { 592 return true; 593 } 594 595 return false; 596 } 597 598 } 599 600 class DataRowListHelper extends ListHelper { 601 602 boolean incorrectObjectType(Object object) { 603 if (!(object instanceof Map )) { 604 return true; 605 } 606 607 Map map = (Map ) object; 608 return map.size() != rowWidth; 609 } 610 } 611 612 class ListIteratorHelper implements ListIterator { 613 614 617 int listIndex; 618 619 public ListIteratorHelper(int startIndex) { 620 this.listIndex = startIndex; 621 } 622 623 public void add(Object o) { 624 throw new UnsupportedOperationException ("add operation not supported"); 625 } 626 627 public boolean hasNext() { 628 return (listIndex < elements.size()); 629 } 630 631 public boolean hasPrevious() { 632 return (listIndex > 0); 633 } 634 635 public Object next() { 636 if (listIndex >= elements.size()) 637 throw new NoSuchElementException ("at the end of the list"); 638 639 return get(listIndex++); 640 } 641 642 public int nextIndex() { 643 return listIndex; 644 } 645 646 public Object previous() { 647 if (listIndex < 1) 648 throw new NoSuchElementException ("at the beginning of the list"); 649 650 return get(--listIndex); 651 } 652 653 public int previousIndex() { 654 return (listIndex - 1); 655 } 656 657 public void remove() { 658 throw new UnsupportedOperationException ("remove operation not supported"); 659 } 660 661 public void set(Object o) { 662 throw new UnsupportedOperationException ("set operation not supported"); 663 } 664 } 665 } 666 | Popular Tags |