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
390
391
392
393
394
395
396
397
398
399
400
401 private void promoteGlobalInlineHandlerToReferenceHandler(
402 int frontierPageNumber)
403 throws IOException
404 {
405
406
407
408
409 clearTableAndPages();
410 _tableBuffer.put(getRowStart(), MAP_TYPE_REFERENCE);
411 writeTable();
412
413
414
415 _handler = new GlobalReferenceHandler();
416
417
418
419
420
421 for(int pageNumber = 0; pageNumber <= frontierPageNumber; ++pageNumber) {
422 _handler.addOrRemovePageNumber(pageNumber, false, true);
423 }
424 }
425
426 private void reAddPages(int oldStartPage, BitSet oldPageNumbers,
427 int newPageNumber)
428 throws IOException
429 {
430
431 for(int i = oldPageNumbers.nextSetBit(0); i >= 0;
432 i = oldPageNumbers.nextSetBit(i + 1)) {
433 addPageNumber(oldStartPage + i);
434 }
435
436 if(newPageNumber > PageChannel.INVALID_PAGE_NUMBER) {
437
438 addPageNumber(newPageNumber);
439 }
440 }
441
442 @Override
443 public String toString() {
444
445 List<String> ranges = new ArrayList<String>();
446 PageCursor pCursor = cursor();
447 int curRangeStart = Integer.MIN_VALUE;
448 int prevPage = Integer.MIN_VALUE;
449 while(true) {
450 int nextPage = pCursor.getNextPage();
451 if(nextPage < 0) {
452 break;
453 }
454
455 if(nextPage != (prevPage + 1)) {
456 if(prevPage >= 0) {
457 rangeToString(ranges, curRangeStart, prevPage);
458 }
459 curRangeStart = nextPage;
460 }
461 prevPage = nextPage;
462 }
463 if(prevPage >= 0) {
464 rangeToString(ranges, curRangeStart, prevPage);
465 }
466
467 return CustomToStringStyle.valueBuilder(
468 _handler.getClass().getSimpleName())
469 .append("range", "(" + _startPage + "-" + _endPage + ")")
470 .append("pageNumbers", ranges)
471 .toString();
472 }
473
474 private static void rangeToString(List<String> ranges, int rangeStart,
475 int rangeEnd)
476 {
477 if(rangeEnd > rangeStart) {
478 ranges.add(rangeStart + "-" + rangeEnd);
479 } else {
480 ranges.add(String.valueOf(rangeStart));
481 }
482 }
483
484 private static int toValidStartPage(int startPage) {
485
486 return ((startPage / 8) * 8);
487 }
488
489 private abstract class Handler
490 {
491 protected Handler() {
492 }
493
494 public boolean containsPageNumber(int pageNumber) {
495 return(isPageWithinRange(pageNumber) &&
496 getPageNumbers().get(pageNumberToBitIndex(pageNumber)));
497 }
498
499
500
501
502
503
504 public abstract void addOrRemovePageNumber(int pageNumber, boolean add,
505 boolean force)
506 throws IOException;
507 }
508
509
510
511
512
513
514
515
516
517 private class InlineHandler extends Handler
518 {
519 private final int _maxInlinePages;
520
521 protected InlineHandler()
522 {
523 _maxInlinePages = (getInlineDataEnd() - getInlineDataStart()) * 8;
524 int startPage = getTableBuffer().getInt(getRowStart() + 1);
525 setInlinePageRange(startPage);
526 processMap(getTableBuffer(), 0);
527 }
528
529 protected final int getMaxInlinePages() {
530 return _maxInlinePages;
531 }
532
533 protected final int getInlineDataStart() {
534 return getRowStart() + getFormat().OFFSET_USAGE_MAP_START;
535 }
536
537 protected final int getInlineDataEnd() {
538 return getRowEnd();
539 }
540
541
542
543
544
545 private void setInlinePageRange(int startPage) {
546 setPageRange(startPage, startPage + getMaxInlinePages());
547 }
548
549 @Override
550 public void addOrRemovePageNumber(int pageNumber, boolean add,
551 boolean force)
552 throws IOException
553 {
554 if(isPageWithinRange(pageNumber)) {
555
556
557 int bufferRelativePageNumber = pageNumberToBitIndex(pageNumber);
558 updateMap(pageNumber, bufferRelativePageNumber, getTableBuffer(), add,
559 force);
560
561 writeTable();
562
563 } else {
564
565
566 addOrRemovePageNumberOutsideRange(pageNumber, add, force);
567 }
568 }
569
570 protected void addOrRemovePageNumberOutsideRange(
571 int pageNumber, boolean add, boolean force)
572 throws IOException
573 {
574
575
576 if(add) {
577
578 int firstPage = getFirstPageNumber();
579 int lastPage = getLastPageNumber();
580
581
582 if(firstPage <= PageChannel.INVALID_PAGE_NUMBER) {
583
584 firstPage = pageNumber;
585 lastPage = pageNumber;
586 } else if(pageNumber > lastPage) {
587 lastPage = pageNumber;
588 } else {
589 firstPage = pageNumber;
590 }
591
592 firstPage = toValidStartPage(firstPage);
593
594 if((lastPage - firstPage + 1) < getMaxInlinePages()) {
595
596
597 moveToNewStartPage(firstPage, pageNumber);
598
599 } else {
600
601
602 promoteInlineHandlerToReferenceHandler(pageNumber);
603 }
604
605 } else {
606
607
608 if(!force) {
609
610
611
612 throw new IOException("Page number " + pageNumber +
613 " already removed from usage map" +
614 ", expected range " +
615 _startPage + " to " + _endPage);
616 }
617 }
618 }
619
620
621
622
623
624
625
626 protected final void moveToNewStartPage(int newStartPage, int newPageNumber)
627 throws IOException
628 {
629 int oldStartPage = getStartPage();
630 BitSet oldPageNumbers = (BitSet)getPageNumbers().clone();
631
632
633 clearTableAndPages();
634
635
636 ByteBuffer tableBuffer = getTableBuffer();
637 tableBuffer.position(getRowStart() + 1);
638 tableBuffer.putInt(newStartPage);
639
640
641 writeTable();
642
643
644 setInlinePageRange(newStartPage);
645
646
647 reAddPages(oldStartPage, oldPageNumbers, newPageNumber);
648 }
649 }
650
651
652
653
654
655
656
657
658
659
660
661
662
663 private class GlobalInlineHandler extends InlineHandler
664 {
665 private GlobalInlineHandler() {
666 }
667
668 @Override
669 public boolean containsPageNumber(int pageNumber) {
670
671 throw new UnsupportedOperationException();
672 }
673
674 @Override
675 protected void addOrRemovePageNumberOutsideRange(
676 int pageNumber, boolean add, boolean force)
677 throws IOException
678 {
679
680
681
682
683
684 if(!add) {
685
686
687
688
689 promoteGlobalInlineHandlerToReferenceHandler(pageNumber);
690 }
691 }
692 }
693
694
695
696
697
698
699
700
701 private class ReferenceHandler extends Handler
702 {
703
704 private final TempPageHolder _mapPageHolder =
705 TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
706 private final int _maxPagesPerUsageMapPage;
707
708 private ReferenceHandler() throws IOException
709 {
710 _maxPagesPerUsageMapPage = ((getFormat().PAGE_SIZE -
711 getFormat().OFFSET_USAGE_MAP_PAGE_DATA) * 8);
712 int numUsagePages = (getRowEnd() - getRowStart() - 1) / 4;
713 setStartOffset(getFormat().OFFSET_USAGE_MAP_PAGE_DATA);
714 setPageRange(0, (numUsagePages * _maxPagesPerUsageMapPage));
715
716
717
718
719 for (int i = 0; i < numUsagePages; i++) {
720 int mapPageNum = getTableBuffer().getInt(
721 calculateMapPagePointerOffset(i));
722 if (mapPageNum > 0) {
723 ByteBuffer mapPageBuffer =
724 _mapPageHolder.setPage(getPageChannel(), mapPageNum);
725 byte pageType = mapPageBuffer.get();
726 if (pageType != PageTypes.USAGE_MAP) {
727 throw new IOException("Looking for usage map at page " +
728 mapPageNum + ", but page type is " +
729 pageType);
730 }
731 mapPageBuffer.position(getFormat().OFFSET_USAGE_MAP_PAGE_DATA);
732 processMap(mapPageBuffer, (_maxPagesPerUsageMapPage * i));
733 }
734 }
735 }
736
737 protected final int getMaxPagesPerUsagePage() {
738 return _maxPagesPerUsageMapPage;
739 }
740
741 @Override
742 public void addOrRemovePageNumber(int pageNumber, boolean add,
743 boolean force)
744 throws IOException
745 {
746 if(!isPageWithinRange(pageNumber)) {
747 if(force) {
748 return;
749 }
750 throw new IOException("Page number " + pageNumber +
751 " is out of supported range");
752 }
753 int pageIndex = (pageNumber / getMaxPagesPerUsagePage());
754 int mapPageNum = getTableBuffer().getInt(
755 calculateMapPagePointerOffset(pageIndex));
756 ByteBuffer mapPageBuffer = null;
757 if(mapPageNum > 0) {
758 mapPageBuffer = _mapPageHolder.setPage(getPageChannel(), mapPageNum);
759 } else {
760
761 mapPageBuffer = createNewUsageMapPage(pageIndex);
762 mapPageNum = _mapPageHolder.getPageNumber();
763 }
764 updateMap(pageNumber,
765 (pageNumber - (getMaxPagesPerUsagePage() * pageIndex)),
766 mapPageBuffer, add, force);
767 getPageChannel().writePage(mapPageBuffer, mapPageNum);
768 }
769
770
771
772
773
774
775 private ByteBuffer createNewUsageMapPage(int pageIndex) throws IOException
776 {
777 ByteBuffer mapPageBuffer = allocateNewUsageMapPage(pageIndex);
778 int mapPageNum = _mapPageHolder.getPageNumber();
779 getTableBuffer().putInt(calculateMapPagePointerOffset(pageIndex),
780 mapPageNum);
781 writeTable();
782 return mapPageBuffer;
783 }
784
785 private int calculateMapPagePointerOffset(int pageIndex) {
786 return getRowStart() + getFormat().OFFSET_REFERENCE_MAP_PAGE_NUMBERS +
787 (pageIndex * 4);
788 }
789
790 protected ByteBuffer allocateNewUsageMapPage(int pageIndex)
791 throws IOException
792 {
793 ByteBuffer mapPageBuffer = _mapPageHolder.setNewPage(getPageChannel());
794 mapPageBuffer.put(PageTypes.USAGE_MAP);
795 mapPageBuffer.put((byte) 0x01);
796 mapPageBuffer.putShort((short) 0);
797 return mapPageBuffer;
798 }
799 }
800
801
802
803
804
805
806
807
808
809
810
811
812
813 private class GlobalReferenceHandler extends ReferenceHandler
814 {
815 private boolean _allocatingPage;
816 private Integer _pendingPage;
817
818 private GlobalReferenceHandler() throws IOException {
819 }
820
821 @Override
822 public boolean containsPageNumber(int pageNumber) {
823
824 throw new UnsupportedOperationException();
825 }
826
827 @Override
828 public void addOrRemovePageNumber(int pageNumber, boolean add,
829 boolean force)
830 throws IOException
831 {
832 if(_allocatingPage && !add) {
833
834
835 if(_pendingPage != null) {
836 throw new IllegalStateException("should only have single pending page");
837 }
838 _pendingPage = pageNumber;
839 return;
840 }
841
842 super.addOrRemovePageNumber(pageNumber, add, force);
843
844 while(_pendingPage != null) {
845
846
847
848
849
850
851
852
853 int removedPageNumber = _pendingPage;
854 _pendingPage = null;
855
856 super.addOrRemovePageNumber(removedPageNumber, false, true);
857 }
858 }
859
860 @Override
861 protected ByteBuffer allocateNewUsageMapPage(int pageIndex)
862 throws IOException
863 {
864 try {
865
866
867 _allocatingPage = true;
868
869 ByteBuffer mapPageBuffer = super.allocateNewUsageMapPage(pageIndex);
870
871
872
873
874
875
876 int dataStart = getFormat().OFFSET_USAGE_MAP_PAGE_DATA;
877 ByteUtil.fillRange(mapPageBuffer, dataStart,
878 getFormat().PAGE_SIZE - dataStart);
879
880 int maxPagesPerUmapPage = getMaxPagesPerUsagePage();
881 int firstNewPage = (pageIndex * maxPagesPerUmapPage);
882 int lastNewPage = firstNewPage + maxPagesPerUmapPage;
883 _pageNumbers.set(firstNewPage, lastNewPage);
884
885 return mapPageBuffer;
886
887 } finally {
888 _allocatingPage = false;
889 }
890 }
891 }
892
893
894
895
896
897 public final class PageCursor
898 {
899
900 private final DirHandler _forwardDirHandler = new ForwardDirHandler();
901
902 private final DirHandler _reverseDirHandler = new ReverseDirHandler();
903
904 private int _curPageNumber;
905
906 private int _prevPageNumber;
907
908
909
910 private int _lastModCount;
911
912 private PageCursor() {
913 reset();
914 }
915
916 public UsageMap getUsageMap() {
917 return UsageMap.this;
918 }
919
920
921
922
923 private DirHandler getDirHandler(boolean moveForward) {
924 return (moveForward ? _forwardDirHandler : _reverseDirHandler);
925 }
926
927
928
929
930
931 public boolean isUpToDate() {
932 return(UsageMap.this._modCount == _lastModCount);
933 }
934
935
936
937
938
939 public int getNextPage() {
940 return getAnotherPage(CursorImpl.MOVE_FORWARD);
941 }
942
943
944
945
946
947 public int getPreviousPage() {
948 return getAnotherPage(CursorImpl.MOVE_REVERSE);
949 }
950
951
952
953
954 private int getAnotherPage(boolean moveForward) {
955 DirHandler handler = getDirHandler(moveForward);
956 if(_curPageNumber == handler.getEndPageNumber()) {
957 if(!isUpToDate()) {
958 restorePosition(_prevPageNumber);
959
960 } else {
961
962 return _curPageNumber;
963 }
964 }
965
966 checkForModification();
967
968 _prevPageNumber = _curPageNumber;
969 _curPageNumber = handler.getAnotherPageNumber(_curPageNumber);
970 return _curPageNumber;
971 }
972
973
974
975
976
977 public void reset() {
978 beforeFirst();
979 }
980
981
982
983
984
985 public void beforeFirst() {
986 reset(CursorImpl.MOVE_FORWARD);
987 }
988
989
990
991
992
993 public void afterLast() {
994 reset(CursorImpl.MOVE_REVERSE);
995 }
996
997
998
999
1000 protected void reset(boolean moveForward) {
1001 _curPageNumber = getDirHandler(moveForward).getBeginningPageNumber();
1002 _prevPageNumber = _curPageNumber;
1003 _lastModCount = UsageMap.this._modCount;
1004 }
1005
1006
1007
1008
1009
1010 private void restorePosition(int curPageNumber)
1011 {
1012 restorePosition(curPageNumber, _curPageNumber);
1013 }
1014
1015
1016
1017
1018 protected void restorePosition(int curPageNumber, int prevPageNumber)
1019 {
1020 if((curPageNumber != _curPageNumber) ||
1021 (prevPageNumber != _prevPageNumber))
1022 {
1023 _prevPageNumber = updatePosition(prevPageNumber);
1024 _curPageNumber = updatePosition(curPageNumber);
1025 _lastModCount = UsageMap.this._modCount;
1026 } else {
1027 checkForModification();
1028 }
1029 }
1030
1031
1032
1033
1034 private void checkForModification() {
1035 if(!isUpToDate()) {
1036 _prevPageNumber = updatePosition(_prevPageNumber);
1037 _curPageNumber = updatePosition(_curPageNumber);
1038 _lastModCount = UsageMap.this._modCount;
1039 }
1040 }
1041
1042 private int updatePosition(int pageNumber) {
1043 if(pageNumber < UsageMap.this.getFirstPageNumber()) {
1044 pageNumber = RowIdImpl.FIRST_PAGE_NUMBER;
1045 } else if(pageNumber > UsageMap.this.getLastPageNumber()) {
1046 pageNumber = RowIdImpl.LAST_PAGE_NUMBER;
1047 }
1048 return pageNumber;
1049 }
1050
1051 @Override
1052 public String toString() {
1053 return getClass().getSimpleName() + " CurPosition " + _curPageNumber +
1054 ", PrevPosition " + _prevPageNumber;
1055 }
1056
1057
1058
1059
1060
1061
1062 private abstract class DirHandler {
1063 public abstract int getAnotherPageNumber(int curPageNumber);
1064 public abstract int getBeginningPageNumber();
1065 public abstract int getEndPageNumber();
1066 }
1067
1068
1069
1070
1071 private final class ForwardDirHandler extends DirHandler {
1072 @Override
1073 public int getAnotherPageNumber(int curPageNumber) {
1074 if(curPageNumber == getBeginningPageNumber()) {
1075 return UsageMap.this.getFirstPageNumber();
1076 }
1077 return UsageMap.this.getNextPageNumber(curPageNumber);
1078 }
1079 @Override
1080 public int getBeginningPageNumber() {
1081 return RowIdImpl.FIRST_PAGE_NUMBER;
1082 }
1083 @Override
1084 public int getEndPageNumber() {
1085 return RowIdImpl.LAST_PAGE_NUMBER;
1086 }
1087 }
1088
1089
1090
1091
1092 private final class ReverseDirHandler extends DirHandler {
1093 @Override
1094 public int getAnotherPageNumber(int curPageNumber) {
1095 if(curPageNumber == getBeginningPageNumber()) {
1096 return UsageMap.this.getLastPageNumber();
1097 }
1098 return UsageMap.this.getPrevPageNumber(curPageNumber);
1099 }
1100 @Override
1101 public int getBeginningPageNumber() {
1102 return RowIdImpl.LAST_PAGE_NUMBER;
1103 }
1104 @Override
1105 public int getEndPageNumber() {
1106 return RowIdImpl.FIRST_PAGE_NUMBER;
1107 }
1108 }
1109
1110 }
1111
1112 }