Object denormalizedValue()

in src/main/java/com/microsoft/sqlserver/jdbc/dtv.java [3428:3660]


    Object denormalizedValue(byte[] decryptedValue, JDBCType jdbcType, TypeInfo baseTypeInfo, SQLServerConnection con,
            InputStreamGetterArgs streamGetterArgs, byte normalizeRuleVersion, Calendar cal) throws SQLServerException {
        if (0x01 != normalizeRuleVersion) {
            MessageFormat form = new MessageFormat(
                    SQLServerException.getErrString("R_UnsupportedNormalizationVersionAE"));
            throw new SQLServerException(form.format(new Object[] {normalizeRuleVersion, 1}), null, 0, null);
        }

        if (aeLogger.isLoggable(java.util.logging.Level.FINE)) {
            aeLogger.fine("Denormalizing decrypted data based on its SQL Server type(" + baseTypeInfo.getSSType()
                    + ") and JDBC type(" + jdbcType + ").");
        }

        SSType baseSSType = baseTypeInfo.getSSType();
        switch (baseSSType) {
            case CHAR:
            case VARCHAR:
            case NCHAR:
            case NVARCHAR:
            case VARCHARMAX:
            case NVARCHARMAX: {
                try {
                    String strVal = new String(decryptedValue, 0, decryptedValue.length,
                            (null == baseTypeInfo.getCharset()) ? con.getDatabaseCollation().getCharset()
                                                                : baseTypeInfo.getCharset());
                    if ((SSType.CHAR == baseSSType) || (SSType.NCHAR == baseSSType)) {
                        // Right pad the string for CHAR types.
                        StringBuilder sb = new StringBuilder(strVal);
                        int padLength = baseTypeInfo.getPrecision() - strVal.length();
                        for (int i = 0; i < padLength; i++) {
                            sb.append(' ');
                        }
                        strVal = sb.toString();
                    }
                    return DDC.convertStringToObject(strVal, baseTypeInfo.getCharset(), jdbcType,
                            streamGetterArgs.streamType);
                } catch (IllegalArgumentException e) {
                    MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_errorConvertingValue"));
                    throw new SQLServerException(form.format(new Object[] {baseSSType, jdbcType}), null, 0, e);
                } catch (UnsupportedEncodingException e) {
                    // Important: we should not pass the exception here as it displays the data.
                    MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_unsupportedEncoding"));
                    throw new SQLServerException(form.format(new Object[] {baseTypeInfo.getCharset()}), null, 0, e);
                }
            }

            case BIT:
            case TINYINT:
            case SMALLINT:
            case INTEGER:
            case BIGINT: {
                // If data is encrypted, then these types are normalized to BIGINT. Need to denormalize here.
                if (8 != decryptedValue.length) {
                    // Integer datatypes are normalized to bigint for AE.
                    MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NormalizationErrorAE"));
                    throw new SQLServerException(form.format(new Object[] {baseSSType}), null, 0, null);
                }
                return DDC.convertLongToObject(Util.readLong(decryptedValue, 0), jdbcType, baseSSType,
                        streamGetterArgs.streamType);

            }

            case REAL:
            case FLOAT: {
                // JDBC driver does not normalize real to float.
                if (8 == decryptedValue.length) {
                    return DDC.convertDoubleToObject(
                            ByteBuffer.wrap(decryptedValue).order(ByteOrder.LITTLE_ENDIAN).getDouble(),
                            JDBCType.VARBINARY == jdbcType ? baseSSType.getJDBCType() : jdbcType, // use jdbc type from
                                                                                                  // baseTypeInfo if
                                                                                                  // using
                                                                                                  // getObject()
                            streamGetterArgs.streamType);
                } else if (4 == decryptedValue.length) {
                    return DDC.convertFloatToObject(
                            ByteBuffer.wrap(decryptedValue).order(ByteOrder.LITTLE_ENDIAN).getFloat(),
                            JDBCType.VARBINARY == jdbcType ? baseSSType.getJDBCType() : jdbcType, // use jdbc type from
                                                                                                  // baseTypeInfo if
                                                                                                  // using
                                                                                                  // getObject()
                            streamGetterArgs.streamType);
                } else {
                    MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NormalizationErrorAE"));
                    throw new SQLServerException(form.format(new Object[] {baseSSType}), null, 0, null);
                }
            }
            case SMALLMONEY: {
                return DDC.convertMoneyToObject(new BigDecimal(BigInteger.valueOf(Util.readInt(decryptedValue, 4)), 4),
                        JDBCType.VARBINARY == jdbcType ? baseSSType.getJDBCType() : jdbcType, // use jdbc type from
                                                                                              // baseTypeInfo if using
                                                                                              // getObject()
                        streamGetterArgs.streamType, 4);
            }

            case MONEY: {
                BigInteger bi = BigInteger.valueOf(((long) Util.readInt(decryptedValue, 0) << 32)
                        | (Util.readInt(decryptedValue, 4) & 0xFFFFFFFFL));

                return DDC.convertMoneyToObject(new BigDecimal(bi, 4),
                        JDBCType.VARBINARY == jdbcType ? baseSSType.getJDBCType() : jdbcType, // use
                                                                                              // jdbc
                                                                                              // type
                                                                                              // from
                                                                                              // baseTypeInfo
                                                                                              // if
                                                                                              // using
                                                                                              // getObject()
                        streamGetterArgs.streamType, 8);
            }

            case NUMERIC:
            case DECIMAL: {
                return DDC.convertBigDecimalToObject(
                        Util.readBigDecimal(decryptedValue, decryptedValue.length, baseTypeInfo.getScale()),
                        JDBCType.VARBINARY == jdbcType ? baseSSType.getJDBCType() : jdbcType, // use jdbc type from
                                                                                              // baseTypeInfo if using
                                                                                              // getObject()
                        streamGetterArgs.streamType);
            }

            case BINARY:
            case VARBINARY:
            case VARBINARYMAX: {
                return DDC.convertBytesToObject(decryptedValue, jdbcType, baseTypeInfo);
            }

            case DATE: {
                // get the number of days !! Size should be 3
                if (3 != decryptedValue.length) {
                    MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NormalizationErrorAE"));
                    throw new SQLServerException(form.format(new Object[] {baseSSType}), null, 0, null);
                }

                // Getting number of days
                // Following Lines of code copied from IOBuffer.readDaysIntoCE as we
                // cannot reuse method
                int daysIntoCE = getDaysIntoCE(decryptedValue, baseSSType);

                return DDC.convertTemporalToObject(jdbcType, baseSSType, cal, daysIntoCE, 0, 0);

            }

            case TIME: {
                long localNanosSinceMidnight = readNanosSinceMidnightAE(decryptedValue, baseTypeInfo.getScale(),
                        baseSSType);

                return DDC.convertTemporalToObject(jdbcType, SSType.TIME, cal, 0, localNanosSinceMidnight,
                        baseTypeInfo.getScale());
            }

            case DATETIME2: {
                if (8 != decryptedValue.length) {
                    MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NormalizationErrorAE"));
                    throw new SQLServerException(form.format(new Object[] {baseSSType}), null, 0, null);
                }

                // Last three bytes are for date and remaining for time
                int dateOffset = decryptedValue.length - 3;
                byte[] timePortion = new byte[dateOffset];
                byte[] datePortion = new byte[3];
                System.arraycopy(decryptedValue, 0, timePortion, 0, dateOffset);
                System.arraycopy(decryptedValue, dateOffset, datePortion, 0, 3);
                long localNanosSinceMidnight = readNanosSinceMidnightAE(timePortion, baseTypeInfo.getScale(),
                        baseSSType);

                int daysIntoCE = getDaysIntoCE(datePortion, baseSSType);

                // Convert the DATETIME2 value to the desired Java type.
                return DDC.convertTemporalToObject(jdbcType, SSType.DATETIME2, cal, daysIntoCE, localNanosSinceMidnight,
                        baseTypeInfo.getScale());

            }

            case SMALLDATETIME: {
                if (4 != decryptedValue.length) {
                    MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NormalizationErrorAE"));
                    throw new SQLServerException(form.format(new Object[] {baseSSType}), null, 0, null);
                }

                // SQL smalldatetime has less precision. It stores 2 bytes
                // for the days since SQL Base Date and 2 bytes for minutes
                // after midnight.
                return DDC.convertTemporalToObject(jdbcType, SSType.DATETIME, cal,
                        Util.readUnsignedShort(decryptedValue, 0),
                        Util.readUnsignedShort(decryptedValue, 2) * 60L * 1000L, 0);
            }

            case DATETIME: {
                int ticksSinceMidnight = (Util.readInt(decryptedValue, 4) * 10 + 1) / 3;

                if (8 != decryptedValue.length || Integer.MAX_VALUE < ticksSinceMidnight) {
                    MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NormalizationErrorAE"));
                    throw new SQLServerException(form.format(new Object[] {baseSSType}), null, 0, null);
                }

                // SQL datetime is 4 bytes for days since SQL Base Date
                // (January 1, 1900 00:00:00 GMT) and 4 bytes for
                // the number of three hundredths (1/300) of a second since midnight.
                return DDC.convertTemporalToObject(jdbcType, SSType.DATETIME, cal, Util.readInt(decryptedValue, 0),
                        ticksSinceMidnight, 0);
            }

            case DATETIMEOFFSET: {
                // Last 5 bytes are for date and offset
                int dateOffset = decryptedValue.length - 5;
                byte[] timePortion = new byte[dateOffset];
                byte[] datePortion = new byte[3];
                byte[] offsetPortion = new byte[2];
                System.arraycopy(decryptedValue, 0, timePortion, 0, dateOffset);
                System.arraycopy(decryptedValue, dateOffset, datePortion, 0, 3);
                System.arraycopy(decryptedValue, dateOffset + 3, offsetPortion, 0, 2);
                long localNanosSinceMidnight = readNanosSinceMidnightAE(timePortion, baseTypeInfo.getScale(),
                        baseSSType);

                int daysIntoCE = getDaysIntoCE(datePortion, baseSSType);

                int localMinutesOffset = ByteBuffer.wrap(offsetPortion).order(ByteOrder.LITTLE_ENDIAN).getShort();

                return DDC.convertTemporalToObject(jdbcType, SSType.DATETIMEOFFSET,
                        new GregorianCalendar(new SimpleTimeZone(localMinutesOffset * 60 * 1000, ""), Locale.US),
                        daysIntoCE, localNanosSinceMidnight, baseTypeInfo.getScale());

            }

            case GUID: {
                return Util.readGUID(decryptedValue);
            }

            default:
                MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_UnsupportedDataTypeAE"));
                throw new SQLServerException(form.format(new Object[] {baseSSType}), null, 0, null);
        }
    }