Source Code Cross Referenced for Table.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) 2005 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.nio.ByteBuffer;
0032:        import java.util.ArrayList;
0033:        import java.util.Arrays;
0034:        import java.util.Collection;
0035:        import java.util.Collections;
0036:        import java.util.Comparator;
0037:        import java.util.Iterator;
0038:        import java.util.LinkedHashMap;
0039:        import java.util.List;
0040:        import java.util.Map;
0041:
0042:        import org.apache.commons.logging.Log;
0043:        import org.apache.commons.logging.LogFactory;
0044:
0045:        /**
0046:         * A single database table
0047:         * <p>
0048:         * Is not thread-safe.
0049:         * 
0050:         * @author Tim McCune
0051:         */
0052:        public class Table implements  Iterable<Map<String, Object>> {
0053:
0054:            private static final Log LOG = LogFactory.getLog(Table.class);
0055:
0056:            private static final short OFFSET_MASK = (short) 0x1FFF;
0057:
0058:            private static final short DELETED_ROW_MASK = (short) 0x8000;
0059:
0060:            private static final short OVERFLOW_ROW_MASK = (short) 0x4000;
0061:
0062:            /** Table type code for system tables */
0063:            public static final byte TYPE_SYSTEM = 0x53;
0064:            /** Table type code for user tables */
0065:            public static final byte TYPE_USER = 0x4e;
0066:
0067:            /** comparator which sorts variable length columns vased on their index into
0068:                the variable length offset table */
0069:            private static final Comparator<Column> VAR_LEN_COLUMN_COMPARATOR = new Comparator<Column>() {
0070:                public int compare(Column c1, Column c2) {
0071:                    return ((c1.getVarLenTableIndex() < c2
0072:                            .getVarLenTableIndex()) ? -1
0073:                            : ((c1.getVarLenTableIndex() > c2
0074:                                    .getVarLenTableIndex()) ? 1 : 0));
0075:                }
0076:            };
0077:
0078:            /** owning database */
0079:            private final Database _database;
0080:            /** Type of the table (either TYPE_SYSTEM or TYPE_USER) */
0081:            private byte _tableType;
0082:            /** Number of indexes on the table */
0083:            private int _indexCount;
0084:            /** Number of index slots for the table */
0085:            private int _indexSlotCount;
0086:            /** Number of rows in the table */
0087:            private int _rowCount;
0088:            /** last auto number for the table */
0089:            private int _lastAutoNumber;
0090:            /** page number of the definition of this table */
0091:            private final int _tableDefPageNumber;
0092:            /** max Number of columns in the table (includes previous deletions) */
0093:            private short _maxColumnCount;
0094:            /** max Number of variable columns in the table */
0095:            private short _maxVarColumnCount;
0096:            /** List of columns in this table, ordered by column number */
0097:            private List<Column> _columns = new ArrayList<Column>();
0098:            /** List of variable length columns in this table, ordered by offset */
0099:            private List<Column> _varColumns = new ArrayList<Column>();
0100:            /** List of indexes on this table */
0101:            private List<Index> _indexes = new ArrayList<Index>();
0102:            /** Table name as stored in Database */
0103:            private final String _name;
0104:            /** Usage map of pages that this table owns */
0105:            private UsageMap _ownedPages;
0106:            /** Usage map of pages that this table owns with free space on them */
0107:            private UsageMap _freeSpacePages;
0108:            /** modification count for the table, keeps row-states up-to-date */
0109:            private int _modCount;
0110:            /** page buffer used to update data pages when adding rows */
0111:            private final TempPageHolder _addRowBufferH = TempPageHolder
0112:                    .newHolder(TempBufferHolder.Type.SOFT);
0113:            /** page buffer used to update the table def page */
0114:            private final TempPageHolder _tableDefBufferH = TempPageHolder
0115:                    .newHolder(TempBufferHolder.Type.SOFT);
0116:            /** buffer used to writing single rows of data */
0117:            private final TempBufferHolder _singleRowBufferH = TempBufferHolder
0118:                    .newHolder(TempBufferHolder.Type.SOFT, true);
0119:            /** "buffer" used to writing multi rows of data (will create new buffer on
0120:                every call) */
0121:            private final TempBufferHolder _multiRowBufferH = TempBufferHolder
0122:                    .newHolder(TempBufferHolder.Type.NONE, true);
0123:
0124:            /** common cursor for iterating through the table, kept here for historic
0125:                reasons */
0126:            private Cursor _cursor;
0127:
0128:            /**
0129:             * Only used by unit tests
0130:             */
0131:            Table(boolean testing, List<Column> columns) throws IOException {
0132:                if (!testing) {
0133:                    throw new IllegalArgumentException();
0134:                }
0135:                _database = null;
0136:                _tableDefPageNumber = PageChannel.INVALID_PAGE_NUMBER;
0137:                _name = null;
0138:                setColumns(columns);
0139:            }
0140:
0141:            /**
0142:             * @param database database which owns this table
0143:             * @param tableBuffer Buffer to read the table with
0144:             * @param pageNumber Page number of the table definition
0145:             * @param name Table name
0146:             */
0147:            protected Table(Database database, ByteBuffer tableBuffer,
0148:                    int pageNumber, String name) throws IOException {
0149:                _database = database;
0150:                _tableDefPageNumber = pageNumber;
0151:                _name = name;
0152:                int nextPage = tableBuffer
0153:                        .getInt(getFormat().OFFSET_NEXT_TABLE_DEF_PAGE);
0154:                ByteBuffer nextPageBuffer = null;
0155:                while (nextPage != 0) {
0156:                    if (nextPageBuffer == null) {
0157:                        nextPageBuffer = getPageChannel().createPageBuffer();
0158:                    }
0159:                    getPageChannel().readPage(nextPageBuffer, nextPage);
0160:                    nextPage = nextPageBuffer
0161:                            .getInt(getFormat().OFFSET_NEXT_TABLE_DEF_PAGE);
0162:                    ByteBuffer newBuffer = getPageChannel().createBuffer(
0163:                            tableBuffer.capacity() + getFormat().PAGE_SIZE - 8);
0164:                    newBuffer.put(tableBuffer);
0165:                    newBuffer.put(nextPageBuffer.array(), 8,
0166:                            getFormat().PAGE_SIZE - 8);
0167:                    tableBuffer = newBuffer;
0168:                    tableBuffer.flip();
0169:                }
0170:                readTableDefinition(tableBuffer);
0171:                tableBuffer = null;
0172:
0173:                // setup common cursor
0174:                _cursor = Cursor.createCursor(this );
0175:            }
0176:
0177:            /**
0178:             * @return The name of the table
0179:             */
0180:            public String getName() {
0181:                return _name;
0182:            }
0183:
0184:            public int getMaxColumnCount() {
0185:                return _maxColumnCount;
0186:            }
0187:
0188:            public int getColumnCount() {
0189:                return _columns.size();
0190:            }
0191:
0192:            public Database getDatabase() {
0193:                return _database;
0194:            }
0195:
0196:            public JetFormat getFormat() {
0197:                return getDatabase().getFormat();
0198:            }
0199:
0200:            public PageChannel getPageChannel() {
0201:                return getDatabase().getPageChannel();
0202:            }
0203:
0204:            protected int getTableDefPageNumber() {
0205:                return _tableDefPageNumber;
0206:            }
0207:
0208:            public RowState createRowState() {
0209:                return new RowState(TempBufferHolder.Type.HARD);
0210:            }
0211:
0212:            protected UsageMap.PageCursor getOwnedPagesCursor() {
0213:                return _ownedPages.cursor();
0214:            }
0215:
0216:            /**
0217:             * @return All of the columns in this table (unmodifiable List)
0218:             */
0219:            public List<Column> getColumns() {
0220:                return Collections.unmodifiableList(_columns);
0221:            }
0222:
0223:            /**
0224:             * @return the column with the given name
0225:             */
0226:            public Column getColumn(String name) {
0227:                for (Column column : _columns) {
0228:                    if (column.getName().equals(name)) {
0229:                        return column;
0230:                    }
0231:                }
0232:                throw new IllegalArgumentException("Column with name " + name
0233:                        + " does not exist in this table");
0234:            }
0235:
0236:            /**
0237:             * Only called by unit tests
0238:             */
0239:            private void setColumns(List<Column> columns) {
0240:                _columns = columns;
0241:                int colIdx = 0;
0242:                int varLenIdx = 0;
0243:                int fixedOffset = 0;
0244:                for (Column col : _columns) {
0245:                    col.setColumnNumber((short) colIdx);
0246:                    col.setColumnIndex(colIdx++);
0247:                    if (col.isVariableLength()) {
0248:                        col.setVarLenTableIndex(varLenIdx++);
0249:                        _varColumns.add(col);
0250:                    } else {
0251:                        col.setFixedDataOffset(fixedOffset);
0252:                        fixedOffset += col.getType().getFixedSize();
0253:                    }
0254:                }
0255:                _maxColumnCount = (short) _columns.size();
0256:                _maxVarColumnCount = (short) _varColumns.size();
0257:            }
0258:
0259:            /**
0260:             * @return All of the Indexes on this table (unmodifiable List)
0261:             */
0262:            public List<Index> getIndexes() {
0263:                return Collections.unmodifiableList(_indexes);
0264:            }
0265:
0266:            /**
0267:             * @return the index with the given name
0268:             */
0269:            public Index getIndex(String name) {
0270:                for (Index index : _indexes) {
0271:                    if (index.getName().equals(name)) {
0272:                        return index;
0273:                    }
0274:                }
0275:                throw new IllegalArgumentException("Index with name " + name
0276:                        + " does not exist on this table");
0277:            }
0278:
0279:            /**
0280:             * Only called by unit tests
0281:             */
0282:            int getIndexSlotCount() {
0283:                return _indexSlotCount;
0284:            }
0285:
0286:            /**
0287:             * After calling this method, getNextRow will return the first row in the
0288:             * table
0289:             */
0290:            public void reset() {
0291:                _cursor.reset();
0292:            }
0293:
0294:            /**
0295:             * Delete the current row (retrieved by a call to {@link #getNextRow()}).
0296:             */
0297:            public void deleteCurrentRow() throws IOException {
0298:                _cursor.deleteCurrentRow();
0299:            }
0300:
0301:            /**
0302:             * Delete the row on which the given rowState is currently positioned.
0303:             */
0304:            public void deleteRow(RowState rowState, RowId rowId)
0305:                    throws IOException {
0306:                requireValidRowId(rowId);
0307:
0308:                // ensure that the relevant row state is up-to-date
0309:                ByteBuffer rowBuffer = positionAtRowHeader(rowState, rowId);
0310:
0311:                requireNonDeletedRow(rowState, rowId);
0312:
0313:                // delete flag always gets set in the "header" row (even if data is on
0314:                // overflow row)
0315:                int pageNumber = rowState.getHeaderRowId().getPageNumber();
0316:                int rowNumber = rowState.getHeaderRowId().getRowNumber();
0317:
0318:                // use any read rowValues to help update the indexes
0319:                Object[] rowValues = (!_indexes.isEmpty() ? rowState
0320:                        .getRowValues() : null);
0321:
0322:                int rowIndex = getRowStartOffset(rowNumber, getFormat());
0323:                rowBuffer.putShort(rowIndex, (short) (rowBuffer
0324:                        .getShort(rowIndex)
0325:                        | DELETED_ROW_MASK | OVERFLOW_ROW_MASK));
0326:                writeDataPage(rowBuffer, pageNumber);
0327:
0328:                // update the indexes
0329:                for (Index index : _indexes) {
0330:                    index.deleteRow(rowValues, rowId);
0331:                }
0332:
0333:                // make sure table def gets updated
0334:                updateTableDefinition(-1);
0335:            }
0336:
0337:            /**
0338:             * @return The next row in this table (Column name -> Column value)
0339:             */
0340:            public Map<String, Object> getNextRow() throws IOException {
0341:                return getNextRow(null);
0342:            }
0343:
0344:            /**
0345:             * @param columnNames Only column names in this collection will be returned
0346:             * @return The next row in this table (Column name -> Column value)
0347:             */
0348:            public Map<String, Object> getNextRow(Collection<String> columnNames)
0349:                    throws IOException {
0350:                return _cursor.getNextRow(columnNames);
0351:            }
0352:
0353:            /**
0354:             * Reads a single column from the given row.
0355:             */
0356:            public Object getRowValue(RowState rowState, RowId rowId,
0357:                    Column column) throws IOException {
0358:                if (this  != column.getTable()) {
0359:                    throw new IllegalArgumentException("Given column " + column
0360:                            + " is not from this table");
0361:                }
0362:                requireValidRowId(rowId);
0363:
0364:                // position at correct row
0365:                ByteBuffer rowBuffer = positionAtRowData(rowState, rowId);
0366:                requireNonDeletedRow(rowState, rowId);
0367:
0368:                Object value = getRowColumn(rowBuffer,
0369:                        getRowNullMask(rowBuffer), column);
0370:
0371:                // cache the row values in order to be able to update the index on row
0372:                // deletion.  note, most of the returned values are immutable, except
0373:                // for binary data (returned as byte[]), but binary data shouldn't be
0374:                // indexed anyway.
0375:                rowState.setRowValue(column.getColumnIndex(), value);
0376:
0377:                return value;
0378:            }
0379:
0380:            /**
0381:             * Reads some columns from the given row.
0382:             * @param columnNames Only column names in this collection will be returned
0383:             */
0384:            public Map<String, Object> getRow(RowState rowState, RowId rowId,
0385:                    Collection<String> columnNames) throws IOException {
0386:                requireValidRowId(rowId);
0387:
0388:                // position at correct row
0389:                ByteBuffer rowBuffer = positionAtRowData(rowState, rowId);
0390:                requireNonDeletedRow(rowState, rowId);
0391:
0392:                return getRow(rowState, rowBuffer, getRowNullMask(rowBuffer),
0393:                        _columns, columnNames);
0394:            }
0395:
0396:            /**
0397:             * Reads the row data from the given row buffer.  Leaves limit unchanged.
0398:             * Saves parsed row values to the given rowState.
0399:             */
0400:            private static Map<String, Object> getRow(RowState rowState,
0401:                    ByteBuffer rowBuffer, NullMask nullMask,
0402:                    Collection<Column> columns, Collection<String> columnNames)
0403:                    throws IOException {
0404:                Map<String, Object> rtn = new LinkedHashMap<String, Object>(
0405:                        columns.size());
0406:                for (Column column : columns) {
0407:
0408:                    if ((columnNames == null)
0409:                            || (columnNames.contains(column.getName()))) {
0410:                        // Add the value to the row data
0411:                        Object value = getRowColumn(rowBuffer, nullMask, column);
0412:                        rtn.put(column.getName(), value);
0413:
0414:                        // cache the row values in order to be able to update the index on row
0415:                        // deletion.  note, most of the returned values are immutable, except
0416:                        // for binary data (returned as byte[]), but binary data shouldn't be
0417:                        // indexed anyway.
0418:                        rowState.setRowValue(column.getColumnIndex(), value);
0419:                    }
0420:                }
0421:                return rtn;
0422:            }
0423:
0424:            /**
0425:             * Reads the column data from the given row buffer.  Leaves limit unchanged.
0426:             */
0427:            private static Object getRowColumn(ByteBuffer rowBuffer,
0428:                    NullMask nullMask, Column column) throws IOException {
0429:                boolean isNull = nullMask.isNull(column);
0430:                if (column.getType() == DataType.BOOLEAN) {
0431:                    return Boolean.valueOf(!isNull); //Boolean values are stored in the null mask
0432:                } else if (isNull) {
0433:                    // well, that's easy!
0434:                    return null;
0435:                }
0436:
0437:                // reset position to row start
0438:                rowBuffer.reset();
0439:
0440:                // locate the column data bytes
0441:                int rowStart = rowBuffer.position();
0442:                int colDataPos = 0;
0443:                int colDataLen = 0;
0444:                if (!column.isVariableLength()) {
0445:
0446:                    // read fixed length value (non-boolean at this point)
0447:                    int dataStart = rowStart + 2;
0448:                    colDataPos = dataStart + column.getFixedDataOffset();
0449:                    colDataLen = column.getType().getFixedSize();
0450:
0451:                } else {
0452:
0453:                    // read var length value
0454:                    int varColumnOffsetPos = (rowBuffer.limit()
0455:                            - nullMask.byteSize() - 4)
0456:                            - (column.getVarLenTableIndex() * 2);
0457:
0458:                    short varDataStart = rowBuffer.getShort(varColumnOffsetPos);
0459:                    short varDataEnd = rowBuffer
0460:                            .getShort(varColumnOffsetPos - 2);
0461:                    colDataPos = rowStart + varDataStart;
0462:                    colDataLen = varDataEnd - varDataStart;
0463:                }
0464:
0465:                // grab the column data
0466:                byte[] columnData = new byte[colDataLen];
0467:                rowBuffer.position(colDataPos);
0468:                rowBuffer.get(columnData);
0469:
0470:                // parse the column data
0471:                return column.read(columnData);
0472:            }
0473:
0474:            /**
0475:             * Reads the null mask from the given row buffer.  Leaves limit unchanged.
0476:             */
0477:            private static NullMask getRowNullMask(ByteBuffer rowBuffer)
0478:                    throws IOException {
0479:                // reset position to row start
0480:                rowBuffer.reset();
0481:
0482:                short columnCount = rowBuffer.getShort(); // Number of columns in this row
0483:
0484:                // read null mask
0485:                NullMask nullMask = new NullMask(columnCount);
0486:                rowBuffer.position(rowBuffer.limit() - nullMask.byteSize()); //Null mask at end
0487:                nullMask.read(rowBuffer);
0488:
0489:                return nullMask;
0490:            }
0491:
0492:            /**
0493:             * Sets a new buffer to the correct row header page using the given rowState
0494:             * according to the given rowId.  Deleted state is
0495:             * determined, but overflow row pointers are not followed.
0496:             * 
0497:             * @return a ByteBuffer of the relevant page, or null if row was invalid
0498:             */
0499:            public static ByteBuffer positionAtRowHeader(RowState rowState,
0500:                    RowId rowId) throws IOException {
0501:                ByteBuffer rowBuffer = rowState.setHeaderRow(rowId);
0502:
0503:                if (rowState.isAtHeaderRow()) {
0504:                    // this task has already been accomplished
0505:                    return rowBuffer;
0506:                }
0507:
0508:                if (!rowState.isValid()) {
0509:                    // this was an invalid page/row
0510:                    rowState.setStatus(RowStateStatus.AT_HEADER);
0511:                    return null;
0512:                }
0513:
0514:                // note, we don't use findRowStart here cause we need the unmasked value
0515:                short rowStart = rowBuffer.getShort(getRowStartOffset(rowId
0516:                        .getRowNumber(), rowState.getTable().getFormat()));
0517:
0518:                // check the deleted, overflow flags for the row (the "real" flags are
0519:                // always set on the header row)
0520:                RowStatus rowStatus = RowStatus.NORMAL;
0521:                if (isDeletedRow(rowStart)) {
0522:                    rowStatus = RowStatus.DELETED;
0523:                } else if (isOverflowRow(rowStart)) {
0524:                    rowStatus = RowStatus.OVERFLOW;
0525:                }
0526:
0527:                rowState.setRowStatus(rowStatus);
0528:                rowState.setStatus(RowStateStatus.AT_HEADER);
0529:                return rowBuffer;
0530:            }
0531:
0532:            /**
0533:             * Sets the position and limit in a new buffer using the given rowState
0534:             * according to the given row number and row end, following overflow row
0535:             * pointers as necessary.
0536:             * 
0537:             * @return a ByteBuffer narrowed to the actual row data, or null if row was
0538:             *         invalid or deleted
0539:             */
0540:            public static ByteBuffer positionAtRowData(RowState rowState,
0541:                    RowId rowId) throws IOException {
0542:                positionAtRowHeader(rowState, rowId);
0543:                if (!rowState.isValid() || rowState.isDeleted()) {
0544:                    // row is invalid or deleted
0545:                    rowState.setStatus(RowStateStatus.AT_FINAL);
0546:                    return null;
0547:                }
0548:
0549:                ByteBuffer rowBuffer = rowState.getFinalPage();
0550:                int rowNum = rowState.getFinalRowId().getRowNumber();
0551:                JetFormat format = rowState.getTable().getFormat();
0552:
0553:                if (rowState.isAtFinalRow()) {
0554:                    // we've already found the final row data
0555:                    return PageChannel.narrowBuffer(rowBuffer, findRowStart(
0556:                            rowBuffer, rowNum, format), findRowEnd(rowBuffer,
0557:                            rowNum, format));
0558:                }
0559:
0560:                while (true) {
0561:
0562:                    // note, we don't use findRowStart here cause we need the unmasked value
0563:                    short rowStart = rowBuffer.getShort(getRowStartOffset(
0564:                            rowNum, format));
0565:                    short rowEnd = findRowEnd(rowBuffer, rowNum, format);
0566:
0567:                    // note, at this point we know the row is not deleted, so ignore any
0568:                    // subsequent deleted flags (as overflow rows are always marked deleted
0569:                    // anyway)
0570:                    boolean overflowRow = isOverflowRow(rowStart);
0571:
0572:                    // now, strip flags from rowStart offset
0573:                    rowStart = (short) (rowStart & OFFSET_MASK);
0574:
0575:                    if (overflowRow) {
0576:
0577:                        if ((rowEnd - rowStart) < 4) {
0578:                            throw new IOException("invalid overflow row info");
0579:                        }
0580:
0581:                        // Overflow page.  the "row" data in the current page points to
0582:                        // another page/row
0583:                        int overflowRowNum = ByteUtil.getUnsignedByte(
0584:                                rowBuffer, rowStart);
0585:                        int overflowPageNum = ByteUtil.get3ByteInt(rowBuffer,
0586:                                rowStart + 1);
0587:                        rowBuffer = rowState.setOverflowRow(new RowId(
0588:                                overflowPageNum, overflowRowNum));
0589:                        rowNum = overflowRowNum;
0590:
0591:                    } else {
0592:
0593:                        rowState.setStatus(RowStateStatus.AT_FINAL);
0594:                        return PageChannel.narrowBuffer(rowBuffer, rowStart,
0595:                                rowEnd);
0596:                    }
0597:                }
0598:            }
0599:
0600:            /**
0601:             * Calls <code>reset</code> on this table and returns an unmodifiable
0602:             * Iterator which will iterate through all the rows of this table.  Use of
0603:             * the Iterator follows the same restrictions as a call to
0604:             * <code>getNextRow</code>.
0605:             * @throws IllegalStateException if an IOException is thrown by one of the
0606:             *         operations, the actual exception will be contained within
0607:             */
0608:            public Iterator<Map<String, Object>> iterator() {
0609:                return iterator(null);
0610:            }
0611:
0612:            /**
0613:             * Calls <code>reset</code> on this table and returns an unmodifiable
0614:             * Iterator which will iterate through all the rows of this table, returning
0615:             * only the given columns.  Use of the Iterator follows the same
0616:             * restrictions as a call to <code>getNextRow</code>.
0617:             * @throws IllegalStateException if an IOException is thrown by one of the
0618:             *         operations, the actual exception will be contained within
0619:             */
0620:            public Iterator<Map<String, Object>> iterator(
0621:                    Collection<String> columnNames) {
0622:                reset();
0623:                return _cursor.iterator(columnNames);
0624:            }
0625:
0626:            /**
0627:             * Writes a new table defined by the given columns to the database.
0628:             * @return the first page of the new table's definition
0629:             */
0630:            public static int writeTableDefinition(List<Column> columns,
0631:                    PageChannel pageChannel, JetFormat format)
0632:                    throws IOException {
0633:                // first, create the usage map page
0634:                int usageMapPageNumber = pageChannel
0635:                        .writeNewPage(createUsageMapDefinitionBuffer(
0636:                                pageChannel, format));
0637:
0638:                // next, determine how big the table def will be (in case it will be more
0639:                // than one page)
0640:                int totalTableDefSize = format.SIZE_TDEF_HEADER
0641:                        + (format.SIZE_COLUMN_DEF_BLOCK * columns.size())
0642:                        + format.SIZE_TDEF_TRAILER;
0643:                for (Column col : columns) {
0644:                    // we add the number of bytes for the column name and 2 bytes for the
0645:                    // length of the column name
0646:                    int nameByteLen = (col.getName().length() * JetFormat.TEXT_FIELD_UNIT_SIZE);
0647:                    totalTableDefSize += nameByteLen + 2;
0648:                }
0649:
0650:                // now, create the table definition
0651:                ByteBuffer buffer = pageChannel.createBuffer(Math.max(
0652:                        totalTableDefSize, format.PAGE_SIZE));
0653:                writeTableDefinitionHeader(buffer, columns, usageMapPageNumber,
0654:                        totalTableDefSize, format);
0655:                writeColumnDefinitions(buffer, columns, format);
0656:
0657:                //End of tabledef
0658:                buffer.put((byte) 0xff);
0659:                buffer.put((byte) 0xff);
0660:
0661:                // write table buffer to database
0662:                int tdefPageNumber = PageChannel.INVALID_PAGE_NUMBER;
0663:                if (totalTableDefSize <= format.PAGE_SIZE) {
0664:
0665:                    // easy case, fits on one page
0666:                    buffer.putShort(format.OFFSET_FREE_SPACE, (short) (buffer
0667:                            .remaining() - 8)); // overwrite page free space
0668:                    // Write the tdef page to disk.
0669:                    tdefPageNumber = pageChannel.writeNewPage(buffer);
0670:
0671:                } else {
0672:
0673:                    // need to split across multiple pages
0674:                    ByteBuffer partialTdef = pageChannel.createPageBuffer();
0675:                    buffer.rewind();
0676:                    int nextTdefPageNumber = PageChannel.INVALID_PAGE_NUMBER;
0677:                    while (buffer.hasRemaining()) {
0678:
0679:                        // reset for next write
0680:                        partialTdef.clear();
0681:
0682:                        if (tdefPageNumber == PageChannel.INVALID_PAGE_NUMBER) {
0683:
0684:                            // this is the first page.  note, the first page already has the
0685:                            // page header, so no need to write it here
0686:                            tdefPageNumber = pageChannel.allocateNewPage();
0687:                            nextTdefPageNumber = tdefPageNumber;
0688:
0689:                        } else {
0690:
0691:                            // write page header
0692:                            writeTablePageHeader(partialTdef);
0693:                        }
0694:
0695:                        // copy the next page of tdef bytes
0696:                        int curTdefPageNumber = nextTdefPageNumber;
0697:                        int writeLen = Math.min(partialTdef.remaining(), buffer
0698:                                .remaining());
0699:                        partialTdef.put(buffer.array(), buffer.position(),
0700:                                writeLen);
0701:                        buffer.position(buffer.position() + writeLen);
0702:
0703:                        if (buffer.hasRemaining()) {
0704:                            // need a next page
0705:                            nextTdefPageNumber = pageChannel.allocateNewPage();
0706:                            partialTdef.putInt(
0707:                                    format.OFFSET_NEXT_TABLE_DEF_PAGE,
0708:                                    nextTdefPageNumber);
0709:                        }
0710:
0711:                        // update page free space
0712:                        partialTdef.putShort(format.OFFSET_FREE_SPACE,
0713:                                (short) (partialTdef.remaining() - 8)); // overwrite page free space
0714:
0715:                        // write partial page to disk
0716:                        pageChannel.writePage(partialTdef, curTdefPageNumber);
0717:                    }
0718:
0719:                }
0720:
0721:                return tdefPageNumber;
0722:            }
0723:
0724:            /**
0725:             * @param buffer Buffer to write to
0726:             * @param columns List of Columns in the table
0727:             */
0728:            private static void writeTableDefinitionHeader(ByteBuffer buffer,
0729:                    List<Column> columns, int usageMapPageNumber,
0730:                    int totalTableDefSize, JetFormat format) throws IOException {
0731:                //Start writing the tdef
0732:                writeTablePageHeader(buffer);
0733:                buffer.putInt(totalTableDefSize); //Length of table def
0734:                buffer.put((byte) 0x59); //Unknown
0735:                buffer.put((byte) 0x06); //Unknown
0736:                buffer.putShort((short) 0); //Unknown
0737:                buffer.putInt(0); //Number of rows
0738:                buffer.putInt(0); //Last Autonumber
0739:                if (countAutoNumberColumns(columns) > 0) {
0740:                    buffer.put((byte) 1);
0741:                } else {
0742:                    buffer.put((byte) 0);
0743:                }
0744:                for (int i = 0; i < 15; i++) { //Unknown
0745:                    buffer.put((byte) 0);
0746:                }
0747:                buffer.put(Table.TYPE_USER); //Table type
0748:                buffer.putShort((short) columns.size()); //Max columns a row will have
0749:                buffer.putShort(Column.countVariableLength(columns)); //Number of variable columns in table
0750:                buffer.putShort((short) columns.size()); //Number of columns in table
0751:                buffer.putInt(0); //Number of indexes in table
0752:                buffer.putInt(0); //Number of indexes in table
0753:                buffer.put((byte) 0); //Usage map row number
0754:                ByteUtil.put3ByteInt(buffer, usageMapPageNumber); //Usage map page number
0755:                buffer.put((byte) 1); //Free map row number
0756:                ByteUtil.put3ByteInt(buffer, usageMapPageNumber); //Free map page number
0757:                if (LOG.isDebugEnabled()) {
0758:                    int position = buffer.position();
0759:                    buffer.rewind();
0760:                    LOG.debug("Creating new table def block:\n"
0761:                            + ByteUtil.toHexString(buffer,
0762:                                    format.SIZE_TDEF_HEADER));
0763:                    buffer.position(position);
0764:                }
0765:            }
0766:
0767:            /**
0768:             * Writes the page header for a table definition page
0769:             * @param buffer Buffer to write to
0770:             */
0771:            private static void writeTablePageHeader(ByteBuffer buffer) {
0772:                buffer.put(PageTypes.TABLE_DEF); //Page type
0773:                buffer.put((byte) 0x01); //Unknown
0774:                buffer.put((byte) 0); //Unknown
0775:                buffer.put((byte) 0); //Unknown
0776:                buffer.putInt(0); //Next TDEF page pointer
0777:            }
0778:
0779:            /**
0780:             * @param buffer Buffer to write to
0781:             * @param columns List of Columns to write definitions for
0782:             */
0783:            private static void writeColumnDefinitions(ByteBuffer buffer,
0784:                    List<Column> columns, JetFormat format) throws IOException {
0785:                short columnNumber = (short) 0;
0786:                short fixedOffset = (short) 0;
0787:                short variableOffset = (short) 0;
0788:                // we specifically put the "long variable" values after the normal
0789:                // variable length values so that we have a better chance of fitting it
0790:                // all (because "long variable" values can go in separate pages)
0791:                short longVariableOffset = Column
0792:                        .countNonLongVariableLength(columns);
0793:                for (Column col : columns) {
0794:                    int position = buffer.position();
0795:                    buffer.put(col.getType().getValue());
0796:                    buffer.put((byte) 0x59); //Unknown
0797:                    buffer.put((byte) 0x06); //Unknown
0798:                    buffer.putShort((short) 0); //Unknown
0799:                    buffer.putShort(columnNumber); //Column Number
0800:                    if (col.isVariableLength()) {
0801:                        if (!col.getType().isLongValue()) {
0802:                            buffer.putShort(variableOffset++);
0803:                        } else {
0804:                            buffer.putShort(longVariableOffset++);
0805:                        }
0806:                    } else {
0807:                        buffer.putShort((short) 0);
0808:                    }
0809:                    buffer.putShort(columnNumber); //Column Number again
0810:                    if (col.getType().getHasScalePrecision()) {
0811:                        buffer.put(col.getPrecision()); // numeric precision
0812:                        buffer.put(col.getScale()); // numeric scale
0813:                    } else {
0814:                        buffer.put((byte) 0x00); //unused
0815:                        buffer.put((byte) 0x00); //unused
0816:                    }
0817:                    buffer.putShort((short) 0); //Unknown
0818:                    buffer.put(getColumnBitFlags(col)); // misc col flags
0819:                    if (col.isCompressedUnicode()) { //Compressed
0820:                        buffer.put((byte) 1);
0821:                    } else {
0822:                        buffer.put((byte) 0);
0823:                    }
0824:                    buffer.putInt(0); //Unknown, but always 0.
0825:                    //Offset for fixed length columns
0826:                    if (col.isVariableLength()) {
0827:                        buffer.putShort((short) 0);
0828:                    } else {
0829:                        buffer.putShort(fixedOffset);
0830:                        fixedOffset += col.getType().getFixedSize();
0831:                    }
0832:                    if (!col.getType().isLongValue()) {
0833:                        buffer.putShort(col.getLength()); //Column length
0834:                    } else {
0835:                        buffer.putShort((short) 0x0000); // unused
0836:                    }
0837:                    columnNumber++;
0838:                    if (LOG.isDebugEnabled()) {
0839:                        LOG.debug("Creating new column def block\n"
0840:                                + ByteUtil.toHexString(buffer, position,
0841:                                        format.SIZE_COLUMN_DEF_BLOCK));
0842:                    }
0843:                }
0844:                for (Column col : columns) {
0845:                    writeName(buffer, col.getName(), format);
0846:                }
0847:            }
0848:
0849:            /**
0850:             * Writes the given name into the given buffer in the format as expected by
0851:             * {@link #readName}.
0852:             */
0853:            private static void writeName(ByteBuffer buffer, String name,
0854:                    JetFormat format) {
0855:                ByteBuffer encName = Column
0856:                        .encodeUncompressedText(name, format);
0857:                buffer.putShort((short) encName.remaining());
0858:                buffer.put(encName);
0859:            }
0860:
0861:            /**
0862:             * Constructs a byte containing the flags for the given column.
0863:             */
0864:            private static byte getColumnBitFlags(Column col) {
0865:                byte flags = Column.UNKNOWN_FLAG_MASK;
0866:                if (!col.isVariableLength()) {
0867:                    flags |= Column.FIXED_LEN_FLAG_MASK;
0868:                }
0869:                if (col.isAutoNumber()) {
0870:                    flags |= Column.AUTO_NUMBER_FLAG_MASK;
0871:                }
0872:                return flags;
0873:            }
0874:
0875:            /**
0876:             * Create the usage map definition page buffer.  The "used pages" map is in
0877:             * row 0, the "pages with free space" map is in row 1.
0878:             */
0879:            private static ByteBuffer createUsageMapDefinitionBuffer(
0880:                    PageChannel pageChannel, JetFormat format)
0881:                    throws IOException {
0882:                // USAGE_MAP_DEF_FREE_SPACE = 3940;
0883:                int usageMapRowLength = format.OFFSET_USAGE_MAP_START
0884:                        + format.USAGE_MAP_TABLE_BYTE_LENGTH;
0885:                int freeSpace = getRowSpaceUsage(format.MAX_ROW_SIZE, format)
0886:                        - (2 * getRowSpaceUsage(usageMapRowLength, format));
0887:
0888:                ByteBuffer rtn = pageChannel.createPageBuffer();
0889:                rtn.put(PageTypes.DATA);
0890:                rtn.put((byte) 0x1); //Unknown
0891:                rtn.putShort((short) freeSpace); //Free space in page
0892:                rtn.putInt(0); //Table definition
0893:                rtn.putInt(0); //Unknown
0894:                rtn.putShort((short) 2); //Number of records on this page
0895:
0896:                // write two rows of usage map definitions
0897:                int rowStart = findRowEnd(rtn, 0, format) - usageMapRowLength;
0898:                for (int i = 0; i < 2; ++i) {
0899:                    rtn
0900:                            .putShort(getRowStartOffset(i, format),
0901:                                    (short) rowStart);
0902:                    if (i == 0) {
0903:                        // initial "usage pages" map definition
0904:                        rtn.put(rowStart, UsageMap.MAP_TYPE_REFERENCE);
0905:                    } else {
0906:                        // initial "pages with free space" map definition
0907:                        rtn.put(rowStart, UsageMap.MAP_TYPE_INLINE);
0908:                    }
0909:                    rowStart -= usageMapRowLength;
0910:                }
0911:
0912:                return rtn;
0913:            }
0914:
0915:            /**
0916:             * Read the table definition
0917:             */
0918:            private void readTableDefinition(ByteBuffer tableBuffer)
0919:                    throws IOException {
0920:                if (LOG.isDebugEnabled()) {
0921:                    tableBuffer.rewind();
0922:                    LOG.debug("Table def block:\n"
0923:                            + ByteUtil.toHexString(tableBuffer,
0924:                                    getFormat().SIZE_TDEF_HEADER));
0925:                }
0926:                _rowCount = tableBuffer.getInt(getFormat().OFFSET_NUM_ROWS);
0927:                _lastAutoNumber = tableBuffer
0928:                        .getInt(getFormat().OFFSET_NEXT_AUTO_NUMBER);
0929:                _tableType = tableBuffer.get(getFormat().OFFSET_TABLE_TYPE);
0930:                _maxColumnCount = tableBuffer
0931:                        .getShort(getFormat().OFFSET_MAX_COLS);
0932:                _maxVarColumnCount = tableBuffer
0933:                        .getShort(getFormat().OFFSET_NUM_VAR_COLS);
0934:                short columnCount = tableBuffer
0935:                        .getShort(getFormat().OFFSET_NUM_COLS);
0936:                _indexSlotCount = tableBuffer
0937:                        .getInt(getFormat().OFFSET_NUM_INDEX_SLOTS);
0938:                _indexCount = tableBuffer
0939:                        .getInt(getFormat().OFFSET_NUM_INDEXES);
0940:
0941:                int rowNum = ByteUtil.getUnsignedByte(tableBuffer,
0942:                        getFormat().OFFSET_OWNED_PAGES);
0943:                int pageNum = ByteUtil.get3ByteInt(tableBuffer,
0944:                        getFormat().OFFSET_OWNED_PAGES + 1);
0945:                _ownedPages = UsageMap.read(getDatabase(), pageNum, rowNum,
0946:                        false);
0947:                rowNum = ByteUtil.getUnsignedByte(tableBuffer,
0948:                        getFormat().OFFSET_FREE_SPACE_PAGES);
0949:                pageNum = ByteUtil.get3ByteInt(tableBuffer,
0950:                        getFormat().OFFSET_FREE_SPACE_PAGES + 1);
0951:                _freeSpacePages = UsageMap.read(getDatabase(), pageNum, rowNum,
0952:                        false);
0953:
0954:                for (int i = 0; i < _indexCount; i++) {
0955:                    int uniqueEntryCountOffset = (getFormat().OFFSET_INDEX_DEF_BLOCK
0956:                            + (i * getFormat().SIZE_INDEX_DEFINITION) + 4);
0957:                    int uniqueEntryCount = tableBuffer
0958:                            .getInt(uniqueEntryCountOffset);
0959:                    _indexes.add(new Index(this , uniqueEntryCount,
0960:                            uniqueEntryCountOffset));
0961:                }
0962:
0963:                int colOffset = getFormat().OFFSET_INDEX_DEF_BLOCK
0964:                        + _indexCount * getFormat().SIZE_INDEX_DEFINITION;
0965:                for (int i = 0; i < columnCount; i++) {
0966:                    Column column = new Column(this , tableBuffer, colOffset
0967:                            + (i * getFormat().SIZE_COLUMN_HEADER));
0968:                    _columns.add(column);
0969:                    if (column.isVariableLength()) {
0970:                        // also shove it in the variable columns list, which is ordered
0971:                        // differently from the _columns list
0972:                        _varColumns.add(column);
0973:                    }
0974:                }
0975:                tableBuffer.position(colOffset
0976:                        + (columnCount * getFormat().SIZE_COLUMN_HEADER));
0977:                for (int i = 0; i < columnCount; i++) {
0978:                    Column column = _columns.get(i);
0979:                    column.setName(readName(tableBuffer));
0980:                }
0981:                Collections.sort(_columns);
0982:
0983:                // setup the data index for the columns
0984:                int colIdx = 0;
0985:                for (Column col : _columns) {
0986:                    col.setColumnIndex(colIdx++);
0987:                }
0988:
0989:                // sort variable length columns based on their index into the variable
0990:                // length offset table, because we will write the columns in this order
0991:                Collections.sort(_varColumns, VAR_LEN_COLUMN_COMPARATOR);
0992:
0993:                int idxOffset = tableBuffer.position();
0994:                tableBuffer
0995:                        .position(idxOffset
0996:                                + (getFormat().OFFSET_INDEX_NUMBER_BLOCK * _indexCount));
0997:
0998:                // if there are more index slots than indexes, the initial slots are
0999:                // always empty/invalid, so we skip that data
1000:                int firstRealIdx = (_indexSlotCount - _indexCount);
1001:
1002:                for (int i = 0; i < _indexSlotCount; i++) {
1003:
1004:                    tableBuffer.getInt(); //Forward past Unknown
1005:                    tableBuffer.getInt(); //Forward past alternate index number
1006:                    int indexNumber = tableBuffer.getInt();
1007:                    tableBuffer.position(tableBuffer.position() + 11);
1008:                    byte indexType = tableBuffer.get();
1009:                    tableBuffer.position(tableBuffer.position() + 4);
1010:
1011:                    if (i < firstRealIdx) {
1012:                        // ignore this info
1013:                        continue;
1014:                    }
1015:
1016:                    Index index = _indexes.get(i - firstRealIdx);
1017:                    index.setIndexNumber(indexNumber);
1018:                    index.setIndexType(indexType);
1019:                }
1020:
1021:                // read actual index names
1022:                for (int i = 0; i < _indexSlotCount; i++) {
1023:                    if (i < firstRealIdx) {
1024:                        // for each empty index slot, there is some weird sort of name, skip
1025:                        // it
1026:                        skipName(tableBuffer);
1027:                        continue;
1028:                    }
1029:
1030:                    _indexes.get(i - firstRealIdx).setName(
1031:                            readName(tableBuffer));
1032:                }
1033:                int idxEndOffset = tableBuffer.position();
1034:
1035:                Collections.sort(_indexes);
1036:
1037:                // go back to index column info after sorting
1038:                tableBuffer.position(idxOffset);
1039:                for (int i = 0; i < _indexCount; i++) {
1040:                    tableBuffer.getInt(); //Forward past Unknown
1041:                    _indexes.get(i).read(tableBuffer, _columns);
1042:                }
1043:
1044:                // reset to end of index info
1045:                tableBuffer.position(idxEndOffset);
1046:            }
1047:
1048:            /**
1049:             * Writes the given page data to the given page number, clears any other
1050:             * relevant buffers.
1051:             */
1052:            private void writeDataPage(ByteBuffer pageBuffer, int pageNumber)
1053:                    throws IOException {
1054:                // write the page data
1055:                getPageChannel().writePage(pageBuffer, pageNumber);
1056:
1057:                // possibly invalidate the add row buffer if a different data buffer is
1058:                // being written (e.g. this happens during deleteRow)
1059:                _addRowBufferH.possiblyInvalidate(pageNumber, pageBuffer);
1060:
1061:                // update modification count so any active RowStates can keep themselves
1062:                // up-to-date
1063:                ++_modCount;
1064:            }
1065:
1066:            /**
1067:             * Returns a name read from the buffer at the current position.  The
1068:             * expected name format is the name length as a short followed by (length *
1069:             * 2) bytes encoded using the {@link JetFormat#CHARSET}
1070:             */
1071:            private String readName(ByteBuffer buffer) {
1072:                short nameLength = buffer.getShort();
1073:                byte[] nameBytes = new byte[nameLength];
1074:                buffer.get(nameBytes);
1075:                return Column.decodeUncompressedText(nameBytes, getFormat());
1076:            }
1077:
1078:            /**
1079:             * Skips past a name int the buffer at the current position.  The
1080:             * expected name format is the same as that for {@link #readName}.
1081:             */
1082:            private void skipName(ByteBuffer buffer) {
1083:                short nameLength = buffer.getShort();
1084:                buffer.position(buffer.position() + nameLength);
1085:            }
1086:
1087:            /**
1088:             * Converts a map of columnName -> columnValue to an array of row values
1089:             * appropriate for a call to {@link #addRow(Object...)}.
1090:             */
1091:            public Object[] asRow(Map<String, Object> rowMap) {
1092:                Object[] row = new Object[_columns.size()];
1093:                if (rowMap == null) {
1094:                    return row;
1095:                }
1096:                for (Column col : _columns) {
1097:                    row[col.getColumnIndex()] = rowMap.get(col.getName());
1098:                }
1099:                return row;
1100:            }
1101:
1102:            /**
1103:             * Add a single row to this table and write it to disk
1104:             */
1105:            public void addRow(Object... row) throws IOException {
1106:                addRows(Collections.singletonList(row), _singleRowBufferH);
1107:            }
1108:
1109:            /**
1110:             * Add multiple rows to this table, only writing to disk after all
1111:             * rows have been written, and every time a data page is filled.  This
1112:             * is much more efficient than calling <code>addRow</code> multiple times.
1113:             * @param rows List of Object[] row values
1114:             */
1115:            public void addRows(List<? extends Object[]> rows)
1116:                    throws IOException {
1117:                addRows(rows, _multiRowBufferH);
1118:            }
1119:
1120:            /**
1121:             * Add multiple rows to this table, only writing to disk after all
1122:             * rows have been written, and every time a data page is filled.  This
1123:             * is much more efficient than calling <code>addRow</code> multiple times.
1124:             * @param rows List of Object[] row values
1125:             * @param writeRowBufferH TempBufferHolder used to generate buffers for
1126:             *                        writing the row data
1127:             */
1128:            private void addRows(List<? extends Object[]> rows,
1129:                    TempBufferHolder writeRowBufferH) throws IOException {
1130:                ByteBuffer[] rowData = new ByteBuffer[rows.size()];
1131:                Iterator<? extends Object[]> iter = rows.iterator();
1132:                for (int i = 0; iter.hasNext(); i++) {
1133:                    rowData[i] = createRow(iter.next(),
1134:                            getFormat().MAX_ROW_SIZE, writeRowBufferH
1135:                                    .getPageBuffer(getPageChannel()));
1136:                    if (rowData[i].limit() > getFormat().MAX_ROW_SIZE) {
1137:                        throw new IOException("Row size " + rowData[i].limit()
1138:                                + " is too large");
1139:                    }
1140:                }
1141:
1142:                ByteBuffer dataPage = null;
1143:                int pageNumber = PageChannel.INVALID_PAGE_NUMBER;
1144:
1145:                // find last data page (Not bothering to check other pages for free
1146:                // space.)
1147:                UsageMap.PageCursor revPageCursor = _ownedPages.cursor();
1148:                revPageCursor.afterLast();
1149:                while (true) {
1150:                    int tmpPageNumber = revPageCursor.getPreviousPage();
1151:                    if (tmpPageNumber < 0) {
1152:                        break;
1153:                    }
1154:                    dataPage = _addRowBufferH.setPage(getPageChannel(),
1155:                            tmpPageNumber);
1156:                    if (dataPage.get() == PageTypes.DATA) {
1157:                        // found last data page, only use if actually listed in free space
1158:                        // pages
1159:                        if (_freeSpacePages.containsPageNumber(tmpPageNumber)) {
1160:                            pageNumber = tmpPageNumber;
1161:                        }
1162:                        break;
1163:                    }
1164:                }
1165:
1166:                if (pageNumber == PageChannel.INVALID_PAGE_NUMBER) {
1167:                    // No data pages exist (with free space).  Create a new one.
1168:                    dataPage = newDataPage();
1169:                    pageNumber = _addRowBufferH.getPageNumber();
1170:                }
1171:
1172:                for (int i = 0; i < rowData.length; i++) {
1173:                    int rowSize = rowData[i].remaining();
1174:                    int rowSpaceUsage = getRowSpaceUsage(rowSize, getFormat());
1175:                    short freeSpaceInPage = dataPage
1176:                            .getShort(getFormat().OFFSET_FREE_SPACE);
1177:                    if (freeSpaceInPage < rowSpaceUsage) {
1178:
1179:                        // Last data page is full.  Create a new one.
1180:                        writeDataPage(dataPage, pageNumber);
1181:                        _freeSpacePages.removePageNumber(pageNumber);
1182:
1183:                        dataPage = newDataPage();
1184:                        pageNumber = _addRowBufferH.getPageNumber();
1185:
1186:                        freeSpaceInPage = dataPage
1187:                                .getShort(getFormat().OFFSET_FREE_SPACE);
1188:                    }
1189:
1190:                    // write out the row data
1191:                    int rowNum = addDataPageRow(dataPage, rowSize, getFormat());
1192:                    dataPage.put(rowData[i]);
1193:
1194:                    // update the indexes
1195:                    for (Index index : _indexes) {
1196:                        index
1197:                                .addRow(rows.get(i), new RowId(pageNumber,
1198:                                        rowNum));
1199:                    }
1200:                }
1201:                writeDataPage(dataPage, pageNumber);
1202:
1203:                // Update tdef page
1204:                updateTableDefinition(rows.size());
1205:            }
1206:
1207:            /**
1208:             * Updates the table definition after rows are modified.
1209:             */
1210:            private void updateTableDefinition(int rowCountInc)
1211:                    throws IOException {
1212:                // load table definition
1213:                ByteBuffer tdefPage = _tableDefBufferH.setPage(
1214:                        getPageChannel(), _tableDefPageNumber);
1215:
1216:                // make sure rowcount and autonumber are up-to-date
1217:                _rowCount += rowCountInc;
1218:                tdefPage.putInt(getFormat().OFFSET_NUM_ROWS, _rowCount);
1219:                tdefPage.putInt(getFormat().OFFSET_NEXT_AUTO_NUMBER,
1220:                        _lastAutoNumber);
1221:
1222:                // write any index changes
1223:                Iterator<Index> indIter = _indexes.iterator();
1224:                for (int i = 0; i < _indexes.size(); i++) {
1225:                    Index index = indIter.next();
1226:                    // write the unique entry count for the index to the table definition
1227:                    // page
1228:                    tdefPage.putInt(index.getUniqueEntryCountOffset(), index
1229:                            .getUniqueEntryCount());
1230:                    // write the entry page for the index
1231:                    index.update();
1232:                }
1233:
1234:                // write modified table definition
1235:                getPageChannel().writePage(tdefPage, _tableDefPageNumber);
1236:            }
1237:
1238:            /**
1239:             * Create a new data page
1240:             * @return Page number of the new page
1241:             */
1242:            private ByteBuffer newDataPage() throws IOException {
1243:                if (LOG.isDebugEnabled()) {
1244:                    LOG.debug("Creating new data page");
1245:                }
1246:                ByteBuffer dataPage = _addRowBufferH
1247:                        .setNewPage(getPageChannel());
1248:                dataPage.put(PageTypes.DATA); //Page type
1249:                dataPage.put((byte) 1); //Unknown
1250:                dataPage.putShort((short) getRowSpaceUsage(
1251:                        getFormat().MAX_ROW_SIZE, getFormat())); //Free space in this page
1252:                dataPage.putInt(_tableDefPageNumber); //Page pointer to table definition
1253:                dataPage.putInt(0); //Unknown
1254:                dataPage.putInt(0); //Number of records on this page
1255:                int pageNumber = _addRowBufferH.getPageNumber();
1256:                getPageChannel().writePage(dataPage, pageNumber);
1257:                _ownedPages.addPageNumber(pageNumber);
1258:                _freeSpacePages.addPageNumber(pageNumber);
1259:                return dataPage;
1260:            }
1261:
1262:            /**
1263:             * Serialize a row of Objects into a byte buffer
1264:             */
1265:            ByteBuffer createRow(Object[] rowArray, int maxRowSize,
1266:                    ByteBuffer buffer) throws IOException {
1267:                buffer.putShort(_maxColumnCount);
1268:                NullMask nullMask = new NullMask(_maxColumnCount);
1269:
1270:                List<Object> row = new ArrayList<Object>(_columns.size());
1271:                for (Object rowValue : rowArray) {
1272:                    row.add(rowValue);
1273:                }
1274:                //Append null for arrays that are too small
1275:                for (int i = rowArray.length; i < _columns.size(); i++) {
1276:                    row.add(null);
1277:                }
1278:
1279:                //Fixed length column data comes first
1280:                int fixedDataStart = buffer.position();
1281:                int fixedDataEnd = fixedDataStart;
1282:                for (Column col : _columns) {
1283:
1284:                    if (!col.isVariableLength()) {
1285:
1286:                        Object rowValue = row.get(col.getColumnIndex());
1287:
1288:                        if (col.getType() == DataType.BOOLEAN) {
1289:
1290:                            if (Column.toBooleanValue(rowValue)) {
1291:                                //Booleans are stored in the null mask
1292:                                nullMask.markNotNull(col);
1293:                            }
1294:
1295:                        } else {
1296:
1297:                            if (col.isAutoNumber()) {
1298:                                // ignore given row value, use next autonumber
1299:                                rowValue = getNextAutoNumber();
1300:                            }
1301:
1302:                            if (rowValue != null) {
1303:
1304:                                // we have a value
1305:                                nullMask.markNotNull(col);
1306:
1307:                                //remainingRowLength is ignored when writing fixed length data
1308:                                buffer.position(fixedDataStart
1309:                                        + col.getFixedDataOffset());
1310:                                buffer.put(col.write(rowValue, 0));
1311:
1312:                                // keep track of the end of fixed data
1313:                                if (buffer.position() > fixedDataEnd) {
1314:                                    fixedDataEnd = buffer.position();
1315:                                }
1316:                            }
1317:                        }
1318:                    }
1319:                }
1320:
1321:                // reposition at end of fixed data
1322:                buffer.position(fixedDataEnd);
1323:
1324:                // only need this info if this table contains any var length data
1325:                if (_maxVarColumnCount > 0) {
1326:
1327:                    // figure out how much space remains for var length data.  first,
1328:                    // account for already written space
1329:                    maxRowSize -= buffer.position();
1330:                    // now, account for trailer space
1331:                    maxRowSize -= (nullMask.byteSize() + 4 + (_maxVarColumnCount * 2));
1332:
1333:                    //Now write out variable length column data
1334:                    short[] varColumnOffsets = new short[_maxVarColumnCount];
1335:                    int varColumnOffsetsIndex = 0;
1336:                    for (Column varCol : _varColumns) {
1337:                        short offset = (short) buffer.position();
1338:                        Object rowValue = row.get(varCol.getColumnIndex());
1339:                        if (rowValue != null) {
1340:                            // we have a value
1341:                            nullMask.markNotNull(varCol);
1342:
1343:                            ByteBuffer varDataBuf = varCol.write(rowValue,
1344:                                    maxRowSize);
1345:                            maxRowSize -= varDataBuf.remaining();
1346:                            buffer.put(varDataBuf);
1347:                        }
1348:
1349:                        // we do a loop here so that we fill in offsets for deleted columns
1350:                        while (varColumnOffsetsIndex <= varCol
1351:                                .getVarLenTableIndex()) {
1352:                            varColumnOffsets[varColumnOffsetsIndex++] = offset;
1353:                        }
1354:                    }
1355:
1356:                    // fill in offsets for any remaining deleted columns
1357:                    while (varColumnOffsetsIndex < varColumnOffsets.length) {
1358:                        varColumnOffsets[varColumnOffsetsIndex++] = (short) buffer
1359:                                .position();
1360:                    }
1361:
1362:                    buffer.putShort((short) buffer.position()); //EOD marker
1363:                    //Now write out variable length offsets
1364:                    //Offsets are stored in reverse order
1365:                    for (int i = _maxVarColumnCount - 1; i >= 0; i--) {
1366:                        buffer.putShort(varColumnOffsets[i]);
1367:                    }
1368:                    buffer.putShort(_maxVarColumnCount); //Number of var length columns
1369:                }
1370:
1371:                nullMask.write(buffer); //Null mask
1372:                buffer.limit(buffer.position());
1373:                buffer.flip();
1374:                if (LOG.isDebugEnabled()) {
1375:                    LOG.debug("Creating new data block:\n"
1376:                            + ByteUtil.toHexString(buffer, buffer.limit()));
1377:                }
1378:                return buffer;
1379:            }
1380:
1381:            public int getRowCount() {
1382:                return _rowCount;
1383:            }
1384:
1385:            private int getNextAutoNumber() {
1386:                // note, the saved value is the last one handed out, so pre-increment
1387:                return ++_lastAutoNumber;
1388:            }
1389:
1390:            int getLastAutoNumber() {
1391:                // gets the last used auto number (does not modify)
1392:                return _lastAutoNumber;
1393:            }
1394:
1395:            @Override
1396:            public String toString() {
1397:                StringBuilder rtn = new StringBuilder();
1398:                rtn.append("Type: " + _tableType);
1399:                rtn.append("\nName: " + _name);
1400:                rtn.append("\nRow count: " + _rowCount);
1401:                rtn.append("\nColumn count: " + _columns.size());
1402:                rtn.append("\nIndex count: " + _indexCount);
1403:                rtn.append("\nColumns:\n");
1404:                for (Column col : _columns) {
1405:                    rtn.append(col);
1406:                }
1407:                rtn.append("\nIndexes:\n");
1408:                for (Index index : _indexes) {
1409:                    rtn.append(index);
1410:                }
1411:                rtn.append("\nOwned pages: " + _ownedPages + "\n");
1412:                return rtn.toString();
1413:            }
1414:
1415:            /**
1416:             * @return A simple String representation of the entire table in tab-delimited format
1417:             */
1418:            public String display() throws IOException {
1419:                return display(Long.MAX_VALUE);
1420:            }
1421:
1422:            /**
1423:             * @param limit Maximum number of rows to display
1424:             * @return A simple String representation of the entire table in tab-delimited format
1425:             */
1426:            public String display(long limit) throws IOException {
1427:                reset();
1428:                StringBuilder rtn = new StringBuilder();
1429:                for (Iterator<Column> iter = _columns.iterator(); iter
1430:                        .hasNext();) {
1431:                    Column col = iter.next();
1432:                    rtn.append(col.getName());
1433:                    if (iter.hasNext()) {
1434:                        rtn.append("\t");
1435:                    }
1436:                }
1437:                rtn.append("\n");
1438:                Map<String, Object> row;
1439:                int rowCount = 0;
1440:                while ((rowCount++ < limit) && (row = getNextRow()) != null) {
1441:                    for (Iterator<Object> iter = row.values().iterator(); iter
1442:                            .hasNext();) {
1443:                        Object obj = iter.next();
1444:                        if (obj instanceof  byte[]) {
1445:                            byte[] b = (byte[]) obj;
1446:                            rtn.append(ByteUtil.toHexString(b));
1447:                            //This block can be used to easily dump a binary column to a file
1448:                            /*java.io.File f = java.io.File.createTempFile("ole", ".bin");
1449:                              java.io.FileOutputStream out = new java.io.FileOutputStream(f);
1450:                              out.write(b);
1451:                              out.flush();
1452:                              out.close();*/
1453:                        } else {
1454:                            rtn.append(String.valueOf(obj));
1455:                        }
1456:                        if (iter.hasNext()) {
1457:                            rtn.append("\t");
1458:                        }
1459:                    }
1460:                    rtn.append("\n");
1461:                }
1462:                return rtn.toString();
1463:            }
1464:
1465:            /**
1466:             * Updates free space and row info for a new row of the given size in the
1467:             * given data page.  Positions the page for writing the row data.
1468:             * @return the row number of the new row
1469:             */
1470:            public static int addDataPageRow(ByteBuffer dataPage, int rowSize,
1471:                    JetFormat format) {
1472:                int rowSpaceUsage = getRowSpaceUsage(rowSize, format);
1473:
1474:                // Decrease free space record.
1475:                short freeSpaceInPage = dataPage
1476:                        .getShort(format.OFFSET_FREE_SPACE);
1477:                dataPage.putShort(format.OFFSET_FREE_SPACE,
1478:                        (short) (freeSpaceInPage - rowSpaceUsage));
1479:
1480:                // Increment row count record.
1481:                short rowCount = dataPage
1482:                        .getShort(format.OFFSET_NUM_ROWS_ON_DATA_PAGE);
1483:                dataPage.putShort(format.OFFSET_NUM_ROWS_ON_DATA_PAGE,
1484:                        (short) (rowCount + 1));
1485:
1486:                // determine row position
1487:                short rowLocation = findRowEnd(dataPage, rowCount, format);
1488:                rowLocation -= rowSize;
1489:
1490:                // write row position
1491:                dataPage.putShort(getRowStartOffset(rowCount, format),
1492:                        rowLocation);
1493:
1494:                // set position for row data
1495:                dataPage.position(rowLocation);
1496:
1497:                return rowCount;
1498:            }
1499:
1500:            /**
1501:             * Returns the row count for the current page.  If the page is invalid
1502:             * ({@code null}) or the page is not a DATA page, 0 is returned.
1503:             */
1504:            private static int getRowsOnDataPage(ByteBuffer rowBuffer,
1505:                    JetFormat format) throws IOException {
1506:                int rowsOnPage = 0;
1507:                if ((rowBuffer != null) && (rowBuffer.get(0) == PageTypes.DATA)) {
1508:                    rowsOnPage = rowBuffer
1509:                            .getShort(format.OFFSET_NUM_ROWS_ON_DATA_PAGE);
1510:                }
1511:                return rowsOnPage;
1512:            }
1513:
1514:            /**
1515:             * @throws IllegalStateException if the given rowId is invalid
1516:             */
1517:            private static void requireValidRowId(RowId rowId) {
1518:                if (!rowId.isValid()) {
1519:                    throw new IllegalArgumentException(
1520:                            "Given rowId is invalid: " + rowId);
1521:                }
1522:            }
1523:
1524:            /**
1525:             * @throws IllegalStateException if the given row is invalid or deleted
1526:             */
1527:            private static void requireNonDeletedRow(RowState rowState,
1528:                    RowId rowId) {
1529:                if (!rowState.isValid()) {
1530:                    throw new IllegalArgumentException(
1531:                            "Given rowId is invalid for this table: " + rowId);
1532:                }
1533:                if (rowState.isDeleted()) {
1534:                    throw new IllegalStateException("Row is deleted: " + rowId);
1535:                }
1536:            }
1537:
1538:            public static boolean isDeletedRow(short rowStart) {
1539:                return ((rowStart & DELETED_ROW_MASK) != 0);
1540:            }
1541:
1542:            public static boolean isOverflowRow(short rowStart) {
1543:                return ((rowStart & OVERFLOW_ROW_MASK) != 0);
1544:            }
1545:
1546:            public static short cleanRowStart(short rowStart) {
1547:                return (short) (rowStart & OFFSET_MASK);
1548:            }
1549:
1550:            public static short findRowStart(ByteBuffer buffer, int rowNum,
1551:                    JetFormat format) {
1552:                return cleanRowStart(buffer.getShort(getRowStartOffset(rowNum,
1553:                        format)));
1554:            }
1555:
1556:            public static int getRowStartOffset(int rowNum, JetFormat format) {
1557:                return format.OFFSET_ROW_START
1558:                        + (format.SIZE_ROW_LOCATION * rowNum);
1559:            }
1560:
1561:            public static short findRowEnd(ByteBuffer buffer, int rowNum,
1562:                    JetFormat format) {
1563:                return (short) ((rowNum == 0) ? format.PAGE_SIZE
1564:                        : cleanRowStart(buffer.getShort(getRowEndOffset(rowNum,
1565:                                format))));
1566:            }
1567:
1568:            public static int getRowEndOffset(int rowNum, JetFormat format) {
1569:                return format.OFFSET_ROW_START
1570:                        + (format.SIZE_ROW_LOCATION * (rowNum - 1));
1571:            }
1572:
1573:            public static int getRowSpaceUsage(int rowSize, JetFormat format) {
1574:                return rowSize + format.SIZE_ROW_LOCATION;
1575:            }
1576:
1577:            /**
1578:             * @return the number of "AutoNumber" columns in the given collection of
1579:             *         columns.
1580:             */
1581:            public static int countAutoNumberColumns(Collection<Column> columns) {
1582:                int numAutoNumCols = 0;
1583:                for (Column c : columns) {
1584:                    if (c.isAutoNumber()) {
1585:                        ++numAutoNumCols;
1586:                    }
1587:                }
1588:                return numAutoNumCols;
1589:            }
1590:
1591:            /** various statuses for the row data */
1592:            private enum RowStatus {
1593:                INIT, INVALID_PAGE, INVALID_ROW, VALID, DELETED, NORMAL, OVERFLOW;
1594:            }
1595:
1596:            /** the phases the RowState moves through as the data is parsed */
1597:            private enum RowStateStatus {
1598:                INIT, AT_HEADER, AT_FINAL;
1599:            }
1600:
1601:            /**
1602:             * Maintains the state of reading a row of data.
1603:             */
1604:            public final class RowState {
1605:                /** Buffer used for reading the header row data pages */
1606:                private final TempPageHolder _headerRowBufferH;
1607:                /** the header rowId */
1608:                private RowId _headerRowId = RowId.FIRST_ROW_ID;
1609:                /** the number of rows on the header page */
1610:                private int _rowsOnHeaderPage;
1611:                /** the rowState status */
1612:                private RowStateStatus _status = RowStateStatus.INIT;
1613:                /** the row status */
1614:                private RowStatus _rowStatus = RowStatus.INIT;
1615:                /** buffer used for reading overflow pages */
1616:                private final TempPageHolder _overflowRowBufferH = TempPageHolder
1617:                        .newHolder(TempBufferHolder.Type.SOFT);
1618:                /** the row buffer which contains the final data (after following any
1619:                    overflow pointers) */
1620:                private ByteBuffer _finalRowBuffer;
1621:                /** the rowId which contains the final data (after following any overflow
1622:                    pointers) */
1623:                private RowId _finalRowId = null;
1624:                /** true if the row values array has data */
1625:                private boolean _haveRowValues;
1626:                /** values read from the last row */
1627:                private final Object[] _rowValues;
1628:                /** last modification count seen on the table we track this so that the
1629:                    rowState can detect updates to the table and re-read any buffered
1630:                    data */
1631:                private int _lastModCount;
1632:
1633:                private RowState(TempBufferHolder.Type headerType) {
1634:                    _headerRowBufferH = TempPageHolder.newHolder(headerType);
1635:                    _rowValues = new Object[Table.this .getColumnCount()];
1636:                    _lastModCount = Table.this ._modCount;
1637:                }
1638:
1639:                public Table getTable() {
1640:                    return Table.this ;
1641:                }
1642:
1643:                public void reset() {
1644:                    _finalRowId = null;
1645:                    _finalRowBuffer = null;
1646:                    _rowsOnHeaderPage = 0;
1647:                    _status = RowStateStatus.INIT;
1648:                    _rowStatus = RowStatus.INIT;
1649:                    if (_haveRowValues) {
1650:                        Arrays.fill(_rowValues, null);
1651:                        _haveRowValues = false;
1652:                    }
1653:                }
1654:
1655:                public boolean isUpToDate() {
1656:                    return (Table.this ._modCount == _lastModCount);
1657:                }
1658:
1659:                private void checkForModification() {
1660:                    if (!isUpToDate()) {
1661:                        reset();
1662:                        _headerRowBufferH.invalidate();
1663:                        _overflowRowBufferH.invalidate();
1664:                        _lastModCount = Table.this ._modCount;
1665:                    }
1666:                }
1667:
1668:                private ByteBuffer getFinalPage() throws IOException {
1669:                    if (_finalRowBuffer == null) {
1670:                        // (re)load current page
1671:                        _finalRowBuffer = getHeaderPage();
1672:                    }
1673:                    return _finalRowBuffer;
1674:                }
1675:
1676:                public RowId getFinalRowId() {
1677:                    if (_finalRowId == null) {
1678:                        _finalRowId = getHeaderRowId();
1679:                    }
1680:                    return _finalRowId;
1681:                }
1682:
1683:                private void setRowStatus(RowStatus rowStatus) {
1684:                    _rowStatus = rowStatus;
1685:                }
1686:
1687:                public boolean isValid() {
1688:                    return (_rowStatus.ordinal() >= RowStatus.VALID.ordinal());
1689:                }
1690:
1691:                public boolean isDeleted() {
1692:                    return (_rowStatus == RowStatus.DELETED);
1693:                }
1694:
1695:                public boolean isOverflow() {
1696:                    return (_rowStatus == RowStatus.OVERFLOW);
1697:                }
1698:
1699:                public boolean isHeaderPageNumberValid() {
1700:                    return (_rowStatus.ordinal() > RowStatus.INVALID_PAGE
1701:                            .ordinal());
1702:                }
1703:
1704:                public boolean isHeaderRowNumberValid() {
1705:                    return (_rowStatus.ordinal() > RowStatus.INVALID_ROW
1706:                            .ordinal());
1707:                }
1708:
1709:                private void setStatus(RowStateStatus status) {
1710:                    _status = status;
1711:                }
1712:
1713:                public boolean isAtHeaderRow() {
1714:                    return (_status.ordinal() >= RowStateStatus.AT_HEADER
1715:                            .ordinal());
1716:                }
1717:
1718:                public boolean isAtFinalRow() {
1719:                    return (_status.ordinal() >= RowStateStatus.AT_FINAL
1720:                            .ordinal());
1721:                }
1722:
1723:                private void setRowValue(int idx, Object value) {
1724:                    _haveRowValues = true;
1725:                    _rowValues[idx] = value;
1726:                }
1727:
1728:                public Object[] getRowValues() {
1729:                    Object[] copy = new Object[_rowValues.length];
1730:                    System.arraycopy(_rowValues, 0, copy, 0, _rowValues.length);
1731:                    return copy;
1732:                }
1733:
1734:                public RowId getHeaderRowId() {
1735:                    return _headerRowId;
1736:                }
1737:
1738:                public int getRowsOnHeaderPage() {
1739:                    return _rowsOnHeaderPage;
1740:                }
1741:
1742:                private ByteBuffer getHeaderPage() throws IOException {
1743:                    checkForModification();
1744:                    return _headerRowBufferH.getPage(getPageChannel());
1745:                }
1746:
1747:                private ByteBuffer setHeaderRow(RowId rowId) throws IOException {
1748:                    checkForModification();
1749:
1750:                    // don't do any work if we are already positioned correctly
1751:                    if (isAtHeaderRow() && (getHeaderRowId().equals(rowId))) {
1752:                        return (isValid() ? getHeaderPage() : null);
1753:                    }
1754:
1755:                    // rejigger everything
1756:                    reset();
1757:                    _headerRowId = rowId;
1758:                    _finalRowId = rowId;
1759:
1760:                    int pageNumber = rowId.getPageNumber();
1761:                    int rowNumber = rowId.getRowNumber();
1762:                    if ((pageNumber < 0)
1763:                            || !_ownedPages.containsPageNumber(pageNumber)) {
1764:                        setRowStatus(RowStatus.INVALID_PAGE);
1765:                        return null;
1766:                    }
1767:
1768:                    _finalRowBuffer = _headerRowBufferH.setPage(
1769:                            getPageChannel(), pageNumber);
1770:                    _rowsOnHeaderPage = getRowsOnDataPage(_finalRowBuffer,
1771:                            getFormat());
1772:
1773:                    if ((rowNumber < 0) || (rowNumber >= _rowsOnHeaderPage)) {
1774:                        setRowStatus(RowStatus.INVALID_ROW);
1775:                        return null;
1776:                    }
1777:
1778:                    setRowStatus(RowStatus.VALID);
1779:                    return _finalRowBuffer;
1780:                }
1781:
1782:                private ByteBuffer setOverflowRow(RowId rowId)
1783:                        throws IOException {
1784:                    // this should never see modifications because it only happens within
1785:                    // the positionAtRowData method
1786:                    if (!isUpToDate()) {
1787:                        throw new IllegalStateException(
1788:                                "Table modified while searching?");
1789:                    }
1790:                    if (_rowStatus != RowStatus.OVERFLOW) {
1791:                        throw new IllegalStateException(
1792:                                "Row is not an overflow row?");
1793:                    }
1794:                    _finalRowId = rowId;
1795:                    _finalRowBuffer = _overflowRowBufferH.setPage(
1796:                            getPageChannel(), rowId.getPageNumber());
1797:                    return _finalRowBuffer;
1798:                }
1799:
1800:            }
1801:
1802:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.