Source Code Cross Referenced for Column.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.io.ObjectStreamException;
0032:        import java.math.BigDecimal;
0033:        import java.math.BigInteger;
0034:        import java.nio.ByteBuffer;
0035:        import java.nio.ByteOrder;
0036:        import java.nio.CharBuffer;
0037:        import java.sql.SQLException;
0038:        import java.util.Calendar;
0039:        import java.util.Date;
0040:        import java.util.List;
0041:        import java.util.regex.Matcher;
0042:        import java.util.regex.Pattern;
0043:
0044:        import com.healthmarketscience.jackcess.scsu.EndOfInputException;
0045:        import com.healthmarketscience.jackcess.scsu.Expand;
0046:        import com.healthmarketscience.jackcess.scsu.IllegalInputException;
0047:        import org.apache.commons.logging.Log;
0048:        import org.apache.commons.logging.LogFactory;
0049:
0050:        /**
0051:         * Access database column definition
0052:         * @author Tim McCune
0053:         */
0054:        public class Column implements  Comparable<Column> {
0055:
0056:            private static final Log LOG = LogFactory.getLog(Column.class);
0057:
0058:            /**
0059:             * Meaningless placeholder object for inserting values in an autonumber
0060:             * column.  it is not required that this value be used (any passed in value
0061:             * is ignored), but using this placeholder may make code more obvious.
0062:             */
0063:            public static final Object AUTO_NUMBER = "<AUTO_NUMBER>";
0064:
0065:            /**
0066:             * Access stores numeric dates in days.  Java stores them in milliseconds.
0067:             */
0068:            private static final double MILLISECONDS_PER_DAY = (24L * 60L * 60L * 1000L);
0069:
0070:            /**
0071:             * Access starts counting dates at Jan 1, 1900.  Java starts counting
0072:             * at Jan 1, 1970.  This is the # of millis between them for conversion.
0073:             */
0074:            private static final long MILLIS_BETWEEN_EPOCH_AND_1900 = 25569L * (long) MILLISECONDS_PER_DAY;
0075:
0076:            /**
0077:             * Long value (LVAL) type that indicates that the value is stored on the same page
0078:             */
0079:            private static final byte LONG_VALUE_TYPE_THIS_PAGE = (byte) 0x80;
0080:            /**
0081:             * Long value (LVAL) type that indicates that the value is stored on another page
0082:             */
0083:            private static final byte LONG_VALUE_TYPE_OTHER_PAGE = (byte) 0x40;
0084:            /**
0085:             * Long value (LVAL) type that indicates that the value is stored on multiple other pages
0086:             */
0087:            private static final byte LONG_VALUE_TYPE_OTHER_PAGES = (byte) 0x00;
0088:
0089:            /** mask for the fixed len bit */
0090:            public static final byte FIXED_LEN_FLAG_MASK = (byte) 0x01;
0091:
0092:            /** mask for the auto number bit */
0093:            public static final byte AUTO_NUMBER_FLAG_MASK = (byte) 0x04;
0094:
0095:            /** mask for the unknown bit */
0096:            public static final byte UNKNOWN_FLAG_MASK = (byte) 0x02;
0097:
0098:            private static final Pattern GUID_PATTERN = Pattern
0099:                    .compile("\\s*[{]?([\\p{XDigit}]{8})-([\\p{XDigit}]{4})-([\\p{XDigit}]{4})-([\\p{XDigit}]{4})-([\\p{XDigit}]{12})[}]?\\s*");
0100:
0101:            /** owning table */
0102:            private final Table _table;
0103:            /** For text columns, whether or not they are compressed */
0104:            private boolean _compressedUnicode = false;
0105:            /** Whether or not the column is of variable length */
0106:            private boolean _variableLength;
0107:            /** Whether or not the column is an autonumber column */
0108:            private boolean _autoNumber;
0109:            /** Numeric precision */
0110:            private byte _precision;
0111:            /** Numeric scale */
0112:            private byte _scale;
0113:            /** Data type */
0114:            private DataType _type;
0115:            /** Maximum column length */
0116:            private short _columnLength;
0117:            /** 0-based column number */
0118:            private short _columnNumber;
0119:            /** index of the data for this column within a list of row data */
0120:            private int _columnIndex;
0121:            /** Column name */
0122:            private String _name;
0123:            /** the offset of the fixed data in the row */
0124:            private int _fixedDataOffset;
0125:            /** the index of the variable length data in the var len offset table */
0126:            private int _varLenTableIndex;
0127:
0128:            public Column() {
0129:                this (JetFormat.VERSION_4);
0130:            }
0131:
0132:            public Column(JetFormat format) {
0133:                _table = null;
0134:            }
0135:
0136:            /**
0137:             * Only used by unit tests
0138:             */
0139:            Column(boolean testing) {
0140:                if (!testing) {
0141:                    throw new IllegalArgumentException();
0142:                }
0143:                _table = null;
0144:            }
0145:
0146:            /**
0147:             * Read a column definition in from a buffer
0148:             * @param table owning table
0149:             * @param buffer Buffer containing column definition
0150:             * @param offset Offset in the buffer at which the column definition starts
0151:             */
0152:            public Column(Table table, ByteBuffer buffer, int offset)
0153:                    throws IOException {
0154:                _table = table;
0155:                if (LOG.isDebugEnabled()) {
0156:                    LOG.debug("Column def block:\n"
0157:                            + ByteUtil.toHexString(buffer, offset, 25));
0158:                }
0159:                setType(DataType.fromByte(buffer.get(offset
0160:                        + getFormat().OFFSET_COLUMN_TYPE)));
0161:                _columnNumber = buffer.getShort(offset
0162:                        + getFormat().OFFSET_COLUMN_NUMBER);
0163:                _columnLength = buffer.getShort(offset
0164:                        + getFormat().OFFSET_COLUMN_LENGTH);
0165:                if (_type.getHasScalePrecision()) {
0166:                    _precision = buffer.get(offset
0167:                            + getFormat().OFFSET_COLUMN_PRECISION);
0168:                    _scale = buffer.get(offset
0169:                            + getFormat().OFFSET_COLUMN_SCALE);
0170:                }
0171:                byte flags = buffer.get(offset
0172:                        + getFormat().OFFSET_COLUMN_FLAGS);
0173:                _variableLength = ((flags & FIXED_LEN_FLAG_MASK) == 0);
0174:                _autoNumber = ((flags & AUTO_NUMBER_FLAG_MASK) != 0);
0175:                _compressedUnicode = ((buffer.get(offset
0176:                        + getFormat().OFFSET_COLUMN_COMPRESSED_UNICODE) & 1) == 1);
0177:
0178:                if (_variableLength) {
0179:                    _varLenTableIndex = buffer.getShort(offset
0180:                            + getFormat().OFFSET_COLUMN_VARIABLE_TABLE_INDEX);
0181:                } else {
0182:                    _fixedDataOffset = buffer.getShort(offset
0183:                            + getFormat().OFFSET_COLUMN_FIXED_DATA_OFFSET);
0184:                }
0185:            }
0186:
0187:            public Table getTable() {
0188:                return _table;
0189:            }
0190:
0191:            public JetFormat getFormat() {
0192:                return getTable().getFormat();
0193:            }
0194:
0195:            public PageChannel getPageChannel() {
0196:                return getTable().getPageChannel();
0197:            }
0198:
0199:            public String getName() {
0200:                return _name;
0201:            }
0202:
0203:            public void setName(String name) {
0204:                _name = name;
0205:            }
0206:
0207:            public boolean isVariableLength() {
0208:                return _variableLength;
0209:            }
0210:
0211:            public void setVariableLength(boolean variableLength) {
0212:                _variableLength = variableLength;
0213:            }
0214:
0215:            public boolean isAutoNumber() {
0216:                return _autoNumber;
0217:            }
0218:
0219:            public void setAutoNumber(boolean autoNumber) {
0220:                _autoNumber = autoNumber;
0221:            }
0222:
0223:            public short getColumnNumber() {
0224:                return _columnNumber;
0225:            }
0226:
0227:            public void setColumnNumber(short newColumnNumber) {
0228:                _columnNumber = newColumnNumber;
0229:            }
0230:
0231:            public int getColumnIndex() {
0232:                return _columnIndex;
0233:            }
0234:
0235:            public void setColumnIndex(int newColumnIndex) {
0236:                _columnIndex = newColumnIndex;
0237:            }
0238:
0239:            /**
0240:             * Also sets the length and the variable length flag, inferred from the
0241:             * type.  For types with scale/precision, sets the scale and precision to
0242:             * default values.
0243:             */
0244:            public void setType(DataType type) {
0245:                _type = type;
0246:                if (!type.isVariableLength()) {
0247:                    setLength((short) type.getFixedSize());
0248:                } else if (!type.isLongValue()) {
0249:                    setLength((short) type.getDefaultSize());
0250:                }
0251:                setVariableLength(type.isVariableLength());
0252:                if (type.getHasScalePrecision()) {
0253:                    setScale((byte) type.getDefaultScale());
0254:                    setPrecision((byte) type.getDefaultPrecision());
0255:                }
0256:            }
0257:
0258:            public DataType getType() {
0259:                return _type;
0260:            }
0261:
0262:            public int getSQLType() throws SQLException {
0263:                return _type.getSQLType();
0264:            }
0265:
0266:            public void setSQLType(int type) throws SQLException {
0267:                setSQLType(type, 0);
0268:            }
0269:
0270:            public void setSQLType(int type, int lengthInUnits)
0271:                    throws SQLException {
0272:                setType(DataType.fromSQLType(type, lengthInUnits));
0273:            }
0274:
0275:            public boolean isCompressedUnicode() {
0276:                return _compressedUnicode;
0277:            }
0278:
0279:            public byte getPrecision() {
0280:                return _precision;
0281:            }
0282:
0283:            public void setPrecision(byte newPrecision) {
0284:                _precision = newPrecision;
0285:            }
0286:
0287:            public byte getScale() {
0288:                return _scale;
0289:            }
0290:
0291:            public void setScale(byte newScale) {
0292:                _scale = newScale;
0293:            }
0294:
0295:            public void setLength(short length) {
0296:                _columnLength = length;
0297:            }
0298:
0299:            public short getLength() {
0300:                return _columnLength;
0301:            }
0302:
0303:            public void setLengthInUnits(short unitLength) {
0304:                setLength((short) (getType().getUnitSize() * unitLength));
0305:            }
0306:
0307:            public short getLengthInUnits() {
0308:                return (short) (getLength() / getType().getUnitSize());
0309:            }
0310:
0311:            public void setVarLenTableIndex(int idx) {
0312:                _varLenTableIndex = idx;
0313:            }
0314:
0315:            public int getVarLenTableIndex() {
0316:                return _varLenTableIndex;
0317:            }
0318:
0319:            public void setFixedDataOffset(int newOffset) {
0320:                _fixedDataOffset = newOffset;
0321:            }
0322:
0323:            public int getFixedDataOffset() {
0324:                return _fixedDataOffset;
0325:            }
0326:
0327:            /**
0328:             * Checks that this column definition is valid.
0329:             *
0330:             * @throws IllegalArgumentException if this column definition is invalid.
0331:             */
0332:            public void validate(JetFormat format) {
0333:                if (getType() == null) {
0334:                    throw new IllegalArgumentException("must have type");
0335:                }
0336:                if ((getName() == null) || (getName().trim().length() == 0)) {
0337:                    throw new IllegalArgumentException("must have valid name");
0338:                }
0339:                if (isVariableLength() != getType().isVariableLength()) {
0340:                    throw new IllegalArgumentException(
0341:                            "invalid variable length setting");
0342:                }
0343:
0344:                if (!isVariableLength()) {
0345:                    if (getLength() != getType().getFixedSize()) {
0346:                        throw new IllegalArgumentException(
0347:                                "invalid fixed length size");
0348:                    }
0349:                } else if (!getType().isLongValue()) {
0350:                    if (!getType().isValidSize(getLength())) {
0351:                        throw new IllegalArgumentException(
0352:                                "var length out of range");
0353:                    }
0354:                }
0355:
0356:                if (getType().getHasScalePrecision()) {
0357:                    if (!getType().isValidScale(getScale())) {
0358:                        throw new IllegalArgumentException(
0359:                                "Scale must be from " + getType().getMinScale()
0360:                                        + " to " + getType().getMaxScale()
0361:                                        + " inclusive");
0362:                    }
0363:                    if (!getType().isValidPrecision(getPrecision())) {
0364:                        throw new IllegalArgumentException(
0365:                                "Precision must be from "
0366:                                        + getType().getMinPrecision() + " to "
0367:                                        + getType().getMaxPrecision()
0368:                                        + " inclusive");
0369:                    }
0370:                }
0371:
0372:                if (isAutoNumber()) {
0373:                    if (getType() != DataType.LONG) {
0374:                        throw new IllegalArgumentException(
0375:                                "Auto number column must be long integer");
0376:                    }
0377:                }
0378:            }
0379:
0380:            /**
0381:             * Deserialize a raw byte value for this column into an Object
0382:             * @param data The raw byte value
0383:             * @return The deserialized Object
0384:             */
0385:            public Object read(byte[] data) throws IOException {
0386:                return read(data, ByteOrder.LITTLE_ENDIAN);
0387:            }
0388:
0389:            /**
0390:             * Deserialize a raw byte value for this column into an Object
0391:             * @param data The raw byte value
0392:             * @param order Byte order in which the raw value is stored
0393:             * @return The deserialized Object
0394:             */
0395:            public Object read(byte[] data, ByteOrder order) throws IOException {
0396:                ByteBuffer buffer = ByteBuffer.wrap(data);
0397:                buffer.order(order);
0398:                if (_type == DataType.BOOLEAN) {
0399:                    throw new IOException(
0400:                            "Tried to read a boolean from data instead of null mask.");
0401:                } else if (_type == DataType.BYTE) {
0402:                    return Byte.valueOf(buffer.get());
0403:                } else if (_type == DataType.INT) {
0404:                    return Short.valueOf(buffer.getShort());
0405:                } else if (_type == DataType.LONG) {
0406:                    return Integer.valueOf(buffer.getInt());
0407:                } else if (_type == DataType.DOUBLE) {
0408:                    return Double.valueOf(buffer.getDouble());
0409:                } else if (_type == DataType.FLOAT) {
0410:                    return Float.valueOf(buffer.getFloat());
0411:                } else if (_type == DataType.SHORT_DATE_TIME) {
0412:                    return readDateValue(buffer);
0413:                } else if (_type == DataType.BINARY) {
0414:                    return data;
0415:                } else if (_type == DataType.TEXT) {
0416:                    return decodeTextValue(data);
0417:                } else if (_type == DataType.MONEY) {
0418:                    return readCurrencyValue(buffer);
0419:                } else if (_type == DataType.OLE) {
0420:                    if (data.length > 0) {
0421:                        return readLongValue(data);
0422:                    }
0423:                    return null;
0424:                } else if (_type == DataType.MEMO) {
0425:                    if (data.length > 0) {
0426:                        return readLongStringValue(data);
0427:                    }
0428:                    return null;
0429:                } else if (_type == DataType.NUMERIC) {
0430:                    return readNumericValue(buffer);
0431:                } else if (_type == DataType.GUID) {
0432:                    return readGUIDValue(buffer);
0433:                } else if (_type == DataType.UNKNOWN_0D) {
0434:                    return null;
0435:                } else {
0436:                    throw new IOException("Unrecognized data type: " + _type);
0437:                }
0438:            }
0439:
0440:            /**
0441:             * @param lvalDefinition Column value that points to an LVAL record
0442:             * @return The LVAL data
0443:             */
0444:            private byte[] readLongValue(byte[] lvalDefinition)
0445:                    throws IOException {
0446:                ByteBuffer def = ByteBuffer.wrap(lvalDefinition);
0447:                def.order(ByteOrder.LITTLE_ENDIAN);
0448:                int length = ByteUtil.get3ByteInt(def);
0449:                // bail out gracefully here as we don't understand the format
0450:                if (length < 0) {
0451:                    return null;
0452:                }
0453:                byte[] rtn = new byte[length];
0454:                byte type = def.get();
0455:
0456:                if (type == LONG_VALUE_TYPE_THIS_PAGE) {
0457:
0458:                    // inline long value
0459:                    def.getInt(); //Skip over lval_dp
0460:                    def.getInt(); //Skip over unknown
0461:                    def.get(rtn);
0462:
0463:                } else {
0464:
0465:                    // long value on other page(s)
0466:                    if (lvalDefinition.length != getFormat().SIZE_LONG_VALUE_DEF) {
0467:                        throw new IOException("Expected "
0468:                                + getFormat().SIZE_LONG_VALUE_DEF
0469:                                + " bytes in long value definition, but found "
0470:                                + lvalDefinition.length);
0471:                    }
0472:
0473:                    int rowNum = ByteUtil.getUnsignedByte(def);
0474:                    int pageNum = ByteUtil.get3ByteInt(def, def.position());
0475:                    ByteBuffer lvalPage = getPageChannel().createPageBuffer();
0476:
0477:                    switch (type) {
0478:                    case LONG_VALUE_TYPE_OTHER_PAGE: {
0479:                        getPageChannel().readPage(lvalPage, pageNum);
0480:
0481:                        short rowStart = Table.findRowStart(lvalPage, rowNum,
0482:                                getFormat());
0483:                        short rowEnd = Table.findRowEnd(lvalPage, rowNum,
0484:                                getFormat());
0485:
0486:                        if ((rowEnd - rowStart) != length) {
0487:                            throw new IOException("Unexpected lval row length");
0488:                        }
0489:
0490:                        lvalPage.position(rowStart);
0491:                        lvalPage.get(rtn);
0492:                    }
0493:                        break;
0494:
0495:                    case LONG_VALUE_TYPE_OTHER_PAGES:
0496:
0497:                        ByteBuffer rtnBuf = ByteBuffer.wrap(rtn);
0498:                        int remainingLen = length;
0499:                        while (remainingLen > 0) {
0500:                            lvalPage.clear();
0501:                            getPageChannel().readPage(lvalPage, pageNum);
0502:
0503:                            short rowStart = Table.findRowStart(lvalPage,
0504:                                    rowNum, getFormat());
0505:                            short rowEnd = Table.findRowEnd(lvalPage, rowNum,
0506:                                    getFormat());
0507:
0508:                            // read next page information
0509:                            lvalPage.position(rowStart);
0510:                            rowNum = ByteUtil.getUnsignedByte(lvalPage);
0511:                            pageNum = ByteUtil.get3ByteInt(lvalPage);
0512:
0513:                            // update rowEnd and remainingLen based on chunkLength
0514:                            int chunkLength = (rowEnd - rowStart) - 4;
0515:                            if (chunkLength > remainingLen) {
0516:                                rowEnd = (short) (rowEnd - (chunkLength - remainingLen));
0517:                                chunkLength = remainingLen;
0518:                            }
0519:                            remainingLen -= chunkLength;
0520:
0521:                            lvalPage.limit(rowEnd);
0522:                            rtnBuf.put(lvalPage);
0523:                        }
0524:
0525:                        break;
0526:
0527:                    default:
0528:                        throw new IOException("Unrecognized long value type: "
0529:                                + type);
0530:                    }
0531:                }
0532:
0533:                return rtn;
0534:            }
0535:
0536:            /**
0537:             * @param lvalDefinition Column value that points to an LVAL record
0538:             * @return The LVAL data
0539:             */
0540:            private String readLongStringValue(byte[] lvalDefinition)
0541:                    throws IOException {
0542:                byte[] binData = readLongValue(lvalDefinition);
0543:                if (binData == null) {
0544:                    return null;
0545:                }
0546:                return decodeTextValue(binData);
0547:            }
0548:
0549:            /**
0550:             * Decodes "Currency" values.
0551:             * 
0552:             * @param buffer Column value that points to currency data
0553:             * @return BigDecimal representing the monetary value
0554:             * @throws IOException if the value cannot be parsed 
0555:             */
0556:            private BigDecimal readCurrencyValue(ByteBuffer buffer)
0557:                    throws IOException {
0558:                if (buffer.remaining() != 8) {
0559:                    throw new IOException("Invalid money value.");
0560:                }
0561:
0562:                return new BigDecimal(BigInteger.valueOf(buffer.getLong(0)), 4);
0563:            }
0564:
0565:            /**
0566:             * Writes "Currency" values.
0567:             */
0568:            private void writeCurrencyValue(ByteBuffer buffer, Object value)
0569:                    throws IOException {
0570:                try {
0571:                    BigDecimal decVal = toBigDecimal(value);
0572:
0573:                    // adjust scale (will cause the an ArithmeticException if number has too
0574:                    // many decimal places)
0575:                    decVal = decVal.setScale(4);
0576:
0577:                    // now, remove scale and convert to long (this will throw if the value is
0578:                    // too big)
0579:                    buffer.putLong(decVal.movePointRight(4).longValueExact());
0580:                } catch (ArithmeticException e) {
0581:                    throw (IOException) new IOException(
0582:                            "Currency value out of range").initCause(e);
0583:                }
0584:            }
0585:
0586:            /**
0587:             * Decodes a NUMERIC field.
0588:             */
0589:            private BigDecimal readNumericValue(ByteBuffer buffer) {
0590:                boolean negate = (buffer.get() != 0);
0591:
0592:                byte[] tmpArr = new byte[16];
0593:                buffer.get(tmpArr);
0594:
0595:                if (buffer.order() != ByteOrder.BIG_ENDIAN) {
0596:                    fixNumericByteOrder(tmpArr);
0597:                }
0598:
0599:                BigInteger intVal = new BigInteger(tmpArr);
0600:                if (negate) {
0601:                    intVal = intVal.negate();
0602:                }
0603:                return new BigDecimal(intVal, getScale());
0604:            }
0605:
0606:            /**
0607:             * Writes a numeric value.
0608:             */
0609:            private void writeNumericValue(ByteBuffer buffer, Object value)
0610:                    throws IOException {
0611:                try {
0612:                    BigDecimal decVal = toBigDecimal(value);
0613:
0614:                    boolean negative = (decVal.compareTo(BigDecimal.ZERO) < 0);
0615:                    if (negative) {
0616:                        decVal = decVal.negate();
0617:                    }
0618:
0619:                    // write sign byte
0620:                    buffer.put(negative ? (byte) 0x80 : (byte) 0);
0621:
0622:                    // adjust scale according to this column type (will cause the an
0623:                    // ArithmeticException if number has too many decimal places)
0624:                    decVal = decVal.setScale(getScale());
0625:
0626:                    // check precision
0627:                    if (decVal.precision() > getPrecision()) {
0628:                        throw new IOException(
0629:                                "Numeric value is too big for specified precision "
0630:                                        + getPrecision() + ": " + decVal);
0631:                    }
0632:
0633:                    // convert to unscaled BigInteger, big-endian bytes
0634:                    byte[] intValBytes = decVal.unscaledValue().toByteArray();
0635:                    int maxByteLen = getType().getFixedSize() - 1;
0636:                    if (intValBytes.length > maxByteLen) {
0637:                        throw new IOException(
0638:                                "Too many bytes for valid BigInteger?");
0639:                    }
0640:                    if (intValBytes.length < maxByteLen) {
0641:                        byte[] tmpBytes = new byte[maxByteLen];
0642:                        System.arraycopy(intValBytes, 0, tmpBytes,
0643:                                (maxByteLen - intValBytes.length),
0644:                                intValBytes.length);
0645:                        intValBytes = tmpBytes;
0646:                    }
0647:                    if (buffer.order() != ByteOrder.BIG_ENDIAN) {
0648:                        fixNumericByteOrder(intValBytes);
0649:                    }
0650:                    buffer.put(intValBytes);
0651:                } catch (ArithmeticException e) {
0652:                    throw (IOException) new IOException(
0653:                            "Numeric value out of range").initCause(e);
0654:                }
0655:            }
0656:
0657:            /**
0658:             * Decodes a date value.
0659:             */
0660:            private Date readDateValue(ByteBuffer buffer) {
0661:                // seems access stores dates in the local timezone.  guess you just hope
0662:                // you read it in the same timezone in which it was written!
0663:                long dateBits = buffer.getLong();
0664:                long time = (long) (Double.longBitsToDouble(dateBits) * MILLISECONDS_PER_DAY);
0665:                time -= MILLIS_BETWEEN_EPOCH_AND_1900;
0666:                time -= getTimeZoneOffset(time);
0667:                return new DateExt(time, dateBits);
0668:            }
0669:
0670:            /**
0671:             * Writes a date value.
0672:             */
0673:            private void writeDateValue(ByteBuffer buffer, Object value) {
0674:                if (value == null) {
0675:                    buffer.putDouble(0d);
0676:                }
0677:                if (value instanceof  DateExt) {
0678:
0679:                    // this is a Date value previously read from readDateValue().  use the
0680:                    // original bits to store the value so we don't lose any precision
0681:                    buffer.putLong(((DateExt) value).getDateBits());
0682:
0683:                } else {
0684:
0685:                    // seems access stores dates in the local timezone.  guess you just
0686:                    // hope you read it in the same timezone in which it was written!
0687:                    long time = ((value instanceof  Date) ? ((Date) value)
0688:                            .getTime() : ((Number) value).longValue());
0689:                    time += getTimeZoneOffset(time);
0690:                    time += MILLIS_BETWEEN_EPOCH_AND_1900;
0691:                    double dTime = time / MILLISECONDS_PER_DAY;
0692:                    buffer.putDouble(dTime);
0693:                }
0694:            }
0695:
0696:            /**
0697:             * Gets the timezone offset from UTC for the given time (including DST).
0698:             */
0699:            private static long getTimeZoneOffset(long time) {
0700:                Calendar c = Calendar.getInstance();
0701:                c.setTimeInMillis(time);
0702:                return ((long) c.get(Calendar.ZONE_OFFSET) + c
0703:                        .get(Calendar.DST_OFFSET));
0704:            }
0705:
0706:            /**
0707:             * Decodes a GUID value.
0708:             */
0709:            private String readGUIDValue(ByteBuffer buffer) {
0710:                StringBuilder sb = new StringBuilder(22);
0711:                sb.append("{");
0712:                sb.append(ByteUtil.toHexString(buffer, 0, 4, false));
0713:                sb.append("-");
0714:                sb.append(ByteUtil.toHexString(buffer, 4, 2, false));
0715:                sb.append("-");
0716:                sb.append(ByteUtil.toHexString(buffer, 6, 2, false));
0717:                sb.append("-");
0718:                sb.append(ByteUtil.toHexString(buffer, 8, 2, false));
0719:                sb.append("-");
0720:                sb.append(ByteUtil.toHexString(buffer, 10, 6, false));
0721:                sb.append("}");
0722:                return (sb.toString());
0723:            }
0724:
0725:            /**
0726:             * Writes a GUID value.
0727:             */
0728:            private void writeGUIDValue(ByteBuffer buffer, Object value)
0729:                    throws IOException {
0730:                Matcher m = GUID_PATTERN.matcher(toCharSequence(value));
0731:                if (m.matches()) {
0732:                    ByteUtil.writeHexString(buffer, m.group(1));
0733:                    ByteUtil.writeHexString(buffer, m.group(2));
0734:                    ByteUtil.writeHexString(buffer, m.group(3));
0735:                    ByteUtil.writeHexString(buffer, m.group(4));
0736:                    ByteUtil.writeHexString(buffer, m.group(5));
0737:                } else {
0738:                    throw new IOException("Invalid GUID: " + value);
0739:                }
0740:            }
0741:
0742:            /**
0743:             * Write an LVAL column into a ByteBuffer inline if it fits, otherwise in
0744:             * other data page(s).
0745:             * @param value Value of the LVAL column
0746:             * @return A buffer containing the LVAL definition and (possibly) the column
0747:             *         value (unless written to other pages)
0748:             */
0749:            public ByteBuffer writeLongValue(byte[] value,
0750:                    int remainingRowLength) throws IOException {
0751:                if (value.length > getType().getMaxSize()) {
0752:                    throw new IOException("value too big for column");
0753:                }
0754:
0755:                // determine which type to write
0756:                byte type = 0;
0757:                int lvalDefLen = getFormat().SIZE_LONG_VALUE_DEF;
0758:                if (((getFormat().SIZE_LONG_VALUE_DEF + value.length) <= remainingRowLength)
0759:                        && (value.length <= getFormat().MAX_INLINE_LONG_VALUE_SIZE)) {
0760:                    type = LONG_VALUE_TYPE_THIS_PAGE;
0761:                    lvalDefLen += value.length;
0762:                } else if (Table.getRowSpaceUsage(value.length, getFormat()) <= getFormat().MAX_ROW_SIZE) {
0763:                    type = LONG_VALUE_TYPE_OTHER_PAGE;
0764:                } else {
0765:                    type = LONG_VALUE_TYPE_OTHER_PAGES;
0766:                }
0767:
0768:                ByteBuffer def = getPageChannel().createBuffer(lvalDefLen);
0769:                ByteUtil.put3ByteInt(def, value.length);
0770:                def.put(type);
0771:
0772:                if (type == LONG_VALUE_TYPE_THIS_PAGE) {
0773:                    // write long value inline
0774:                    def.putInt(0);
0775:                    def.putInt(0); //Unknown
0776:                    def.put(value);
0777:                } else {
0778:
0779:                    int firstLvalPageNum = PageChannel.INVALID_PAGE_NUMBER;
0780:                    byte firstLvalRow = 0;
0781:
0782:                    ByteBuffer lvalPage = getPageChannel().createPageBuffer();
0783:
0784:                    // write other page(s)
0785:                    switch (type) {
0786:                    case LONG_VALUE_TYPE_OTHER_PAGE:
0787:                        writeLongValueHeader(lvalPage);
0788:                        firstLvalRow = (byte) Table.addDataPageRow(lvalPage,
0789:                                value.length, getFormat());
0790:                        lvalPage.put(value);
0791:                        firstLvalPageNum = getPageChannel().writeNewPage(
0792:                                lvalPage);
0793:                        break;
0794:
0795:                    case LONG_VALUE_TYPE_OTHER_PAGES:
0796:
0797:                        ByteBuffer buffer = ByteBuffer.wrap(value);
0798:                        int remainingLen = buffer.remaining();
0799:                        buffer.limit(0);
0800:                        int lvalPageNum = getPageChannel().allocateNewPage();
0801:                        byte lvalRow = 0;
0802:                        int nextLvalPageNum = 0;
0803:                        while (remainingLen > 0) {
0804:                            lvalPage.clear();
0805:                            writeLongValueHeader(lvalPage);
0806:
0807:                            // figure out how much we will put in this page
0808:                            int chunkLength = Math.min(
0809:                                    getFormat().MAX_ROW_SIZE - 4, remainingLen);
0810:                            nextLvalPageNum = ((chunkLength < remainingLen) ? getPageChannel()
0811:                                    .allocateNewPage()
0812:                                    : 0);
0813:
0814:                            // add row to this page
0815:                            lvalRow = (byte) Table.addDataPageRow(lvalPage,
0816:                                    chunkLength + 4, getFormat());
0817:
0818:                            // write next page info (we'll always be writing into row 0 for
0819:                            // newly created pages)
0820:                            lvalPage.put((byte) 0); // row number
0821:                            ByteUtil.put3ByteInt(lvalPage, nextLvalPageNum); // page number
0822:
0823:                            // write this page's chunk of data
0824:                            buffer.limit(buffer.limit() + chunkLength);
0825:                            lvalPage.put(buffer);
0826:                            remainingLen -= chunkLength;
0827:
0828:                            // write new page to database
0829:                            getPageChannel().writePage(lvalPage, lvalPageNum);
0830:
0831:                            // hang onto first page info
0832:                            if (firstLvalPageNum == PageChannel.INVALID_PAGE_NUMBER) {
0833:                                firstLvalPageNum = lvalPageNum;
0834:                                firstLvalRow = lvalRow;
0835:                            }
0836:
0837:                            // move to next page
0838:                            lvalPageNum = nextLvalPageNum;
0839:                        }
0840:                        break;
0841:
0842:                    default:
0843:                        throw new IOException("Unrecognized long value type: "
0844:                                + type);
0845:                    }
0846:
0847:                    // update def
0848:                    def.put(firstLvalRow);
0849:                    ByteUtil.put3ByteInt(def, firstLvalPageNum);
0850:                    def.putInt(0); //Unknown
0851:
0852:                }
0853:
0854:                def.flip();
0855:                return def;
0856:            }
0857:
0858:            /**
0859:             * Writes the header info for a long value page.
0860:             */
0861:            private void writeLongValueHeader(ByteBuffer lvalPage) {
0862:                lvalPage.put(PageTypes.DATA); //Page type
0863:                lvalPage.put((byte) 1); //Unknown
0864:                lvalPage
0865:                        .putShort((short) (getFormat().PAGE_SIZE - getFormat().OFFSET_ROW_START)); //Free space
0866:                lvalPage.put((byte) 'L');
0867:                lvalPage.put((byte) 'V');
0868:                lvalPage.put((byte) 'A');
0869:                lvalPage.put((byte) 'L');
0870:                lvalPage.putShort((short) 0); // num rows in page
0871:                lvalPage.putInt(0); //unknown
0872:            }
0873:
0874:            /**
0875:             * Serialize an Object into a raw byte value for this column in little endian order
0876:             * @param obj Object to serialize
0877:             * @return A buffer containing the bytes
0878:             */
0879:            public ByteBuffer write(Object obj, int remainingRowLength)
0880:                    throws IOException {
0881:                return write(obj, remainingRowLength, ByteOrder.LITTLE_ENDIAN);
0882:            }
0883:
0884:            /**
0885:             * Serialize an Object into a raw byte value for this column
0886:             * @param obj Object to serialize
0887:             * @param order Order in which to serialize
0888:             * @return A buffer containing the bytes
0889:             */
0890:            public ByteBuffer write(Object obj, int remainingRowLength,
0891:                    ByteOrder order) throws IOException {
0892:                if (!isVariableLength()) {
0893:                    return writeFixedLengthField(obj, order);
0894:                }
0895:
0896:                // var length column
0897:                if (!getType().isLongValue()) {
0898:
0899:                    // this is an "inline" var length field
0900:                    switch (getType()) {
0901:                    case NUMERIC:
0902:                        // don't ask me why numerics are "var length" columns...
0903:                        ByteBuffer buffer = getPageChannel().createBuffer(
0904:                                getType().getFixedSize(), order);
0905:                        writeNumericValue(buffer, obj);
0906:                        buffer.flip();
0907:                        return buffer;
0908:
0909:                    case TEXT:
0910:                        CharSequence text = toCharSequence(obj);
0911:                        int maxChars = getLengthInUnits();
0912:                        if (text.length() > maxChars) {
0913:                            throw new IOException("Text is too big for column");
0914:                        }
0915:                        byte[] encodedData = encodeUncompressedText(text,
0916:                                getFormat()).array();
0917:                        obj = encodedData;
0918:                        break;
0919:
0920:                    case BINARY:
0921:                        // should already be "encoded"
0922:                        break;
0923:                    default:
0924:                        throw new RuntimeException(
0925:                                "unexpected inline var length type: "
0926:                                        + getType());
0927:                    }
0928:
0929:                    ByteBuffer buffer = ByteBuffer.wrap((byte[]) obj);
0930:                    buffer.order(order);
0931:                    return buffer;
0932:                }
0933:
0934:                // var length, long value column
0935:                switch (getType()) {
0936:                case OLE:
0937:                    // should already be "encoded"
0938:                    break;
0939:                case MEMO:
0940:                    obj = encodeUncompressedText(toCharSequence(obj),
0941:                            getFormat()).array();
0942:                    break;
0943:                default:
0944:                    throw new RuntimeException(
0945:                            "unexpected var length, long value type: "
0946:                                    + getType());
0947:                }
0948:
0949:                // create long value buffer
0950:                return writeLongValue((byte[]) obj, remainingRowLength);
0951:            }
0952:
0953:            /**
0954:             * Serialize an Object into a raw byte value for this column
0955:             * @param obj Object to serialize
0956:             * @param order Order in which to serialize
0957:             * @return A buffer containing the bytes
0958:             */
0959:            public ByteBuffer writeFixedLengthField(Object obj, ByteOrder order)
0960:                    throws IOException {
0961:                int size = getType().getFixedSize();
0962:
0963:                // create buffer for data
0964:                ByteBuffer buffer = getPageChannel().createBuffer(size, order);
0965:
0966:                // since booleans are not written by this method, it's safe to convert any
0967:                // incoming boolean into an integer.
0968:                obj = booleanToInteger(obj);
0969:
0970:                switch (getType()) {
0971:                case BOOLEAN:
0972:                    //Do nothing
0973:                    break;
0974:                case BYTE:
0975:                    buffer.put(toNumber(obj).byteValue());
0976:                    break;
0977:                case INT:
0978:                    buffer.putShort(toNumber(obj).shortValue());
0979:                    break;
0980:                case LONG:
0981:                    buffer.putInt(toNumber(obj).intValue());
0982:                    break;
0983:                case DOUBLE:
0984:                    buffer.putDouble(toNumber(obj).doubleValue());
0985:                    break;
0986:                case FLOAT:
0987:                    buffer.putFloat(toNumber(obj).floatValue());
0988:                    break;
0989:                case SHORT_DATE_TIME:
0990:                    writeDateValue(buffer, obj);
0991:                    break;
0992:                case MONEY:
0993:                    writeCurrencyValue(buffer, obj);
0994:                    break;
0995:                case GUID:
0996:                    writeGUIDValue(buffer, obj);
0997:                    break;
0998:                case NUMERIC:
0999:                    // yes, that's right, occasionally numeric values are written as fixed
1000:                    // length...
1001:                    writeNumericValue(buffer, obj);
1002:                    break;
1003:                default:
1004:                    throw new IOException("Unsupported data type: " + getType());
1005:                }
1006:                buffer.flip();
1007:                return buffer;
1008:            }
1009:
1010:            /**
1011:             * Decodes a compressed or uncompressed text value.
1012:             */
1013:            private String decodeTextValue(byte[] data) throws IOException {
1014:                try {
1015:
1016:                    // see if data is compressed.  the 0xFF, 0xFE sequence indicates that
1017:                    // compression is used (sort of, see algorithm below)
1018:                    boolean isCompressed = ((data.length > 1)
1019:                            && (data[0] == (byte) 0xFF) && (data[1] == (byte) 0xFE));
1020:                    if (isCompressed) {
1021:
1022:                        Expand expander = new Expand();
1023:
1024:                        // this is a whacky compression combo that switches back and forth
1025:                        // between compressed/uncompressed using a 0x00 byte (starting in
1026:                        // compressed mode)
1027:                        StringBuilder textBuf = new StringBuilder(data.length);
1028:                        // start after two bytes indicating compression use
1029:                        int dataStart = 2;
1030:                        int dataEnd = dataStart;
1031:                        boolean inCompressedMode = true;
1032:                        while (dataEnd < data.length) {
1033:                            if (data[dataEnd] == (byte) 0x00) {
1034:
1035:                                // handle current segment
1036:                                decodeTextSegment(data, dataStart, dataEnd,
1037:                                        inCompressedMode, expander, textBuf);
1038:                                inCompressedMode = !inCompressedMode;
1039:                                ++dataEnd;
1040:                                dataStart = dataEnd;
1041:
1042:                            } else {
1043:                                ++dataEnd;
1044:                            }
1045:                        }
1046:                        // handle last segment
1047:                        decodeTextSegment(data, dataStart, dataEnd,
1048:                                inCompressedMode, expander, textBuf);
1049:
1050:                        return textBuf.toString();
1051:
1052:                    }
1053:
1054:                    return decodeUncompressedText(data, getFormat());
1055:
1056:                } catch (IllegalInputException e) {
1057:                    throw (IOException) new IOException(
1058:                            "Can't expand text column").initCause(e);
1059:                } catch (EndOfInputException e) {
1060:                    throw (IOException) new IOException(
1061:                            "Can't expand text column").initCause(e);
1062:                }
1063:            }
1064:
1065:            /**
1066:             * Decodes a segnment of a text value into the given buffer according to the
1067:             * given status of the segment (compressed/uncompressed).
1068:             */
1069:            private void decodeTextSegment(byte[] data, int dataStart,
1070:                    int dataEnd, boolean inCompressedMode, Expand expander,
1071:                    StringBuilder textBuf) throws IllegalInputException,
1072:                    EndOfInputException {
1073:                if (dataEnd <= dataStart) {
1074:                    // no data
1075:                    return;
1076:                }
1077:                int dataLength = dataEnd - dataStart;
1078:                if (inCompressedMode) {
1079:                    // handle compressed data
1080:                    byte[] tmpData = new byte[dataLength];
1081:                    System.arraycopy(data, dataStart, tmpData, 0, dataLength);
1082:                    expander.reset();
1083:                    textBuf.append(expander.expand(tmpData));
1084:                } else {
1085:                    // handle uncompressed data
1086:                    textBuf.append(decodeUncompressedText(data, dataStart,
1087:                            dataLength, getFormat()));
1088:                }
1089:            }
1090:
1091:            /**
1092:             * @param textBytes bytes of text to decode
1093:             * @return the decoded string
1094:             */
1095:            private static CharBuffer decodeUncompressedText(byte[] textBytes,
1096:                    int startPos, int length, JetFormat format) {
1097:                return format.CHARSET.decode(ByteBuffer.wrap(textBytes,
1098:                        startPos, length));
1099:            }
1100:
1101:            @Override
1102:            public String toString() {
1103:                StringBuilder rtn = new StringBuilder();
1104:                rtn.append("\tName: (" + _table.getName() + ") " + _name);
1105:                rtn.append("\n\tType: 0x"
1106:                        + Integer.toHexString(_type.getValue()) + " (" + _type
1107:                        + ")");
1108:                rtn.append("\n\tNumber: " + _columnNumber);
1109:                rtn.append("\n\tLength: " + _columnLength);
1110:                rtn.append("\n\tVariable length: " + _variableLength);
1111:                if (_variableLength) {
1112:                    rtn.append("\n\tCompressed Unicode: " + _compressedUnicode);
1113:                }
1114:                if (_autoNumber) {
1115:                    rtn.append("\n\tNext AutoNumber: "
1116:                            + (_table.getLastAutoNumber() + 1));
1117:                }
1118:                rtn.append("\n\n");
1119:                return rtn.toString();
1120:            }
1121:
1122:            /**
1123:             * @param textBytes bytes of text to decode
1124:             * @param format relevant db format
1125:             * @return the decoded string
1126:             */
1127:            public static String decodeUncompressedText(byte[] textBytes,
1128:                    JetFormat format) {
1129:                return decodeUncompressedText(textBytes, 0, textBytes.length,
1130:                        format).toString();
1131:            }
1132:
1133:            /**
1134:             * @param text Text to encode
1135:             * @param format relevant db format
1136:             * @return A buffer with the text encoded
1137:             */
1138:            public static ByteBuffer encodeUncompressedText(CharSequence text,
1139:                    JetFormat format) {
1140:                return format.CHARSET.encode(CharBuffer.wrap(text));
1141:            }
1142:
1143:            public int compareTo(Column other) {
1144:                if (_columnNumber > other.getColumnNumber()) {
1145:                    return 1;
1146:                } else if (_columnNumber < other.getColumnNumber()) {
1147:                    return -1;
1148:                } else {
1149:                    return 0;
1150:                }
1151:            }
1152:
1153:            /**
1154:             * @param columns A list of columns in a table definition
1155:             * @return The number of variable length columns found in the list
1156:             */
1157:            public static short countVariableLength(List<Column> columns) {
1158:                short rtn = 0;
1159:                for (Column col : columns) {
1160:                    if (col.isVariableLength()) {
1161:                        rtn++;
1162:                    }
1163:                }
1164:                return rtn;
1165:            }
1166:
1167:            /**
1168:             * @param columns A list of columns in a table definition
1169:             * @return The number of variable length columns which are not long values
1170:             *         found in the list
1171:             */
1172:            public static short countNonLongVariableLength(List<Column> columns) {
1173:                short rtn = 0;
1174:                for (Column col : columns) {
1175:                    if (col.isVariableLength() && !col.getType().isLongValue()) {
1176:                        rtn++;
1177:                    }
1178:                }
1179:                return rtn;
1180:            }
1181:
1182:            /**
1183:             * @return an appropriate BigDecimal representation of the given object.
1184:             *         <code>null</code> is returned as 0 and Numbers are converted
1185:             *         using their double representation.
1186:             */
1187:            private static BigDecimal toBigDecimal(Object value) {
1188:                if (value == null) {
1189:                    return BigDecimal.ZERO;
1190:                } else if (value instanceof  BigDecimal) {
1191:                    return (BigDecimal) value;
1192:                } else if (value instanceof  BigInteger) {
1193:                    return new BigDecimal((BigInteger) value);
1194:                } else if (value instanceof  Number) {
1195:                    return new BigDecimal(((Number) value).doubleValue());
1196:                }
1197:                return new BigDecimal(value.toString());
1198:            }
1199:
1200:            /**
1201:             * @return an appropriate Number representation of the given object.
1202:             *         <code>null</code> is returned as 0 and Strings are parsed as
1203:             *         Doubles.
1204:             */
1205:            private static Number toNumber(Object value) {
1206:                if (value == null) {
1207:                    return BigDecimal.ZERO;
1208:                }
1209:                if (value instanceof  Number) {
1210:                    return (Number) value;
1211:                }
1212:                return Double.valueOf(value.toString());
1213:            }
1214:
1215:            /**
1216:             * @return an appropriate CharSequence representation of the given object.
1217:             */
1218:            public static CharSequence toCharSequence(Object value) {
1219:                if (value == null) {
1220:                    return null;
1221:                } else if (value instanceof  CharSequence) {
1222:                    return (CharSequence) value;
1223:                }
1224:                return value.toString();
1225:            }
1226:
1227:            /**
1228:             * Interpret a boolean value (null == false)
1229:             */
1230:            public static boolean toBooleanValue(Object obj) {
1231:                return ((obj != null) && ((Boolean) obj).booleanValue());
1232:            }
1233:
1234:            /**
1235:             * Swaps the bytes of the given numeric in place.
1236:             */
1237:            private static void fixNumericByteOrder(byte[] bytes) {
1238:                // fix endianness of each 4 byte segment
1239:                for (int i = 0; i < 4; ++i) {
1240:                    int idx = i * 4;
1241:                    byte b = bytes[idx + 0];
1242:                    bytes[idx + 0] = bytes[idx + 3];
1243:                    bytes[idx + 3] = b;
1244:                    b = bytes[idx + 1];
1245:                    bytes[idx + 1] = bytes[idx + 2];
1246:                    bytes[idx + 2] = b;
1247:                }
1248:            }
1249:
1250:            /**
1251:             * Treat booleans as integers (C-style).
1252:             */
1253:            private static Object booleanToInteger(Object obj) {
1254:                if (obj instanceof  Boolean) {
1255:                    obj = ((Boolean) obj) ? 1 : 0;
1256:                }
1257:                return obj;
1258:            }
1259:
1260:            /**
1261:             * Date subclass which stashes the original date bits, in case we attempt to
1262:             * re-write the value (will not lose precision).
1263:             */
1264:            private static final class DateExt extends Date {
1265:                private static final long serialVersionUID = 0L;
1266:
1267:                /** cached bits of the original date value */
1268:                private transient final long _dateBits;
1269:
1270:                private DateExt(long time, long dateBits) {
1271:                    super (time);
1272:                    _dateBits = dateBits;
1273:                }
1274:
1275:                public long getDateBits() {
1276:                    return _dateBits;
1277:                }
1278:
1279:                private Object writeReplace() throws ObjectStreamException {
1280:                    // if we are going to serialize this Date, convert it back to a normal
1281:                    // Date (in case it is restored outside of the context of jackcess)
1282:                    return new Date(super.getTime());
1283:                }
1284:            }
1285:
1286:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.