1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.healthmarketscience.jackcess.impl;
18
19 import java.io.IOException;
20 import java.nio.ByteBuffer;
21 import java.util.ArrayList;
22 import java.util.BitSet;
23 import java.util.List;
24
25
26
27
28
29
30 public class UsageMap
31 {
32
33 public static final byte MAP_TYPE_INLINE = 0x0;
34
35 public static final byte MAP_TYPE_REFERENCE = 0x1;
36
37
38 private static final int INVALID_BIT_INDEX = -1;
39
40
41 private final DatabaseImpl _database;
42
43 private final int _tablePageNum;
44
45 private int _startOffset;
46
47 private final short _rowStart;
48
49 private int _startPage;
50
51 private int _endPage;
52
53 private BitSet _pageNumbers = new BitSet();
54
55 private final ByteBuffer _tableBuffer;
56
57
58 private int _modCount;
59
60
61 private Handler _handler;
62
63
64 static final String MSG_PREFIX_UNRECOGNIZED_MAP = "Unrecognized map type: ";
65
66
67
68
69
70
71
72 private UsageMap(DatabaseImpl database, ByteBuffer tableBuffer,
73 int pageNum, short rowStart)
74 throws IOException
75 {
76 _database = database;
77 _tableBuffer = tableBuffer;
78 _tablePageNum = pageNum;
79 _rowStart = rowStart;
80 _tableBuffer.position(_rowStart + getFormat().OFFSET_USAGE_MAP_START);
81 _startOffset = _tableBuffer.position();
82 }
83
84 public DatabaseImpl getDatabase() {
85 return _database;
86 }
87
88 public JetFormat getFormat() {
89 return getDatabase().getFormat();
90 }
91
92 public PageChannel getPageChannel() {
93 return getDatabase().getPageChannel();
94 }
95
96
97
98
99
100
101
102 public static UsageMap read(DatabaseImpl database, ByteBuffer buf)
103 throws IOException
104 {
105 int umapRowNum = buf.get();
106 int umapPageNum = ByteUtil.get3ByteInt(buf);
107 return read(database, umapPageNum, umapRowNum, false);
108 }
109
110
111
112
113
114
115
116
117
118 static UsageMap read(DatabaseImpl database, int pageNum,
119 int rowNum, boolean isGlobal)
120 throws IOException
121 {
122 if(pageNum <= 0) {
123
124 throw new IllegalStateException("Invalid usage map page number " + pageNum);
125 }
126
127 JetFormat format = database.getFormat();
128 PageChannel pageChannel = database.getPageChannel();
129 ByteBuffer tableBuffer = pageChannel.createPageBuffer();
130 pageChannel.readPage(tableBuffer, pageNum);
131 short rowStart = TableImpl.findRowStart(tableBuffer, rowNum, format);
132 int rowEnd = TableImpl.findRowEnd(tableBuffer, rowNum, format);
133 tableBuffer.limit(rowEnd);
134 byte mapType = tableBuffer.get(rowStart);
135 UsageMapkcess/impl/UsageMap.html#UsageMap">UsageMap rtn = new UsageMap(database, tableBuffer, pageNum, rowStart);
136 rtn.initHandler(mapType, isGlobal);
137 return rtn;
138 }
139
140 private void initHandler(byte mapType, boolean isGlobal)
141 throws IOException
142 {
143 if (mapType == MAP_TYPE_INLINE) {
144 _handler = (isGlobal ? new GlobalInlineHandler() :
145 new InlineHandler());
146 } else if (mapType == MAP_TYPE_REFERENCE) {
147 _handler = (isGlobal ? new GlobalReferenceHandler() :
148 new ReferenceHandler());
149 } else {
150 throw new IOException(MSG_PREFIX_UNRECOGNIZED_MAP + mapType);
151 }
152 }
153
154 public PageCursor cursor() {
155 return new PageCursor();
156 }
157
158 public int getPageCount() {
159 return _pageNumbers.cardinality();
160 }
161
162 protected short getRowStart() {
163 return _rowStart;
164 }
165
166 protected int getRowEnd() {
167 return getTableBuffer().limit();
168 }
169
170 protected void setStartOffset(int startOffset) {
171 _startOffset = startOffset;
172 }
173
174 protected int getStartOffset() {
175 return _startOffset;
176 }
177
178 protected ByteBuffer getTableBuffer() {
179 return _tableBuffer;
180 }
181
182 protected int getTablePageNumber() {
183 return _tablePageNum;
184 }
185
186 protected int getStartPage() {
187 return _startPage;
188 }
189
190 protected int getEndPage() {
191 return _endPage;
192 }
193
194 protected BitSet getPageNumbers() {
195 return _pageNumbers;
196 }
197
198 protected void setPageRange(int newStartPage, int newEndPage) {
199 _startPage = newStartPage;
200 _endPage = newEndPage;
201 }
202
203 protected boolean isPageWithinRange(int pageNumber)
204 {
205 return((pageNumber >= _startPage) && (pageNumber < _endPage));
206 }
207
208 protected int getFirstPageNumber() {
209 return bitIndexToPageNumber(getNextBitIndex(-1),
210 RowIdImpl.LAST_PAGE_NUMBER);
211 }
212
213 protected int getNextPageNumber(int curPage) {
214 return bitIndexToPageNumber(
215 getNextBitIndex(pageNumberToBitIndex(curPage)),
216 RowIdImpl.LAST_PAGE_NUMBER);
217 }
218
219 protected int getNextBitIndex(int curIndex) {
220 return _pageNumbers.nextSetBit(curIndex + 1);
221 }
222
223 protected int getLastPageNumber() {
224 return bitIndexToPageNumber(getPrevBitIndex(_pageNumbers.length()),
225 RowIdImpl.FIRST_PAGE_NUMBER);
226 }
227
228 protected int getPrevPageNumber(int curPage) {
229 return bitIndexToPageNumber(
230 getPrevBitIndex(pageNumberToBitIndex(curPage)),
231 RowIdImpl.FIRST_PAGE_NUMBER);
232 }
233
234 protected int getPrevBitIndex(int curIndex) {
235 --curIndex;
236 while((curIndex >= 0) && !_pageNumbers.get(curIndex)) {
237 --curIndex;
238 }
239 return curIndex;
240 }
241
242 protected int bitIndexToPageNumber(int bitIndex,
243 int invalidPageNumber) {
244 return((bitIndex >= 0) ? (_startPage + bitIndex) : invalidPageNumber);
245 }
246
247 protected int pageNumberToBitIndex(int pageNumber) {
248 return((pageNumber >= 0) ? (pageNumber - _startPage) :
249 INVALID_BIT_INDEX);
250 }
251
252 protected void clearTableAndPages()
253 {
254
255 _pageNumbers.clear();
256 _startPage = 0;
257 _endPage = 0;
258 ++_modCount;
259
260
261 int tableStart = getRowStart() + 1;
262 int tableEnd = getRowEnd();
263 ByteUtil.clearRange(_tableBuffer, tableStart, tableEnd);
264 }
265
266 protected void writeTable()
267 throws IOException
268 {
269
270 getPageChannel().writePage(_tableBuffer, _tablePageNum, _rowStart);
271 }
272
273
274
275
276 protected void processMap(ByteBuffer buffer, int bufferStartPage)
277 {
278 int byteCount = 0;
279 while (buffer.hasRemaining()) {
280 byte b = buffer.get();
281 if(b != (byte)0) {
282 for (int i = 0; i < 8; i++) {
283 if ((b & (1 << i)) != 0) {
284 int pageNumberOffset = (byteCount * 8 + i) + bufferStartPage;
285 int pageNumber = bitIndexToPageNumber(
286 pageNumberOffset,
287 PageChannel.INVALID_PAGE_NUMBER);
288 if(!isPageWithinRange(pageNumber)) {
289 throw new IllegalStateException(
290 "found page number " + pageNumber
291 + " in usage map outside of expected range " +
292 _startPage + " to " + _endPage);
293 }
294 _pageNumbers.set(pageNumberOffset);
295 }
296 }
297 }
298 byteCount++;
299 }
300 }
301
302
303
304
305 public boolean containsPageNumber(int pageNumber) {
306 return _handler.containsPageNumber(pageNumber);
307 }
308
309
310
311
312 public void addPageNumber(int pageNumber) throws IOException {
313 ++_modCount;
314 _handler.addOrRemovePageNumber(pageNumber, true, false);
315 }
316
317
318
319
320 public void removePageNumber(int pageNumber)
321 throws IOException
322 {
323 removePageNumber(pageNumber, true);
324 }
325
326 private void removePageNumber(int pageNumber, boolean force)
327 throws IOException
328 {
329 ++_modCount;
330 _handler.addOrRemovePageNumber(pageNumber, false, force);
331 }
332
333 protected void updateMap(int absolutePageNumber,
334 int bufferRelativePageNumber,
335 ByteBuffer buffer, boolean add, boolean force)
336 throws IOException
337 {
338
339 int offset = bufferRelativePageNumber / 8;
340 int bitmask = 1 << (bufferRelativePageNumber % 8);
341 byte b = buffer.get(_startOffset + offset);
342
343
344 int pageNumberOffset = pageNumberToBitIndex(absolutePageNumber);
345 boolean isOn = _pageNumbers.get(pageNumberOffset);
346 if((isOn == add) && !force) {
347 throw new IOException("Page number " + absolutePageNumber + " already " +
348 ((add) ? "added to" : "removed from") +
349 " usage map, expected range " +
350 _startPage + " to " + _endPage);
351 }
352
353
354 if (add) {
355 b |= bitmask;
356 _pageNumbers.set(pageNumberOffset);
357 } else {
358 b &= ~bitmask;
359 _pageNumbers.clear(pageNumberOffset);
360 }
361 buffer.put(_startOffset + offset, b);
362 }
363
364
365
366
367 private void promoteInlineHandlerToReferenceHandler(int newPageNumber)
368 throws IOException
369 {
370
371 int oldStartPage = _startPage;
372 BitSet oldPageNumbers = (BitSet)_pageNumbers.clone();
373
374
375 clearTableAndPages();
376
377
378 _tableBuffer.put(getRowStart(), MAP_TYPE_REFERENCE);
379
380
381 writeTable();
382
383
384 _handler = new ReferenceHandler();
385
386
387 reAddPages(oldStartPage, oldPageNumbers, newPageNumber);
388 }
389
390 private void reAddPages(int oldStartPage, BitSet oldPageNumbers,
391 int newPageNumber)
392 throws IOException
393 {
394
395 for(int i = oldPageNumbers.nextSetBit(0); i >= 0;
396 i = oldPageNumbers.nextSetBit(i + 1)) {
397 addPageNumber(oldStartPage + i);
398 }
399
400 if(newPageNumber > PageChannel.INVALID_PAGE_NUMBER) {
401
402 addPageNumber(newPageNumber);
403 }
404 }
405
406 @Override
407 public String toString() {
408
409 List<String> ranges = new ArrayList<String>();
410 PageCursor pCursor = cursor();
411 int curRangeStart = Integer.MIN_VALUE;
412 int prevPage = Integer.MIN_VALUE;
413 while(true) {
414 int nextPage = pCursor.getNextPage();
415 if(nextPage < 0) {
416 break;
417 }
418
419 if(nextPage != (prevPage + 1)) {
420 if(prevPage >= 0) {
421 rangeToString(ranges, curRangeStart, prevPage);
422 }
423 curRangeStart = nextPage;
424 }
425 prevPage = nextPage;
426 }
427 if(prevPage >= 0) {
428 rangeToString(ranges, curRangeStart, prevPage);
429 }
430
431 return CustomToStringStyle.valueBuilder(
432 _handler.getClass().getSimpleName())
433 .append("range", "(" + _startPage + "-" + _endPage + ")")
434 .append("pageNumbers", ranges)
435 .toString();
436 }
437
438 private static void rangeToString(List<String> ranges, int rangeStart,
439 int rangeEnd)
440 {
441 if(rangeEnd > rangeStart) {
442 ranges.add(rangeStart + "-" + rangeEnd);
443 } else {
444 ranges.add(String.valueOf(rangeStart));
445 }
446 }
447
448 private static int toValidStartPage(int startPage) {
449
450 return ((startPage / 8) * 8);
451 }
452
453 private abstract class Handler
454 {
455 protected Handler() {
456 }
457
458 public boolean containsPageNumber(int pageNumber) {
459 return(isPageWithinRange(pageNumber) &&
460 getPageNumbers().get(pageNumberToBitIndex(pageNumber)));
461 }
462
463
464
465
466
467
468 public abstract void addOrRemovePageNumber(int pageNumber, boolean add,
469 boolean force)
470 throws IOException;
471 }
472
473
474
475
476
477
478
479
480
481 private class InlineHandler extends Handler
482 {
483 private final int _maxInlinePages;
484
485 protected InlineHandler() throws IOException
486 {
487 _maxInlinePages = (getInlineDataEnd() - getInlineDataStart()) * 8;
488 int startPage = getTableBuffer().getInt(getRowStart() + 1);
489 setInlinePageRange(startPage);
490 processMap(getTableBuffer(), 0);
491 }
492
493 protected final int getMaxInlinePages() {
494 return _maxInlinePages;
495 }
496
497 protected final int getInlineDataStart() {
498 return getRowStart() + getFormat().OFFSET_USAGE_MAP_START;
499 }
500
501 protected final int getInlineDataEnd() {
502 return getRowEnd();
503 }
504
505
506
507
508
509 private void setInlinePageRange(int startPage) {
510 setPageRange(startPage, startPage + getMaxInlinePages());
511 }
512
513 @Override
514 public void addOrRemovePageNumber(int pageNumber, boolean add,
515 boolean force)
516 throws IOException
517 {
518 if(isPageWithinRange(pageNumber)) {
519
520
521 int bufferRelativePageNumber = pageNumberToBitIndex(pageNumber);
522 updateMap(pageNumber, bufferRelativePageNumber, getTableBuffer(), add,
523 force);
524
525 writeTable();
526
527 } else {
528
529
530 addOrRemovePageNumberOutsideRange(pageNumber, add, force);
531 }
532 }
533
534 protected void addOrRemovePageNumberOutsideRange(
535 int pageNumber, boolean add, boolean force)
536 throws IOException
537 {
538
539
540 if(add) {
541
542 int firstPage = getFirstPageNumber();
543 int lastPage = getLastPageNumber();
544
545
546 if(firstPage <= PageChannel.INVALID_PAGE_NUMBER) {
547
548 firstPage = pageNumber;
549 lastPage = pageNumber;
550 } else if(pageNumber > lastPage) {
551 lastPage = pageNumber;
552 } else {
553 firstPage = pageNumber;
554 }
555
556 firstPage = toValidStartPage(firstPage);
557
558 if((lastPage - firstPage + 1) < getMaxInlinePages()) {
559
560
561 moveToNewStartPage(firstPage, pageNumber);
562
563 } else {
564
565
566 promoteInlineHandlerToReferenceHandler(pageNumber);
567 }
568
569 } else {
570
571
572 if(!force) {
573
574
575
576 throw new IOException("Page number " + pageNumber +
577 " already removed from usage map" +
578 ", expected range " +
579 _startPage + " to " + _endPage);
580 }
581 }
582 }
583
584
585
586
587
588
589
590 protected final void moveToNewStartPage(int newStartPage, int newPageNumber)
591 throws IOException
592 {
593 int oldStartPage = getStartPage();
594 BitSet oldPageNumbers = (BitSet)getPageNumbers().clone();
595
596
597 clearTableAndPages();
598
599
600 ByteBuffer tableBuffer = getTableBuffer();
601 tableBuffer.position(getRowStart() + 1);
602 tableBuffer.putInt(newStartPage);
603
604
605 writeTable();
606
607
608 setInlinePageRange(newStartPage);
609
610
611 reAddPages(oldStartPage, oldPageNumbers, newPageNumber);
612 }
613 }
614
615
616
617
618
619
620
621
622
623
624 private class GlobalInlineHandler extends InlineHandler
625 {
626 private GlobalInlineHandler() throws IOException {
627 }
628
629 @Override
630 public boolean containsPageNumber(int pageNumber) {
631
632 throw new UnsupportedOperationException();
633 }
634
635 @Override
636 protected void addOrRemovePageNumberOutsideRange(
637 int pageNumber, boolean add, boolean force)
638 throws IOException
639 {
640
641
642
643
644
645
646
647 if(!add) {
648
649 int firstPage = getFirstPageNumber();
650 int lastPage = getLastPageNumber();
651
652
653
654
655
656 if((firstPage <= PageChannel.INVALID_PAGE_NUMBER) ||
657 (pageNumber > lastPage)) {
658
659
660 moveToNewStartPageForRemove(firstPage, pageNumber);
661 }
662 }
663 }
664
665
666
667
668
669
670
671
672
673 private void moveToNewStartPageForRemove(int firstPage, int newPageNumber)
674 throws IOException
675 {
676 int oldEndPage = getEndPage();
677 int newStartPage =
678 toValidStartPage(
679 ((firstPage <= PageChannel.INVALID_PAGE_NUMBER) ? newPageNumber :
680
681 (newPageNumber - (getMaxInlinePages() / 2))));
682
683
684 moveToNewStartPage(newStartPage, PageChannel.INVALID_PAGE_NUMBER);
685
686 if(firstPage <= PageChannel.INVALID_PAGE_NUMBER) {
687
688
689 ByteUtil.fillRange(_tableBuffer, getInlineDataStart(),
690 getInlineDataEnd());
691
692
693 writeTable();
694
695
696 getPageNumbers().set(0, getMaxInlinePages());
697
698 } else {
699
700
701 for(int i = oldEndPage; i < getEndPage(); ++i) {
702 addPageNumber(i);
703 }
704 }
705
706
707 removePageNumber(newPageNumber, false);
708 }
709 }
710
711
712
713
714
715
716
717
718 private class ReferenceHandler extends Handler
719 {
720
721 private final TempPageHolder _mapPageHolder =
722 TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
723 private final int _maxPagesPerUsageMapPage;
724
725 private ReferenceHandler() throws IOException
726 {
727 _maxPagesPerUsageMapPage = ((getFormat().PAGE_SIZE -
728 getFormat().OFFSET_USAGE_MAP_PAGE_DATA) * 8);
729 int numUsagePages = (getRowEnd() - getRowStart() - 1) / 4;
730 setStartOffset(getFormat().OFFSET_USAGE_MAP_PAGE_DATA);
731 setPageRange(0, (numUsagePages * _maxPagesPerUsageMapPage));
732
733
734
735
736 for (int i = 0; i < numUsagePages; i++) {
737 int mapPageNum = getTableBuffer().getInt(
738 calculateMapPagePointerOffset(i));
739 if (mapPageNum > 0) {
740 ByteBuffer mapPageBuffer =
741 _mapPageHolder.setPage(getPageChannel(), mapPageNum);
742 byte pageType = mapPageBuffer.get();
743 if (pageType != PageTypes.USAGE_MAP) {
744 throw new IOException("Looking for usage map at page " +
745 mapPageNum + ", but page type is " +
746 pageType);
747 }
748 mapPageBuffer.position(getFormat().OFFSET_USAGE_MAP_PAGE_DATA);
749 processMap(mapPageBuffer, (_maxPagesPerUsageMapPage * i));
750 }
751 }
752 }
753
754 protected final int getMaxPagesPerUsagePage() {
755 return _maxPagesPerUsageMapPage;
756 }
757
758 @Override
759 public void addOrRemovePageNumber(int pageNumber, boolean add,
760 boolean force)
761 throws IOException
762 {
763 if(!isPageWithinRange(pageNumber)) {
764 if(force) {
765 return;
766 }
767 throw new IOException("Page number " + pageNumber +
768 " is out of supported range");
769 }
770 int pageIndex = (pageNumber / getMaxPagesPerUsagePage());
771 int mapPageNum = getTableBuffer().getInt(
772 calculateMapPagePointerOffset(pageIndex));
773 ByteBuffer mapPageBuffer = null;
774 if(mapPageNum > 0) {
775 mapPageBuffer = _mapPageHolder.setPage(getPageChannel(), mapPageNum);
776 } else {
777
778 mapPageBuffer = createNewUsageMapPage(pageIndex);
779 mapPageNum = _mapPageHolder.getPageNumber();
780 }
781 updateMap(pageNumber,
782 (pageNumber - (getMaxPagesPerUsagePage() * pageIndex)),
783 mapPageBuffer, add, force);
784 getPageChannel().writePage(mapPageBuffer, mapPageNum);
785 }
786
787
788
789
790
791
792 private ByteBuffer createNewUsageMapPage(int pageIndex) throws IOException
793 {
794 ByteBuffer mapPageBuffer = allocateNewUsageMapPage(pageIndex);
795 int mapPageNum = _mapPageHolder.getPageNumber();
796 getTableBuffer().putInt(calculateMapPagePointerOffset(pageIndex),
797 mapPageNum);
798 writeTable();
799 return mapPageBuffer;
800 }
801
802 private int calculateMapPagePointerOffset(int pageIndex) {
803 return getRowStart() + getFormat().OFFSET_REFERENCE_MAP_PAGE_NUMBERS +
804 (pageIndex * 4);
805 }
806
807 protected ByteBuffer allocateNewUsageMapPage(int pageIndex)
808 throws IOException
809 {
810 ByteBuffer mapPageBuffer = _mapPageHolder.setNewPage(getPageChannel());
811 mapPageBuffer.put(PageTypes.USAGE_MAP);
812 mapPageBuffer.put((byte) 0x01);
813 mapPageBuffer.putShort((short) 0);
814 return mapPageBuffer;
815 }
816 }
817
818
819
820
821
822
823
824
825
826
827
828
829
830 private class GlobalReferenceHandler extends ReferenceHandler
831 {
832 private boolean _allocatingPage;
833 private Integer _pendingPage;
834
835 private GlobalReferenceHandler() throws IOException {
836 }
837
838 @Override
839 public boolean containsPageNumber(int pageNumber) {
840
841 throw new UnsupportedOperationException();
842 }
843
844 @Override
845 public void addOrRemovePageNumber(int pageNumber, boolean add,
846 boolean force)
847 throws IOException
848 {
849 if(_allocatingPage && !add) {
850
851
852 if(_pendingPage != null) {
853 throw new IllegalStateException("should only have single pending page");
854 }
855 _pendingPage = pageNumber;
856 return;
857 }
858
859 super.addOrRemovePageNumber(pageNumber, add, force);
860
861 while(_pendingPage != null) {
862
863
864
865
866
867
868
869
870 int removedPageNumber = _pendingPage;
871 _pendingPage = null;
872
873 super.addOrRemovePageNumber(removedPageNumber, false, true);
874 }
875 }
876
877 @Override
878 protected ByteBuffer allocateNewUsageMapPage(int pageIndex)
879 throws IOException
880 {
881 try {
882
883
884 _allocatingPage = true;
885
886 ByteBuffer mapPageBuffer = super.allocateNewUsageMapPage(pageIndex);
887
888
889
890
891
892
893 int dataStart = getFormat().OFFSET_USAGE_MAP_PAGE_DATA;
894 ByteUtil.fillRange(mapPageBuffer, dataStart,
895 getFormat().PAGE_SIZE - dataStart);
896
897 int maxPagesPerUmapPage = getMaxPagesPerUsagePage();
898 int firstNewPage = (pageIndex * maxPagesPerUmapPage);
899 int lastNewPage = firstNewPage + maxPagesPerUmapPage;
900 _pageNumbers.set(firstNewPage, lastNewPage);
901
902 return mapPageBuffer;
903
904 } finally {
905 _allocatingPage = false;
906 }
907 }
908 }
909
910
911
912
913
914 public final class PageCursor
915 {
916
917 private final DirHandler _forwardDirHandler = new ForwardDirHandler();
918
919 private final DirHandler _reverseDirHandler = new ReverseDirHandler();
920
921 private int _curPageNumber;
922
923 private int _prevPageNumber;
924
925
926
927 private int _lastModCount;
928
929 private PageCursor() {
930 reset();
931 }
932
933 public UsageMap getUsageMap() {
934 return UsageMap.this;
935 }
936
937
938
939
940 private DirHandler getDirHandler(boolean moveForward) {
941 return (moveForward ? _forwardDirHandler : _reverseDirHandler);
942 }
943
944
945
946
947
948 public boolean isUpToDate() {
949 return(UsageMap.this._modCount == _lastModCount);
950 }
951
952
953
954
955
956 public int getNextPage() {
957 return getAnotherPage(CursorImpl.MOVE_FORWARD);
958 }
959
960
961
962
963
964 public int getPreviousPage() {
965 return getAnotherPage(CursorImpl.MOVE_REVERSE);
966 }
967
968
969
970
971 private int getAnotherPage(boolean moveForward) {
972 DirHandler handler = getDirHandler(moveForward);
973 if(_curPageNumber == handler.getEndPageNumber()) {
974 if(!isUpToDate()) {
975 restorePosition(_prevPageNumber);
976
977 } else {
978
979 return _curPageNumber;
980 }
981 }
982
983 checkForModification();
984
985 _prevPageNumber = _curPageNumber;
986 _curPageNumber = handler.getAnotherPageNumber(_curPageNumber);
987 return _curPageNumber;
988 }
989
990
991
992
993
994 public void reset() {
995 beforeFirst();
996 }
997
998
999
1000
1001
1002 public void beforeFirst() {
1003 reset(CursorImpl.MOVE_FORWARD);
1004 }
1005
1006
1007
1008
1009
1010 public void afterLast() {
1011 reset(CursorImpl.MOVE_REVERSE);
1012 }
1013
1014
1015
1016
1017 protected void reset(boolean moveForward) {
1018 _curPageNumber = getDirHandler(moveForward).getBeginningPageNumber();
1019 _prevPageNumber = _curPageNumber;
1020 _lastModCount = UsageMap.this._modCount;
1021 }
1022
1023
1024
1025
1026
1027 private void restorePosition(int curPageNumber)
1028 {
1029 restorePosition(curPageNumber, _curPageNumber);
1030 }
1031
1032
1033
1034
1035 protected void restorePosition(int curPageNumber, int prevPageNumber)
1036 {
1037 if((curPageNumber != _curPageNumber) ||
1038 (prevPageNumber != _prevPageNumber))
1039 {
1040 _prevPageNumber = updatePosition(prevPageNumber);
1041 _curPageNumber = updatePosition(curPageNumber);
1042 _lastModCount = UsageMap.this._modCount;
1043 } else {
1044 checkForModification();
1045 }
1046 }
1047
1048
1049
1050
1051 private void checkForModification() {
1052 if(!isUpToDate()) {
1053 _prevPageNumber = updatePosition(_prevPageNumber);
1054 _curPageNumber = updatePosition(_curPageNumber);
1055 _lastModCount = UsageMap.this._modCount;
1056 }
1057 }
1058
1059 private int updatePosition(int pageNumber) {
1060 if(pageNumber < UsageMap.this.getFirstPageNumber()) {
1061 pageNumber = RowIdImpl.FIRST_PAGE_NUMBER;
1062 } else if(pageNumber > UsageMap.this.getLastPageNumber()) {
1063 pageNumber = RowIdImpl.LAST_PAGE_NUMBER;
1064 }
1065 return pageNumber;
1066 }
1067
1068 @Override
1069 public String toString() {
1070 return getClass().getSimpleName() + " CurPosition " + _curPageNumber +
1071 ", PrevPosition " + _prevPageNumber;
1072 }
1073
1074
1075
1076
1077
1078
1079 private abstract class DirHandler {
1080 public abstract int getAnotherPageNumber(int curPageNumber);
1081 public abstract int getBeginningPageNumber();
1082 public abstract int getEndPageNumber();
1083 }
1084
1085
1086
1087
1088 private final class ForwardDirHandler extends DirHandler {
1089 @Override
1090 public int getAnotherPageNumber(int curPageNumber) {
1091 if(curPageNumber == getBeginningPageNumber()) {
1092 return UsageMap.this.getFirstPageNumber();
1093 }
1094 return UsageMap.this.getNextPageNumber(curPageNumber);
1095 }
1096 @Override
1097 public int getBeginningPageNumber() {
1098 return RowIdImpl.FIRST_PAGE_NUMBER;
1099 }
1100 @Override
1101 public int getEndPageNumber() {
1102 return RowIdImpl.LAST_PAGE_NUMBER;
1103 }
1104 }
1105
1106
1107
1108
1109 private final class ReverseDirHandler extends DirHandler {
1110 @Override
1111 public int getAnotherPageNumber(int curPageNumber) {
1112 if(curPageNumber == getBeginningPageNumber()) {
1113 return UsageMap.this.getLastPageNumber();
1114 }
1115 return UsageMap.this.getPrevPageNumber(curPageNumber);
1116 }
1117 @Override
1118 public int getBeginningPageNumber() {
1119 return RowIdImpl.LAST_PAGE_NUMBER;
1120 }
1121 @Override
1122 public int getEndPageNumber() {
1123 return RowIdImpl.FIRST_PAGE_NUMBER;
1124 }
1125 }
1126
1127 }
1128
1129 }