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