1 19 20 package org.apache.cayenne.access; 21 22 import java.util.ArrayList ; 23 import java.util.Collection ; 24 import java.util.Collections ; 25 import java.util.Iterator ; 26 import java.util.List ; 27 import java.util.ListIterator ; 28 import java.util.Map ; 29 import java.util.NoSuchElementException ; 30 31 import org.apache.cayenne.CayenneException; 32 import org.apache.cayenne.CayenneRuntimeException; 33 import org.apache.cayenne.Persistent; 34 import org.apache.cayenne.exp.Expression; 35 import org.apache.cayenne.exp.ExpressionFactory; 36 import org.apache.cayenne.map.DbAttribute; 37 import org.apache.cayenne.map.DbEntity; 38 import org.apache.cayenne.map.ObjEntity; 39 import org.apache.cayenne.query.Query; 40 import org.apache.cayenne.query.QueryMetadata; 41 import org.apache.cayenne.query.SelectQuery; 42 import org.apache.cayenne.util.Util; 43 44 62 public class IncrementalFaultList implements List { 63 64 protected int pageSize; 65 protected List elements; 66 protected DataContext dataContext; 67 protected ObjEntity rootEntity; 68 protected SelectQuery internalQuery; 69 protected int unfetchedObjects; 70 71 75 protected int rowWidth; 76 77 private IncrementalListHelper helper; 78 79 83 protected int maxFetchSize = 10000; 84 85 91 95 public IncrementalFaultList(IncrementalFaultList list) { 96 this.pageSize = list.pageSize; 97 this.internalQuery = list.internalQuery; 98 this.dataContext = list.dataContext; 99 this.rootEntity = list.rootEntity; 100 this.maxFetchSize = list.maxFetchSize; 101 this.rowWidth = list.rowWidth; 102 this.helper = list.helper; 103 elements = Collections.synchronizedList(new ArrayList ()); 104 } 105 106 114 public IncrementalFaultList(DataContext dataContext, Query query) { 115 QueryMetadata metadata = query.getMetaData(dataContext.getEntityResolver()); 116 if (metadata.getPageSize() <= 0) { 117 throw new CayenneRuntimeException( 118 "IncrementalFaultList does not support unpaged queries. Query page size is " 119 + metadata.getPageSize()); 120 } 121 122 this.dataContext = dataContext; 123 this.pageSize = metadata.getPageSize(); 124 this.rootEntity = metadata.getObjEntity(); 125 126 this.internalQuery = new SelectQuery(rootEntity); 130 this.internalQuery.setFetchingDataRows(metadata.isFetchingDataRows()); 131 this.internalQuery.setResolvingInherited(metadata.isResolvingInherited()); 132 133 if (metadata.isFetchingDataRows()) { 134 helper = new DataRowListHelper(); 135 } 136 else { 137 helper = new PersistentListHelper(); 138 } 139 140 boolean resolvesFirstPage = true; 141 142 if (!metadata.isFetchingDataRows() && (query instanceof SelectQuery)) { 143 SelectQuery select = (SelectQuery) query; 144 145 this.internalQuery.setPrefetchTree(select.getPrefetchTree()); 146 147 152 SelectQuery clone = select.queryWithParameters(Collections.EMPTY_MAP, true); 153 clone.clearPrefetches(); 154 155 if (!select.isFetchingCustomAttributes()) { 157 Iterator pk = rootEntity.getDbEntity().getPrimaryKey().iterator(); 158 while (pk.hasNext()) { 159 DbAttribute attribute = (DbAttribute) pk.next(); 160 clone.addCustomDbAttribute(attribute.getName()); 161 } 162 } 163 164 query = clone; 165 resolvesFirstPage = false; 166 } 167 168 List elementsUnsynced = new ArrayList (); 169 fillIn(query, elementsUnsynced, resolvesFirstPage); 170 this.elements = Collections.synchronizedList(elementsUnsynced); 171 } 172 173 176 SelectQuery getInternalQuery() { 177 return internalQuery; 178 } 179 180 189 protected void fillIn(Query query) { 190 synchronized (elements) { 191 fillIn(query, elements, true); 192 } 193 } 194 195 201 protected void fillIn(Query query, List elementsList, boolean resolvesFirstPage) { 202 QueryMetadata info = query.getMetaData(dataContext.getEntityResolver()); 203 boolean fetchesDataRows = internalQuery.isFetchingDataRows(); 204 205 elementsList.clear(); 207 rowWidth = 0; 208 209 try { 210 int lastResolved = 0; 211 long t1 = System.currentTimeMillis(); 212 ResultIterator it = dataContext.performIteratedQuery(query); 213 try { 214 215 rowWidth = it.getDataRowWidth(); 216 217 if (resolvesFirstPage) { 219 for (int i = 0; i < pageSize && it.hasNextRow(); i++) { 221 elementsList.add(it.nextDataRow()); 222 lastResolved++; 223 } 224 225 } 227 228 DbEntity entity = rootEntity.getDbEntity(); 230 while (it.hasNextRow()) { 231 elementsList.add(it.nextObjectId(entity)); 232 } 233 234 QueryLogger.logSelectCount(elementsList.size(), System 235 .currentTimeMillis() 236 - t1); 237 } 238 finally { 239 it.close(); 240 } 241 242 if (!fetchesDataRows && lastResolved > 0) { 245 List objects = dataContext.objectsFromDataRows(rootEntity, elementsList 246 .subList(0, lastResolved), info.isRefreshingObjects(), info 247 .isResolvingInherited()); 248 249 for (int i = 0; i < lastResolved; i++) { 250 elementsList.set(i, objects.get(i)); 251 } 252 } 253 } 254 catch (CayenneException e) { 255 throw new CayenneRuntimeException("Error performing query.", Util 256 .unwindException(e)); 257 } 258 259 unfetchedObjects = (resolvesFirstPage) 260 ? elementsList.size() - pageSize 261 : elementsList.size(); 262 } 263 264 267 public void resolveAll() { 268 resolveInterval(0, size()); 269 } 270 271 276 private boolean isUnresolved(Object object) { 277 if (object instanceof Persistent) { 278 return false; 279 } 280 281 if (internalQuery.isFetchingDataRows()) { 282 Map map = (Map ) object; 285 int size = map.size(); 286 return size < rowWidth; 287 } 288 289 return true; 290 } 291 292 296 private void validateListObject(Object object) throws IllegalArgumentException { 297 298 300 if (internalQuery.isFetchingDataRows()) { 301 if (!(object instanceof Map )) { 302 throw new IllegalArgumentException ( 303 "Only Map objects can be stored in this list."); 304 } 305 } 306 else { 307 if (!(object instanceof Persistent)) { 308 throw new IllegalArgumentException ( 309 "Only DataObjects can be stored in this list."); 310 } 311 } 312 } 313 314 319 protected void resolveInterval(int fromIndex, int toIndex) { 320 if (fromIndex >= toIndex) { 321 return; 322 } 323 324 synchronized (elements) { 325 if (elements.size() == 0) { 326 return; 327 } 328 329 if (fromIndex < 0) { 331 fromIndex = 0; 332 } 333 334 if (toIndex > elements.size()) { 335 toIndex = elements.size(); 336 } 337 338 List quals = new ArrayList (pageSize); 339 List ids = new ArrayList (pageSize); 340 for (int i = fromIndex; i < toIndex; i++) { 341 Object obj = elements.get(i); 342 if (isUnresolved(obj)) { 343 ids.add(obj); 344 345 Map map = (Map ) obj; 346 if (map.isEmpty()) { 347 throw new CayenneRuntimeException("Empty id map at index " + i); 348 } 349 350 quals.add(ExpressionFactory.matchAllDbExp(map, Expression.EQUAL_TO)); 351 } 352 } 353 354 int qualsSize = quals.size(); 355 if (qualsSize == 0) { 356 return; 357 } 358 359 boolean fetchesDataRows = internalQuery.isFetchingDataRows(); 361 List objects = new ArrayList (qualsSize); 362 int fetchEnd = Math.min(qualsSize, maxFetchSize); 363 int fetchBegin = 0; 364 while (fetchBegin < qualsSize) { 365 SelectQuery query = new SelectQuery(rootEntity, ExpressionFactory 366 .joinExp(Expression.OR, quals.subList(fetchBegin, fetchEnd))); 367 368 query.setFetchingDataRows(fetchesDataRows); 369 370 if (!query.isFetchingDataRows()) { 371 query.setPrefetchTree(internalQuery.getPrefetchTree()); 372 } 373 374 objects.addAll(dataContext.performQuery(query)); 375 fetchBegin = fetchEnd; 376 fetchEnd += Math.min(maxFetchSize, qualsSize - fetchEnd); 377 } 378 379 if (objects.size() < ids.size()) { 381 StringBuffer buf = new StringBuffer (); 383 buf.append("Some ObjectIds are missing from the database. "); 384 buf.append("Expected ").append(ids.size()).append(", fetched ").append( 385 objects.size()); 386 387 Iterator idsIt = ids.iterator(); 388 boolean first = true; 389 while (idsIt.hasNext()) { 390 boolean found = false; 391 Object id = idsIt.next(); 392 Iterator oIt = objects.iterator(); 393 while (oIt.hasNext()) { 394 if (((Persistent) oIt.next()) 395 .getObjectId() 396 .getIdSnapshot() 397 .equals(id)) { 398 found = true; 399 break; 400 } 401 } 402 403 if (!found) { 404 if (first) { 405 first = false; 406 } 407 else { 408 buf.append(", "); 409 } 410 411 buf.append(id.toString()); 412 } 413 } 414 415 throw new CayenneRuntimeException(buf.toString()); 416 } 417 else if (objects.size() > ids.size()) { 418 throw new CayenneRuntimeException("Expected " 419 + ids.size() 420 + " objects, retrieved " 421 + objects.size()); 422 } 423 424 Iterator it = objects.iterator(); 426 while (it.hasNext()) { 427 helper.updateWithResolvedObjectInRange(it.next(), fromIndex, toIndex); 428 } 429 430 unfetchedObjects -= objects.size(); 431 } 432 } 433 434 437 public int pageIndex(int elementIndex) { 438 if (elementIndex < 0 || elementIndex > size()) { 439 throw new IndexOutOfBoundsException ("Index: " + elementIndex); 440 } 441 442 if (pageSize <= 0 || elementIndex < 0) { 443 return -1; 444 } 445 446 return elementIndex / pageSize; 447 } 448 449 457 public int getMaxFetchSize() { 458 return maxFetchSize; 459 } 460 461 public void setMaxFetchSize(int fetchSize) { 462 this.maxFetchSize = fetchSize; 463 } 464 465 470 public DataContext getDataContext() { 471 return dataContext; 472 } 473 474 479 public int getPageSize() { 480 return pageSize; 481 } 482 483 488 public ListIterator listIterator() { 489 return new IncrementalListIterator(0); 490 } 491 492 500 public ListIterator listIterator(int index) { 501 if (index < 0 || index > size()) { 502 throw new IndexOutOfBoundsException ("Index: " + index); 503 } 504 505 return new IncrementalListIterator(index); 506 } 507 508 512 public Iterator iterator() { 513 return new Iterator () { 516 517 int listIndex = 0; 518 519 public boolean hasNext() { 520 return (listIndex < elements.size()); 521 } 522 523 public Object next() { 524 if (listIndex >= elements.size()) 525 throw new NoSuchElementException ("no more elements"); 526 527 return get(listIndex++); 528 } 529 530 public void remove() { 531 throw new UnsupportedOperationException ("remove not supported."); 532 } 533 }; 534 } 535 536 539 public void add(int index, Object element) { 540 validateListObject(element); 541 542 synchronized (elements) { 543 elements.add(index, element); 544 } 545 } 546 547 550 public boolean add(Object o) { 551 validateListObject(o); 552 553 synchronized (elements) { 554 return elements.add(o); 555 } 556 } 557 558 561 public boolean addAll(Collection c) { 562 synchronized (elements) { 563 return elements.addAll(c); 564 } 565 } 566 567 570 public boolean addAll(int index, Collection c) { 571 synchronized (elements) { 572 return elements.addAll(index, c); 573 } 574 } 575 576 579 public void clear() { 580 synchronized (elements) { 581 elements.clear(); 582 } 583 } 584 585 588 public boolean contains(Object o) { 589 synchronized (elements) { 590 return elements.contains(o); 591 } 592 } 593 594 597 public boolean containsAll(Collection c) { 598 synchronized (elements) { 599 return elements.containsAll(c); 600 } 601 } 602 603 606 public Object get(int index) { 607 synchronized (elements) { 608 Object o = elements.get(index); 609 610 if (isUnresolved(o)) { 611 int pageStart = pageIndex(index) * pageSize; 613 resolveInterval(pageStart, pageStart + pageSize); 614 615 return elements.get(index); 616 } 617 else { 618 return o; 619 } 620 } 621 } 622 623 626 public int indexOf(Object o) { 627 return helper.indexOfObject(o); 628 } 629 630 633 public boolean isEmpty() { 634 synchronized (elements) { 635 return elements.isEmpty(); 636 } 637 } 638 639 642 public int lastIndexOf(Object o) { 643 return helper.lastIndexOfObject(o); 644 } 645 646 649 public Object remove(int index) { 650 synchronized (elements) { 651 return elements.remove(index); 652 } 653 } 654 655 658 public boolean remove(Object o) { 659 synchronized (elements) { 660 return elements.remove(o); 661 } 662 } 663 664 667 public boolean removeAll(Collection c) { 668 synchronized (elements) { 669 return elements.removeAll(c); 670 } 671 } 672 673 676 public boolean retainAll(Collection c) { 677 synchronized (elements) { 678 return elements.retainAll(c); 679 } 680 } 681 682 685 public Object set(int index, Object element) { 686 validateListObject(element); 687 688 synchronized (elements) { 689 return elements.set(index, element); 690 } 691 } 692 693 696 public int size() { 697 synchronized (elements) { 698 return elements.size(); 699 } 700 } 701 702 public List subList(int fromIndex, int toIndex) { 703 synchronized (elements) { 704 resolveInterval(fromIndex, toIndex); 705 return elements.subList(fromIndex, toIndex); 706 } 707 } 708 709 public Object [] toArray() { 710 resolveAll(); 711 712 return elements.toArray(); 713 } 714 715 718 public Object [] toArray(Object [] a) { 719 resolveAll(); 720 721 return elements.toArray(a); 722 } 723 724 727 public int getUnfetchedObjects() { 728 return unfetchedObjects; 729 } 730 731 abstract class IncrementalListHelper { 732 733 int indexOfObject(Object object) { 734 if (incorrectObjectType(object)) { 735 return -1; 736 } 737 738 synchronized (elements) { 739 for (int i = 0; i < elements.size(); i++) { 740 if (objectsAreEqual(object, elements.get(i))) { 741 return i; 742 } 743 } 744 } 745 return -1; 746 } 747 748 int lastIndexOfObject(Object object) { 749 if (incorrectObjectType(object)) { 750 return -1; 751 } 752 753 synchronized (elements) { 754 for (int i = elements.size() - 1; i >= 0; i--) { 755 if (objectsAreEqual(object, elements.get(i))) { 756 return i; 757 } 758 } 759 } 760 761 return -1; 762 } 763 764 void updateWithResolvedObjectInRange(Object object, int from, int to) { 765 boolean found = false; 766 767 synchronized (elements) { 768 769 for (int i = from; i < to; i++) { 770 if (replacesObject(object, elements.get(i))) { 771 elements.set(i, object); 772 found = true; 773 break; 774 } 775 } 776 } 777 778 if (!found) { 779 throw new CayenneRuntimeException("Can't find id for " + object); 780 } 781 } 782 783 abstract boolean incorrectObjectType(Object object); 784 785 abstract boolean objectsAreEqual(Object object, Object objectInTheList); 786 787 abstract boolean replacesObject(Object object, Object objectInTheList); 788 } 789 790 class PersistentListHelper extends IncrementalListHelper { 791 792 boolean incorrectObjectType(Object object) { 793 if (!(object instanceof Persistent)) { 794 return true; 795 } 796 797 Persistent dataObj = (Persistent) object; 798 if (dataObj.getObjectContext() != dataContext) { 799 return true; 800 } 801 802 if (!dataObj.getObjectId().getEntityName().equals(rootEntity.getName())) { 803 return true; 804 } 805 806 return false; 807 } 808 809 boolean objectsAreEqual(Object object, Object objectInTheList) { 810 811 if (objectInTheList instanceof Persistent) { 812 return object == objectInTheList; 814 } 815 else { 816 return ((Persistent) object).getObjectId().getIdSnapshot().equals( 817 objectInTheList); 818 } 819 } 820 821 boolean replacesObject(Object object, Object objectInTheList) { 822 if (objectInTheList instanceof Persistent) { 823 return false; 824 } 825 826 Persistent dataObject = (Persistent) object; 827 return dataObject.getObjectId().getIdSnapshot().equals(objectInTheList); 828 } 829 } 830 831 class DataRowListHelper extends IncrementalListHelper { 832 833 boolean incorrectObjectType(Object object) { 834 if (!(object instanceof Map )) { 835 return true; 836 } 837 838 Map map = (Map ) object; 839 return map.size() != rowWidth; 840 } 841 842 boolean objectsAreEqual(Object object, Object objectInTheList) { 843 if (object == null && objectInTheList == null) { 844 return true; 845 } 846 847 if (object != null && objectInTheList != null) { 848 849 Map id = (Map ) objectInTheList; 850 Map map = (Map ) object; 851 852 Iterator it = id.entrySet().iterator(); 854 855 while (it.hasNext()) { 856 Map.Entry entry = (Map.Entry ) it.next(); 857 Object key = entry.getKey(); 858 Object value = entry.getValue(); 859 if (!Util.nullSafeEquals(value, map.get(key))) { 860 return false; 861 } 862 } 863 864 return true; 865 } 866 867 return false; 868 } 869 870 boolean replacesObject(Object object, Object objectInTheList) { 871 872 Map id = (Map ) objectInTheList; 873 if (id.size() == rowWidth) { 874 return false; 875 } 876 877 Map map = (Map ) object; 879 Iterator it = id.entrySet().iterator(); 880 881 while (it.hasNext()) { 882 Map.Entry entry = (Map.Entry ) it.next(); 883 Object key = entry.getKey(); 884 Object value = entry.getValue(); 885 if (!Util.nullSafeEquals(value, map.get(key))) { 886 return false; 887 } 888 } 889 890 return true; 891 } 892 } 893 894 class IncrementalListIterator implements ListIterator { 895 896 899 int listIndex; 900 901 public IncrementalListIterator(int startIndex) { 902 this.listIndex = startIndex; 903 } 904 905 public void add(Object o) { 906 throw new UnsupportedOperationException ("add operation not supported"); 907 } 908 909 public boolean hasNext() { 910 return (listIndex < elements.size()); 911 } 912 913 public boolean hasPrevious() { 914 return (listIndex > 0); 915 } 916 917 public Object next() { 918 if (listIndex >= elements.size()) 919 throw new NoSuchElementException ("at the end of the list"); 920 921 return get(listIndex++); 922 } 923 924 public int nextIndex() { 925 return listIndex; 926 } 927 928 public Object previous() { 929 if (listIndex < 1) 930 throw new NoSuchElementException ("at the beginning of the list"); 931 932 return get(--listIndex); 933 } 934 935 public int previousIndex() { 936 return (listIndex - 1); 937 } 938 939 public void remove() { 940 throw new UnsupportedOperationException ("remove operation not supported"); 941 } 942 943 public void set(Object o) { 944 throw new UnsupportedOperationException ("set operation not supported"); 945 } 946 } 947 } 948 | Popular Tags |