Source Code Cross Referenced for Cursor.java in  » Database-Client » Jackcess » com » healthmarketscience » jackcess » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database Client » Jackcess » com.healthmarketscience.jackcess 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:        Copyright (c) 2007 Health Market Science, Inc.
0003:
0004:        This library is free software; you can redistribute it and/or
0005:        modify it under the terms of the GNU Lesser General Public
0006:        License as published by the Free Software Foundation; either
0007:        version 2.1 of the License, or (at your option) any later version.
0008:
0009:        This library is distributed in the hope that it will be useful,
0010:        but WITHOUT ANY WARRANTY; without even the implied warranty of
0011:        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012:        Lesser General Public License for more details.
0013:
0014:        You should have received a copy of the GNU Lesser General Public
0015:        License along with this library; if not, write to the Free Software
0016:        Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
0017:        USA
0018:
0019:        You can contact Health Market Science at info@healthmarketscience.com
0020:        or at the following address:
0021:
0022:        Health Market Science
0023:        2700 Horizon Drive
0024:        Suite 200
0025:        King of Prussia, PA 19406
0026:         */
0027:
0028:        package com.healthmarketscience.jackcess;
0029:
0030:        import java.io.IOException;
0031:        import java.util.Collection;
0032:        import java.util.Iterator;
0033:        import java.util.LinkedHashMap;
0034:        import java.util.Map;
0035:        import java.util.NoSuchElementException;
0036:
0037:        import com.healthmarketscience.jackcess.Table.RowState;
0038:        import org.apache.commons.lang.ObjectUtils;
0039:        import org.apache.commons.logging.Log;
0040:        import org.apache.commons.logging.LogFactory;
0041:
0042:        /**
0043:         * Manages iteration for a Table.  Different cursors provide different methods
0044:         * of traversing a table.  Cursors should be fairly robust in the face of
0045:         * table modification during traversal (although depending on how the table is
0046:         * traversed, row updates may or may not be seen).  Multiple cursors may
0047:         * traverse the same table simultaneously.
0048:         * <p>
0049:         * The Cursor provides a variety of static utility methods to construct
0050:         * cursors with given characteristics or easily search for specific values.
0051:         * For even friendlier and more flexible construction, see
0052:         * {@link CursorBuilder}.
0053:         * <p>
0054:         * Is not thread-safe.
0055:         *
0056:         * @author james
0057:         */
0058:        public abstract class Cursor implements  Iterable<Map<String, Object>> {
0059:            private static final Log LOG = LogFactory.getLog(Cursor.class);
0060:
0061:            /** first position for the TableScanCursor */
0062:            private static final ScanPosition FIRST_SCAN_POSITION = new ScanPosition(
0063:                    RowId.FIRST_ROW_ID);
0064:            /** last position for the TableScanCursor */
0065:            private static final ScanPosition LAST_SCAN_POSITION = new ScanPosition(
0066:                    RowId.LAST_ROW_ID);
0067:
0068:            /** identifier for this cursor */
0069:            private final Id _id;
0070:            /** owning table */
0071:            private final Table _table;
0072:            /** State used for reading the table rows */
0073:            private final RowState _rowState;
0074:            /** the first (exclusive) row id for this cursor */
0075:            private final Position _firstPos;
0076:            /** the last (exclusive) row id for this cursor */
0077:            private final Position _lastPos;
0078:            /** the previous row */
0079:            private Position _prevPos;
0080:            /** the current row */
0081:            private Position _curPos;
0082:
0083:            protected Cursor(Id id, Table table, Position firstPos,
0084:                    Position lastPos) {
0085:                _id = id;
0086:                _table = table;
0087:                _rowState = _table.createRowState();
0088:                _firstPos = firstPos;
0089:                _lastPos = lastPos;
0090:                _curPos = firstPos;
0091:                _prevPos = firstPos;
0092:            }
0093:
0094:            /**
0095:             * Creates a normal, un-indexed cursor for the given table.
0096:             * @param table the table over which this cursor will traverse
0097:             */
0098:            public static Cursor createCursor(Table table) {
0099:                return new TableScanCursor(table);
0100:            }
0101:
0102:            /**
0103:             * Creates an indexed cursor for the given table.
0104:             * @param table the table over which this cursor will traverse
0105:             * @param index index for the table which will define traversal order as
0106:             *              well as enhance certain lookups
0107:             */
0108:            public static Cursor createIndexCursor(Table table, Index index)
0109:                    throws IOException {
0110:                return createIndexCursor(table, index, null, null);
0111:            }
0112:
0113:            /**
0114:             * Creates an indexed cursor for the given table, narrowed to the given
0115:             * range.
0116:             * @param table the table over which this cursor will traverse
0117:             * @param index index for the table which will define traversal order as
0118:             *              well as enhance certain lookups
0119:             * @param startRow the first row of data for the cursor (inclusive), or
0120:             *                 {@code null} for the first entry
0121:             * @param endRow the last row of data for the cursor (inclusive), or
0122:             *               {@code null} for the last entry
0123:             */
0124:            public static Cursor createIndexCursor(Table table, Index index,
0125:                    Object[] startRow, Object[] endRow) throws IOException {
0126:                return createIndexCursor(table, index, startRow, true, endRow,
0127:                        true);
0128:            }
0129:
0130:            /**
0131:             * Creates an indexed cursor for the given table, narrowed to the given
0132:             * range.
0133:             * @param table the table over which this cursor will traverse
0134:             * @param index index for the table which will define traversal order as
0135:             *              well as enhance certain lookups
0136:             * @param startRow the first row of data for the cursor, or {@code null} for
0137:             *                 the first entry
0138:             * @param startInclusive whether or not startRow is inclusive or exclusive
0139:             * @param endRow the last row of data for the cursor, or {@code null} for
0140:             *               the last entry
0141:             * @param endInclusive whether or not endRow is inclusive or exclusive
0142:             */
0143:            public static Cursor createIndexCursor(Table table, Index index,
0144:                    Object[] startRow, boolean startInclusive, Object[] endRow,
0145:                    boolean endInclusive) throws IOException {
0146:                if (table != index.getTable()) {
0147:                    throw new IllegalArgumentException(
0148:                            "Given index is not for given table: " + index
0149:                                    + ", " + table);
0150:                }
0151:                return new IndexCursor(table, index, index.cursor(startRow,
0152:                        startInclusive, endRow, endInclusive));
0153:            }
0154:
0155:            /**
0156:             * Convenience method for finding a specific row in a table which matches a
0157:             * given row "pattern".  See {@link #findRow(Map)} for details on the
0158:             * rowPattern.
0159:             * 
0160:             * @param table the table to search
0161:             * @param rowPattern pattern to be used to find the row
0162:             * @return the matching row or {@code null} if a match could not be found.
0163:             */
0164:            public static Map<String, Object> findRow(Table table,
0165:                    Map<String, Object> rowPattern) throws IOException {
0166:                Cursor cursor = createCursor(table);
0167:                if (cursor.findRow(rowPattern)) {
0168:                    return cursor.getCurrentRow();
0169:                }
0170:                return null;
0171:            }
0172:
0173:            /**
0174:             * Convenience method for finding a specific row in a table which matches a
0175:             * given row "pattern".  See {@link #findRow(Column,Object)} for details on
0176:             * the pattern.
0177:             * <p>
0178:             * Note, a {@code null} result value is ambiguous in that it could imply no
0179:             * match or a matching row with {@code null} for the desired value.  If
0180:             * distinguishing this situation is important, you will need to use a Cursor
0181:             * directly instead of this convenience method.
0182:             * 
0183:             * @param table the table to search
0184:             * @param column column whose value should be returned
0185:             * @param columnPattern column being matched by the valuePattern
0186:             * @param valuePattern value from the columnPattern which will match the
0187:             *                     desired row
0188:             * @return the matching row or {@code null} if a match could not be found.
0189:             */
0190:            public static Object findValue(Table table, Column column,
0191:                    Column columnPattern, Object valuePattern)
0192:                    throws IOException {
0193:                Cursor cursor = createCursor(table);
0194:                if (cursor.findRow(columnPattern, valuePattern)) {
0195:                    return cursor.getCurrentRowValue(column);
0196:                }
0197:                return null;
0198:            }
0199:
0200:            /**
0201:             * Convenience method for finding a specific row in an indexed table which
0202:             * matches a given row "pattern".  See {@link #findRow(Map)} for details on
0203:             * the rowPattern.
0204:             * 
0205:             * @param table the table to search
0206:             * @param index index to assist the search
0207:             * @param rowPattern pattern to be used to find the row
0208:             * @return the matching row or {@code null} if a match could not be found.
0209:             */
0210:            public static Map<String, Object> findRow(Table table, Index index,
0211:                    Map<String, Object> rowPattern) throws IOException {
0212:                Cursor cursor = createIndexCursor(table, index);
0213:                if (cursor.findRow(rowPattern)) {
0214:                    return cursor.getCurrentRow();
0215:                }
0216:                return null;
0217:            }
0218:
0219:            /**
0220:             * Convenience method for finding a specific row in a table which matches a
0221:             * given row "pattern".  See {@link #findRow(Column,Object)} for details on
0222:             * the pattern.
0223:             * <p>
0224:             * Note, a {@code null} result value is ambiguous in that it could imply no
0225:             * match or a matching row with {@code null} for the desired value.  If
0226:             * distinguishing this situation is important, you will need to use a Cursor
0227:             * directly instead of this convenience method.
0228:             * 
0229:             * @param table the table to search
0230:             * @param index index to assist the search
0231:             * @param column column whose value should be returned
0232:             * @param columnPattern column being matched by the valuePattern
0233:             * @param valuePattern value from the columnPattern which will match the
0234:             *                     desired row
0235:             * @return the matching row or {@code null} if a match could not be found.
0236:             */
0237:            public static Object findValue(Table table, Index index,
0238:                    Column column, Column columnPattern, Object valuePattern)
0239:                    throws IOException {
0240:                Cursor cursor = createIndexCursor(table, index);
0241:                if (cursor.findRow(columnPattern, valuePattern)) {
0242:                    return cursor.getCurrentRowValue(column);
0243:                }
0244:                return null;
0245:            }
0246:
0247:            public Id getId() {
0248:                return _id;
0249:            }
0250:
0251:            public Table getTable() {
0252:                return _table;
0253:            }
0254:
0255:            public Index getIndex() {
0256:                return null;
0257:            }
0258:
0259:            public JetFormat getFormat() {
0260:                return getTable().getFormat();
0261:            }
0262:
0263:            public PageChannel getPageChannel() {
0264:                return getTable().getPageChannel();
0265:            }
0266:
0267:            /**
0268:             * Returns the current state of the cursor which can be restored at a future
0269:             * point in time by a call to {@link #restoreSavepoint}.
0270:             * <p>
0271:             * Savepoints may be used across different cursor instances for the same
0272:             * table, but they must have the same {@link Id}.
0273:             */
0274:            public Savepoint getSavepoint() {
0275:                return new Savepoint(_id, _curPos, _prevPos);
0276:            }
0277:
0278:            /**
0279:             * Moves the cursor to a savepoint previously returned from
0280:             * {@link #getSavepoint}.
0281:             * @throws IllegalArgumentException if the given savepoint does not have a
0282:             *         cursorId equal to this cursor's id
0283:             */
0284:            public void restoreSavepoint(Savepoint savepoint)
0285:                    throws IOException {
0286:                if (!_id.equals(savepoint.getCursorId())) {
0287:                    throw new IllegalArgumentException("Savepoint " + savepoint
0288:                            + " is not valid for this cursor with id " + _id);
0289:                }
0290:                restorePosition(savepoint.getCurrentPosition(), savepoint
0291:                        .getPreviousPosition());
0292:            }
0293:
0294:            /**
0295:             * Returns the first row id (exclusive) as defined by this cursor.
0296:             */
0297:            protected Position getFirstPosition() {
0298:                return _firstPos;
0299:            }
0300:
0301:            /**
0302:             * Returns the last row id (exclusive) as defined by this cursor.
0303:             */
0304:            protected Position getLastPosition() {
0305:                return _lastPos;
0306:            }
0307:
0308:            /**
0309:             * Resets this cursor for forward traversal.  Calls {@link #beforeFirst}.
0310:             */
0311:            public void reset() {
0312:                beforeFirst();
0313:            }
0314:
0315:            /**
0316:             * Resets this cursor for forward traversal (sets cursor to before the first
0317:             * row).
0318:             */
0319:            public void beforeFirst() {
0320:                reset(true);
0321:            }
0322:
0323:            /**
0324:             * Resets this cursor for reverse traversal (sets cursor to after the last
0325:             * row).
0326:             */
0327:            public void afterLast() {
0328:                reset(false);
0329:            }
0330:
0331:            /**
0332:             * Returns {@code true} if the cursor is currently positioned before the
0333:             * first row, {@code false} otherwise.
0334:             */
0335:            public boolean isBeforeFirst() throws IOException {
0336:                if (getFirstPosition().equals(_curPos)) {
0337:                    return !recheckPosition(false);
0338:                }
0339:                return false;
0340:            }
0341:
0342:            /**
0343:             * Returns {@code true} if the cursor is currently positioned after the
0344:             * last row, {@code false} otherwise.
0345:             */
0346:            public boolean isAfterLast() throws IOException {
0347:                if (getLastPosition().equals(_curPos)) {
0348:                    return !recheckPosition(true);
0349:                }
0350:                return false;
0351:            }
0352:
0353:            /**
0354:             * Returns {@code true} if the row at which the cursor is currently
0355:             * positioned is deleted, {@code false} otherwise (including invalid rows).
0356:             */
0357:            public boolean isCurrentRowDeleted() throws IOException {
0358:                // we need to ensure that the "deleted" flag has been read for this row
0359:                // (or re-read if the table has been recently modified)
0360:                Table.positionAtRowData(_rowState, _curPos.getRowId());
0361:                return _rowState.isDeleted();
0362:            }
0363:
0364:            /**
0365:             * Resets this cursor for traversing the given direction.
0366:             */
0367:            protected void reset(boolean moveForward) {
0368:                _curPos = getDirHandler(moveForward).getBeginningPosition();
0369:                _prevPos = _curPos;
0370:                _rowState.reset();
0371:            }
0372:
0373:            /**
0374:             * Returns an Iterable whose iterator() method calls <code>afterLast</code>
0375:             * on this cursor and returns an unmodifiable Iterator which will iterate
0376:             * through all the rows of this table in reverse order.  Use of the Iterator
0377:             * follows the same restrictions as a call to <code>getPreviousRow</code>.
0378:             * @throws IllegalStateException if an IOException is thrown by one of the
0379:             *         operations, the actual exception will be contained within
0380:             */
0381:            public Iterable<Map<String, Object>> reverseIterable() {
0382:                return reverseIterable(null);
0383:            }
0384:
0385:            /**
0386:             * Returns an Iterable whose iterator() method calls <code>afterLast</code>
0387:             * on this table and returns an unmodifiable Iterator which will iterate
0388:             * through all the rows of this table in reverse order, returning only the
0389:             * given columns.  Use of the Iterator follows the same restrictions as a
0390:             * call to <code>getPreviousRow</code>.
0391:             * @throws IllegalStateException if an IOException is thrown by one of the
0392:             *         operations, the actual exception will be contained within
0393:             */
0394:            public Iterable<Map<String, Object>> reverseIterable(
0395:                    final Collection<String> columnNames) {
0396:                return new Iterable<Map<String, Object>>() {
0397:                    public Iterator<Map<String, Object>> iterator() {
0398:                        return new RowIterator(columnNames, false);
0399:                    }
0400:                };
0401:            }
0402:
0403:            /**
0404:             * Calls <code>beforeFirst</code> on this cursor and returns an unmodifiable
0405:             * Iterator which will iterate through all the rows of this table.  Use of
0406:             * the Iterator follows the same restrictions as a call to
0407:             * <code>getNextRow</code>.
0408:             * @throws IllegalStateException if an IOException is thrown by one of the
0409:             *         operations, the actual exception will be contained within
0410:             */
0411:            public Iterator<Map<String, Object>> iterator() {
0412:                return iterator(null);
0413:            }
0414:
0415:            /**
0416:             * Returns an Iterable whose iterator() method returns the result of a call
0417:             * to {@link #iterator(Collection<String>)}
0418:             * @throws IllegalStateException if an IOException is thrown by one of the
0419:             *         operations, the actual exception will be contained within
0420:             */
0421:            public Iterable<Map<String, Object>> iterable(
0422:                    final Collection<String> columnNames) {
0423:                return new Iterable<Map<String, Object>>() {
0424:                    public Iterator<Map<String, Object>> iterator() {
0425:                        return Cursor.this .iterator(columnNames);
0426:                    }
0427:                };
0428:            }
0429:
0430:            /**
0431:             * Calls <code>beforeFirst</code> on this table and returns an unmodifiable
0432:             * Iterator which will iterate through all the rows of this table, returning
0433:             * only the given columns.  Use of the Iterator follows the same
0434:             * restrictions as a call to <code>getNextRow</code>.
0435:             * @throws IllegalStateException if an IOException is thrown by one of the
0436:             *         operations, the actual exception will be contained within
0437:             */
0438:            public Iterator<Map<String, Object>> iterator(
0439:                    Collection<String> columnNames) {
0440:                return new RowIterator(columnNames, true);
0441:            }
0442:
0443:            /**
0444:             * Delete the current row.
0445:             * @throws IllegalStateException if the current row is not valid (at
0446:             *         beginning or end of table), or already deleted.
0447:             */
0448:            public void deleteCurrentRow() throws IOException {
0449:                _table.deleteRow(_rowState, _curPos.getRowId());
0450:            }
0451:
0452:            /**
0453:             * Moves to the next row in the table and returns it.
0454:             * @return The next row in this table (Column name -> Column value), or
0455:             *         {@code null} if no next row is found
0456:             */
0457:            public Map<String, Object> getNextRow() throws IOException {
0458:                return getNextRow(null);
0459:            }
0460:
0461:            /**
0462:             * Moves to the next row in the table and returns it.
0463:             * @param columnNames Only column names in this collection will be returned
0464:             * @return The next row in this table (Column name -> Column value), or
0465:             *         {@code null} if no next row is found
0466:             */
0467:            public Map<String, Object> getNextRow(Collection<String> columnNames)
0468:                    throws IOException {
0469:                return getAnotherRow(columnNames, true);
0470:            }
0471:
0472:            /**
0473:             * Moves to the previous row in the table and returns it.
0474:             * @return The previous row in this table (Column name -> Column value), or
0475:             *         {@code null} if no previous row is found
0476:             */
0477:            public Map<String, Object> getPreviousRow() throws IOException {
0478:                return getPreviousRow(null);
0479:            }
0480:
0481:            /**
0482:             * Moves to the previous row in the table and returns it.
0483:             * @param columnNames Only column names in this collection will be returned
0484:             * @return The previous row in this table (Column name -> Column value), or
0485:             *         {@code null} if no previous row is found
0486:             */
0487:            public Map<String, Object> getPreviousRow(
0488:                    Collection<String> columnNames) throws IOException {
0489:                return getAnotherRow(columnNames, false);
0490:            }
0491:
0492:            /**
0493:             * Moves to another row in the table based on the given direction and
0494:             * returns it.
0495:             * @param columnNames Only column names in this collection will be returned
0496:             * @return another row in this table (Column name -> Column value), where
0497:             *         "next" may be backwards if moveForward is {@code false}, or
0498:             *         {@code null} if there is not another row in the given direction.
0499:             */
0500:            private Map<String, Object> getAnotherRow(
0501:                    Collection<String> columnNames, boolean moveForward)
0502:                    throws IOException {
0503:                if (moveToAnotherRow(moveForward)) {
0504:                    return getCurrentRow(columnNames);
0505:                }
0506:                return null;
0507:            }
0508:
0509:            /**
0510:             * Moves to the next row as defined by this cursor.
0511:             * @return {@code true} if a valid next row was found, {@code false}
0512:             *         otherwise
0513:             */
0514:            public boolean moveToNextRow() throws IOException {
0515:                return moveToAnotherRow(true);
0516:            }
0517:
0518:            /**
0519:             * Moves to the previous row as defined by this cursor.
0520:             * @return {@code true} if a valid previous row was found, {@code false}
0521:             *         otherwise
0522:             */
0523:            public boolean moveToPreviousRow() throws IOException {
0524:                return moveToAnotherRow(false);
0525:            }
0526:
0527:            /**
0528:             * Moves to another row in the given direction as defined by this cursor.
0529:             * @return {@code true} if another valid row was found in the given
0530:             *         direction, {@code false} otherwise
0531:             */
0532:            private boolean moveToAnotherRow(boolean moveForward)
0533:                    throws IOException {
0534:                if (_curPos.equals(getDirHandler(moveForward).getEndPosition())) {
0535:                    // already at end, make sure nothing has changed
0536:                    return recheckPosition(moveForward);
0537:                }
0538:
0539:                return moveToAnotherRowImpl(moveForward);
0540:            }
0541:
0542:            /**
0543:             * Restores a current position for the cursor (current position becomes
0544:             * previous position).
0545:             */
0546:            protected void restorePosition(Position curPos) throws IOException {
0547:                restorePosition(curPos, _curPos);
0548:            }
0549:
0550:            /**
0551:             * Restores a current and previous position for the cursor if the given
0552:             * positions are different from the current positions.
0553:             */
0554:            protected final void restorePosition(Position curPos,
0555:                    Position prevPos) throws IOException {
0556:                if (!curPos.equals(_curPos) || !prevPos.equals(_prevPos)) {
0557:                    restorePositionImpl(curPos, prevPos);
0558:                }
0559:            }
0560:
0561:            /**
0562:             * Restores a current and previous position for the cursor.
0563:             */
0564:            protected void restorePositionImpl(Position curPos, Position prevPos)
0565:                    throws IOException {
0566:                // make the current position previous, and the new position current
0567:                _prevPos = _curPos;
0568:                _curPos = curPos;
0569:                _rowState.reset();
0570:            }
0571:
0572:            /**
0573:             * Rechecks the current position if the underlying data structures have been
0574:             * modified.
0575:             * @return {@code true} if the cursor ended up in a new position,
0576:             *         {@code false} otherwise.
0577:             */
0578:            private boolean recheckPosition(boolean moveForward)
0579:                    throws IOException {
0580:                if (isUpToDate()) {
0581:                    // nothing has changed
0582:                    return false;
0583:                }
0584:
0585:                // move the cursor back to the previous position
0586:                restorePosition(_prevPos);
0587:                return moveToAnotherRowImpl(moveForward);
0588:            }
0589:
0590:            /**
0591:             * Does the grunt work of moving the cursor to another position in the given
0592:             * direction.
0593:             */
0594:            private boolean moveToAnotherRowImpl(boolean moveForward)
0595:                    throws IOException {
0596:                _rowState.reset();
0597:                _prevPos = _curPos;
0598:                _curPos = findAnotherPosition(_rowState, _curPos, moveForward);
0599:                Table.positionAtRowHeader(_rowState, _curPos.getRowId());
0600:                return (!_curPos.equals(getDirHandler(moveForward)
0601:                        .getEndPosition()));
0602:            }
0603:
0604:            /**
0605:             * Moves to the first row (as defined by the cursor) where the given column
0606:             * has the given value.  This may be more efficient on some cursors than
0607:             * others.  If a match is not found (or an exception is thrown), the cursor
0608:             * is restored to its previous state.
0609:             *
0610:             * @param columnPattern column from the table for this cursor which is being
0611:             *                      matched by the valuePattern
0612:             * @param valuePattern value which is equal to the corresponding value in
0613:             *                     the matched row
0614:             * @return {@code true} if a valid row was found with the given value,
0615:             *         {@code false} if no row was found
0616:             */
0617:            public boolean findRow(Column columnPattern, Object valuePattern)
0618:                    throws IOException {
0619:                Position curPos = _curPos;
0620:                Position prevPos = _prevPos;
0621:                boolean found = false;
0622:                try {
0623:                    found = findRowImpl(columnPattern, valuePattern);
0624:                    return found;
0625:                } finally {
0626:                    if (!found) {
0627:                        try {
0628:                            restorePosition(curPos, prevPos);
0629:                        } catch (IOException e) {
0630:                            LOG.error("Failed restoring position", e);
0631:                        }
0632:                    }
0633:                }
0634:            }
0635:
0636:            /**
0637:             * Moves to the first row (as defined by the cursor) where the given columns
0638:             * have the given values.  This may be more efficient on some cursors than
0639:             * others.  If a match is not found (or an exception is thrown), the cursor
0640:             * is restored to its previous state.
0641:             *
0642:             * @param rowPattern column names and values which must be equal to the
0643:             *                   corresponding values in the matched row
0644:             * @return {@code true} if a valid row was found with the given values,
0645:             *         {@code false} if no row was found
0646:             */
0647:            public boolean findRow(Map<String, Object> rowPattern)
0648:                    throws IOException {
0649:                Position curPos = _curPos;
0650:                Position prevPos = _prevPos;
0651:                boolean found = false;
0652:                try {
0653:                    found = findRowImpl(rowPattern);
0654:                    return found;
0655:                } finally {
0656:                    if (!found) {
0657:                        try {
0658:                            restorePosition(curPos, prevPos);
0659:                        } catch (IOException e) {
0660:                            LOG.error("Failed restoring position", e);
0661:                        }
0662:                    }
0663:                }
0664:            }
0665:
0666:            /**
0667:             * Returns {@code true} if the current row matches the given pattern.
0668:             * @param columnPattern column from the table for this cursor which is being
0669:             *                      matched by the valuePattern
0670:             * @param valuePattern value which is tested for equality with the
0671:             *                     corresponding value in the current row
0672:             */
0673:            public boolean currentRowMatches(Column columnPattern,
0674:                    Object valuePattern) throws IOException {
0675:                return ObjectUtils.equals(valuePattern,
0676:                        getCurrentRowValue(columnPattern));
0677:            }
0678:
0679:            /**
0680:             * Returns {@code true} if the current row matches the given pattern.
0681:             * @param rowPattern column names and values which must be equal to the
0682:             *                   corresponding values in the current row
0683:             */
0684:            public boolean currentRowMatches(Map<String, Object> rowPattern)
0685:                    throws IOException {
0686:                return ObjectUtils.equals(rowPattern, getCurrentRow(rowPattern
0687:                        .keySet()));
0688:            }
0689:
0690:            /**
0691:             * Moves to the first row (as defined by the cursor) where the given column
0692:             * has the given value.  Caller manages save/restore on failure.
0693:             * <p>
0694:             * Default implementation scans the table from beginning to end.
0695:             *
0696:             * @param columnPattern column from the table for this cursor which is being
0697:             *                      matched by the valuePattern
0698:             * @param valuePattern value which is equal to the corresponding value in
0699:             *                     the matched row
0700:             * @return {@code true} if a valid row was found with the given value,
0701:             *         {@code false} if no row was found
0702:             */
0703:            protected boolean findRowImpl(Column columnPattern,
0704:                    Object valuePattern) throws IOException {
0705:                beforeFirst();
0706:                while (moveToNextRow()) {
0707:                    if (currentRowMatches(columnPattern, valuePattern)) {
0708:                        return true;
0709:                    }
0710:                }
0711:                return false;
0712:            }
0713:
0714:            /**
0715:             * Moves to the first row (as defined by the cursor) where the given columns
0716:             * have the given values.  Caller manages save/restore on failure.
0717:             * <p>
0718:             * Default implementation scans the table from beginning to end.
0719:             *
0720:             * @param rowPattern column names and values which must be equal to the
0721:             *                   corresponding values in the matched row
0722:             * @return {@code true} if a valid row was found with the given values,
0723:             *         {@code false} if no row was found
0724:             */
0725:            protected boolean findRowImpl(Map<String, Object> rowPattern)
0726:                    throws IOException {
0727:                beforeFirst();
0728:                while (moveToNextRow()) {
0729:                    if (currentRowMatches(rowPattern)) {
0730:                        return true;
0731:                    }
0732:                }
0733:                return false;
0734:            }
0735:
0736:            /**
0737:             * Moves forward as many rows as possible up to the given number of rows.
0738:             * @return the number of rows moved.
0739:             */
0740:            public int moveNextRows(int numRows) throws IOException {
0741:                return moveSomeRows(numRows, true);
0742:            }
0743:
0744:            /**
0745:             * Moves backward as many rows as possible up to the given number of rows.
0746:             * @return the number of rows moved.
0747:             */
0748:            public int movePreviousRows(int numRows) throws IOException {
0749:                return moveSomeRows(numRows, false);
0750:            }
0751:
0752:            /**
0753:             * Moves as many rows as possible in the given direction up to the given
0754:             * number of rows.
0755:             * @return the number of rows moved.
0756:             */
0757:            private int moveSomeRows(int numRows, boolean moveForward)
0758:                    throws IOException {
0759:                int numMovedRows = 0;
0760:                while ((numMovedRows < numRows)
0761:                        && moveToAnotherRow(moveForward)) {
0762:                    ++numMovedRows;
0763:                }
0764:                return numMovedRows;
0765:            }
0766:
0767:            /**
0768:             * Returns the current row in this cursor (Column name -> Column value).
0769:             */
0770:            public Map<String, Object> getCurrentRow() throws IOException {
0771:                return getCurrentRow(null);
0772:            }
0773:
0774:            /**
0775:             * Returns the current row in this cursor (Column name -> Column value).
0776:             * @param columnNames Only column names in this collection will be returned
0777:             */
0778:            public Map<String, Object> getCurrentRow(
0779:                    Collection<String> columnNames) throws IOException {
0780:                return _table
0781:                        .getRow(_rowState, _curPos.getRowId(), columnNames);
0782:            }
0783:
0784:            /**
0785:             * Returns the given column from the current row.
0786:             */
0787:            public Object getCurrentRowValue(Column column) throws IOException {
0788:                return _table
0789:                        .getRowValue(_rowState, _curPos.getRowId(), column);
0790:            }
0791:
0792:            /**
0793:             * Returns {@code true} if this cursor is up-to-date with respect to the
0794:             * relevant table and related table objects, {@code false} otherwise.
0795:             */
0796:            protected boolean isUpToDate() {
0797:                return _rowState.isUpToDate();
0798:            }
0799:
0800:            @Override
0801:            public String toString() {
0802:                return getClass().getSimpleName() + " CurPosition " + _curPos
0803:                        + ", PrevPosition " + _prevPos;
0804:            }
0805:
0806:            /**
0807:             * Finds the next non-deleted row after the given row (as defined by this
0808:             * cursor) and returns the id of the row, where "next" may be backwards if
0809:             * moveForward is {@code false}.  If there are no more rows, the returned
0810:             * rowId should equal the value returned by {@link #getLastPosition} if moving
0811:             * forward and {@link #getFirstPosition} if moving backward.
0812:             */
0813:            protected abstract Position findAnotherPosition(RowState rowState,
0814:                    Position curPos, boolean moveForward) throws IOException;
0815:
0816:            /**
0817:             * Returns the DirHandler for the given movement direction.
0818:             */
0819:            protected abstract DirHandler getDirHandler(boolean moveForward);
0820:
0821:            /**
0822:             * Row iterator for this table, unmodifiable.
0823:             */
0824:            private final class RowIterator implements 
0825:                    Iterator<Map<String, Object>> {
0826:                private final Collection<String> _columnNames;
0827:                private final boolean _moveForward;
0828:                private boolean _hasNext = false;
0829:
0830:                private RowIterator(Collection<String> columnNames,
0831:                        boolean moveForward) {
0832:                    try {
0833:                        _columnNames = columnNames;
0834:                        _moveForward = moveForward;
0835:                        reset(_moveForward);
0836:                        _hasNext = moveToAnotherRow(_moveForward);
0837:                    } catch (IOException e) {
0838:                        throw new IllegalStateException(e);
0839:                    }
0840:                }
0841:
0842:                public boolean hasNext() {
0843:                    return _hasNext;
0844:                }
0845:
0846:                public void remove() {
0847:                    throw new UnsupportedOperationException();
0848:                }
0849:
0850:                public Map<String, Object> next() {
0851:                    if (!hasNext()) {
0852:                        throw new NoSuchElementException();
0853:                    }
0854:                    try {
0855:                        Map<String, Object> rtn = getCurrentRow(_columnNames);
0856:                        _hasNext = moveToAnotherRow(_moveForward);
0857:                        return rtn;
0858:                    } catch (IOException e) {
0859:                        throw new IllegalStateException(e);
0860:                    }
0861:                }
0862:
0863:            }
0864:
0865:            /**
0866:             * Handles moving the cursor in a given direction.  Separates cursor
0867:             * logic from value storage.
0868:             */
0869:            protected abstract class DirHandler {
0870:                public abstract Position getBeginningPosition();
0871:
0872:                public abstract Position getEndPosition();
0873:            }
0874:
0875:            /**
0876:             * Simple un-indexed cursor.
0877:             */
0878:            private static final class TableScanCursor extends Cursor {
0879:                /** ScanDirHandler for forward traversal */
0880:                private final ScanDirHandler _forwardDirHandler = new ForwardScanDirHandler();
0881:                /** ScanDirHandler for backward traversal */
0882:                private final ScanDirHandler _reverseDirHandler = new ReverseScanDirHandler();
0883:                /** Cursor over the pages that this table owns */
0884:                private final UsageMap.PageCursor _ownedPagesCursor;
0885:
0886:                private TableScanCursor(Table table) {
0887:                    super (new Id(table, null), table, FIRST_SCAN_POSITION,
0888:                            LAST_SCAN_POSITION);
0889:                    _ownedPagesCursor = table.getOwnedPagesCursor();
0890:                }
0891:
0892:                @Override
0893:                protected ScanDirHandler getDirHandler(boolean moveForward) {
0894:                    return (moveForward ? _forwardDirHandler
0895:                            : _reverseDirHandler);
0896:                }
0897:
0898:                @Override
0899:                protected boolean isUpToDate() {
0900:                    return (super .isUpToDate() && _ownedPagesCursor
0901:                            .isUpToDate());
0902:                }
0903:
0904:                @Override
0905:                protected void reset(boolean moveForward) {
0906:                    _ownedPagesCursor.reset(moveForward);
0907:                    super .reset(moveForward);
0908:                }
0909:
0910:                @Override
0911:                protected void restorePositionImpl(Position curPos,
0912:                        Position prevPos) throws IOException {
0913:                    if (!(curPos instanceof  ScanPosition)
0914:                            || !(prevPos instanceof  ScanPosition)) {
0915:                        throw new IllegalArgumentException(
0916:                                "Restored positions must be scan positions");
0917:                    }
0918:                    _ownedPagesCursor.restorePosition(curPos.getRowId()
0919:                            .getPageNumber(), prevPos.getRowId()
0920:                            .getPageNumber());
0921:                    super .restorePositionImpl(curPos, prevPos);
0922:                }
0923:
0924:                @Override
0925:                protected Position findAnotherPosition(RowState rowState,
0926:                        Position curPos, boolean moveForward)
0927:                        throws IOException {
0928:                    ScanDirHandler handler = getDirHandler(moveForward);
0929:
0930:                    // figure out how many rows are left on this page so we can find the
0931:                    // next row
0932:                    RowId curRowId = curPos.getRowId();
0933:                    Table.positionAtRowHeader(rowState, curRowId);
0934:                    int currentRowNumber = curRowId.getRowNumber();
0935:
0936:                    // loop until we find the next valid row or run out of pages
0937:                    while (true) {
0938:
0939:                        currentRowNumber = handler
0940:                                .getAnotherRowNumber(currentRowNumber);
0941:                        curRowId = new RowId(curRowId.getPageNumber(),
0942:                                currentRowNumber);
0943:                        Table.positionAtRowHeader(rowState, curRowId);
0944:
0945:                        if (!rowState.isValid()) {
0946:
0947:                            // load next page
0948:                            curRowId = new RowId(
0949:                                    handler.getAnotherPageNumber(),
0950:                                    RowId.INVALID_ROW_NUMBER);
0951:                            Table.positionAtRowHeader(rowState, curRowId);
0952:
0953:                            if (!rowState.isHeaderPageNumberValid()) {
0954:                                //No more owned pages.  No more rows.
0955:                                return handler.getEndPosition();
0956:                            }
0957:
0958:                            // update row count and initial row number
0959:                            currentRowNumber = handler
0960:                                    .getInitialRowNumber(rowState
0961:                                            .getRowsOnHeaderPage());
0962:
0963:                        } else if (!rowState.isDeleted()) {
0964:
0965:                            // we found a valid, non-deleted row, return it
0966:                            return new ScanPosition(curRowId);
0967:                        }
0968:
0969:                    }
0970:                }
0971:
0972:                /**
0973:                 * Handles moving the table scan cursor in a given direction.  Separates
0974:                 * cursor logic from value storage.
0975:                 */
0976:                private abstract class ScanDirHandler extends DirHandler {
0977:                    public abstract int getAnotherRowNumber(int curRowNumber);
0978:
0979:                    public abstract int getAnotherPageNumber();
0980:
0981:                    public abstract int getInitialRowNumber(int rowsOnPage);
0982:                }
0983:
0984:                /**
0985:                 * Handles moving the table scan cursor forward.
0986:                 */
0987:                private final class ForwardScanDirHandler extends
0988:                        ScanDirHandler {
0989:                    @Override
0990:                    public Position getBeginningPosition() {
0991:                        return getFirstPosition();
0992:                    }
0993:
0994:                    @Override
0995:                    public Position getEndPosition() {
0996:                        return getLastPosition();
0997:                    }
0998:
0999:                    @Override
1000:                    public int getAnotherRowNumber(int curRowNumber) {
1001:                        return curRowNumber + 1;
1002:                    }
1003:
1004:                    @Override
1005:                    public int getAnotherPageNumber() {
1006:                        return _ownedPagesCursor.getNextPage();
1007:                    }
1008:
1009:                    @Override
1010:                    public int getInitialRowNumber(int rowsOnPage) {
1011:                        return -1;
1012:                    }
1013:                }
1014:
1015:                /**
1016:                 * Handles moving the table scan cursor backward.
1017:                 */
1018:                private final class ReverseScanDirHandler extends
1019:                        ScanDirHandler {
1020:                    @Override
1021:                    public Position getBeginningPosition() {
1022:                        return getLastPosition();
1023:                    }
1024:
1025:                    @Override
1026:                    public Position getEndPosition() {
1027:                        return getFirstPosition();
1028:                    }
1029:
1030:                    @Override
1031:                    public int getAnotherRowNumber(int curRowNumber) {
1032:                        return curRowNumber - 1;
1033:                    }
1034:
1035:                    @Override
1036:                    public int getAnotherPageNumber() {
1037:                        return _ownedPagesCursor.getPreviousPage();
1038:                    }
1039:
1040:                    @Override
1041:                    public int getInitialRowNumber(int rowsOnPage) {
1042:                        return rowsOnPage;
1043:                    }
1044:                }
1045:
1046:            }
1047:
1048:            /**
1049:             * Indexed cursor.
1050:             */
1051:            private static final class IndexCursor extends Cursor {
1052:                /** IndexDirHandler for forward traversal */
1053:                private final IndexDirHandler _forwardDirHandler = new ForwardIndexDirHandler();
1054:                /** IndexDirHandler for backward traversal */
1055:                private final IndexDirHandler _reverseDirHandler = new ReverseIndexDirHandler();
1056:                /** Cursor over the entries of the relvant index */
1057:                private final Index.EntryCursor _entryCursor;
1058:
1059:                private IndexCursor(Table table, Index index,
1060:                        Index.EntryCursor entryCursor) throws IOException {
1061:                    super (new Id(table, index), table, new IndexPosition(
1062:                            entryCursor.getFirstEntry()), new IndexPosition(
1063:                            entryCursor.getLastEntry()));
1064:                    _entryCursor = entryCursor;
1065:                }
1066:
1067:                @Override
1068:                public Index getIndex() {
1069:                    return _entryCursor.getIndex();
1070:                }
1071:
1072:                @Override
1073:                protected IndexDirHandler getDirHandler(boolean moveForward) {
1074:                    return (moveForward ? _forwardDirHandler
1075:                            : _reverseDirHandler);
1076:                }
1077:
1078:                @Override
1079:                protected boolean isUpToDate() {
1080:                    return (super .isUpToDate() && _entryCursor.isUpToDate());
1081:                }
1082:
1083:                @Override
1084:                protected void reset(boolean moveForward) {
1085:                    _entryCursor.reset(moveForward);
1086:                    super .reset(moveForward);
1087:                }
1088:
1089:                @Override
1090:                protected void restorePositionImpl(Position curPos,
1091:                        Position prevPos) throws IOException {
1092:                    if (!(curPos instanceof  IndexPosition)
1093:                            || !(prevPos instanceof  IndexPosition)) {
1094:                        throw new IllegalArgumentException(
1095:                                "Restored positions must be index positions");
1096:                    }
1097:                    _entryCursor.restorePosition(((IndexPosition) curPos)
1098:                            .getEntry(), ((IndexPosition) prevPos).getEntry());
1099:                    super .restorePositionImpl(curPos, prevPos);
1100:                }
1101:
1102:                @Override
1103:                protected boolean findRowImpl(Column columnPattern,
1104:                        Object valuePattern) throws IOException {
1105:                    Object[] rowValues = _entryCursor.getIndex()
1106:                            .constructIndexRow(columnPattern.getName(),
1107:                                    valuePattern);
1108:
1109:                    if (rowValues == null) {
1110:                        // bummer, use the default table scan
1111:                        return super .findRowImpl(columnPattern, valuePattern);
1112:                    }
1113:
1114:                    // sweet, we can use our index
1115:                    _entryCursor.beforeEntry(rowValues);
1116:                    Index.Entry startEntry = _entryCursor.getNextEntry();
1117:                    if (!startEntry.getRowId().isValid()) {
1118:                        // at end of index, no potential matches
1119:                        return false;
1120:                    }
1121:
1122:                    // either we found a row with the given value, or none exist in the
1123:                    // table
1124:                    restorePosition(new IndexPosition(startEntry));
1125:                    return currentRowMatches(columnPattern, valuePattern);
1126:                }
1127:
1128:                @Override
1129:                protected boolean findRowImpl(Map<String, Object> rowPattern)
1130:                        throws IOException {
1131:                    Index index = _entryCursor.getIndex();
1132:                    Object[] rowValues = index.constructIndexRow(rowPattern);
1133:
1134:                    if (rowValues == null) {
1135:                        // bummer, use the default table scan
1136:                        return super .findRowImpl(rowPattern);
1137:                    }
1138:
1139:                    // sweet, we can use our index
1140:                    _entryCursor.beforeEntry(rowValues);
1141:                    Index.Entry startEntry = _entryCursor.getNextEntry();
1142:                    if (!startEntry.getRowId().isValid()) {
1143:                        // at end of index, no potential matches
1144:                        return false;
1145:                    }
1146:                    restorePosition(new IndexPosition(startEntry));
1147:
1148:                    Map<String, Object> indexRowPattern = null;
1149:                    if (rowPattern.size() == index.getColumns().size()) {
1150:                        // the rowPattern matches our index columns exactly, so we can
1151:                        // streamline our testing below
1152:                        indexRowPattern = rowPattern;
1153:                    } else {
1154:                        // the rowPattern has more columns than just the index, so we need to
1155:                        // do more work when testing below
1156:                        indexRowPattern = new LinkedHashMap<String, Object>();
1157:                        for (Index.ColumnDescriptor idxCol : index.getColumns()) {
1158:                            indexRowPattern.put(idxCol.getName(),
1159:                                    rowValues[idxCol.getColumnIndex()]);
1160:                        }
1161:                    }
1162:
1163:                    // there may be multiple columns which fit the pattern subset used by
1164:                    // the index, so we need to keep checking until our index values no
1165:                    // longer match
1166:                    do {
1167:
1168:                        if (!currentRowMatches(indexRowPattern)) {
1169:                            // there are no more rows which could possibly match
1170:                            break;
1171:                        }
1172:
1173:                        // note, if rowPattern == indexRowPattern, no need to do an extra
1174:                        // comparison with the current row
1175:                        if ((rowPattern == indexRowPattern)
1176:                                || currentRowMatches(rowPattern)) {
1177:                            // found it!
1178:                            return true;
1179:                        }
1180:
1181:                    } while (moveToNextRow());
1182:
1183:                    // none of the potential rows matched
1184:                    return false;
1185:                }
1186:
1187:                @Override
1188:                protected Position findAnotherPosition(RowState rowState,
1189:                        Position curPos, boolean moveForward)
1190:                        throws IOException {
1191:                    IndexDirHandler handler = getDirHandler(moveForward);
1192:                    IndexPosition endPos = (IndexPosition) handler
1193:                            .getEndPosition();
1194:                    Index.Entry entry = handler.getAnotherEntry();
1195:                    return ((!entry.equals(endPos.getEntry())) ? new IndexPosition(
1196:                            entry)
1197:                            : endPos);
1198:                }
1199:
1200:                /**
1201:                 * Handles moving the table index cursor in a given direction.  Separates
1202:                 * cursor logic from value storage.
1203:                 */
1204:                private abstract class IndexDirHandler extends DirHandler {
1205:                    public abstract Index.Entry getAnotherEntry();
1206:                }
1207:
1208:                /**
1209:                 * Handles moving the table index cursor forward.
1210:                 */
1211:                private final class ForwardIndexDirHandler extends
1212:                        IndexDirHandler {
1213:                    @Override
1214:                    public Position getBeginningPosition() {
1215:                        return getFirstPosition();
1216:                    }
1217:
1218:                    @Override
1219:                    public Position getEndPosition() {
1220:                        return getLastPosition();
1221:                    }
1222:
1223:                    @Override
1224:                    public Index.Entry getAnotherEntry() {
1225:                        return _entryCursor.getNextEntry();
1226:                    }
1227:                }
1228:
1229:                /**
1230:                 * Handles moving the table index cursor backward.
1231:                 */
1232:                private final class ReverseIndexDirHandler extends
1233:                        IndexDirHandler {
1234:                    @Override
1235:                    public Position getBeginningPosition() {
1236:                        return getLastPosition();
1237:                    }
1238:
1239:                    @Override
1240:                    public Position getEndPosition() {
1241:                        return getFirstPosition();
1242:                    }
1243:
1244:                    @Override
1245:                    public Index.Entry getAnotherEntry() {
1246:                        return _entryCursor.getPreviousEntry();
1247:                    }
1248:                }
1249:
1250:            }
1251:
1252:            /**
1253:             * Identifier for a cursor.  Will be equal to any other cursor of the same
1254:             * type for the same table.  Primarily used to check the validity of a
1255:             * Savepoint.
1256:             */
1257:            public static final class Id {
1258:                private final String _tableName;
1259:                private final String _indexName;
1260:
1261:                private Id(Table table, Index index) {
1262:                    _tableName = table.getName();
1263:                    _indexName = ((index != null) ? index.getName() : null);
1264:                }
1265:
1266:                @Override
1267:                public int hashCode() {
1268:                    return _tableName.hashCode();
1269:                }
1270:
1271:                @Override
1272:                public boolean equals(Object o) {
1273:                    return ((this  == o) || ((o != null)
1274:                            && (getClass() == o.getClass())
1275:                            && ObjectUtils.equals(_tableName,
1276:                                    ((Id) o)._tableName) && ObjectUtils.equals(
1277:                            _indexName, ((Id) o)._indexName)));
1278:                }
1279:
1280:                @Override
1281:                public String toString() {
1282:                    return getClass().getSimpleName() + " " + _tableName + ":"
1283:                            + _indexName;
1284:                }
1285:            }
1286:
1287:            /**
1288:             * Value object which represents a complete save state of the cursor.
1289:             */
1290:            public static final class Savepoint {
1291:                private final Id _cursorId;
1292:                private final Position _curPos;
1293:                private final Position _prevPos;
1294:
1295:                private Savepoint(Id cursorId, Position curPos, Position prevPos) {
1296:                    _cursorId = cursorId;
1297:                    _curPos = curPos;
1298:                    _prevPos = prevPos;
1299:                }
1300:
1301:                public Id getCursorId() {
1302:                    return _cursorId;
1303:                }
1304:
1305:                public Position getCurrentPosition() {
1306:                    return _curPos;
1307:                }
1308:
1309:                private Position getPreviousPosition() {
1310:                    return _prevPos;
1311:                }
1312:
1313:                @Override
1314:                public String toString() {
1315:                    return getClass().getSimpleName() + " " + _cursorId
1316:                            + " CurPosition " + _curPos + ", PrevPosition "
1317:                            + _prevPos;
1318:                }
1319:            }
1320:
1321:            /**
1322:             * Value object which maintains the current position of the cursor.
1323:             */
1324:            public static abstract class Position {
1325:                protected Position() {
1326:                }
1327:
1328:                @Override
1329:                public final int hashCode() {
1330:                    return getRowId().hashCode();
1331:                }
1332:
1333:                @Override
1334:                public final boolean equals(Object o) {
1335:                    return ((this  == o) || ((o != null)
1336:                            && (getClass() == o.getClass()) && equalsImpl(o)));
1337:                }
1338:
1339:                /**
1340:                 * Returns the unique RowId of the position of the cursor.
1341:                 */
1342:                public abstract RowId getRowId();
1343:
1344:                /**
1345:                 * Returns {@code true} if the subclass specific info in a Position is
1346:                 * equal, {@code false} otherwise.
1347:                 * @param o object being tested for equality, guaranteed to be the same
1348:                 *          class as this object
1349:                 */
1350:                protected abstract boolean equalsImpl(Object o);
1351:            }
1352:
1353:            /**
1354:             * Value object which maintains the current position of a TableScanCursor.
1355:             */
1356:            private static final class ScanPosition extends Position {
1357:                private final RowId _rowId;
1358:
1359:                private ScanPosition(RowId rowId) {
1360:                    _rowId = rowId;
1361:                }
1362:
1363:                @Override
1364:                public RowId getRowId() {
1365:                    return _rowId;
1366:                }
1367:
1368:                @Override
1369:                protected boolean equalsImpl(Object o) {
1370:                    return getRowId().equals(((ScanPosition) o).getRowId());
1371:                }
1372:
1373:                @Override
1374:                public String toString() {
1375:                    return "RowId = " + getRowId();
1376:                }
1377:            }
1378:
1379:            /**
1380:             * Value object which maintains the current position of an IndexCursor.
1381:             */
1382:            private static final class IndexPosition extends Position {
1383:                private final Index.Entry _entry;
1384:
1385:                private IndexPosition(Index.Entry entry) {
1386:                    _entry = entry;
1387:                }
1388:
1389:                @Override
1390:                public RowId getRowId() {
1391:                    return getEntry().getRowId();
1392:                }
1393:
1394:                public Index.Entry getEntry() {
1395:                    return _entry;
1396:                }
1397:
1398:                @Override
1399:                protected boolean equalsImpl(Object o) {
1400:                    return getEntry().equals(((IndexPosition) o).getEntry());
1401:                }
1402:
1403:                @Override
1404:                public String toString() {
1405:                    return "Entry = " + getEntry();
1406:                }
1407:            }
1408:
1409:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.