1 56 package org.objectstyle.cayenne.access; 57 58 import java.util.ArrayList ; 59 import java.util.Collection ; 60 import java.util.Collections ; 61 import java.util.Iterator ; 62 import java.util.List ; 63 import java.util.ListIterator ; 64 import java.util.Map ; 65 import java.util.NoSuchElementException ; 66 67 import org.objectstyle.cayenne.CayenneException; 68 import org.objectstyle.cayenne.CayenneRuntimeException; 69 import org.objectstyle.cayenne.DataObject; 70 import org.objectstyle.cayenne.exp.Expression; 71 import org.objectstyle.cayenne.exp.ExpressionFactory; 72 import org.objectstyle.cayenne.map.DbEntity; 73 import org.objectstyle.cayenne.map.ObjEntity; 74 import org.objectstyle.cayenne.query.GenericSelectQuery; 75 import org.objectstyle.cayenne.query.SelectQuery; 76 import org.objectstyle.cayenne.util.Util; 77 78 94 public class IncrementalFaultList implements List { 95 96 protected int pageSize; 97 protected List elements; 98 protected DataContext dataContext; 99 protected ObjEntity rootEntity; 100 protected SelectQuery internalQuery; 101 protected int unfetchedObjects; 102 103 107 protected int rowWidth; 108 109 private IncrementalListHelper helper; 110 111 114 protected int maxFetchSize = 10000; 115 121 125 public IncrementalFaultList(IncrementalFaultList list) { 126 this.pageSize = list.pageSize; 127 this.internalQuery = list.internalQuery; 128 this.dataContext = list.dataContext; 129 this.rootEntity = list.rootEntity; 130 this.maxFetchSize = list.maxFetchSize; 131 this.rowWidth = list.rowWidth; 132 this.helper = list.helper; 133 elements = Collections.synchronizedList(new ArrayList ()); 134 } 135 136 143 public IncrementalFaultList(DataContext dataContext, GenericSelectQuery query) { 144 if (query.getPageSize() <= 0) { 145 throw new CayenneRuntimeException( 146 "IncrementalFaultList does not support unpaged queries. Query page size is " 147 + query.getPageSize()); 148 } 149 150 this.elements = Collections.synchronizedList(new ArrayList ()); 151 this.dataContext = dataContext; 152 this.pageSize = query.getPageSize(); 153 this.rootEntity = dataContext.getEntityResolver().lookupObjEntity(query); 154 155 this.internalQuery = new SelectQuery(); 159 this.internalQuery.setRoot(query.getRoot()); 160 this.internalQuery.setLoggingLevel(query.getLoggingLevel()); 161 this.internalQuery.setFetchingDataRows(query.isFetchingDataRows()); 162 this.internalQuery.setResolvingInherited(query.isResolvingInherited()); 163 164 if (query.isFetchingDataRows()) { 165 helper = new DataRowListHelper(); 166 } 167 else { 168 helper = new DataObjectListHelper(); 169 } 170 171 if (!query.isFetchingDataRows() && (query instanceof SelectQuery)) { 172 SelectQuery select = (SelectQuery) query; 173 174 this.internalQuery.addPrefetches(select.getPrefetches()); 175 176 if (!select.getPrefetches().isEmpty()) { 179 SelectQuery clone = select.queryWithParameters( 180 Collections.EMPTY_MAP, 181 true); 182 183 Iterator it = select.getPrefetches().iterator(); 184 while (it.hasNext()) { 185 clone.removePrefetch((String ) it.next()); 186 } 187 188 query = clone; 189 } 190 } 191 192 fillIn(query); 193 } 194 195 private boolean resolvesFirstPage() { 196 return internalQuery.getPrefetches().isEmpty(); 197 } 198 199 206 protected void fillIn(GenericSelectQuery query) { 207 synchronized (elements) { 208 209 boolean fetchesDataRows = internalQuery.isFetchingDataRows(); 210 211 elements.clear(); 213 rowWidth = 0; 214 215 try { 216 long t1 = System.currentTimeMillis(); 217 ResultIterator it = dataContext.performIteratedQuery(query); 218 try { 219 220 rowWidth = it.getDataRowWidth(); 221 222 if (resolvesFirstPage()) { 224 List firstPage = 226 (fetchesDataRows) ? elements : new ArrayList (pageSize); 227 for (int i = 0; i < pageSize && it.hasNextRow(); i++) { 228 firstPage.add(it.nextDataRow()); 229 } 230 231 if (!fetchesDataRows) { 233 elements.addAll( 234 dataContext.objectsFromDataRows( 235 rootEntity, 236 firstPage, 237 query.isRefreshingObjects(), 238 query.isResolvingInherited())); 239 } 240 } 241 242 DbEntity entity = rootEntity.getDbEntity(); 244 while (it.hasNextRow()) { 245 elements.add(it.nextObjectId(entity)); 246 } 247 248 QueryLogger.logSelectCount( 249 query.getLoggingLevel(), 250 elements.size(), 251 System.currentTimeMillis() - t1); 252 253 } 254 finally { 255 it.close(); 256 } 257 } 258 catch (CayenneException e) { 259 throw new CayenneRuntimeException( 260 "Error performing query.", 261 Util.unwindException(e)); 262 } 263 264 unfetchedObjects = 265 (resolvesFirstPage()) ? elements.size() - pageSize : elements.size(); 266 } 267 } 268 269 272 public void resolveAll() { 273 resolveInterval(0, size()); 274 } 275 276 281 private boolean isUnresolved(Object object) { 282 if (object instanceof DataObject) { 283 return false; 284 } 285 286 if (internalQuery.isFetchingDataRows()) { 287 Map map = (Map ) object; 290 int size = map.size(); 291 return size < rowWidth; 292 } 293 294 return true; 295 } 296 297 301 private void validateListObject(Object object) throws IllegalArgumentException { 302 303 305 if (internalQuery.isFetchingDataRows()) { 306 if (!(object instanceof Map )) { 307 throw new IllegalArgumentException ("Only Map objects can be stored in this list."); 308 } 309 } 310 else { 311 if (!(object instanceof DataObject)) { 312 throw new IllegalArgumentException ("Only DataObjects can be stored in this list."); 313 } 314 } 315 } 316 317 322 protected void resolveInterval(int fromIndex, int toIndex) { 323 if (fromIndex >= toIndex) { 324 return; 325 } 326 327 synchronized (elements) { 328 if (elements.size() == 0) { 329 return; 330 } 331 332 if (fromIndex < 0) { 334 fromIndex = 0; 335 } 336 337 if (toIndex > elements.size()) { 338 toIndex = elements.size(); 339 } 340 341 List quals = new ArrayList (pageSize); 342 List ids = new ArrayList (pageSize); 343 for (int i = fromIndex; i < toIndex; i++) { 344 Object obj = elements.get(i); 345 if (isUnresolved(obj)) { 346 ids.add(obj); 347 348 Map map = (Map ) obj; 349 if (map.isEmpty()) { 350 throw new CayenneRuntimeException("Empty id map at index " + i); 351 } 352 353 quals.add(ExpressionFactory.matchAllDbExp(map, Expression.EQUAL_TO)); 354 } 355 } 356 357 int qualsSize = quals.size(); 358 if (qualsSize == 0) { 359 return; 360 } 361 362 boolean fetchesDataRows = internalQuery.isFetchingDataRows(); 364 List objects = new ArrayList (qualsSize); 365 int fetchEnd = Math.min(qualsSize, maxFetchSize); 366 int fetchBegin = 0; 367 while (fetchBegin < qualsSize) { 368 SelectQuery query = 369 new SelectQuery( 370 rootEntity, 371 ExpressionFactory.joinExp( 372 Expression.OR, 373 quals.subList(fetchBegin, fetchEnd))); 374 375 query.setFetchingDataRows(fetchesDataRows); 376 377 if (!query.isFetchingDataRows()) { 378 query.addPrefetches(internalQuery.getPrefetches()); 379 } 380 381 objects.addAll(dataContext.performQuery(query)); 382 fetchBegin = fetchEnd; 383 fetchEnd += Math.min(maxFetchSize, qualsSize - fetchEnd); 384 } 385 386 if (objects.size() < ids.size()) { 388 StringBuffer buf = new StringBuffer (); 390 buf.append("Some ObjectIds are missing from the database. "); 391 buf.append("Expected ").append(ids.size()).append(", fetched ").append( 392 objects.size()); 393 394 Iterator idsIt = ids.iterator(); 395 boolean first = true; 396 while (idsIt.hasNext()) { 397 boolean found = false; 398 Object id = idsIt.next(); 399 Iterator oIt = objects.iterator(); 400 while (oIt.hasNext()) { 401 if (((DataObject) oIt.next()) 402 .getObjectId() 403 .getIdSnapshot() 404 .equals(id)) { 405 found = true; 406 break; 407 } 408 } 409 410 if (!found) { 411 if (first) { 412 first = false; 413 } 414 else { 415 buf.append(", "); 416 } 417 418 buf.append(id.toString()); 419 } 420 } 421 422 throw new CayenneRuntimeException(buf.toString()); 423 } 424 else if (objects.size() > ids.size()) { 425 throw new CayenneRuntimeException( 426 "Expected " + ids.size() + " objects, retrieved " + objects.size()); 427 } 428 429 Iterator it = objects.iterator(); 431 while (it.hasNext()) { 432 helper.updateWithResolvedObjectInRange(it.next(), fromIndex, toIndex); 433 } 434 435 unfetchedObjects -= objects.size(); 436 } 437 } 438 439 443 public int pageIndex(int elementIndex) { 444 if (elementIndex < 0 || elementIndex > size()) { 445 throw new IndexOutOfBoundsException ("Index: " + elementIndex); 446 } 447 448 if (pageSize <= 0 || elementIndex < 0) { 449 return -1; 450 } 451 452 return elementIndex / pageSize; 453 } 454 455 463 public int getMaxFetchSize() { 464 return maxFetchSize; 465 } 466 467 public void setMaxFetchSize(int fetchSize) { 468 this.maxFetchSize = fetchSize; 469 } 470 471 475 public DataContext getDataContext() { 476 return dataContext; 477 } 478 479 483 public int getPageSize() { 484 return pageSize; 485 } 486 487 492 public ListIterator listIterator() { 493 return new IncrementalListIterator(0); 494 } 495 496 507 public ListIterator listIterator(int index) { 508 if (index < 0 || index > size()) { 509 throw new IndexOutOfBoundsException ("Index: " + index); 510 } 511 512 return new IncrementalListIterator(index); 513 } 514 515 520 public Iterator iterator() { 521 return new Iterator () { 524 int listIndex = 0; 525 526 public boolean hasNext() { 527 return (listIndex < elements.size()); 528 } 529 530 public Object next() { 531 if (listIndex >= elements.size()) 532 throw new NoSuchElementException ("no more elements"); 533 534 return get(listIndex++); 535 } 536 537 public void remove() { 538 throw new UnsupportedOperationException ("remove not supported."); 539 } 540 }; 541 } 542 543 546 public void add(int index, Object element) { 547 validateListObject(element); 548 549 synchronized (elements) { 550 elements.add(index, element); 551 } 552 } 553 554 557 public boolean add(Object o) { 558 validateListObject(o); 559 560 synchronized (elements) { 561 return elements.add(o); 562 } 563 } 564 565 568 public boolean addAll(Collection c) { 569 synchronized (elements) { 570 return elements.addAll(c); 571 } 572 } 573 574 577 public boolean addAll(int index, Collection c) { 578 synchronized (elements) { 579 return elements.addAll(index, c); 580 } 581 } 582 583 586 public void clear() { 587 synchronized (elements) { 588 elements.clear(); 589 } 590 } 591 592 595 public boolean contains(Object o) { 596 synchronized (elements) { 597 return elements.contains(o); 598 } 599 } 600 601 604 public boolean containsAll(Collection c) { 605 synchronized (elements) { 606 return elements.containsAll(c); 607 } 608 } 609 610 613 public Object get(int index) { 614 synchronized (elements) { 615 Object o = elements.get(index); 616 617 if (isUnresolved(o)) { 618 int pageStart = pageIndex(index) * pageSize; 620 resolveInterval(pageStart, pageStart + pageSize); 621 622 return elements.get(index); 623 } 624 else { 625 return o; 626 } 627 } 628 } 629 630 633 public int indexOf(Object o) { 634 return helper.indexOfObject(o); 635 } 636 637 640 public boolean isEmpty() { 641 synchronized (elements) { 642 return elements.isEmpty(); 643 } 644 } 645 646 649 public int lastIndexOf(Object o) { 650 return helper.lastIndexOfObject(o); 651 } 652 653 656 public Object remove(int index) { 657 synchronized (elements) { 658 return elements.remove(index); 659 } 660 } 661 662 665 public boolean remove(Object o) { 666 synchronized (elements) { 667 return elements.remove(o); 668 } 669 } 670 671 674 public boolean removeAll(Collection c) { 675 synchronized (elements) { 676 return elements.removeAll(c); 677 } 678 } 679 680 683 public boolean retainAll(Collection c) { 684 synchronized (elements) { 685 return elements.retainAll(c); 686 } 687 } 688 689 692 public Object set(int index, Object element) { 693 validateListObject(element); 694 695 synchronized (elements) { 696 return elements.set(index, element); 697 } 698 } 699 700 703 public int size() { 704 synchronized (elements) { 705 return elements.size(); 706 } 707 } 708 709 public List subList(int fromIndex, int toIndex) { 710 synchronized (elements) { 711 resolveInterval(fromIndex, toIndex); 712 return elements.subList(fromIndex, toIndex); 713 } 714 } 715 716 public Object [] toArray() { 717 resolveAll(); 718 719 return elements.toArray(); 720 } 721 722 725 public Object [] toArray(Object [] a) { 726 resolveAll(); 727 728 return elements.toArray(a); 729 } 730 731 734 public int getUnfetchedObjects() { 735 return unfetchedObjects; 736 } 737 738 abstract class IncrementalListHelper { 739 int indexOfObject(Object object) { 740 if (incorrectObjectType(object)) { 741 return -1; 742 } 743 744 synchronized (elements) { 745 for (int i = 0; i < elements.size(); i++) { 746 if (objectsAreEqual(object, elements.get(i))) { 747 return i; 748 } 749 } 750 } 751 return -1; 752 } 753 754 int lastIndexOfObject(Object object) { 755 if (incorrectObjectType(object)) { 756 return -1; 757 } 758 759 synchronized (elements) { 760 for (int i = elements.size() - 1; i >= 0; i--) { 761 if (objectsAreEqual(object, elements.get(i))) { 762 return i; 763 } 764 } 765 } 766 767 return -1; 768 } 769 770 void updateWithResolvedObjectInRange(Object object, int from, int to) { 771 boolean found = false; 772 773 synchronized (elements) { 774 775 for (int i = from; i < to; i++) { 776 if (replacesObject(object, elements.get(i))) { 777 elements.set(i, object); 778 found = true; 779 break; 780 } 781 } 782 } 783 784 if (!found) { 785 throw new CayenneRuntimeException("Can't find id for " + object); 786 } 787 } 788 789 abstract boolean incorrectObjectType(Object object); 790 791 abstract boolean objectsAreEqual(Object object, Object objectInTheList); 792 793 abstract boolean replacesObject(Object object, Object objectInTheList); 794 } 795 796 class DataObjectListHelper extends IncrementalListHelper { 797 boolean incorrectObjectType(Object object) { 798 if (!(object instanceof DataObject)) { 799 800 return true; 801 } 802 803 DataObject dataObj = (DataObject) object; 804 if (dataObj.getDataContext() != dataContext) { 805 return true; 806 } 807 808 if (!dataObj 809 .getObjectId() 810 .getObjectClass() 811 .getName() 812 .equals(rootEntity.getClassName())) { 813 return true; 814 } 815 816 return false; 817 } 818 819 boolean objectsAreEqual(Object object, Object objectInTheList) { 820 821 if (objectInTheList instanceof DataObject) { 822 return object == objectInTheList; 824 } 825 else { 826 return ((DataObject) object).getObjectId().getIdSnapshot().equals( 827 objectInTheList); 828 } 829 } 830 831 boolean replacesObject(Object object, Object objectInTheList) { 832 if (objectInTheList instanceof DataObject) { 833 return false; 834 } 835 836 DataObject dataObject = (DataObject) object; 837 return dataObject.getObjectId().getIdSnapshot().equals(objectInTheList); 838 } 839 } 840 841 class DataRowListHelper extends IncrementalListHelper { 842 boolean incorrectObjectType(Object object) { 843 if (!(object instanceof Map )) { 844 return true; 845 } 846 847 Map map = (Map ) object; 848 return map.size() != rowWidth; 849 } 850 851 boolean objectsAreEqual(Object object, Object objectInTheList) { 852 if (object == null && objectInTheList == null) { 853 return true; 854 } 855 856 if (object != null && objectInTheList != null) { 857 858 Map id = (Map ) objectInTheList; 859 Map map = (Map ) object; 860 861 Iterator it = id.keySet().iterator(); 863 864 while (it.hasNext()) { 865 Object key = it.next(); 866 Object value = id.get(key); 867 if (!Util.nullSafeEquals(value, map.get(key))) { 868 return false; 869 } 870 } 871 872 return true; 873 } 874 875 return false; 876 } 877 878 boolean replacesObject(Object object, Object objectInTheList) { 879 880 Map id = (Map ) objectInTheList; 881 if (id.size() == rowWidth) { 882 return false; 883 } 884 885 Map map = (Map ) object; 887 Iterator it = id.keySet().iterator(); 888 889 while (it.hasNext()) { 890 Object key = it.next(); 891 Object value = id.get(key); 892 if (!Util.nullSafeEquals(value, map.get(key))) { 893 return false; 894 } 895 } 896 897 return true; 898 } 899 } 900 901 class IncrementalListIterator implements ListIterator { 902 905 int listIndex; 906 907 public IncrementalListIterator(int startIndex) { 908 this.listIndex = startIndex; 909 } 910 911 public void add(Object o) { 912 throw new UnsupportedOperationException ("add operation not supported"); 913 } 914 915 public boolean hasNext() { 916 return (listIndex < elements.size()); 917 } 918 919 public boolean hasPrevious() { 920 return (listIndex > 0); 921 } 922 923 public Object next() { 924 if (listIndex >= elements.size()) 925 throw new NoSuchElementException ("at the end of the list"); 926 927 return get(listIndex++); 928 } 929 930 public int nextIndex() { 931 return listIndex; 932 } 933 934 public Object previous() { 935 if (listIndex < 1) 936 throw new NoSuchElementException ("at the beginning of the list"); 937 938 return get(--listIndex); 939 } 940 941 public int previousIndex() { 942 return (listIndex - 1); 943 } 944 945 public void remove() { 946 throw new UnsupportedOperationException ("remove operation not supported"); 947 } 948 949 public void set(Object o) { 950 throw new UnsupportedOperationException ("set operation not supported"); 951 } 952 } 953 } 954 | Popular Tags |