//CHECKSTYLE:FileLength:OFF
/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2017 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package org.pentaho.di.core.row.value;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.SocketTimeoutException;
import java.nio.charset.Charset;
import java.sql.Blob;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.text.Collator;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import org.pentaho.di.compatibility.Value;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.util.Utils;
import org.pentaho.di.core.database.DatabaseInterface;
import org.pentaho.di.core.database.DatabaseMeta;
import org.pentaho.di.core.database.GreenplumDatabaseMeta;
import org.pentaho.di.core.database.NetezzaDatabaseMeta;
import org.pentaho.di.core.database.OracleDatabaseMeta;
import org.pentaho.di.core.database.PostgreSQLDatabaseMeta;
import org.pentaho.di.core.database.SQLiteDatabaseMeta;
import org.pentaho.di.core.database.TeradataDatabaseMeta;
import org.pentaho.di.core.exception.KettleDatabaseException;
import org.pentaho.di.core.exception.KettleEOFException;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.exception.KettleFileException;
import org.pentaho.di.core.exception.KettleValueException;
import org.pentaho.di.core.gui.PrimitiveGCInterface;
import org.pentaho.di.core.logging.KettleLogStore;
import org.pentaho.di.core.logging.LogChannelInterface;
import org.pentaho.di.core.row.ValueDataUtil;
import org.pentaho.di.core.row.ValueMetaInterface;
import org.pentaho.di.core.util.EnvUtil;
import org.pentaho.di.core.xml.XMLHandler;
import org.pentaho.di.i18n.BaseMessages;
import org.w3c.dom.Node;
public class ValueMetaBase implements ValueMetaInterface {
// region Default Numeric Types Parse Format
public static final String DEFAULT_INTEGER_PARSE_MASK = "####0";
public static final String DEFAULT_NUMBER_PARSE_MASK = "####0.0#########";
public static final String DEFAULT_BIGNUMBER_PARSE_MASK = "######0.0###################";
public static final String DEFAULT_DATE_PARSE_MASK = "yyyy/MM/dd HH:mm:ss.SSS";
public static final String DEFAULT_TIMESTAMP_PARSE_MASK = "yyyy/MM/dd HH:mm:ss.SSSSSSSSS";
// endregion
// region Default Types Format
public static final String DEFAULT_INTEGER_FORMAT_MASK = Const.NVL(
EnvUtil.getSystemProperty( Const.KETTLE_DEFAULT_INTEGER_FORMAT ),
"####0;-####0" );
public static final String DEFAULT_NUMBER_FORMAT_MASK = Const.NVL(
EnvUtil.getSystemProperty( Const.KETTLE_DEFAULT_NUMBER_FORMAT ),
"####0.0#########;-####0.0#########" );
public static final String DEFAULT_BIG_NUMBER_FORMAT_MASK = Const.NVL(
EnvUtil.getSystemProperty( Const.KETTLE_DEFAULT_BIGNUMBER_FORMAT ),
"######0.0###################;-######0.0###################" );
public static final String DEFAULT_DATE_FORMAT_MASK = Const.NVL( EnvUtil
.getSystemProperty( Const.KETTLE_DEFAULT_DATE_FORMAT ), "yyyy/MM/dd HH:mm:ss.SSS" );
public static final String DEFAULT_TIMESTAMP_FORMAT_MASK = Const.NVL( EnvUtil
.getSystemProperty( Const.KETTLE_DEFAULT_TIMESTAMP_FORMAT ), "yyyy/MM/dd HH:mm:ss.SSSSSSSSS" );
// endregion
// region ValueMetaBase Attributes
protected static Class<?> PKG = Const.class; // for i18n purposes, needed by Translator2
public static final String XML_META_TAG = "value-meta";
public static final String XML_DATA_TAG = "value-data";
public static final String COMPATIBLE_DATE_FORMAT_PATTERN = "yyyy/MM/dd HH:mm:ss.SSS";
public static final boolean EMPTY_STRING_AND_NULL_ARE_DIFFERENT = convertStringToBoolean(
Const.NVL( System.getProperty( Const.KETTLE_EMPTY_STRING_DIFFERS_FROM_NULL, "N" ), "N" ) );
protected String name;
protected int length;
protected int precision;
protected int type;
protected int trimType;
protected int storageType;
protected String origin;
protected String comments;
protected Object[] index;
protected String conversionMask;
protected String stringEncoding;
protected String decimalSymbol;
protected String groupingSymbol;
protected String currencySymbol;
protected int collatorStrength;
protected boolean caseInsensitive;
protected boolean collatorDisabled;
protected boolean sortedDescending;
protected boolean outputPaddingEnabled;
protected boolean largeTextField;
protected Locale collatorLocale;
protected Collator collator;
protected Locale dateFormatLocale;
protected TimeZone dateFormatTimeZone;
protected boolean dateFormatLenient;
protected boolean lenientStringToNumber;
protected boolean ignoreTimezone;
protected SimpleDateFormat dateFormat;
protected boolean dateFormatChanged;
protected DecimalFormat decimalFormat;
protected boolean decimalFormatChanged;
protected ValueMetaInterface storageMetadata;
protected boolean identicalFormat;
protected ValueMetaInterface conversionMetadata;
boolean singleByteEncoding;
protected long numberOfBinaryStringConversions;
protected boolean bigNumberFormatting;
// get & store original result set meta data for later use
// @see java.sql.ResultSetMetaData
//
protected int originalColumnType;
protected String originalColumnTypeName;
protected int originalPrecision;
protected int originalScale;
protected boolean originalAutoIncrement;
protected int originalNullable;
protected boolean originalSigned;
private static final LogChannelInterface log = KettleLogStore.getLogChannelInterfaceFactory().create( "ValueMetaBase" );
/**
* The trim type codes
*/
public static final String[] trimTypeCode = { "none", "left", "right", "both" };
/**
* The trim description
*/
public static final String[] trimTypeDesc =
{ BaseMessages.getString( PKG, "ValueMeta.TrimType.None" ),
BaseMessages.getString( PKG, "ValueMeta.TrimType.Left" ),
BaseMessages.getString( PKG, "ValueMeta.TrimType.Right" ),
BaseMessages.getString( PKG, "ValueMeta.TrimType.Both" ) };
// endregion
public ValueMetaBase() {
this( null, ValueMetaInterface.TYPE_NONE, -1, -1 );
}
public ValueMetaBase( String name ) {
this( name, ValueMetaInterface.TYPE_NONE, -1, -1 );
}
public ValueMetaBase( String name, int type ) {
this( name, type, -1, -1 );
}
public ValueMetaBase( String name, int type, int length, int precision ) {
this.name = name;
this.type = type;
this.length = length;
this.precision = precision;
this.storageType = STORAGE_TYPE_NORMAL;
this.sortedDescending = false;
this.outputPaddingEnabled = false;
this.decimalSymbol = "" + Const.DEFAULT_DECIMAL_SEPARATOR;
this.groupingSymbol = "" + Const.DEFAULT_GROUPING_SEPARATOR;
this.dateFormatLocale = Locale.getDefault();
this.collatorDisabled = true;
this.collatorLocale = Locale.getDefault();
this.collator = Collator.getInstance( this.collatorLocale );
this.collatorStrength = 0;
this.dateFormatTimeZone = TimeZone.getDefault();
this.identicalFormat = true;
this.bigNumberFormatting = true;
this.lenientStringToNumber =
convertStringToBoolean( Const.NVL( System.getProperty( Const.KETTLE_LENIENT_STRING_TO_NUMBER_CONVERSION, "N" ),
"N" ) );
this.ignoreTimezone =
convertStringToBoolean( Const.NVL( System.getProperty( Const.KETTLE_COMPATIBILITY_DB_IGNORE_TIMEZONE, "N" ),
"N" ) );
determineSingleByteEncoding();
setDefaultConversionMask();
}
public ValueMetaBase( Node node ) throws KettleException {
this();
type = getType( XMLHandler.getTagValue( node, "type" ) );
storageType = getStorageType( XMLHandler.getTagValue( node, "storagetype" ) );
switch ( storageType ) {
case STORAGE_TYPE_INDEXED:
Node indexNode = XMLHandler.getSubNode( node, "index" );
int nrIndexes = XMLHandler.countNodes( indexNode, "value" );
index = new Object[nrIndexes];
for ( int i = 0; i < index.length; i++ ) {
Node valueNode = XMLHandler.getSubNodeByNr( indexNode, "value", i );
String valueString = XMLHandler.getNodeValue( valueNode );
if ( Utils.isEmpty( valueString ) ) {
index[i] = null;
} else {
switch ( type ) {
case TYPE_STRING:
index[i] = valueString;
break;
case TYPE_NUMBER:
index[i] = Double.parseDouble( valueString );
break;
case TYPE_INTEGER:
index[i] = Long.parseLong( valueString );
break;
case TYPE_DATE:
index[i] = XMLHandler.stringToDate( valueString );
break;
case TYPE_BIGNUMBER:
index[i] = new BigDecimal( valueString );
break;
case TYPE_BOOLEAN:
index[i] = Boolean.valueOf( "Y".equalsIgnoreCase( valueString ) );
break;
case TYPE_BINARY:
index[i] = XMLHandler.stringToBinary( valueString );
break;
default:
throw new KettleException( toString()
+ " : Unable to de-serialize indexe storage type from XML for data type " + getType() );
}
}
}
break;
case STORAGE_TYPE_BINARY_STRING:
// Load the storage meta data...
//
Node storageMetaNode = XMLHandler.getSubNode( node, "storage-meta" );
Node storageValueMetaNode = XMLHandler.getSubNode( storageMetaNode, XML_META_TAG );
if ( storageValueMetaNode != null ) {
storageMetadata = new ValueMetaBase( storageValueMetaNode );
}
break;
default:
break;
}
name = XMLHandler.getTagValue( node, "name" );
length = Integer.parseInt( XMLHandler.getTagValue( node, "length" ) );
precision = Integer.parseInt( XMLHandler.getTagValue( node, "precision" ) );
origin = XMLHandler.getTagValue( node, "origin" );
comments = XMLHandler.getTagValue( node, "comments" );
conversionMask = XMLHandler.getTagValue( node, "conversion_Mask" );
decimalSymbol = XMLHandler.getTagValue( node, "decimal_symbol" );
groupingSymbol = XMLHandler.getTagValue( node, "grouping_symbol" );
currencySymbol = XMLHandler.getTagValue( node, "currency_symbol" );
trimType = getTrimTypeByCode( XMLHandler.getTagValue( node, "trim_type" ) );
caseInsensitive = "Y".equalsIgnoreCase( XMLHandler.getTagValue( node, "case_insensitive" ) );
collatorDisabled = "Y".equalsIgnoreCase( XMLHandler.getTagValue( node, "collator_disabled" ) );
if ( XMLHandler.getTagValue( node, "collator_strength" ) != null ) {
collatorStrength = Integer.parseInt( XMLHandler.getTagValue( node, "collator_strength" ) );
}
sortedDescending = "Y".equalsIgnoreCase( XMLHandler.getTagValue( node, "sort_descending" ) );
outputPaddingEnabled = "Y".equalsIgnoreCase( XMLHandler.getTagValue( node, "output_padding" ) );
dateFormatLenient = "Y".equalsIgnoreCase( XMLHandler.getTagValue( node, "date_format_lenient" ) );
String dateFormatLocaleString = XMLHandler.getTagValue( node, "date_format_locale" );
if ( !Utils.isEmpty( dateFormatLocaleString ) ) {
dateFormatLocale = EnvUtil.createLocale( dateFormatLocaleString );
}
String dateTimeZoneString = XMLHandler.getTagValue( node, "date_format_timezone" );
if ( !Utils.isEmpty( dateTimeZoneString ) ) {
dateFormatTimeZone = EnvUtil.createTimeZone( dateTimeZoneString );
} else {
dateFormatTimeZone = TimeZone.getDefault();
}
lenientStringToNumber = "Y".equalsIgnoreCase( XMLHandler.getTagValue( node, "lenient_string_to_number" ) );
}
/**
* Create a new Value meta object.
*
* @param inputStream
* @throws KettleFileException
* @throws KettleEOFException
* @deprecated in favor of a combination of {@link ValueMetaFactory}.createValueMeta() and the loadMetaData() method.
*/
@Deprecated
public ValueMetaBase( DataInputStream inputStream ) throws KettleFileException, KettleEOFException {
this();
try {
type = inputStream.readInt();
} catch ( EOFException e ) {
throw new KettleEOFException( e );
} catch ( IOException e ) {
throw new KettleFileException( toString() + " : Unable to read value metadata from input stream", e );
}
readMetaData( inputStream );
}
/**
* @deprecated use {@link #ValueMetaBase(String, int)} and {@link #setStorageType(int)} instead
*
* @param name
* @param type
* @param storageType
*/
@Deprecated
public ValueMetaBase( String name, int type, int storageType ) {
this( name, type, -1, -1 );
this.storageType = storageType;
setDefaultConversionMask();
}
public static final String[] SINGLE_BYTE_ENCODINGS = new String[] { "ISO8859_1", "Cp1252", "ASCII", "Cp037", "Cp273",
"Cp277", "Cp278", "Cp280", "Cp284", "Cp285", "Cp297", "Cp420", "Cp424", "Cp437", "Cp500", "Cp737", "Cp775",
"Cp850", "Cp852", "Cp855", "Cp856", "Cp857", "Cp858", "Cp860", "Cp861", "Cp862", "Cp863", "Cp865", "Cp866",
"Cp869", "Cp870", "Cp871", "Cp875", "Cp918", "Cp921", "Cp922", "Cp1140", "Cp1141", "Cp1142", "Cp1143", "Cp1144",
"Cp1145", "Cp1146", "Cp1147", "Cp1148", "Cp1149", "Cp1250", "Cp1251", "Cp1253", "Cp1254", "Cp1255", "Cp1257",
"ISO8859_2", "ISO8859_3", "ISO8859_5", "ISO8859_5", "ISO8859_6", "ISO8859_7", "ISO8859_8", "ISO8859_9",
"ISO8859_13", "ISO8859_15", "ISO8859_15_FDIS", "MacCentralEurope", "MacCroatian", "MacCyrillic", "MacDingbat",
"MacGreek", "MacHebrew", "MacIceland", "MacRoman", "MacRomania", "MacSymbol", "MacTurkish", "MacUkraine", };
protected void setDefaultConversionMask() {
// Set some sensible default mask on the numbers
//
switch ( getType() ) {
case TYPE_INTEGER:
setConversionMask( DEFAULT_INTEGER_FORMAT_MASK );
break;
case TYPE_NUMBER:
setConversionMask( DEFAULT_NUMBER_FORMAT_MASK );
break;
case TYPE_BIGNUMBER:
setConversionMask( DEFAULT_BIG_NUMBER_FORMAT_MASK );
setGroupingSymbol( null );
setDecimalSymbol( "." ); // For backward compatibility reasons!
break;
default:
// does nothing
}
}
protected void determineSingleByteEncoding() {
singleByteEncoding = false;
Charset cs;
if ( Utils.isEmpty( stringEncoding ) ) {
cs = Charset.defaultCharset();
} else {
cs = Charset.forName( stringEncoding );
}
// See if the default character set for input is single byte encoded.
//
for ( String charSetEncoding : SINGLE_BYTE_ENCODINGS ) {
if ( cs.toString().equalsIgnoreCase( charSetEncoding ) ) {
singleByteEncoding = true;
}
}
}
@Override
public ValueMetaBase clone() {
try {
ValueMetaBase valueMeta = (ValueMetaBase) super.clone();
valueMeta.dateFormat = null;
valueMeta.decimalFormat = null;
if ( dateFormatLocale != null ) {
valueMeta.dateFormatLocale = (Locale) dateFormatLocale.clone();
}
if ( dateFormatTimeZone != null ) {
valueMeta.dateFormatTimeZone = (TimeZone) dateFormatTimeZone.clone();
}
if ( storageMetadata != null ) {
valueMeta.storageMetadata = storageMetadata.clone();
}
if ( conversionMetadata != null ) {
valueMeta.conversionMetadata = conversionMetadata.clone();
}
valueMeta.compareStorageAndActualFormat();
return valueMeta;
} catch ( CloneNotSupportedException e ) {
return null;
}
}
/**
* @return the comments
*/
@Override
public String getComments() {
return comments;
}
/**
* @param comments
* the comments to set
*/
@Override
public void setComments( String comments ) {
this.comments = comments;
}
/**
* @return the index
*/
@Override
public Object[] getIndex() {
return index;
}
/**
* @param index
* the index to set
*/
@Override
public void setIndex( Object[] index ) {
this.index = index;
}
/**
* @return the length
*/
@Override
public int getLength() {
return length;
}
/**
* @param length
* the length to set
*/
@Override
public void setLength( int length ) {
this.length = length;
}
/**
* @param length
* the length to set
*/
@Override
public void setLength( int length, int precision ) {
this.length = length;
this.precision = precision;
}
/**
*
* @return
*/
boolean isLengthInvalidOrZero() {
return this.length < 1;
}
/**
* @return the name
*/
@Override
public String getName() {
return name;
}
/**
* @param name
* the name to set
*/
@Override
public void setName( String name ) {
this.name = name;
}
/**
* @return the origin
*/
@Override
public String getOrigin() {
return origin;
}
/**
* @param origin
* the origin to set
*/
@Override
public void setOrigin( String origin ) {
this.origin = origin;
}
/**
* @return the precision
*/
@Override
public int getPrecision() {
// For backward compatibility we need to tweak a bit...
//
if ( isInteger() || isBinary() ) {
return 0;
}
if ( isString() || isBoolean() ) {
return -1;
}
return precision;
}
/**
* @param precision
* the precision to set
*/
@Override
public void setPrecision( int precision ) {
this.precision = precision;
}
/**
* @return the storageType
*/
@Override
public int getStorageType() {
return storageType;
}
/**
* @param storageType
* the storageType to set
*/
@Override
public void setStorageType( int storageType ) {
this.storageType = storageType;
}
@Override
public boolean isStorageNormal() {
return storageType == STORAGE_TYPE_NORMAL;
}
@Override
public boolean isStorageIndexed() {
return storageType == STORAGE_TYPE_INDEXED;
}
@Override
public boolean isStorageBinaryString() {
return storageType == STORAGE_TYPE_BINARY_STRING;
}
/**
* @return the type
*/
@Override
public int getType() {
return type;
}
/**
* @param type
* the type to set
* @deprecated
*/
@Override
@Deprecated
public void setType( int type ) {
this.type = type;
}
/**
* @return the conversionMask
*/
@Override
@Deprecated
public String getConversionMask() {
return conversionMask;
}
/**
* @param conversionMask
* the conversionMask to set
*/
@Override
public void setConversionMask( String conversionMask ) {
this.conversionMask = conversionMask;
dateFormatChanged = true;
decimalFormatChanged = true;
compareStorageAndActualFormat();
}
/**
* @return the encoding
*/
@Override
public String getStringEncoding() {
return stringEncoding;
}
/**
* @param encoding
* the encoding to set
*/
@Override
public void setStringEncoding( String encoding ) {
this.stringEncoding = encoding;
determineSingleByteEncoding();
compareStorageAndActualFormat();
}
/**
* @return the decimalSymbol
*/
@Override
public String getDecimalSymbol() {
return decimalSymbol;
}
/**
* @param decimalSymbol
* the decimalSymbol to set
*/
@Override
public void setDecimalSymbol( String decimalSymbol ) {
this.decimalSymbol = decimalSymbol;
decimalFormatChanged = true;
compareStorageAndActualFormat();
}
/**
* @return the groupingSymbol
*/
@Override
public String getGroupingSymbol() {
return groupingSymbol;
}
/**
* @param groupingSymbol
* the groupingSymbol to set
*/
@Override
public void setGroupingSymbol( String groupingSymbol ) {
this.groupingSymbol = groupingSymbol;
decimalFormatChanged = true;
compareStorageAndActualFormat();
}
/**
* @return the currencySymbol
*/
@Override
public String getCurrencySymbol() {
return currencySymbol;
}
/**
* @param currencySymbol
* the currencySymbol to set
*/
@Override
public void setCurrencySymbol( String currencySymbol ) {
this.currencySymbol = currencySymbol;
decimalFormatChanged = true;
}
/**
* @return the caseInsensitive
*/
@Override
public boolean isCaseInsensitive() {
return caseInsensitive;
}
/**
* @param caseInsensitive
* the caseInsensitive to set
*/
@Override
public void setCaseInsensitive( boolean caseInsensitive ) {
this.caseInsensitive = caseInsensitive;
}
/**
* @return the collatorDisabled
*/
@Override
public boolean isCollatorDisabled() {
return collatorDisabled;
}
/**
* @param collatorDisabled
* the collatorDisabled to set
*/
@Override
public void setCollatorDisabled( boolean collatorDisabled ) {
this.collatorDisabled = collatorDisabled;
}
@Override
public Locale getCollatorLocale() {
return this.collatorLocale;
}
/**
* @ sets the collator Locale
*/
@Override
public void setCollatorLocale( Locale locale ) {
// Update the collator only if required
if ( collatorLocale == null || !collatorLocale.equals( locale ) ) {
this.collatorLocale = locale;
this.collator = Collator.getInstance( locale );
}
}
/**
* @get the collatorStrength
*/
@Override
public int getCollatorStrength() {
return collatorStrength;
}
/**
* @param collatorStrength
* the collatorStrength to set
*/
@Override
public void setCollatorStrength( int collatorStrength ) throws IllegalArgumentException {
try {
if ( collator != null ) {
this.collator.setStrength( collatorStrength );
this.collatorStrength = collatorStrength;
}
} catch ( IllegalArgumentException e ) {
throw new IllegalArgumentException( " : Collator strength must be an int between 0 and 3. " );
}
}
/**
* @return the sortedDescending
*/
@Override
public boolean isSortedDescending() {
return sortedDescending;
}
/**
* @param sortedDescending
* the sortedDescending to set
*/
@Override
public void setSortedDescending( boolean sortedDescending ) {
this.sortedDescending = sortedDescending;
}
/**
* @return true if output padding is enabled (padding to specified length)
*/
@Override
public boolean isOutputPaddingEnabled() {
return outputPaddingEnabled;
}
/**
* @param outputPaddingEnabled
* Set to true if output padding is to be enabled (padding to specified length)
*/
@Override
public void setOutputPaddingEnabled( boolean outputPaddingEnabled ) {
this.outputPaddingEnabled = outputPaddingEnabled;
}
/**
* @return true if this is a large text field (CLOB, TEXT) with arbitrary length.
*/
@Override
public boolean isLargeTextField() {
return largeTextField;
}
/**
* @param largeTextField
* Set to true if this is to be a large text field (CLOB, TEXT) with arbitrary length.
*/
@Override
public void setLargeTextField( boolean largeTextField ) {
this.largeTextField = largeTextField;
}
/**
* @return the dateFormatLenient
*/
@Override
public boolean isDateFormatLenient() {
return dateFormatLenient;
}
/**
* @param dateFormatLenient
* the dateFormatLenient to set
*/
@Override
public void setDateFormatLenient( boolean dateFormatLenient ) {
this.dateFormatLenient = dateFormatLenient;
dateFormatChanged = true;
}
/**
* @return the dateFormatLocale
*/
@Override
public Locale getDateFormatLocale() {
return dateFormatLocale;
}
/**
* @param dateFormatLocale
* the dateFormatLocale to set
*/
@Override
public void setDateFormatLocale( Locale dateFormatLocale ) {
this.dateFormatLocale = dateFormatLocale;
dateFormatChanged = true;
}
// DATE + STRING
protected synchronized String convertDateToString( Date date ) {
if ( date == null ) {
return null;
}
return getDateFormat().format( date );
}
protected static SimpleDateFormat compatibleDateFormat = new SimpleDateFormat( COMPATIBLE_DATE_FORMAT_PATTERN );
protected synchronized String convertDateToCompatibleString( Date date ) {
if ( date == null ) {
return null;
}
return compatibleDateFormat.format( date );
}
protected synchronized Date convertStringToDate( String string ) throws KettleValueException {
string = Const.trimToType( string, getTrimType() ); // see if trimming needs
// to be performed before
// conversion
if ( Utils.isEmpty( string ) ) {
return null;
}
try {
ParsePosition pp = new ParsePosition( 0 );
Date result = getDateFormat( TYPE_DATE ).parse( string, pp );
if ( pp.getErrorIndex() >= 0 ) {
// error happen
throw new ParseException( string, pp.getErrorIndex() );
}
// some chars can be after pp.getIndex(). That means, not full value was parsed. For example, for value
// "25-03-1918 11:54" and format "dd-MM-yyyy", value will be "25-03-1918 00:00" without any exception.
// If there are only spaces after pp.getIndex() - that means full values was parsed
return result;
} catch ( ParseException e ) {
String dateFormat = ( getDateFormat() != null ) ? getDateFormat().toPattern() : "null";
throw new KettleValueException( toString() + " : couldn't convert string [" + string
+ "] to a date using format [" + dateFormat + "] on offset location " + e.getErrorOffset(), e );
}
}
// DATE + NUMBER
protected Double convertDateToNumber( Date date ) {
return new Double( date.getTime() );
}
protected Date convertNumberToDate( Double number ) {
return new Date( number.longValue() );
}
// DATE + INTEGER
protected Long convertDateToInteger( Date date ) {
return new Long( date.getTime() );
}
protected Date convertIntegerToDate( Long number ) {
return new Date( number.longValue() );
}
// DATE + BIGNUMBER
protected BigDecimal convertDateToBigNumber( Date date ) {
return new BigDecimal( date.getTime() );
}
protected Date convertBigNumberToDate( BigDecimal number ) {
return new Date( number.longValue() );
}
protected synchronized String convertNumberToString( Double number ) throws KettleValueException {
if ( number == null ) {
if ( !outputPaddingEnabled || length < 1 ) {
return null;
} else {
// Return strings padded to the specified length...
// This is done for backward compatibility with 2.5.x
// We just optimized this a bit...
//
String[] emptyPaddedStrings = Const.getEmptyPaddedStrings();
if ( length < emptyPaddedStrings.length ) {
return emptyPaddedStrings[length];
} else {
return Const.rightPad( "", length );
}
}
}
try {
return getDecimalFormat( false ).format( number );
} catch ( Exception e ) {
throw new KettleValueException( toString() + " : couldn't convert Number to String ", e );
}
}
protected synchronized String convertNumberToCompatibleString( Double number ) throws KettleValueException {
if ( number == null ) {
return null;
}
return Double.toString( number );
}
protected synchronized Double convertStringToNumber( String string ) throws KettleValueException {
string = Const.trimToType( string, getTrimType() ); // see if trimming needs
// to be performed before
// conversion
if ( Utils.isEmpty( string ) ) {
return null;
}
try {
DecimalFormat format = getDecimalFormat( false );
Number number;
if ( lenientStringToNumber ) {
number = format.parse( string );
} else {
ParsePosition parsePosition = new ParsePosition( 0 );
number = format.parse( string, parsePosition );
if ( parsePosition.getIndex() < string.length() ) {
throw new KettleValueException( toString()
+ " : couldn't convert String to number : non-numeric character found at position "
+ ( parsePosition.getIndex() + 1 ) + " for value [" + string + "]" );
}
}
return new Double( number.doubleValue() );
} catch ( Exception e ) {
throw new KettleValueException( toString() + " : couldn't convert String to number ", e );
}
}
@Override
public synchronized SimpleDateFormat getDateFormat() {
return getDateFormat( getType() );
}
private synchronized SimpleDateFormat getDateFormat( int valueMetaType ) {
// If we have a Date that is represented as a String
// In that case we can set the format of the original Date on the String
// value metadata in the form of a conversion metadata object.
// That way, we can always convert from Date to String and back without a
// problem, no matter how complex the format was.
// As such, we should return the date SimpleDateFormat of the conversion
// metadata.
//
if ( conversionMetadata != null ) {
return conversionMetadata.getDateFormat();
}
if ( dateFormat == null || dateFormatChanged ) {
// This may not become static as the class is not thread-safe!
dateFormat = new SimpleDateFormat();
String mask = this.getMask( valueMetaType );
// Do we have a locale?
//
if ( dateFormatLocale == null || dateFormatLocale.equals( Locale.getDefault() ) ) {
dateFormat = new SimpleDateFormat( mask );
} else {
dateFormat = new SimpleDateFormat( mask, dateFormatLocale );
}
// Do we have a time zone?
//
if ( dateFormatTimeZone != null ) {
dateFormat.setTimeZone( dateFormatTimeZone );
}
// Set the conversion leniency as well
//
dateFormat.setLenient( dateFormatLenient );
dateFormatChanged = false;
}
return dateFormat;
}
@Override
public synchronized DecimalFormat getDecimalFormat() {
return getDecimalFormat( false );
}
@Override
public synchronized DecimalFormat getDecimalFormat( boolean useBigDecimal ) {
// If we have an Integer that is represented as a String
// In that case we can set the format of the original Integer on the String
// value metadata in the form of a conversion metadata object.
// That way, we can always convert from Integer to String and back without a
// problem, no matter how complex the format was.
// As such, we should return the decimal format of the conversion metadata.
//
if ( conversionMetadata != null ) {
return conversionMetadata.getDecimalFormat( useBigDecimal );
}
// Calculate the decimal format as few times as possible.
// That is because creating or changing a DecimalFormat object is very CPU
// hungry.
//
if ( decimalFormat == null || decimalFormatChanged ) {
decimalFormat = (DecimalFormat) NumberFormat.getInstance();
decimalFormat.setParseBigDecimal( useBigDecimal );
DecimalFormatSymbols decimalFormatSymbols = decimalFormat.getDecimalFormatSymbols();
if ( !Utils.isEmpty( currencySymbol ) ) {
decimalFormatSymbols.setCurrencySymbol( currencySymbol );
}
if ( !Utils.isEmpty( groupingSymbol ) ) {
decimalFormatSymbols.setGroupingSeparator( groupingSymbol.charAt( 0 ) );
}
if ( !Utils.isEmpty( decimalSymbol ) ) {
decimalFormatSymbols.setDecimalSeparator( decimalSymbol.charAt( 0 ) );
}
decimalFormat.setDecimalFormatSymbols( decimalFormatSymbols );
String decimalPattern = getMask( getType() );
if ( !Utils.isEmpty( decimalPattern ) ) {
decimalFormat.applyPattern( decimalPattern );
}
decimalFormatChanged = false;
}
return decimalFormat;
}
@Override
public String getFormatMask() {
return getMask( getType() );
}
String getMask( int type ) {
if ( !Utils.isEmpty( this.conversionMask ) ) {
return this.conversionMask;
}
boolean fromString = isString();
switch ( type ) {
case TYPE_INTEGER:
return fromString ? DEFAULT_INTEGER_PARSE_MASK : getIntegerFormatMask();
case TYPE_NUMBER:
return fromString ? DEFAULT_NUMBER_PARSE_MASK : getNumberFormatMask();
case TYPE_BIGNUMBER:
return fromString ? DEFAULT_BIGNUMBER_PARSE_MASK : getBigNumberFormatMask();
case TYPE_DATE:
return fromString ? DEFAULT_DATE_PARSE_MASK : getDateFormatMask();
case TYPE_TIMESTAMP:
return fromString ? DEFAULT_TIMESTAMP_PARSE_MASK : getTimestampFormatMask();
}
return null;
}
String getNumberFormatMask() {
String numberMask = this.conversionMask;
if ( Utils.isEmpty( numberMask ) ) {
if ( this.isLengthInvalidOrZero() ) {
numberMask = DEFAULT_NUMBER_FORMAT_MASK;
} else {
numberMask = this.buildNumberPattern();
}
}
return numberMask;
}
String getIntegerFormatMask() {
String integerMask = this.conversionMask;
if ( Utils.isEmpty( integerMask ) ) {
if ( this.isLengthInvalidOrZero() ) {
integerMask = DEFAULT_INTEGER_FORMAT_MASK;
// as
// before
// version
// 3.0
} else {
StringBuilder integerPattern = new StringBuilder();
// First the format for positive integers...
//
integerPattern.append( " " );
for ( int i = 0; i < getLength(); i++ ) {
integerPattern.append( '0' ); // all zeroes.
}
integerPattern.append( ";" );
// Then the format for the negative numbers...
//
integerPattern.append( "-" );
for ( int i = 0; i < getLength(); i++ ) {
integerPattern.append( '0' ); // all zeroes.
}
integerMask = integerPattern.toString();
}
}
return integerMask;
}
String getBigNumberFormatMask() {
String bigNumberMask = this.conversionMask;
if ( Utils.isEmpty( bigNumberMask ) ) {
if ( this.isLengthInvalidOrZero() ) {
bigNumberMask = DEFAULT_BIG_NUMBER_FORMAT_MASK;
} else {
bigNumberMask = this.buildNumberPattern();
}
}
return bigNumberMask;
}
String getDateFormatMask() {
String mask = this.conversionMask;
if ( Utils.isEmpty( mask ) ) {
mask = DEFAULT_DATE_FORMAT_MASK;
}
return mask;
}
String getTimestampFormatMask() {
String mask = conversionMask;
if ( Utils.isEmpty( mask ) ) {
mask = DEFAULT_TIMESTAMP_FORMAT_MASK;
}
return mask;
}
private String buildNumberPattern() {
StringBuilder numberPattern = new StringBuilder();
// First do the format for positive numbers...
//
numberPattern.append( ' ' ); // to compensate for minus sign.
if ( precision < 0 ) {
// Default: two decimals
for ( int i = 0; i < length; i++ ) {
numberPattern.append( '0' );
}
numberPattern.append( ".00" ); // for the .00
} else {
// Floating point format 00001234,56 --> (12,2)
for ( int i = 0; i <= length; i++ ) {
numberPattern.append( '0' ); // all zeroes.
}
int pos = length - precision + 1;
if ( pos >= 0 && pos < numberPattern.length() ) {
numberPattern.setCharAt( length - precision + 1, '.' ); // one
// 'comma'
}
}
// Now do the format for negative numbers...
//
StringBuilder negativePattern = new StringBuilder( numberPattern );
negativePattern.setCharAt( 0, '-' );
numberPattern.append( ";" );
numberPattern.append( negativePattern );
// Return the pattern...
//
return numberPattern.toString();
}
protected synchronized String convertIntegerToString( Long integer ) throws KettleValueException {
if ( integer == null ) {
if ( !outputPaddingEnabled || length < 1 ) {
return null;
} else {
// Return strings padded to the specified length...
// This is done for backward compatibility with 2.5.x
// We just optimized this a bit...
//
String[] emptyPaddedStrings = Const.getEmptyPaddedStrings();
if ( length < emptyPaddedStrings.length ) {
return emptyPaddedStrings[length];
} else {
return Const.rightPad( "", length );
}
}
}
try {
return getDecimalFormat( false ).format( integer );
} catch ( Exception e ) {
throw new KettleValueException( toString() + " : couldn't convert Long to String ", e );
}
}
protected synchronized String convertIntegerToCompatibleString( Long integer ) throws KettleValueException {
if ( integer == null ) {
return null;
}
return Long.toString( integer );
}
protected synchronized Long convertStringToInteger( String string ) throws KettleValueException {
string = Const.trimToType( string, getTrimType() ); // see if trimming needs
// to be performed before
// conversion
if ( Utils.isEmpty( string ) ) {
return null;
}
try {
Number number;
if ( lenientStringToNumber ) {
number = new Long( getDecimalFormat( false ).parse( string ).longValue() );
} else {
ParsePosition parsePosition = new ParsePosition( 0 );
number = getDecimalFormat( false ).parse( string, parsePosition );
if ( parsePosition.getIndex() < string.length() ) {
throw new KettleValueException( toString()
+ " : couldn't convert String to number : non-numeric character found at position "
+ ( parsePosition.getIndex() + 1 ) + " for value [" + string + "]" );
}
}
return new Long( number.longValue() );
} catch ( Exception e ) {
throw new KettleValueException( toString() + " : couldn't convert String to Integer", e );
}
}
protected synchronized String convertBigNumberToString( BigDecimal number ) throws KettleValueException {
if ( number == null ) {
return null;
}
try {
return getDecimalFormat( bigNumberFormatting ).format( number );
} catch ( Exception e ) {
throw new KettleValueException( toString() + " : couldn't convert BigNumber to String ", e );
}
}
protected synchronized BigDecimal convertStringToBigNumber( String string ) throws KettleValueException {
string = Const.trimToType( string, getTrimType() ); // see if trimming needs
// to be performed before
// conversion
if ( Utils.isEmpty( string ) ) {
return null;
}
try {
DecimalFormat format = getDecimalFormat( bigNumberFormatting );
Number number;
if ( lenientStringToNumber ) {
number = format.parse( string );
} else {
ParsePosition parsePosition = new ParsePosition( 0 );
number = format.parse( string, parsePosition );
if ( parsePosition.getIndex() < string.length() ) {
throw new KettleValueException( toString()
+ " : couldn't convert String to number : non-numeric character found at position "
+ ( parsePosition.getIndex() + 1 ) + " for value [" + string + "]" );
}
}
return (BigDecimal) number;
} catch ( Exception e ) {
// We added this workaround for PDI-1824
//
try {
return new BigDecimal( string );
} catch ( NumberFormatException ex ) {
throw new KettleValueException( toString() + " : couldn't convert string value '" + string
+ "' to a big number.", ex );
}
}
}
// BOOLEAN + STRING
protected String convertBooleanToString( Boolean bool ) {
if ( bool == null ) {
return null;
}
if ( length >= 3 ) {
return bool.booleanValue() ? "true" : "false";
} else {
return bool.booleanValue() ? "Y" : "N";
}
}
public static Boolean convertStringToBoolean( String string ) {
if ( Utils.isEmpty( string ) ) {
return null;
}
return Boolean.valueOf( "Y".equalsIgnoreCase( string ) || "TRUE".equalsIgnoreCase( string )
|| "YES".equalsIgnoreCase( string ) || "1".equals( string ) );
}
// BOOLEAN + NUMBER
protected Double convertBooleanToNumber( Boolean bool ) {
if ( bool == null ) {
return null;
}
return new Double( bool.booleanValue() ? 1.0 : 0.0 );
}
protected Boolean convertNumberToBoolean( Double number ) {
if ( number == null ) {
return null;
}
return Boolean.valueOf( number.intValue() != 0 );
}
// BOOLEAN + INTEGER
protected Long convertBooleanToInteger( Boolean bool ) {
if ( bool == null ) {
return null;
}
return Long.valueOf( bool.booleanValue() ? 1L : 0L );
}
protected Boolean convertIntegerToBoolean( Long number ) {
if ( number == null ) {
return null;
}
return Boolean.valueOf( number.longValue() != 0 );
}
// BOOLEAN + BIGNUMBER
protected BigDecimal convertBooleanToBigNumber( Boolean bool ) {
if ( bool == null ) {
return null;
}
return bool.booleanValue() ? BigDecimal.ONE : BigDecimal.ZERO;
}
protected Boolean convertBigNumberToBoolean( BigDecimal number ) {
if ( number == null ) {
return null;
}
return Boolean.valueOf( number.signum() != 0 );
}
/**
* Converts a byte[] stored in a binary string storage type into a String;
*
* @param binary
* the binary string
* @return the String in the correct encoding.
* @throws KettleValueException
*/
protected String convertBinaryStringToString( byte[] binary ) throws KettleValueException {
//noinspection deprecation
return convertBinaryStringToString( binary, EMPTY_STRING_AND_NULL_ARE_DIFFERENT );
}
/*
* Do not use this method directly! It is for tests!
*/
@Deprecated
String convertBinaryStringToString( byte[] binary, boolean emptyStringDiffersFromNull ) throws KettleValueException {
// OK, so we have an internal representation of the original object, read
// from file.
// Before we release it back, we have to see if we don't have to do a
// String-<type>-String
// conversion with different masks.
// This obviously only applies to numeric data and dates.
// We verify if this is true or false in advance for performance reasons
//
// if (binary==null || binary.length==0) return null;
if ( binary == null || binary.length == 0 ) {
return ( emptyStringDiffersFromNull && binary != null ) ? "" : null;
}
String encoding;
if ( identicalFormat ) {
encoding = getStringEncoding();
} else {
encoding = storageMetadata.getStringEncoding();
}
if ( Utils.isEmpty( encoding ) ) {
return new String( binary );
} else {
try {
return new String( binary, encoding );
} catch ( UnsupportedEncodingException e ) {
throw new KettleValueException( toString()
+ " : couldn't convert binary value to String with specified string encoding [" + stringEncoding + "]", e );
}
}
}
/**
* Converts the specified data object to the normal storage type.
*
* @param object
* the data object to convert
* @return the data in a normal storage type
* @throws KettleValueException
* In case there is a data conversion error.
*/
@Override
public Object convertToNormalStorageType( Object object ) throws KettleValueException {
if ( object == null ) {
return null;
}
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return object;
case STORAGE_TYPE_BINARY_STRING:
return convertBinaryStringToNativeType( (byte[]) object );
case STORAGE_TYPE_INDEXED:
return index[(Integer) object];
default:
throw new KettleValueException( toStringMeta() + " : Unknown storage type [" + storageType
+ "] while converting to normal storage type" );
}
}
/**
* Converts the specified data object to the binary string storage type.
*
* @param object
* the data object to convert
* @return the data in a binary string storage type
* @throws KettleValueException
* In case there is a data conversion error.
*/
@Override
public Object convertToBinaryStringStorageType( Object object ) throws KettleValueException {
if ( object == null ) {
return null;
}
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertNormalStorageTypeToBinaryString( object );
case STORAGE_TYPE_BINARY_STRING:
return object;
case STORAGE_TYPE_INDEXED:
return convertNormalStorageTypeToBinaryString( index[(Integer) object] );
default:
throw new KettleValueException( toStringMeta() + " : Unknown storage type [" + storageType
+ "] while converting to normal storage type" );
}
}
/**
* Convert the binary data to the actual data type.<br>
* - byte[] --> Long (Integer) - byte[] --> Double (Number) - byte[] --> BigDecimal (BigNumber) - byte[] --> Date
* (Date) - byte[] --> Boolean (Boolean) - byte[] --> byte[] (Binary)
*
* @param binary
* @return
* @throws KettleValueException
*/
@Override
public Object convertBinaryStringToNativeType( byte[] binary ) throws KettleValueException {
if ( binary == null ) {
return null;
}
numberOfBinaryStringConversions++;
// OK, so we have an internal representation of the original object, read
// from file.
// First we decode it in the correct encoding
//
String string = convertBinaryStringToString( binary );
// In this method we always must convert the data.
// We use the storageMetadata object to convert the binary string object.
//
// --> Convert from the String format to the current data type...
//
return convertData( storageMetadata, string );
}
@Override
public Object convertNormalStorageTypeToBinaryString( Object object ) throws KettleValueException {
if ( object == null ) {
return null;
}
String string = getString( object );
return convertStringToBinaryString( string );
}
protected byte[] convertStringToBinaryString( String string ) throws KettleValueException {
if ( string == null ) {
return null;
}
if ( Utils.isEmpty( stringEncoding ) ) {
return string.getBytes();
} else {
try {
return string.getBytes( stringEncoding );
} catch ( UnsupportedEncodingException e ) {
throw new KettleValueException( toString()
+ " : couldn't convert String to Binary with specified string encoding [" + stringEncoding + "]", e );
}
}
}
/**
* Clones the data. Normally, we don't have to do anything here, but just for arguments and safety, we do a little
* extra work in case of binary blobs and Date objects. We should write a programmers manual later on to specify in
* all clarity that "we always overwrite/replace values in the Object[] data rows, we never modify them" .
*
* @return a cloned data object if needed
*/
@Override
public Object cloneValueData( Object object ) throws KettleValueException {
if ( object == null ) {
return null;
}
if ( storageType == STORAGE_TYPE_NORMAL ) {
switch ( getType() ) {
case ValueMetaInterface.TYPE_STRING:
case ValueMetaInterface.TYPE_NUMBER:
case ValueMetaInterface.TYPE_INTEGER:
case ValueMetaInterface.TYPE_BOOLEAN:
case ValueMetaInterface.TYPE_BIGNUMBER: // primitive data types: we can only
// overwrite these, not change them
return object;
case ValueMetaInterface.TYPE_DATE:
return new Date( ( (Date) object ).getTime() ); // just to make sure: very
// inexpensive too.
case ValueMetaInterface.TYPE_BINARY:
byte[] origin = (byte[]) object;
byte[] target = new byte[origin.length];
System.arraycopy( origin, 0, target, 0, origin.length );
return target;
case ValueMetaInterface.TYPE_SERIALIZABLE:
// Let's not create a copy but simply return the same value.
//
return object;
default:
throw new KettleValueException( toString() + ": unable to make copy of value type: " + getType() );
}
} else {
return object;
}
}
@Override
public String getCompatibleString( Object object ) throws KettleValueException {
try {
String string;
switch ( type ) {
case TYPE_DATE:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
string = convertDateToCompatibleString( (Date) object );
break;
case STORAGE_TYPE_BINARY_STRING:
string = convertDateToCompatibleString( (Date) convertBinaryStringToNativeType( (byte[]) object ) );
break;
case STORAGE_TYPE_INDEXED:
if ( object == null ) {
string = null;
} else {
string = convertDateToCompatibleString( (Date) index[( (Integer) object ).intValue()] );
}
break;
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
break;
case TYPE_NUMBER:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
string = convertNumberToCompatibleString( (Double) object );
break;
case STORAGE_TYPE_BINARY_STRING:
string = convertNumberToCompatibleString( (Double) convertBinaryStringToNativeType( (byte[]) object ) );
break;
case STORAGE_TYPE_INDEXED:
string =
object == null ? null : convertNumberToCompatibleString( (Double) index[( (Integer) object )
.intValue()] );
break;
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
break;
case TYPE_INTEGER:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
string = convertIntegerToCompatibleString( (Long) object );
break;
case STORAGE_TYPE_BINARY_STRING:
try {
string = convertIntegerToCompatibleString( (Long) convertBinaryStringToNativeType( (byte[]) object ) );
} catch ( ClassCastException e ) {
string = convertIntegerToCompatibleString( (Long) object );
}
break;
case STORAGE_TYPE_INDEXED:
string =
object == null ? null : convertIntegerToCompatibleString( (Long) index[( (Integer) object )
.intValue()] );
break;
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
break;
default:
return getString( object );
}
return string;
} catch ( ClassCastException e ) {
throw new KettleValueException( toString() + " : There was a data type error: the data type of "
+ object.getClass().getName() + " object [" + object + "] does not correspond to value meta ["
+ toStringMeta() + "]" );
}
}
@Override
public String getString( Object object ) throws KettleValueException {
try {
String string;
switch ( type ) {
case TYPE_STRING:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
string = object == null ? null : object.toString();
break;
case STORAGE_TYPE_BINARY_STRING:
string = (String) convertBinaryStringToNativeType( (byte[]) object );
break;
case STORAGE_TYPE_INDEXED:
string = object == null ? null : (String) index[( (Integer) object ).intValue()];
break;
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
if ( string != null ) {
string = trim( string );
}
break;
case TYPE_DATE:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
string = convertDateToString( (Date) object );
break;
case STORAGE_TYPE_BINARY_STRING:
string = convertDateToString( (Date) convertBinaryStringToNativeType( (byte[]) object ) );
break;
case STORAGE_TYPE_INDEXED:
string = object == null ? null : convertDateToString( (Date) index[( (Integer) object ).intValue()] );
break;
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
break;
case TYPE_NUMBER:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
string = convertNumberToString( (Double) object );
break;
case STORAGE_TYPE_BINARY_STRING:
string = convertNumberToString( (Double) convertBinaryStringToNativeType( (byte[]) object ) );
break;
case STORAGE_TYPE_INDEXED:
string = object == null ? null : convertNumberToString( (Double) index[( (Integer) object ).intValue()] );
break;
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
break;
case TYPE_INTEGER:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
string = convertIntegerToString( (Long) object );
break;
case STORAGE_TYPE_BINARY_STRING:
string = convertIntegerToString( (Long) convertBinaryStringToNativeType( (byte[]) object ) );
break;
case STORAGE_TYPE_INDEXED:
string = object == null ? null : convertIntegerToString( (Long) index[( (Integer) object ).intValue()] );
break;
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
break;
case TYPE_BIGNUMBER:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
string = convertBigNumberToString( (BigDecimal) object );
break;
case STORAGE_TYPE_BINARY_STRING:
string = convertBigNumberToString( (BigDecimal) convertBinaryStringToNativeType( (byte[]) object ) );
break;
case STORAGE_TYPE_INDEXED:
string =
object == null ? null
: convertBigNumberToString( (BigDecimal) index[( (Integer) object ).intValue()] );
break;
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
break;
case TYPE_BOOLEAN:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
string = convertBooleanToString( (Boolean) object );
break;
case STORAGE_TYPE_BINARY_STRING:
string = convertBooleanToString( (Boolean) convertBinaryStringToNativeType( (byte[]) object ) );
break;
case STORAGE_TYPE_INDEXED:
string =
object == null ? null : convertBooleanToString( (Boolean) index[( (Integer) object ).intValue()] );
break;
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
break;
case TYPE_BINARY:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
string = convertBinaryStringToString( (byte[]) object );
break;
case STORAGE_TYPE_BINARY_STRING:
string = convertBinaryStringToString( (byte[]) object );
break;
case STORAGE_TYPE_INDEXED:
string =
object == null ? null : convertBinaryStringToString( (byte[]) index[( (Integer) object ).intValue()] );
break;
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
break;
case TYPE_SERIALIZABLE:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
string = object == null ? null : object.toString();
break; // just go for the default toString()
case STORAGE_TYPE_BINARY_STRING:
string = convertBinaryStringToString( (byte[]) object );
break;
case STORAGE_TYPE_INDEXED:
string = object == null ? null : index[( (Integer) object ).intValue()].toString();
break; // just go for the default toString()
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
break;
default:
throw new KettleValueException( toString() + " : Unknown type " + type + " specified." );
}
if ( isOutputPaddingEnabled() && getLength() > 0 ) {
string = ValueDataUtil.rightPad( string, getLength() );
}
return string;
} catch ( ClassCastException e ) {
throw new KettleValueException( toString() + " : There was a data type error: the data type of "
+ object.getClass().getName() + " object [" + object + "] does not correspond to value meta ["
+ toStringMeta() + "]" );
}
}
protected String trim( String string ) {
switch ( getTrimType() ) {
case TRIM_TYPE_NONE:
break;
case TRIM_TYPE_RIGHT:
string = Const.rtrim( string );
break;
case TRIM_TYPE_LEFT:
string = Const.ltrim( string );
break;
case TRIM_TYPE_BOTH:
string = Const.trim( string );
break;
default:
break;
}
return string;
}
@Override
public Double getNumber( Object object ) throws KettleValueException {
try {
if ( isNull( object ) ) {
return null;
}
switch ( type ) {
case TYPE_NUMBER:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return (Double) object;
case STORAGE_TYPE_BINARY_STRING:
return (Double) convertBinaryStringToNativeType( (byte[]) object );
case STORAGE_TYPE_INDEXED:
return (Double) index[( (Integer) object ).intValue()];
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_STRING:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertStringToNumber( (String) object );
case STORAGE_TYPE_BINARY_STRING:
return convertStringToNumber( (String) convertBinaryStringToNativeType( (byte[]) object ) );
case STORAGE_TYPE_INDEXED:
return convertStringToNumber( (String) index[( (Integer) object ).intValue()] );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_DATE:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertDateToNumber( (Date) object );
case STORAGE_TYPE_BINARY_STRING:
return convertDateToNumber( (Date) convertBinaryStringToNativeType( (byte[]) object ) );
case STORAGE_TYPE_INDEXED:
return new Double( ( (Date) index[( (Integer) object ).intValue()] ).getTime() );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_INTEGER:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return new Double( ( (Long) object ).doubleValue() );
case STORAGE_TYPE_BINARY_STRING:
return new Double( ( (Long) convertBinaryStringToNativeType( (byte[]) object ) ).doubleValue() );
case STORAGE_TYPE_INDEXED:
return new Double( ( (Long) index[( (Integer) object ).intValue()] ).doubleValue() );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_BIGNUMBER:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return new Double( ( (BigDecimal) object ).doubleValue() );
case STORAGE_TYPE_BINARY_STRING:
return new Double( ( (BigDecimal) convertBinaryStringToNativeType( (byte[]) object ) ).doubleValue() );
case STORAGE_TYPE_INDEXED:
return new Double( ( (BigDecimal) index[( (Integer) object ).intValue()] ).doubleValue() );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_BOOLEAN:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertBooleanToNumber( (Boolean) object );
case STORAGE_TYPE_BINARY_STRING:
return convertBooleanToNumber( (Boolean) convertBinaryStringToNativeType( (byte[]) object ) );
case STORAGE_TYPE_INDEXED:
return convertBooleanToNumber( (Boolean) index[( (Integer) object ).intValue()] );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_BINARY:
throw new KettleValueException( toString() + " : I don't know how to convert binary values to numbers." );
case TYPE_SERIALIZABLE:
throw new KettleValueException( toString() + " : I don't know how to convert serializable values to numbers." );
default:
throw new KettleValueException( toString() + " : Unknown type " + type + " specified." );
}
} catch ( Exception e ) {
throw new KettleValueException( "Unexpected conversion error while converting value [" + toString()
+ "] to a Number", e );
}
}
@Override
public Long getInteger( Object object ) throws KettleValueException {
try {
if ( isNull( object ) ) {
return null;
}
switch ( type ) {
case TYPE_INTEGER:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return (Long) object;
case STORAGE_TYPE_BINARY_STRING:
return (Long) convertBinaryStringToNativeType( (byte[]) object );
case STORAGE_TYPE_INDEXED:
return (Long) index[( (Integer) object ).intValue()];
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_STRING:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertStringToInteger( (String) object );
case STORAGE_TYPE_BINARY_STRING:
return convertStringToInteger( (String) convertBinaryStringToNativeType( (byte[]) object ) );
case STORAGE_TYPE_INDEXED:
return convertStringToInteger( (String) index[( (Integer) object ).intValue()] );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_NUMBER:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return new Long( Math.round( ( (Double) object ).doubleValue() ) );
case STORAGE_TYPE_BINARY_STRING:
return new Long( Math.round( ( (Double) convertBinaryStringToNativeType( (byte[]) object ) )
.doubleValue() ) );
case STORAGE_TYPE_INDEXED:
return new Long( Math.round( ( (Double) index[( (Integer) object ).intValue()] ).doubleValue() ) );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_DATE:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertDateToInteger( (Date) object );
case STORAGE_TYPE_BINARY_STRING:
return new Long( ( (Date) convertBinaryStringToNativeType( (byte[]) object ) ).getTime() );
case STORAGE_TYPE_INDEXED:
return convertDateToInteger( (Date) index[( (Integer) object ).intValue()] );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_BIGNUMBER:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return new Long( ( (BigDecimal) object ).longValue() );
case STORAGE_TYPE_BINARY_STRING:
return new Long( ( (BigDecimal) convertBinaryStringToNativeType( (byte[]) object ) ).longValue() );
case STORAGE_TYPE_INDEXED:
return new Long( ( (BigDecimal) index[( (Integer) object ).intValue()] ).longValue() );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_BOOLEAN:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertBooleanToInteger( (Boolean) object );
case STORAGE_TYPE_BINARY_STRING:
return convertBooleanToInteger( (Boolean) convertBinaryStringToNativeType( (byte[]) object ) );
case STORAGE_TYPE_INDEXED:
return convertBooleanToInteger( (Boolean) index[( (Integer) object ).intValue()] );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_BINARY:
throw new KettleValueException( toString() + " : I don't know how to convert binary values to integers." );
case TYPE_SERIALIZABLE:
throw new KettleValueException( toString()
+ " : I don't know how to convert serializable values to integers." );
default:
throw new KettleValueException( toString() + " : Unknown type " + type + " specified." );
}
} catch ( Exception e ) {
throw new KettleValueException( "Unexpected conversion error while converting value [" + toString()
+ "] to an Integer", e );
}
}
@Override
public BigDecimal getBigNumber( Object object ) throws KettleValueException {
try {
if ( isNull( object ) ) {
return null;
}
switch ( type ) {
case TYPE_BIGNUMBER:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return (BigDecimal) object;
case STORAGE_TYPE_BINARY_STRING:
return (BigDecimal) convertBinaryStringToNativeType( (byte[]) object );
case STORAGE_TYPE_INDEXED:
return (BigDecimal) index[( (Integer) object ).intValue()];
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_STRING:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertStringToBigNumber( (String) object );
case STORAGE_TYPE_BINARY_STRING:
return convertStringToBigNumber( (String) convertBinaryStringToNativeType( (byte[]) object ) );
case STORAGE_TYPE_INDEXED:
return convertStringToBigNumber( (String) index[( (Integer) object ).intValue()] );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_INTEGER:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return BigDecimal.valueOf( ( (Long) object ).longValue() );
case STORAGE_TYPE_BINARY_STRING:
return BigDecimal.valueOf( ( (Long) convertBinaryStringToNativeType( (byte[]) object ) ).longValue() );
case STORAGE_TYPE_INDEXED:
return BigDecimal.valueOf( ( (Long) index[( (Integer) object ).intValue()] ).longValue() );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_NUMBER:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return BigDecimal.valueOf( ( (Double) object ).doubleValue() );
case STORAGE_TYPE_BINARY_STRING:
return BigDecimal.valueOf( ( (Double) convertBinaryStringToNativeType( (byte[]) object ) ).doubleValue() );
case STORAGE_TYPE_INDEXED:
return BigDecimal.valueOf( ( (Double) index[( (Integer) object ).intValue()] ).doubleValue() );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_DATE:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertDateToBigNumber( (Date) object );
case STORAGE_TYPE_BINARY_STRING:
return convertDateToBigNumber( (Date) convertBinaryStringToNativeType( (byte[]) object ) );
case STORAGE_TYPE_INDEXED:
return convertDateToBigNumber( (Date) index[( (Integer) object ).intValue()] );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_BOOLEAN:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertBooleanToBigNumber( (Boolean) object );
case STORAGE_TYPE_BINARY_STRING:
return convertBooleanToBigNumber( (Boolean) convertBinaryStringToNativeType( (byte[]) object ) );
case STORAGE_TYPE_INDEXED:
return convertBooleanToBigNumber( (Boolean) index[( (Integer) object ).intValue()] );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_BINARY:
throw new KettleValueException( toString() + " : I don't know how to convert binary values to BigDecimals." );
case TYPE_SERIALIZABLE:
throw new KettleValueException( toString() + " : I don't know how to convert serializable values to BigDecimals." );
default:
throw new KettleValueException( toString() + " : Unknown type " + type + " specified." );
}
} catch ( Exception e ) {
throw new KettleValueException( "Unexpected conversion error while converting value [" + toString()
+ "] to a BigNumber", e );
}
}
@Override
public Boolean getBoolean( Object object ) throws KettleValueException {
if ( object == null ) {
return null;
}
switch ( type ) {
case TYPE_BOOLEAN:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return (Boolean) object;
case STORAGE_TYPE_BINARY_STRING:
return (Boolean) convertBinaryStringToNativeType( (byte[]) object );
case STORAGE_TYPE_INDEXED:
return (Boolean) index[( (Integer) object ).intValue()];
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_STRING:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertStringToBoolean( trim( (String) object ) );
case STORAGE_TYPE_BINARY_STRING:
return convertStringToBoolean( trim( (String) convertBinaryStringToNativeType( (byte[]) object ) ) );
case STORAGE_TYPE_INDEXED:
return convertStringToBoolean( trim( (String) index[( (Integer) object ).intValue()] ) );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_INTEGER:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertIntegerToBoolean( (Long) object );
case STORAGE_TYPE_BINARY_STRING:
return convertIntegerToBoolean( (Long) convertBinaryStringToNativeType( (byte[]) object ) );
case STORAGE_TYPE_INDEXED:
return convertIntegerToBoolean( (Long) index[( (Integer) object ).intValue()] );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_NUMBER:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertNumberToBoolean( (Double) object );
case STORAGE_TYPE_BINARY_STRING:
return convertNumberToBoolean( (Double) convertBinaryStringToNativeType( (byte[]) object ) );
case STORAGE_TYPE_INDEXED:
return convertNumberToBoolean( (Double) index[( (Integer) object ).intValue()] );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_BIGNUMBER:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertBigNumberToBoolean( (BigDecimal) object );
case STORAGE_TYPE_BINARY_STRING:
return convertBigNumberToBoolean( (BigDecimal) convertBinaryStringToNativeType( (byte[]) object ) );
case STORAGE_TYPE_INDEXED:
return convertBigNumberToBoolean( (BigDecimal) index[( (Integer) object ).intValue()] );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_DATE:
throw new KettleValueException( toString() + " : I don't know how to convert date values to booleans." );
case TYPE_BINARY:
throw new KettleValueException( toString() + " : I don't know how to convert binary values to booleans." );
case TYPE_SERIALIZABLE:
throw new KettleValueException( toString() + " : I don't know how to convert serializable values to booleans." );
default:
throw new KettleValueException( toString() + " : Unknown type " + type + " specified." );
}
}
@Override
public Date getDate( Object object ) throws KettleValueException {
if ( isNull( object ) ) {
return null;
}
switch ( type ) {
case TYPE_DATE:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return (Date) object;
case STORAGE_TYPE_BINARY_STRING:
return (Date) convertBinaryStringToNativeType( (byte[]) object );
case STORAGE_TYPE_INDEXED:
return (Date) index[( (Integer) object ).intValue()];
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_STRING:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertStringToDate( (String) object );
case STORAGE_TYPE_BINARY_STRING:
return convertStringToDate( (String) convertBinaryStringToNativeType( (byte[]) object ) );
case STORAGE_TYPE_INDEXED:
return convertStringToDate( (String) index[( (Integer) object ).intValue()] );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_NUMBER:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertNumberToDate( (Double) object );
case STORAGE_TYPE_BINARY_STRING:
return convertNumberToDate( (Double) convertBinaryStringToNativeType( (byte[]) object ) );
case STORAGE_TYPE_INDEXED:
return convertNumberToDate( (Double) index[( (Integer) object ).intValue()] );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_INTEGER:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertIntegerToDate( (Long) object );
case STORAGE_TYPE_BINARY_STRING:
return convertIntegerToDate( (Long) convertBinaryStringToNativeType( (byte[]) object ) );
case STORAGE_TYPE_INDEXED:
return convertIntegerToDate( (Long) index[( (Integer) object ).intValue()] );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_BIGNUMBER:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertBigNumberToDate( (BigDecimal) object );
case STORAGE_TYPE_BINARY_STRING:
return convertBigNumberToDate( (BigDecimal) convertBinaryStringToNativeType( (byte[]) object ) );
case STORAGE_TYPE_INDEXED:
return convertBigNumberToDate( (BigDecimal) index[( (Integer) object ).intValue()] );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_BOOLEAN:
throw new KettleValueException( toString() + " : I don't know how to convert a boolean to a date." );
case TYPE_BINARY:
throw new KettleValueException( toString() + " : I don't know how to convert a binary value to date." );
case TYPE_SERIALIZABLE:
throw new KettleValueException( toString() + " : I don't know how to convert a serializable value to date." );
default:
throw new KettleValueException( toString() + " : Unknown type " + type + " specified." );
}
}
@Override
public byte[] getBinary( Object object ) throws KettleValueException {
if ( object == null ) {
return null;
}
switch ( type ) {
case TYPE_BINARY:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return (byte[]) object;
case STORAGE_TYPE_BINARY_STRING:
return (byte[]) object;
case STORAGE_TYPE_INDEXED:
return (byte[]) index[( (Integer) object ).intValue()];
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_DATE:
throw new KettleValueException( toString() + " : I don't know how to convert a date to binary." );
case TYPE_STRING:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertStringToBinaryString( (String) object );
case STORAGE_TYPE_BINARY_STRING:
return (byte[]) object;
case STORAGE_TYPE_INDEXED:
return convertStringToBinaryString( (String) index[( (Integer) object ).intValue()] );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_NUMBER:
throw new KettleValueException( toString() + " : I don't know how to convert a number to binary." );
case TYPE_INTEGER:
throw new KettleValueException( toString() + " : I don't know how to convert an integer to binary." );
case TYPE_BIGNUMBER:
throw new KettleValueException( toString() + " : I don't know how to convert a bignumber to binary." );
case TYPE_BOOLEAN:
throw new KettleValueException( toString() + " : I don't know how to convert a boolean to binary." );
case TYPE_SERIALIZABLE:
throw new KettleValueException( toString() + " : I don't know how to convert a serializable to binary." );
default:
throw new KettleValueException( toString() + " : Unknown type " + type + " specified." );
}
}
@Override
public byte[] getBinaryString( Object object ) throws KettleValueException {
// If the input is a binary string, we should return the exact same binary
// object IF
// and only IF the formatting options for the storage metadata and this
// object are the same.
//
if ( isStorageBinaryString() && identicalFormat ) {
return (byte[]) object; // shortcut it directly for better performance.
}
try {
if ( object == null ) {
return null;
}
switch ( type ) {
case TYPE_STRING:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertStringToBinaryString( (String) object );
case STORAGE_TYPE_BINARY_STRING:
return convertStringToBinaryString( (String) convertBinaryStringToNativeType( (byte[]) object ) );
case STORAGE_TYPE_INDEXED:
return convertStringToBinaryString( (String) index[( (Integer) object ).intValue()] );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_DATE:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertStringToBinaryString( convertDateToString( (Date) object ) );
case STORAGE_TYPE_BINARY_STRING:
String string = convertDateToString( (Date) convertBinaryStringToNativeType( (byte[]) object ) );
return convertStringToBinaryString( string );
case STORAGE_TYPE_INDEXED:
return convertStringToBinaryString( convertDateToString( (Date) index[( (Integer) object ).intValue()] ) );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_NUMBER:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertStringToBinaryString( convertNumberToString( (Double) object ) );
case STORAGE_TYPE_BINARY_STRING:
String string = convertNumberToString( (Double) convertBinaryStringToNativeType( (byte[]) object ) );
return convertStringToBinaryString( string );
case STORAGE_TYPE_INDEXED:
return convertStringToBinaryString( convertNumberToString( (Double) index[( (Integer) object ).intValue()] ) );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_INTEGER:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertStringToBinaryString( convertIntegerToString( (Long) object ) );
case STORAGE_TYPE_BINARY_STRING:
String string = convertIntegerToString( (Long) convertBinaryStringToNativeType( (byte[]) object ) );
return convertStringToBinaryString( string );
case STORAGE_TYPE_INDEXED:
return convertStringToBinaryString( convertIntegerToString( (Long) index[( (Integer) object ).intValue()] ) );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_BIGNUMBER:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertStringToBinaryString( convertBigNumberToString( (BigDecimal) object ) );
case STORAGE_TYPE_BINARY_STRING:
String string =
convertBigNumberToString( (BigDecimal) convertBinaryStringToNativeType( (byte[]) object ) );
return convertStringToBinaryString( string );
case STORAGE_TYPE_INDEXED:
return convertStringToBinaryString( convertBigNumberToString( (BigDecimal) index[( (Integer) object )
.intValue()] ) );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_BOOLEAN:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertStringToBinaryString( convertBooleanToString( (Boolean) object ) );
case STORAGE_TYPE_BINARY_STRING:
String string = convertBooleanToString( (Boolean) convertBinaryStringToNativeType( (byte[]) object ) );
return convertStringToBinaryString( string );
case STORAGE_TYPE_INDEXED:
return convertStringToBinaryString( convertBooleanToString( (Boolean) index[( (Integer) object )
.intValue()] ) );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_BINARY:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return (byte[]) object;
case STORAGE_TYPE_BINARY_STRING:
return (byte[]) object;
case STORAGE_TYPE_INDEXED:
return (byte[]) index[( (Integer) object ).intValue()];
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_SERIALIZABLE:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertStringToBinaryString( object.toString() );
case STORAGE_TYPE_BINARY_STRING:
return (byte[]) object;
case STORAGE_TYPE_INDEXED:
return convertStringToBinaryString( index[( (Integer) object ).intValue()].toString() );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
default:
throw new KettleValueException( toString() + " : Unknown type " + type + " specified." );
}
} catch ( ClassCastException e ) {
throw new KettleValueException( toString() + " : There was a data type error: the data type of "
+ object.getClass().getName() + " object [" + object + "] does not correspond to value meta ["
+ toStringMeta() + "]" );
}
}
/**
* Checks whether or not the value is a String.
*
* @return true if the value is a String.
*/
@Override
public boolean isString() {
return type == TYPE_STRING;
}
/**
* Checks whether or not this value is a Date
*
* @return true if the value is a Date
*/
@Override
public boolean isDate() {
return type == TYPE_DATE || type == TYPE_TIMESTAMP;
}
/**
* Checks whether or not the value is a Big Number
*
* @return true is this value is a big number
*/
@Override
public boolean isBigNumber() {
return type == TYPE_BIGNUMBER;
}
/**
* Checks whether or not the value is a Number
*
* @return true is this value is a number
*/
@Override
public boolean isNumber() {
return type == TYPE_NUMBER;
}
/**
* Checks whether or not this value is a boolean
*
* @return true if this value has type boolean.
*/
@Override
public boolean isBoolean() {
return type == TYPE_BOOLEAN;
}
/**
* Checks whether or not this value is of type Serializable
*
* @return true if this value has type Serializable
*/
@Override
public boolean isSerializableType() {
return type == TYPE_SERIALIZABLE;
}
/**
* Checks whether or not this value is of type Binary
*
* @return true if this value has type Binary
*/
@Override
public boolean isBinary() {
return type == TYPE_BINARY;
}
/**
* Checks whether or not this value is an Integer
*
* @return true if this value is an integer
*/
@Override
public boolean isInteger() {
return type == TYPE_INTEGER;
}
/**
* Checks whether or not this Value is Numeric A Value is numeric if it is either of type Number or Integer
*
* @return true if the value is either of type Number or Integer
*/
@Override
public boolean isNumeric() {
return isInteger() || isNumber() || isBigNumber();
}
/**
* Checks whether or not the specified type is either Integer or Number
*
* @param t
* the type to check
* @return true if the type is Integer or Number
*/
public static final boolean isNumeric( int t ) {
return t == TYPE_INTEGER || t == TYPE_NUMBER || t == TYPE_BIGNUMBER;
}
public boolean isSortedAscending() {
return !isSortedDescending();
}
/**
* Return the type of a value in a textual form: "String", "Number", "Integer", "Boolean", "Date", ...
*
* @return A String describing the type of value.
*/
@Override
public String getTypeDesc() {
return getTypeDesc( type );
}
/**
* Return the storage type of a value in a textual form: "normal", "binary-string", "indexes"
*
* @return A String describing the storage type of the value metadata
*/
public String getStorageTypeDesc() {
return storageTypeCodes[storageType];
}
@Override
public String toString() {
return name + " " + toStringMeta();
}
/**
* a String text representation of this Value, optionally padded to the specified length
*
* @return a String text representation of this Value, optionally padded to the specified length
*/
@Override
public String toStringMeta() {
// We (Sven Boden) did explicit performance testing for this
// part. The original version used Strings instead of StringBuilders,
// performance between the 2 does not differ that much. A few milliseconds
// on 100000 iterations in the advantage of StringBuilders. The
// lessened creation of objects may be worth it in the long run.
StringBuilder retval = new StringBuilder( getTypeDesc() );
switch ( getType() ) {
case TYPE_STRING:
if ( getLength() > 0 ) {
retval.append( '(' ).append( getLength() ).append( ')' );
}
break;
case TYPE_NUMBER:
case TYPE_BIGNUMBER:
if ( getLength() > 0 ) {
retval.append( '(' ).append( getLength() );
if ( getPrecision() > 0 ) {
retval.append( ", " ).append( getPrecision() );
}
retval.append( ')' );
}
break;
case TYPE_INTEGER:
if ( getLength() > 0 ) {
retval.append( '(' ).append( getLength() ).append( ')' );
}
break;
default:
break;
}
if ( !isStorageNormal() ) {
retval.append( "<" ).append( getStorageTypeDesc() ).append( ">" );
}
return retval.toString();
}
@Override
public void writeData( DataOutputStream outputStream, Object object ) throws KettleFileException {
try {
// Is the value NULL?
outputStream.writeBoolean( object == null );
if ( object != null ) {
// otherwise there is no point
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
// Handle Content -- only when not NULL
switch ( getType() ) {
case TYPE_STRING:
writeString( outputStream, (String) object );
break;
case TYPE_NUMBER:
writeNumber( outputStream, (Double) object );
break;
case TYPE_INTEGER:
writeInteger( outputStream, (Long) object );
break;
case TYPE_DATE:
writeDate( outputStream, (Date) object );
break;
case TYPE_BIGNUMBER:
writeBigNumber( outputStream, (BigDecimal) object );
break;
case TYPE_BOOLEAN:
writeBoolean( outputStream, (Boolean) object );
break;
case TYPE_BINARY:
writeBinary( outputStream, (byte[]) object );
break;
case TYPE_INET:
writeBinary( outputStream, ( (InetAddress) object ).getAddress() );
break;
default:
throw new KettleFileException( toString() + " : Unable to serialize data type " + getType() );
}
break;
case STORAGE_TYPE_BINARY_STRING:
// Handle binary string content -- only when not NULL
// In this case, we opt not to convert anything at all for speed.
// That way, we can save on CPU power.
// Since the streams can be compressed, volume shouldn't be an issue
// at all.
//
writeBinaryString( outputStream, (byte[]) object );
break;
case STORAGE_TYPE_INDEXED:
writeInteger( outputStream, (Integer) object ); // just an index
break;
default:
throw new KettleFileException( toString() + " : Unknown storage type " + getStorageType() );
}
}
} catch ( ClassCastException e ) {
throw new RuntimeException( toString() + " : There was a data type error: the data type of "
+ object.getClass().getName() + " object [" + object + "] does not correspond to value meta ["
+ toStringMeta() + "]" );
} catch ( IOException e ) {
throw new KettleFileException( toString() + " : Unable to write value data to output stream", e );
}
}
@Override
public Object readData( DataInputStream inputStream ) throws KettleFileException, KettleEOFException,
SocketTimeoutException {
try {
// Is the value NULL?
if ( inputStream.readBoolean() ) {
return null; // done
}
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
// Handle Content -- only when not NULL
switch ( getType() ) {
case TYPE_STRING:
return readString( inputStream );
case TYPE_NUMBER:
return readNumber( inputStream );
case TYPE_INTEGER:
return readInteger( inputStream );
case TYPE_DATE:
return readDate( inputStream );
case TYPE_BIGNUMBER:
return readBigNumber( inputStream );
case TYPE_BOOLEAN:
return readBoolean( inputStream );
case TYPE_BINARY:
return readBinary( inputStream );
case TYPE_INET:
return InetAddress.getByAddress( readBinary( inputStream ) );
default:
throw new KettleFileException( toString() + " : Unable to de-serialize data of type " + getType() );
}
case STORAGE_TYPE_BINARY_STRING:
return readBinaryString( inputStream );
case STORAGE_TYPE_INDEXED:
return readSmallInteger( inputStream ); // just an index: 4-bytes should
// be enough.
default:
throw new KettleFileException( toString() + " : Unknown storage type " + getStorageType() );
}
} catch ( EOFException e ) {
throw new KettleEOFException( e );
} catch ( SocketTimeoutException e ) {
throw e;
} catch ( IOException e ) {
throw new KettleFileException( toString() + " : Unable to read value data from input stream", e );
}
}
protected void writeString( DataOutputStream outputStream, String string ) throws IOException {
// Write the length and then the bytes
if ( string == null ) {
outputStream.writeInt( -1 );
} else {
byte[] chars = string.getBytes( Const.XML_ENCODING );
outputStream.writeInt( chars.length );
outputStream.write( chars );
}
}
protected void writeBinaryString( DataOutputStream outputStream, byte[] binaryString ) throws IOException {
// Write the length and then the bytes
if ( binaryString == null ) {
outputStream.writeInt( -1 );
} else {
outputStream.writeInt( binaryString.length );
outputStream.write( binaryString );
}
}
protected String readString( DataInputStream inputStream ) throws IOException {
// Read the length and then the bytes
int length = inputStream.readInt();
if ( length < 0 ) {
return null;
}
byte[] chars = new byte[length];
inputStream.readFully( chars );
String string = new String( chars, Const.XML_ENCODING );
// System.out.println("Read string("+getName()+"), length "+length+": "+string);
return string;
}
protected byte[] readBinaryString( DataInputStream inputStream ) throws IOException {
// Read the length and then the bytes
int length = inputStream.readInt();
if ( length < 0 ) {
return null;
}
byte[] chars = new byte[length];
inputStream.readFully( chars );
return chars;
}
protected void writeBigNumber( DataOutputStream outputStream, BigDecimal number ) throws IOException {
String string = number.toString();
writeString( outputStream, string );
}
protected BigDecimal readBigNumber( DataInputStream inputStream ) throws IOException {
String string = readString( inputStream );
// System.out.println("Read big number("+getName()+") ["+string+"]");
return new BigDecimal( string );
}
protected void writeDate( DataOutputStream outputStream, Date date ) throws IOException {
outputStream.writeLong( date.getTime() );
}
protected Date readDate( DataInputStream inputStream ) throws IOException {
long time = inputStream.readLong();
// System.out.println("Read Date("+getName()+") ["+new Date(time)+"]");
return new Date( time );
}
protected void writeBoolean( DataOutputStream outputStream, Boolean bool ) throws IOException {
outputStream.writeBoolean( bool.booleanValue() );
}
protected Boolean readBoolean( DataInputStream inputStream ) throws IOException {
Boolean bool = Boolean.valueOf( inputStream.readBoolean() );
// System.out.println("Read boolean("+getName()+") ["+bool+"]");
return bool;
}
protected void writeNumber( DataOutputStream outputStream, Double number ) throws IOException {
outputStream.writeDouble( number.doubleValue() );
}
protected Double readNumber( DataInputStream inputStream ) throws IOException {
Double d = new Double( inputStream.readDouble() );
// System.out.println("Read number("+getName()+") ["+d+"]");
return d;
}
protected void writeInteger( DataOutputStream outputStream, Long number ) throws IOException {
outputStream.writeLong( number.longValue() );
}
protected Long readInteger( DataInputStream inputStream ) throws IOException {
Long l = new Long( inputStream.readLong() );
// System.out.println("Read integer("+getName()+") ["+l+"]");
return l;
}
protected void writeInteger( DataOutputStream outputStream, Integer number ) throws IOException {
outputStream.writeInt( number.intValue() );
}
protected Integer readSmallInteger( DataInputStream inputStream ) throws IOException {
Integer i = Integer.valueOf( inputStream.readInt() );
// System.out.println("Read index integer("+getName()+") ["+i+"]");
return i;
}
protected void writeBinary( DataOutputStream outputStream, byte[] binary ) throws IOException {
outputStream.writeInt( binary.length );
outputStream.write( binary );
}
protected byte[] readBinary( DataInputStream inputStream ) throws IOException {
int size = inputStream.readInt();
byte[] buffer = new byte[size];
inputStream.readFully( buffer );
// System.out.println("Read binary("+getName()+") with size="+size);
return buffer;
}
@Override
public void writeMeta( DataOutputStream outputStream ) throws KettleFileException {
try {
int type = getType();
// Handle type
outputStream.writeInt( type );
// Handle storage type
outputStream.writeInt( storageType );
switch ( storageType ) {
case STORAGE_TYPE_INDEXED:
// Save the indexed strings...
if ( index == null ) {
outputStream.writeInt( -1 ); // null
} else {
outputStream.writeInt( index.length );
for ( int i = 0; i < index.length; i++ ) {
try {
switch ( type ) {
case TYPE_STRING:
writeString( outputStream, (String) index[i] );
break;
case TYPE_NUMBER:
writeNumber( outputStream, (Double) index[i] );
break;
case TYPE_INTEGER:
writeInteger( outputStream, (Long) index[i] );
break;
case TYPE_DATE:
writeDate( outputStream, (Date) index[i] );
break;
case TYPE_BIGNUMBER:
writeBigNumber( outputStream, (BigDecimal) index[i] );
break;
case TYPE_BOOLEAN:
writeBoolean( outputStream, (Boolean) index[i] );
break;
case TYPE_BINARY:
writeBinary( outputStream, (byte[]) index[i] );
break;
default:
throw new KettleFileException( toString()
+ " : Unable to serialize indexe storage type for data type " + getType() );
}
} catch ( ClassCastException e ) {
throw new RuntimeException( toString() + " : There was a data type error: the data type of "
+ index[i].getClass().getName() + " object [" + index[i] + "] does not correspond to value meta ["
+ toStringMeta() + "]" );
}
}
}
break;
case STORAGE_TYPE_BINARY_STRING:
// Save the storage meta data...
//
outputStream.writeBoolean( storageMetadata != null );
if ( storageMetadata != null ) {
storageMetadata.writeMeta( outputStream );
}
break;
default:
break;
}
// Handle name-length
writeString( outputStream, name );
// length & precision
outputStream.writeInt( getLength() );
outputStream.writeInt( getPrecision() );
// Origin
writeString( outputStream, origin );
// Comments
writeString( outputStream, comments );
// formatting Mask, decimal, grouping, currency
writeString( outputStream, conversionMask );
writeString( outputStream, decimalSymbol );
writeString( outputStream, groupingSymbol );
writeString( outputStream, currencySymbol );
outputStream.writeInt( trimType );
// Case sensitivity of compare
outputStream.writeBoolean( caseInsensitive );
// Collator Locale
writeString( outputStream, collatorLocale.toLanguageTag() );
// Collator Disabled of compare
outputStream.writeBoolean( collatorDisabled );
// Collator strength of compare
outputStream.writeInt( collatorStrength );
// Sorting information
outputStream.writeBoolean( sortedDescending );
// Padding information
outputStream.writeBoolean( outputPaddingEnabled );
// date format lenient?
outputStream.writeBoolean( dateFormatLenient );
// date format locale?
writeString( outputStream, dateFormatLocale != null ? dateFormatLocale.toString() : null );
// date time zone?
writeString( outputStream, dateFormatTimeZone != null ? dateFormatTimeZone.getID() : null );
// string to number conversion lenient?
outputStream.writeBoolean( lenientStringToNumber );
} catch ( IOException e ) {
throw new KettleFileException( toString() + " : Unable to write value metadata to output stream", e );
}
}
/**
* Load the attributes of this particular value meta object from the input stream. Loading the type is not handled
* here, this should be read from the stream previously!
*
* @param inputStream
* the input stream to read from
* @throws KettleFileException
* In case there was a IO problem
* @throws KettleEOFException
* If we reached the end of the stream
*/
@Override
public void readMetaData( DataInputStream inputStream ) throws KettleFileException, KettleEOFException {
// Loading the type is not handled here, this should be read from the stream previously!
//
try {
// Handle storage type
storageType = inputStream.readInt();
// Read the data in the index
switch ( storageType ) {
case STORAGE_TYPE_INDEXED:
int indexSize = inputStream.readInt();
if ( indexSize < 0 ) {
index = null;
} else {
index = new Object[indexSize];
for ( int i = 0; i < indexSize; i++ ) {
switch ( type ) {
case TYPE_STRING:
index[i] = readString( inputStream );
break;
case TYPE_NUMBER:
index[i] = readNumber( inputStream );
break;
case TYPE_INTEGER:
index[i] = readInteger( inputStream );
break;
case TYPE_DATE:
index[i] = readDate( inputStream );
break;
case TYPE_BIGNUMBER:
index[i] = readBigNumber( inputStream );
break;
case TYPE_BOOLEAN:
index[i] = readBoolean( inputStream );
break;
case TYPE_BINARY:
index[i] = readBinary( inputStream );
break;
default:
throw new KettleFileException( toString()
+ " : Unable to de-serialize indexed storage type for data type " + getType() );
}
}
}
break;
case STORAGE_TYPE_BINARY_STRING:
// In case we do have storage metadata defined, we read that back in as
// well..
if ( inputStream.readBoolean() ) {
storageMetadata = new ValueMetaBase( inputStream );
}
break;
default:
break;
}
// name
name = readString( inputStream );
// length & precision
length = inputStream.readInt();
precision = inputStream.readInt();
// Origin
origin = readString( inputStream );
// Comments
comments = readString( inputStream );
// formatting Mask, decimal, grouping, currency
conversionMask = readString( inputStream );
decimalSymbol = readString( inputStream );
groupingSymbol = readString( inputStream );
currencySymbol = readString( inputStream );
trimType = inputStream.readInt();
// Case sensitivity
caseInsensitive = inputStream.readBoolean();
// Collator locale
setCollatorLocale( Locale.forLanguageTag( readString( inputStream ) ) );
// Collator disabled
collatorDisabled = inputStream.readBoolean();
// Collator strength
collatorStrength = inputStream.readInt();
// Sorting type
sortedDescending = inputStream.readBoolean();
// Output padding?
outputPaddingEnabled = inputStream.readBoolean();
// is date parsing lenient?
//
dateFormatLenient = inputStream.readBoolean();
// What is the date format locale?
//
String strDateFormatLocale = readString( inputStream );
if ( Utils.isEmpty( strDateFormatLocale ) ) {
dateFormatLocale = null;
} else {
dateFormatLocale = EnvUtil.createLocale( strDateFormatLocale );
}
// What is the time zone to use for date parsing?
//
String strTimeZone = readString( inputStream );
if ( Utils.isEmpty( strTimeZone ) ) {
dateFormatTimeZone = TimeZone.getDefault();
} else {
dateFormatTimeZone = EnvUtil.createTimeZone( strTimeZone );
}
// is string to number parsing lenient?
lenientStringToNumber = inputStream.readBoolean();
} catch ( EOFException e ) {
throw new KettleEOFException( e );
} catch ( IOException e ) {
throw new KettleFileException( toString() + " : Unable to read value metadata from input stream", e );
}
}
@Override
public String getMetaXML() throws IOException {
StringBuilder xml = new StringBuilder();
xml.append( XMLHandler.openTag( XML_META_TAG ) );
xml.append( XMLHandler.addTagValue( "type", getTypeDesc() ) );
xml.append( XMLHandler.addTagValue( "storagetype", getStorageTypeCode( getStorageType() ) ) );
switch ( storageType ) {
case STORAGE_TYPE_INDEXED:
xml.append( XMLHandler.openTag( "index" ) );
// Save the indexed strings...
//
if ( index != null ) {
for ( int i = 0; i < index.length; i++ ) {
try {
switch ( type ) {
case TYPE_STRING:
xml.append( XMLHandler.addTagValue( "value", (String) index[i] ) );
break;
case TYPE_NUMBER:
xml.append( XMLHandler.addTagValue( "value", (Double) index[i] ) );
break;
case TYPE_INTEGER:
xml.append( XMLHandler.addTagValue( "value", (Long) index[i] ) );
break;
case TYPE_DATE:
xml.append( XMLHandler.addTagValue( "value", (Date) index[i] ) );
break;
case TYPE_BIGNUMBER:
xml.append( XMLHandler.addTagValue( "value", (BigDecimal) index[i] ) );
break;
case TYPE_BOOLEAN:
xml.append( XMLHandler.addTagValue( "value", (Boolean) index[i] ) );
break;
case TYPE_BINARY:
xml.append( XMLHandler.addTagValue( "value", (byte[]) index[i] ) );
break;
default:
throw new IOException( toString() + " : Unable to serialize index storage type to XML for data type "
+ getType() );
}
} catch ( ClassCastException e ) {
throw new RuntimeException( toString() + " : There was a data type error: the data type of "
+ index[i].getClass().getName() + " object [" + index[i] + "] does not correspond to value meta ["
+ toStringMeta() + "]" );
}
}
}
xml.append( XMLHandler.closeTag( "index" ) );
break;
case STORAGE_TYPE_BINARY_STRING:
// Save the storage meta data...
//
if ( storageMetadata != null ) {
xml.append( XMLHandler.openTag( "storage-meta" ) );
xml.append( storageMetadata.getMetaXML() );
xml.append( XMLHandler.closeTag( "storage-meta" ) );
}
break;
default:
break;
}
xml.append( XMLHandler.addTagValue( "name", name ) );
xml.append( XMLHandler.addTagValue( "length", length ) );
xml.append( XMLHandler.addTagValue( "precision", precision ) );
xml.append( XMLHandler.addTagValue( "origin", origin ) );
xml.append( XMLHandler.addTagValue( "comments", comments ) );
xml.append( XMLHandler.addTagValue( "conversion_Mask", conversionMask ) );
xml.append( XMLHandler.addTagValue( "decimal_symbol", decimalSymbol ) );
xml.append( XMLHandler.addTagValue( "grouping_symbol", groupingSymbol ) );
xml.append( XMLHandler.addTagValue( "currency_symbol", currencySymbol ) );
xml.append( XMLHandler.addTagValue( "trim_type", getTrimTypeCode( trimType ) ) );
xml.append( XMLHandler.addTagValue( "case_insensitive", caseInsensitive ) );
xml.append( XMLHandler.addTagValue( "collator_disabled", collatorDisabled ) );
xml.append( XMLHandler.addTagValue( "collator_strength", collatorStrength ) );
xml.append( XMLHandler.addTagValue( "sort_descending", sortedDescending ) );
xml.append( XMLHandler.addTagValue( "output_padding", outputPaddingEnabled ) );
xml.append( XMLHandler.addTagValue( "date_format_lenient", dateFormatLenient ) );
xml.append( XMLHandler.addTagValue( "date_format_locale", dateFormatLocale != null ? dateFormatLocale.toString()
: null ) );
xml.append( XMLHandler.addTagValue( "date_format_timezone", dateFormatTimeZone != null ? dateFormatTimeZone.getID()
: null ) );
xml.append( XMLHandler.addTagValue( "lenient_string_to_number", lenientStringToNumber ) );
xml.append( XMLHandler.closeTag( XML_META_TAG ) );
return xml.toString();
}
@Override
public String getDataXML( Object object ) throws IOException {
StringBuilder xml = new StringBuilder();
String string;
if ( object != null ) {
try {
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
// Handle Content -- only when not NULL
//
switch ( getType() ) {
case TYPE_STRING:
string = (String) object;
break;
case TYPE_NUMBER:
string = Double.toString( (Double) object );
break;
case TYPE_INTEGER:
string = Long.toString( (Long) object );
break;
case TYPE_DATE:
string = XMLHandler.date2string( (Date) object );
break;
case TYPE_BIGNUMBER:
string = ( (BigDecimal) object ).toString();
break;
case TYPE_BOOLEAN:
string = Boolean.toString( (Boolean) object );
break;
case TYPE_BINARY:
string = XMLHandler.encodeBinaryData( (byte[]) object );
break;
case TYPE_TIMESTAMP:
string = XMLHandler.timestamp2string( (Timestamp) object );
break;
case TYPE_INET:
string = ( (InetAddress) object ).toString();
break;
default:
throw new IOException( toString() + " : Unable to serialize data type to XML " + getType() );
}
break;
case STORAGE_TYPE_BINARY_STRING:
// Handle binary string content -- only when not NULL
// In this case, we opt not to convert anything at all for speed.
// That way, we can save on CPU power.
// Since the streams can be compressed, volume shouldn't be an issue
// at all.
//
string = XMLHandler.addTagValue( "binary-string", (byte[]) object );
xml.append( XMLHandler.openTag( XML_DATA_TAG ) ).append( string ).append( XMLHandler.closeTag( XML_DATA_TAG ) );
return xml.toString();
case STORAGE_TYPE_INDEXED:
// Just an index
string = XMLHandler.addTagValue( "index-value", (Integer) object );
break;
default:
throw new IOException( toString() + " : Unknown storage type " + getStorageType() );
}
} catch ( ClassCastException e ) {
throw new RuntimeException( toString() + " : There was a data type error: the data type of "
+ object.getClass().getName() + " object [" + object + "] does not correspond to value meta ["
+ toStringMeta() + "]", e );
} catch ( Exception e ) {
throw new RuntimeException( toString() + " : there was a value XML encoding error", e );
}
} else {
// If the object is null: give an empty string
//
string = "";
}
xml.append( XMLHandler.addTagValue( XML_DATA_TAG, string ) );
return xml.toString();
}
/**
* Convert a data XML node to an Object that corresponds to the metadata. This is basically String to Object
* conversion that is being done.
*
* @param node
* the node to retrieve the data value from
* @return the converted data value
* @throws IOException
* thrown in case there is a problem with the XML to object conversion
*/
@Override
public Object getValue( Node node ) throws KettleException {
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
String valueString = XMLHandler.getNodeValue( node );
if ( Utils.isEmpty( valueString ) ) {
return null;
}
// Handle Content -- only when not NULL
//
switch ( getType() ) {
case TYPE_STRING:
return valueString;
case TYPE_NUMBER:
return Double.parseDouble( valueString );
case TYPE_INTEGER:
return Long.parseLong( valueString );
case TYPE_DATE:
return XMLHandler.stringToDate( valueString );
case TYPE_BIGNUMBER:
return new BigDecimal( valueString );
case TYPE_BOOLEAN:
return Boolean.valueOf( "Y".equalsIgnoreCase( valueString ) );
case TYPE_BINARY:
return XMLHandler.stringToBinary( XMLHandler.getTagValue( node, "binary-value" ) );
default:
throw new KettleException( toString() + " : Unable to de-serialize '" + valueString
+ "' from XML for data type " + getType() );
}
case STORAGE_TYPE_BINARY_STRING:
// Handle binary string content -- only when not NULL
// In this case, we opt not to convert anything at all for speed.
// That way, we can save on CPU power.
// Since the streams can be compressed, volume shouldn't be an issue at
// all.
//
String binaryString = XMLHandler.getTagValue( node, "binary-string" );
if ( Utils.isEmpty( binaryString ) ) {
return null;
}
return XMLHandler.stringToBinary( binaryString );
case STORAGE_TYPE_INDEXED:
String indexString = XMLHandler.getTagValue( node, "index-value" );
if ( Utils.isEmpty( indexString ) ) {
return null;
}
return Integer.parseInt( indexString );
default:
throw new KettleException( toString() + " : Unknown storage type " + getStorageType() );
}
}
/**
* get an array of String describing the possible types a Value can have.
*
* @return an array of String describing the possible types a Value can have.
*/
public static final String[] getTypes() {
return ValueMetaFactory.getValueMetaNames();
/*
* String retval[] = new String[typeCodes.length - 1]; System.arraycopy(typeCodes, 1, retval, 0, typeCodes.length -
* 1); return retval;
*/
}
/**
* Get an array of String describing the possible types a Value can have.
*
* @return an array of String describing the possible types a Value can have.
*/
public static final String[] getAllTypes() {
return ValueMetaFactory.getAllValueMetaNames();
/*
* String retval[] = new String[typeCodes.length]; System.arraycopy(typeCodes, 0, retval, 0, typeCodes.length);
* return retval;
*/
}
/**
* TODO: change Desc to Code all over the place. Make sure we can localise this stuff later on.
*
* @param type
* the type
* @return the description (code) of the type
*/
public static final String getTypeDesc( int type ) {
return ValueMetaFactory.getValueMetaName( type );
// return typeCodes[type];
}
/**
* Convert the String description of a type to an integer type.
*
* @param desc
* The description of the type to convert
* @return The integer type of the given String. (ValueMetaInterface.TYPE_...)
*/
public static final int getType( String desc ) {
return ValueMetaFactory.getIdForValueMeta( desc );
/*
* for (int i = 1; i < typeCodes.length; i++) { if (typeCodes[i].equalsIgnoreCase(desc)) { return i; } }
*
* return TYPE_NONE;
*/
}
/**
* Convert the String description of a storage type to an integer type.
*
* @param desc
* The description of the storage type to convert
* @return The integer storage type of the given String. (ValueMetaInterface.STORAGE_TYPE_...) or -1 if the storage
* type code not be found.
*/
public static final int getStorageType( String desc ) {
for ( int i = 0; i < storageTypeCodes.length; i++ ) {
if ( storageTypeCodes[i].equalsIgnoreCase( desc ) ) {
return i;
}
}
return -1;
}
public static final String getStorageTypeCode( int storageType ) {
if ( storageType >= STORAGE_TYPE_NORMAL && storageType <= STORAGE_TYPE_INDEXED ) {
return storageTypeCodes[storageType];
}
return null;
}
/**
* Determine if an object is null. This is the case if data==null or if it's an empty string.
*
* @param data
* the object to test
* @return true if the object is considered null.
* @throws KettleValueException
* in case there is a conversion error (only thrown in case of lazy conversion)
*/
@Override
public boolean isNull( Object data ) throws KettleValueException {
//noinspection deprecation
return isNull( data, EMPTY_STRING_AND_NULL_ARE_DIFFERENT );
}
/*
* Do not use this method directly! It is for tests!
*/
@Deprecated
boolean isNull( Object data, boolean emptyStringDiffersFromNull ) throws KettleValueException {
try {
Object value = data;
if ( isStorageBinaryString() ) {
if ( value == null || !emptyStringDiffersFromNull && ( (byte[]) value ).length == 0 ) {
return true; // shortcut
}
value = convertBinaryStringToNativeType( (byte[]) data );
}
// Re-check for null, even for lazy conversion.
// A value (5 spaces for example) can be null after trim and conversion
//
if ( value == null ) {
return true;
}
if ( emptyStringDiffersFromNull ) {
return false;
}
// If it's a string and the string is empty, it's a null value as well
//
if ( isString() ) {
if ( value.toString().length() == 0 ) {
return true;
}
}
// We tried everything else so we assume this value is not null.
//
return false;
} catch ( ClassCastException e ) {
throw new RuntimeException( "Unable to verify if [" + toString() + "] is null or not because of an error:"
+ e.toString(), e );
}
}
/*
* Compare 2 binary strings, one byte at a time.<br> This algorithm is very fast but most likely wrong as well.<br>
*
* @param one The first binary string to compare with
*
* @param two the second binary string to compare to
*
* @return -1 if <i>one</i> is smaller than <i>two</i>, 0 is both byte arrays are identical and 1 if <i>one</i> is
* larger than <i>two</i> protected int compareBinaryStrings(byte[] one, byte[] two) {
*
* for (int i=0;i<one.length;i++) { if (i>=two.length) return 1; // larger if (one[i]>two[i]) return 1; // larger if
* (one[i]<two[i]) return -1; // smaller } if (one.length>two.length) return 1; // larger if (one.length>two.length)
* return -11; // smaller return 0; }
*/
/**
* Compare 2 values of the same data type
*
* @param data1
* the first value
* @param data2
* the second value
* @return 0 if the values are equal, -1 if data1 is smaller than data2 and +1 if it's larger.
* @throws KettleValueException
* In case we get conversion errors
*/
@Override
public int compare( Object data1, Object data2 ) throws KettleValueException {
boolean n1 = isNull( data1 );
boolean n2 = isNull( data2 );
if ( n1 && !n2 ) {
if ( isSortedDescending() ) {
// BACKLOG-14028
return 1;
} else {
return -1;
}
}
if ( !n1 && n2 ) {
if ( isSortedDescending() ) {
return -1;
} else {
return 1;
}
}
if ( n1 && n2 ) {
return 0;
}
int cmp = 0;
switch ( getType() ) {
case TYPE_STRING:
// if (isStorageBinaryString() && identicalFormat &&
// storageMetadata.isSingleByteEncoding()) return
// compareBinaryStrings((byte[])data1, (byte[])data2); TODO
String one = getString( data1 );
String two = getString( data2 );
if ( collatorDisabled ) {
if ( caseInsensitive ) {
cmp = one.compareToIgnoreCase( two );
} else {
cmp = one.compareTo( two );
}
} else {
cmp = collator.compare( one, two );
}
break;
case TYPE_INTEGER:
// if (isStorageBinaryString() && identicalFormat) return
// compareBinaryStrings((byte[])data1, (byte[])data2); TODO
cmp = getInteger( data1 ).compareTo( getInteger( data2 ) );
break;
case TYPE_NUMBER:
cmp = Double.compare( getNumber( data1 ).doubleValue(), getNumber( data2 ).doubleValue() );
break;
case TYPE_DATE:
cmp = Long.valueOf( getDate( data1 ).getTime() ).compareTo( Long.valueOf( getDate( data2 ).getTime() ) );
break;
case TYPE_BIGNUMBER:
cmp = getBigNumber( data1 ).compareTo( getBigNumber( data2 ) );
break;
case TYPE_BOOLEAN:
if ( getBoolean( data1 ).booleanValue() == getBoolean( data2 ).booleanValue() ) {
cmp = 0; // true == true, false == false
} else if ( getBoolean( data1 ).booleanValue() && !getBoolean( data2 ).booleanValue() ) {
cmp = 1; // true > false
} else {
cmp = -1; // false < true
}
break;
case TYPE_BINARY:
byte[] b1 = (byte[]) data1;
byte[] b2 = (byte[]) data2;
int length = b1.length < b2.length ? b1.length : b2.length;
for ( int i = 0; i < length; i++ ) {
cmp = b1[i] - b2[i];
if ( cmp != 0 ) {
cmp = cmp < 0 ? -1 : 1;
break;
}
}
cmp = b1.length - b2.length;
break;
default:
throw new KettleValueException( toString() + " : Comparing values can not be done with data type : "
+ getType() );
}
if ( isSortedDescending() ) {
return -cmp;
} else {
return cmp;
}
}
/**
* Compare 2 values of the same data type
*
* @param data1
* the first value
* @param meta2
* the second value's metadata
* @param data2
* the second value
* @return 0 if the values are equal, -1 if data1 is smaller than data2 and +1 if it's larger.
* @throws KettleValueException
* In case we get conversion errors
*/
@Override
public int compare( Object data1, ValueMetaInterface meta2, Object data2 ) throws KettleValueException {
if ( meta2 == null ) {
throw new KettleValueException( toStringMeta()
+ " : Second meta data (meta2) is null, please check one of the previous steps." );
}
try {
// Before we can compare data1 to data2 we need to make sure they have the
// same data type etc.
//
if ( getType() == meta2.getType() ) {
if ( getStorageType() == meta2.getStorageType() ) {
return compare( data1, data2 );
}
// Convert the storage type to compare the data.
//
switch ( getStorageType() ) {
case STORAGE_TYPE_NORMAL:
return compare( data1, meta2.convertToNormalStorageType( data2 ) );
case STORAGE_TYPE_BINARY_STRING:
return compare( data1, meta2.convertToBinaryStringStorageType( data2 ) );
case STORAGE_TYPE_INDEXED:
switch ( meta2.getStorageType() ) {
case STORAGE_TYPE_INDEXED:
return compare( data1, data2 ); // not accessible, just to make sure.
case STORAGE_TYPE_NORMAL:
return -meta2.compare( data2, convertToNormalStorageType( data1 ) );
case STORAGE_TYPE_BINARY_STRING:
return -meta2.compare( data2, convertToBinaryStringStorageType( data1 ) );
default:
throw new KettleValueException( meta2.toStringMeta() + " : Unknown storage type : "
+ meta2.getStorageType() );
}
default:
throw new KettleValueException( toStringMeta() + " : Unknown storage type : " + getStorageType() );
}
}
// If the data types are not the same, the first one is the driver...
// The second data type is converted to the first one.
//
return compare( data1, convertData( meta2, data2 ) );
} catch ( Exception e ) {
throw new KettleValueException(
toStringMeta() + " : Unable to compare with value [" + meta2.toStringMeta() + "]", e );
}
}
/**
* Convert the specified data to the data type specified in this object.
*
* @param meta2
* the metadata of the object to be converted
* @param data2
* the data of the object to be converted
* @return the object in the data type of this value metadata object
* @throws KettleValueException
* in case there is a data conversion error
*/
@Override
public Object convertData( ValueMetaInterface meta2, Object data2 ) throws KettleValueException {
switch ( getType() ) {
case TYPE_STRING:
return meta2.getString( data2 );
case TYPE_NUMBER:
return meta2.getNumber( data2 );
case TYPE_INTEGER:
return meta2.getInteger( data2 );
case TYPE_DATE:
return meta2.getDate( data2 );
case TYPE_BIGNUMBER:
return meta2.getBigNumber( data2 );
case TYPE_BOOLEAN:
return meta2.getBoolean( data2 );
case TYPE_BINARY:
return meta2.getBinary( data2 );
default:
throw new KettleValueException( toString() + " : I can't convert the specified value to data type : "
+ getType() );
}
}
/**
* Convert the specified data to the data type specified in this object. For String conversion, be compatible with
* version 2.5.2.
*
* @param meta2
* the metadata of the object to be converted
* @param data2
* the data of the object to be converted
* @return the object in the data type of this value metadata object
* @throws KettleValueException
* in case there is a data conversion error
*/
@Override
public Object convertDataCompatible( ValueMetaInterface meta2, Object data2 ) throws KettleValueException {
switch ( getType() ) {
case TYPE_STRING:
return meta2.getCompatibleString( data2 );
case TYPE_NUMBER:
return meta2.getNumber( data2 );
case TYPE_INTEGER:
return meta2.getInteger( data2 );
case TYPE_DATE:
return meta2.getDate( data2 );
case TYPE_BIGNUMBER:
return meta2.getBigNumber( data2 );
case TYPE_BOOLEAN:
return meta2.getBoolean( data2 );
case TYPE_BINARY:
return meta2.getBinary( data2 );
default:
throw new KettleValueException( toString() + " : I can't convert the specified value to data type : "
+ getType() );
}
}
/**
* Convert an object to the data type specified in the conversion metadata
*
* @param data
* The data
* @return The data converted to the storage data type
* @throws KettleValueException
* in case there is a conversion error.
*/
@Override
public Object convertDataUsingConversionMetaData( Object data ) throws KettleValueException {
if ( conversionMetadata == null ) {
throw new KettleValueException(
"API coding error: please specify the conversion metadata before attempting to convert value " + name );
}
// Suppose we have an Integer 123, length 5
// The string variation of this is " 00123"
// To convert this back to an Integer we use the storage metadata
// Specifically, in method convertStringToInteger() we consult the
// storageMetaData to get the correct conversion mask
// That way we're always sure that a conversion works both ways.
//
switch ( conversionMetadata.getType() ) {
case TYPE_STRING:
return getString( data );
case TYPE_INTEGER:
return getInteger( data );
case TYPE_NUMBER:
return getNumber( data );
case TYPE_DATE:
return getDate( data );
case TYPE_BIGNUMBER:
return getBigNumber( data );
case TYPE_BOOLEAN:
return getBoolean( data );
case TYPE_BINARY:
return getBinary( data );
default:
throw new KettleValueException( toString() + " : I can't convert the specified value to data type : "
+ conversionMetadata.getType() );
}
}
/**
* Convert the specified string to the data type specified in this object.
*
* @param pol
* the string to be converted
* @param convertMeta
* the metadata of the object (only string type) to be converted
* @param nullIf
* set the object to null if pos equals nullif (IgnoreCase)
* @param ifNull
* set the object to ifNull when pol is empty or null
* @param trim_type
* the trim type to be used (ValueMetaInterface.TRIM_TYPE_XXX)
* @return the object in the data type of this value metadata object
* @throws KettleValueException
* in case there is a data conversion error
*/
@Override
public Object convertDataFromString( String pol, ValueMetaInterface convertMeta, String nullIf, String ifNull,
int trim_type ) throws KettleValueException {
if ( convertMeta == null ) {
throw new KettleValueException( "API coding error: convertMeta input parameter should not be equals to null" );
}
// null handling and conversion of value to null
//
String null_value = nullIf;
int inValueType = convertMeta.getType();
int outValueType = getType();
if ( null_value == null ) {
switch ( inValueType ) {
case ValueMetaInterface.TYPE_BOOLEAN:
null_value = Const.NULL_BOOLEAN;
break;
case ValueMetaInterface.TYPE_STRING:
null_value = Const.NULL_STRING;
break;
case ValueMetaInterface.TYPE_BIGNUMBER:
null_value = Const.NULL_BIGNUMBER;
break;
case ValueMetaInterface.TYPE_NUMBER:
null_value = Const.NULL_NUMBER;
break;
case ValueMetaInterface.TYPE_INTEGER:
null_value = Const.NULL_INTEGER;
break;
case ValueMetaInterface.TYPE_DATE:
null_value = Const.NULL_DATE;
break;
case ValueMetaInterface.TYPE_BINARY:
null_value = Const.NULL_BINARY;
break;
default:
null_value = Const.NULL_NONE;
break;
}
}
// See if we need to convert a null value into a String
// For example, we might want to convert null into "Empty".
//
if ( !Utils.isEmpty( ifNull ) ) {
// Note that you can't pull the pad method up here as a nullComp variable
// because you could get an NPE since you haven't checked isEmpty(pol)
// yet!
if ( Utils.isEmpty( pol )
|| pol.equalsIgnoreCase( Const.rightPad( new StringBuilder( null_value ), pol.length() ) ) ) {
pol = ifNull;
}
}
// See if the polled value is empty
// In that case, we have a null value on our hands...
boolean isStringValue = outValueType == Value.VALUE_TYPE_STRING;
Object emptyValue = isStringValue ? Const.NULL_STRING : null;
Boolean isEmptyAndNullDiffer = convertStringToBoolean(
Const.NVL( System.getProperty( Const.KETTLE_EMPTY_STRING_DIFFERS_FROM_NULL, "N" ), "N" ) );
if ( pol == null && isStringValue && isEmptyAndNullDiffer ) {
pol = Const.NULL_STRING;
}
if ( pol == null ) {
return null;
} else if ( Utils.isEmpty( pol ) && !isStringValue ) {
return null;
} else {
// if the null_value is specified, we try to match with that.
//
if ( !Utils.isEmpty( null_value ) ) {
if ( null_value.length() <= pol.length() ) {
// If the polled value is equal to the spaces right-padded null_value,
// we have a match
//
if ( pol.equalsIgnoreCase( Const.rightPad( new StringBuilder( null_value ), pol.length() ) ) ) {
return emptyValue;
}
}
} else {
// Verify if there are only spaces in the polled value...
// We consider that empty as well...
//
if ( Const.onlySpaces( pol ) ) {
return emptyValue;
}
}
}
// Trimming
StringBuilder strpol;
switch ( trim_type ) {
case ValueMetaInterface.TRIM_TYPE_LEFT:
strpol = new StringBuilder( pol );
while ( strpol.length() > 0 && strpol.charAt( 0 ) == ' ' ) {
strpol.deleteCharAt( 0 );
}
pol = strpol.toString();
break;
case ValueMetaInterface.TRIM_TYPE_RIGHT:
strpol = new StringBuilder( pol );
while ( strpol.length() > 0 && strpol.charAt( strpol.length() - 1 ) == ' ' ) {
strpol.deleteCharAt( strpol.length() - 1 );
}
pol = strpol.toString();
break;
case ValueMetaInterface.TRIM_TYPE_BOTH:
strpol = new StringBuilder( pol );
while ( strpol.length() > 0 && strpol.charAt( 0 ) == ' ' ) {
strpol.deleteCharAt( 0 );
}
while ( strpol.length() > 0 && strpol.charAt( strpol.length() - 1 ) == ' ' ) {
strpol.deleteCharAt( strpol.length() - 1 );
}
pol = strpol.toString();
break;
default:
break;
}
// On with the regular program...
// Simply call the ValueMeta routines to do the conversion
// We need to do some effort here: copy all
//
return convertData( convertMeta, pol );
}
/**
* Calculate the hashcode of the specified data object
*
* @param object
* the data value to calculate a hashcode for
* @return the calculated hashcode
* @throws KettleValueException
*/
@Override
public int hashCode( Object object ) throws KettleValueException {
int hash = 0;
if ( isNull( object ) ) {
switch ( getType() ) {
case TYPE_BOOLEAN:
hash ^= 1;
break;
case TYPE_DATE:
hash ^= 2;
break;
case TYPE_NUMBER:
hash ^= 4;
break;
case TYPE_STRING:
hash ^= 8;
break;
case TYPE_INTEGER:
hash ^= 16;
break;
case TYPE_BIGNUMBER:
hash ^= 32;
break;
case TYPE_NONE:
break;
default:
break;
}
} else {
switch ( getType() ) {
case TYPE_BOOLEAN:
hash ^= getBoolean( object ).hashCode();
break;
case TYPE_DATE:
hash ^= getDate( object ).hashCode();
break;
case TYPE_INTEGER:
hash ^= getInteger( object ).hashCode();
break;
case TYPE_NUMBER:
hash ^= getNumber( object ).hashCode();
break;
case TYPE_STRING:
hash ^= getString( object ).hashCode();
break;
case TYPE_BIGNUMBER:
hash ^= getBigNumber( object ).hashCode();
break;
case TYPE_NONE:
break;
default:
break;
}
}
return hash;
}
/**
* Create an old-style value for backward compatibility reasons
*
* @param data
* the data to store in the value
* @return a newly created Value object
* @throws KettleValueException
* case there is a data conversion problem
*/
@Override
public Value createOriginalValue( Object data ) throws KettleValueException {
Value value = new Value( name, type );
value.setLength( length, precision );
if ( isNull( data ) ) {
value.setNull();
} else {
switch ( value.getType() ) {
case TYPE_STRING:
value.setValue( getString( data ) );
break;
case TYPE_NUMBER:
value.setValue( getNumber( data ).doubleValue() );
break;
case TYPE_INTEGER:
value.setValue( getInteger( data ).longValue() );
break;
case TYPE_DATE:
value.setValue( getDate( data ) );
break;
case TYPE_BOOLEAN:
value.setValue( getBoolean( data ).booleanValue() );
break;
case TYPE_BIGNUMBER:
value.setValue( getBigNumber( data ) );
break;
case TYPE_BINARY:
value.setValue( getBinary( data ) );
break;
default:
throw new KettleValueException( toString() + " : We can't convert data type " + getTypeDesc()
+ " to an original (V2) Value" );
}
}
return value;
}
/**
* Extracts the primitive data from an old style Value object
*
* @param value
* the old style Value object
* @return the value's data, NOT the meta data.
* @throws KettleValueException
* case there is a data conversion problem
*/
@Override
public Object getValueData( Value value ) throws KettleValueException {
if ( value == null || value.isNull() ) {
return null;
}
// So far the old types and the new types map to the same thing.
// For compatibility we just ask the old-style value to convert to the new
// one.
// In the old transformation this would happen sooner or later anyway.
// It doesn't throw exceptions or complain either (unfortunately).
//
switch ( getType() ) {
case ValueMetaInterface.TYPE_STRING:
return value.getString();
case ValueMetaInterface.TYPE_NUMBER:
return value.getNumber();
case ValueMetaInterface.TYPE_INTEGER:
return value.getInteger();
case ValueMetaInterface.TYPE_DATE:
return value.getDate();
case ValueMetaInterface.TYPE_BOOLEAN:
return value.getBoolean();
case ValueMetaInterface.TYPE_BIGNUMBER:
return value.getBigNumber();
case ValueMetaInterface.TYPE_BINARY:
return value.getBytes();
default:
throw new KettleValueException( toString() + " : We can't convert original data type " + value.getTypeDesc()
+ " to a primitive data type" );
}
}
/**
* @return the storageMetadata
*/
@Override
public ValueMetaInterface getStorageMetadata() {
return storageMetadata;
}
/**
* @param storageMetadata
* the storageMetadata to set
*/
@Override
public void setStorageMetadata( ValueMetaInterface storageMetadata ) {
this.storageMetadata = storageMetadata;
compareStorageAndActualFormat();
}
protected void compareStorageAndActualFormat() {
if ( storageMetadata == null ) {
identicalFormat = true;
} else {
// If a trim type is set, we need to at least try to trim the strings.
// In that case, we have to set the identical format off.
//
if ( trimType != TRIM_TYPE_NONE ) {
identicalFormat = false;
} else {
// If there is a string encoding set and it's the same encoding in the
// binary string, then we don't have to convert
// If there are no encodings set, then we're certain we don't have to
// convert as well.
//
if ( getStringEncoding() != null && getStringEncoding().equals( storageMetadata.getStringEncoding() )
|| getStringEncoding() == null && storageMetadata.getStringEncoding() == null ) {
// However, perhaps the conversion mask changed since we read the
// binary string?
// The output can be different from the input. If the mask is
// different, we need to do conversions.
// Otherwise, we can just ignore it...
//
if ( isDate() ) {
if ( ( getConversionMask() != null && getConversionMask().equals( storageMetadata.getConversionMask() ) )
|| ( getConversionMask() == null && storageMetadata.getConversionMask() == null ) ) {
identicalFormat = true;
} else {
identicalFormat = false;
}
} else if ( isNumeric() ) {
// Check the lengths first
//
if ( getLength() != storageMetadata.getLength() ) {
identicalFormat = false;
} else if ( getPrecision() != storageMetadata.getPrecision() ) {
identicalFormat = false;
} else if ( ( getConversionMask() != null
&& getConversionMask().equals( storageMetadata.getConversionMask() ) || ( getConversionMask() == null && storageMetadata
.getConversionMask() == null ) ) ) {
// For the same reasons as above, if the conversion mask, the
// decimal or the grouping symbol changes
// we need to convert from the binary strings to the target data
// type and then back to a string in the required format.
//
if ( ( getGroupingSymbol() != null && getGroupingSymbol().equals( storageMetadata.getGroupingSymbol() ) )
|| ( getConversionMask() == null && storageMetadata.getConversionMask() == null ) ) {
if ( ( getDecimalFormat( false ) != null && getDecimalFormat( false ).equals(
storageMetadata.getDecimalFormat( false ) ) )
|| ( getDecimalFormat( false ) == null && storageMetadata.getDecimalFormat( false ) == null ) ) {
identicalFormat = true;
} else {
identicalFormat = false;
}
} else {
identicalFormat = false;
}
} else {
identicalFormat = false;
}
}
}
}
}
}
/**
* @return the trimType
*/
@Override
public int getTrimType() {
return trimType;
}
/**
* @param trimType
* the trimType to set
*/
@Override
public void setTrimType( int trimType ) {
this.trimType = trimType;
}
public static final int getTrimTypeByCode( String tt ) {
if ( tt == null ) {
return 0;
}
for ( int i = 0; i < trimTypeCode.length; i++ ) {
if ( trimTypeCode[i].equalsIgnoreCase( tt ) ) {
return i;
}
}
return 0;
}
public static final int getTrimTypeByDesc( String tt ) {
if ( tt == null ) {
return 0;
}
for ( int i = 0; i < trimTypeDesc.length; i++ ) {
if ( trimTypeDesc[i].equalsIgnoreCase( tt ) ) {
return i;
}
}
// If this fails, try to match using the code.
return getTrimTypeByCode( tt );
}
public static final String getTrimTypeCode( int i ) {
if ( i < 0 || i >= trimTypeCode.length ) {
return trimTypeCode[0];
}
return trimTypeCode[i];
}
public static final String getTrimTypeDesc( int i ) {
if ( i < 0 || i >= trimTypeDesc.length ) {
return trimTypeDesc[0];
}
return trimTypeDesc[i];
}
/**
* @return the conversionMetadata
*/
@Override
public ValueMetaInterface getConversionMetadata() {
return conversionMetadata;
}
/**
* @param conversionMetadata
* the conversionMetadata to set
*/
@Override
public void setConversionMetadata( ValueMetaInterface conversionMetadata ) {
this.conversionMetadata = conversionMetadata;
}
/**
* @return true if the String encoding used (storage) is single byte encoded.
*/
@Override
public boolean isSingleByteEncoding() {
return singleByteEncoding;
}
/**
* @return the number of binary string to native data type conversions done with this object conversions
*/
@Override
public long getNumberOfBinaryStringConversions() {
return numberOfBinaryStringConversions;
}
/**
* @param numberOfBinaryStringConversions
* the number of binary string to native data type done with this object conversions to set
*/
@Override
public void setNumberOfBinaryStringConversions( long numberOfBinaryStringConversions ) {
this.numberOfBinaryStringConversions = numberOfBinaryStringConversions;
}
/*
* Original JDBC RecordSetMetaData
*
* @see java.sql.ResultSetMetaData#isAutoIncrement()
*/
@Override
public boolean isOriginalAutoIncrement() {
return originalAutoIncrement;
}
/*
* Original JDBC RecordSetMetaData
*
* @see java.sql.ResultSetMetaData#setAutoIncrement(boolean)
*/
@Override
public void setOriginalAutoIncrement( boolean originalAutoIncrement ) {
this.originalAutoIncrement = originalAutoIncrement;
}
/*
* Original JDBC RecordSetMetaData
*
* @see java.sql.ResultSetMetaData#getColumnType()
*/
@Override
public int getOriginalColumnType() {
return originalColumnType;
}
/*
* Original JDBC RecordSetMetaData
*
* @see java.sql.ResultSetMetaData#setColumnType(int)
*/
@Override
public void setOriginalColumnType( int originalColumnType ) {
this.originalColumnType = originalColumnType;
}
/*
* Original JDBC RecordSetMetaData
*
* @see java.sql.ResultSetMetaData#getColumnTypeName()
*/
@Override
public String getOriginalColumnTypeName() {
return originalColumnTypeName;
}
/*
* Original JDBC RecordSetMetaData
*
* @see java.sql.ResultSetMetaData#setColumnTypeName(java.lang.String)
*/
@Override
public void setOriginalColumnTypeName( String originalColumnTypeName ) {
this.originalColumnTypeName = originalColumnTypeName;
}
/*
* Original JDBC RecordSetMetaData
*
* @see java.sql.ResultSetMetaData#isNullable()
*/
@Override
public int isOriginalNullable() {
return originalNullable;
}
/*
* Original JDBC RecordSetMetaData
*
* @see java.sql.ResultSetMetaData#setNullable(int)
*/
@Override
public void setOriginalNullable( int originalNullable ) {
this.originalNullable = originalNullable;
}
/*
* Original JDBC RecordSetMetaData
*
* @see java.sql.ResultSetMetaData#getPrecision()
*/
@Override
public int getOriginalPrecision() {
return originalPrecision;
}
/*
* Original JDBC RecordSetMetaData
*
* @see java.sql.ResultSetMetaData#setPrecision(int)
*/
@Override
public void setOriginalPrecision( int originalPrecision ) {
this.originalPrecision = originalPrecision;
}
/*
* Original JDBC RecordSetMetaData
*
* @see java.sql.ResultSetMetaData#getScale()
*/
@Override
public int getOriginalScale() {
return originalScale;
}
/*
* Original JDBC RecordSetMetaData
*
* @see java.sql.ResultSetMetaData#setScale(int)
*/
@Override
public void setOriginalScale( int originalScale ) {
this.originalScale = originalScale;
}
/*
* Original JDBC RecordSetMetaData
*
* @see java.sql.ResultSetMetaData#isSigned()
*/
@Override
public boolean isOriginalSigned() {
return originalSigned;
}
/*
* Original JDBC RecordSetMetaData
*
* @see java.sql.ResultSetMetaData#setOriginalSigned(boolean)
*/
@Override
public void setOriginalSigned( boolean originalSigned ) {
this.originalSigned = originalSigned;
}
/**
* @return the bigNumberFormatting flag : true if BigNumbers of formatted as well
*/
public boolean isBigNumberFormatting() {
return bigNumberFormatting;
}
/**
* @param bigNumberFormatting
* the bigNumberFormatting flag to set : true if BigNumbers of formatted as well
*/
public void setBigNumberFormatting( boolean bigNumberFormatting ) {
this.bigNumberFormatting = bigNumberFormatting;
}
/**
* @return The available trim type codes (NOT localized, use for persistence)
*/
public static String[] getTrimTypeCodes() {
return trimTypeCode;
}
/**
* @return The available trim type descriptions (localized)
*/
public static String[] getTrimTypeDescriptions() {
return trimTypeDesc;
}
@Override
public boolean requiresRealClone() {
return type == TYPE_BINARY || type == TYPE_SERIALIZABLE;
}
/**
* @return the lenientStringToNumber
*/
@Override
public boolean isLenientStringToNumber() {
return lenientStringToNumber;
}
/**
* @param lenientStringToNumber
* the lenientStringToNumber to set
*/
@Override
public void setLenientStringToNumber( boolean lenientStringToNumber ) {
this.lenientStringToNumber = lenientStringToNumber;
}
/**
* @return the date format time zone
*/
@Override
public TimeZone getDateFormatTimeZone() {
return dateFormatTimeZone;
}
/**
* @param dateFormatTimeZone
* the date format time zone to set
*/
@Override
public void setDateFormatTimeZone( TimeZone dateFormatTimeZone ) {
this.dateFormatTimeZone = dateFormatTimeZone;
dateFormatChanged = true;
}
@Override
public void drawValue( PrimitiveGCInterface gc, Object value ) throws KettleValueException {
// Just draw the string by default.
//
gc.drawText( getString( value ), 0, 0 );
}
@SuppressWarnings( "fallthrough" )
@Override
public ValueMetaInterface getValueFromSQLType( DatabaseMeta databaseMeta, String name, java.sql.ResultSetMetaData rm,
int index, boolean ignoreLength, boolean lazyConversion ) throws KettleDatabaseException {
try {
int length = -1;
int precision = -1;
int valtype = ValueMetaInterface.TYPE_NONE;
boolean isClob = false;
int type = rm.getColumnType( index );
boolean signed = false;
try {
signed = rm.isSigned( index );
} catch ( Exception ignored ) {
// This JDBC Driver doesn't support the isSigned method
// nothing more we can do here by catch the exception.
}
switch ( type ) {
case java.sql.Types.CHAR:
case java.sql.Types.VARCHAR:
case java.sql.Types.NVARCHAR:
case java.sql.Types.LONGVARCHAR: // Character Large Object
valtype = ValueMetaInterface.TYPE_STRING;
if ( !ignoreLength ) {
length = rm.getColumnDisplaySize( index );
}
break;
case java.sql.Types.CLOB:
case java.sql.Types.NCLOB:
valtype = ValueMetaInterface.TYPE_STRING;
length = DatabaseMeta.CLOB_LENGTH;
isClob = true;
break;
case java.sql.Types.BIGINT:
// verify Unsigned BIGINT overflow!
//
if ( signed ) {
valtype = ValueMetaInterface.TYPE_INTEGER;
precision = 0; // Max 9.223.372.036.854.775.807
length = 15;
} else {
valtype = ValueMetaInterface.TYPE_BIGNUMBER;
precision = 0; // Max 18.446.744.073.709.551.615
length = 16;
}
break;
case java.sql.Types.INTEGER:
valtype = ValueMetaInterface.TYPE_INTEGER;
precision = 0; // Max 2.147.483.647
length = 9;
break;
case java.sql.Types.SMALLINT:
valtype = ValueMetaInterface.TYPE_INTEGER;
precision = 0; // Max 32.767
length = 4;
break;
case java.sql.Types.TINYINT:
valtype = ValueMetaInterface.TYPE_INTEGER;
precision = 0; // Max 127
length = 2;
break;
case java.sql.Types.DECIMAL:
case java.sql.Types.DOUBLE:
case java.sql.Types.FLOAT:
case java.sql.Types.REAL:
case java.sql.Types.NUMERIC:
valtype = ValueMetaInterface.TYPE_NUMBER;
length = rm.getPrecision( index );
precision = rm.getScale( index );
if ( length >= 126 ) {
length = -1;
}
if ( precision >= 126 ) {
precision = -1;
}
if ( type == java.sql.Types.DOUBLE || type == java.sql.Types.FLOAT || type == java.sql.Types.REAL ) {
if ( precision == 0 ) {
precision = -1; // precision is obviously incorrect if the type if
// Double/Float/Real
}
// If we're dealing with PostgreSQL and double precision types
if ( databaseMeta.getDatabaseInterface() instanceof PostgreSQLDatabaseMeta && type == java.sql.Types.DOUBLE
&& precision >= 16 && length >= 16 ) {
precision = -1;
length = -1;
}
// MySQL: max resolution is double precision floating point (double)
// The (12,31) that is given back is not correct
if ( databaseMeta.getDatabaseInterface().isMySQLVariant() ) {
if ( precision >= length ) {
precision = -1;
length = -1;
}
}
// if the length or precision needs a BIGNUMBER
if ( length > 15 || precision > 15 ) {
valtype = ValueMetaInterface.TYPE_BIGNUMBER;
}
} else {
if ( precision == 0 ) {
if ( length <= 18 && length > 0 ) { // Among others Oracle is affected
// here.
valtype = ValueMetaInterface.TYPE_INTEGER; // Long can hold up to 18
// significant digits
} else if ( length > 18 ) {
valtype = ValueMetaInterface.TYPE_BIGNUMBER;
}
} else { // we have a precision: keep NUMBER or change to BIGNUMBER?
if ( length > 15 || precision > 15 ) {
valtype = ValueMetaInterface.TYPE_BIGNUMBER;
}
}
}
if ( databaseMeta.getDatabaseInterface() instanceof PostgreSQLDatabaseMeta
|| databaseMeta.getDatabaseInterface() instanceof GreenplumDatabaseMeta ) {
// undefined size => arbitrary precision
if ( type == java.sql.Types.NUMERIC && length == 0 && precision == 0 ) {
valtype = ValueMetaInterface.TYPE_BIGNUMBER;
length = -1;
precision = -1;
}
}
if ( databaseMeta.getDatabaseInterface() instanceof OracleDatabaseMeta ) {
if ( precision == 0 && length == 38 ) {
valtype = ValueMetaInterface.TYPE_INTEGER;
}
if ( precision <= 0 && length <= 0 ) {
// undefined size: BIGNUMBER,
// precision on Oracle can be 38, too
// big for a Number type
valtype = ValueMetaInterface.TYPE_BIGNUMBER;
length = -1;
precision = -1;
}
}
break;
case java.sql.Types.TIMESTAMP:
if ( databaseMeta.supportsTimestampDataType() ) {
valtype = ValueMetaInterface.TYPE_TIMESTAMP;
length = rm.getScale( index );
}
break;
case java.sql.Types.DATE:
if ( databaseMeta.getDatabaseInterface() instanceof TeradataDatabaseMeta ) {
precision = 1;
}
case java.sql.Types.TIME:
valtype = ValueMetaInterface.TYPE_DATE;
//
if ( databaseMeta.getDatabaseInterface().isMySQLVariant() ) {
String property = databaseMeta.getConnectionProperties().getProperty( "yearIsDateType" );
if ( property != null && property.equalsIgnoreCase( "false" )
&& rm.getColumnTypeName( index ).equalsIgnoreCase( "YEAR" ) ) {
valtype = ValueMetaInterface.TYPE_INTEGER;
precision = 0;
length = 4;
break;
}
}
break;
case java.sql.Types.BOOLEAN:
case java.sql.Types.BIT:
valtype = ValueMetaInterface.TYPE_BOOLEAN;
break;
case java.sql.Types.BINARY:
case java.sql.Types.BLOB:
case java.sql.Types.VARBINARY:
case java.sql.Types.LONGVARBINARY:
valtype = ValueMetaInterface.TYPE_BINARY;
if ( databaseMeta.isDisplaySizeTwiceThePrecision()
&& ( 2 * rm.getPrecision( index ) ) == rm.getColumnDisplaySize( index ) ) {
// set the length for "CHAR(X) FOR BIT DATA"
length = rm.getPrecision( index );
} else if ( ( databaseMeta.getDatabaseInterface() instanceof OracleDatabaseMeta )
&& ( type == java.sql.Types.VARBINARY || type == java.sql.Types.LONGVARBINARY ) ) {
// set the length for Oracle "RAW" or "LONGRAW" data types
valtype = ValueMetaInterface.TYPE_STRING;
length = rm.getColumnDisplaySize( index );
} else if ( databaseMeta.isMySQLVariant()
&& ( type == java.sql.Types.VARBINARY || type == java.sql.Types.LONGVARBINARY ) ) {
// set the data type to String, see PDI-4812
valtype = ValueMetaInterface.TYPE_STRING;
// PDI-6677 - don't call 'length = rm.getColumnDisplaySize(index);'
length = -1; // keep the length to -1, e.g. for string functions (e.g.
// CONCAT see PDI-4812)
} else if ( databaseMeta.getDatabaseInterface() instanceof SQLiteDatabaseMeta ) {
valtype = ValueMetaInterface.TYPE_STRING;
} else {
length = -1;
}
precision = -1;
break;
default:
valtype = ValueMetaInterface.TYPE_STRING;
precision = rm.getScale( index );
break;
}
ValueMetaInterface v = ValueMetaFactory.createValueMeta( name, valtype );
v.setLength( length );
v.setPrecision( precision );
v.setLargeTextField( isClob );
getOriginalColumnMetadata( v, rm, index, ignoreLength );
// See if we need to enable lazy conversion...
//
if ( lazyConversion && valtype == ValueMetaInterface.TYPE_STRING ) {
v.setStorageType( ValueMetaInterface.STORAGE_TYPE_BINARY_STRING );
// TODO set some encoding to go with this.
// Also set the storage metadata. a copy of the parent, set to String too.
//
try {
ValueMetaInterface storageMetaData = ValueMetaFactory.cloneValueMeta( v, ValueMetaInterface.TYPE_STRING );
storageMetaData.setStorageType( ValueMetaInterface.STORAGE_TYPE_NORMAL );
v.setStorageMetadata( storageMetaData );
} catch ( Exception e ) {
throw new SQLException( e );
}
}
ValueMetaInterface newV = null;
try {
newV = databaseMeta.getDatabaseInterface().customizeValueFromSQLType( v, rm, index );
} catch ( SQLException e ) {
throw new SQLException( e );
}
return newV == null ? v : newV;
} catch ( Exception e ) {
throw new KettleDatabaseException( "Error determining value metadata from SQL resultset metadata", e );
}
}
protected void getOriginalColumnMetadata( ValueMetaInterface v, ResultSetMetaData rm, int index, boolean ignoreLength )
throws SQLException {
// Grab the comment as a description to the field as well.
String comments = rm.getColumnLabel( index );
v.setComments( comments );
// get & store more result set meta data for later use
int originalColumnType = rm.getColumnType( index );
v.setOriginalColumnType( originalColumnType );
String originalColumnTypeName = rm.getColumnTypeName( index );
v.setOriginalColumnTypeName( originalColumnTypeName );
int originalPrecision = -1;
if ( !ignoreLength ) {
// Throws exception on MySQL
originalPrecision = rm.getPrecision( index );
}
v.setOriginalPrecision( originalPrecision );
int originalScale = rm.getScale( index );
v.setOriginalScale( originalScale );
// DISABLED FOR PERFORMANCE REASONS : PDI-1788
//
// boolean originalAutoIncrement=rm.isAutoIncrement(index); DISABLED FOR
// PERFORMANCE REASONS : PDI-1788
// v.setOriginalAutoIncrement(originalAutoIncrement);
// int originalNullable=rm.isNullable(index); DISABLED FOR PERFORMANCE
// REASONS : PDI-1788
// v.setOriginalNullable(originalNullable);
//
boolean originalSigned = false;
try {
originalSigned = rm.isSigned( index );
} catch ( Exception ignored ) {
// This JDBC Driver doesn't support the isSigned method.
// Nothing more we can do here.
}
v.setOriginalSigned( originalSigned );
}
/**
* Get a value from a result set column based on the current value metadata
*
* @param databaseInterface
* the database metadata to use
* @param resultSet
* The JDBC result set to read from
* @param index
* The column index (1-based)
* @return The Kettle native data type based on the value metadata
* @throws KettleDatabaseException
* in case something goes wrong.
*/
@Override
public Object getValueFromResultSet( DatabaseInterface databaseInterface, ResultSet resultSet, int index )
throws KettleDatabaseException {
try {
Object data = null;
switch ( getType() ) {
case ValueMetaInterface.TYPE_BOOLEAN:
data = Boolean.valueOf( resultSet.getBoolean( index + 1 ) );
break;
case ValueMetaInterface.TYPE_NUMBER:
data = new Double( resultSet.getDouble( index + 1 ) );
break;
case ValueMetaInterface.TYPE_BIGNUMBER:
data = resultSet.getBigDecimal( index + 1 );
break;
case ValueMetaInterface.TYPE_INTEGER:
data = Long.valueOf( resultSet.getLong( index + 1 ) );
break;
case ValueMetaInterface.TYPE_STRING:
if ( isStorageBinaryString() ) {
data = resultSet.getBytes( index + 1 );
} else {
data = resultSet.getString( index + 1 );
}
break;
case ValueMetaInterface.TYPE_BINARY:
if ( databaseInterface.supportsGetBlob() ) {
Blob blob = resultSet.getBlob( index + 1 );
if ( blob != null ) {
data = blob.getBytes( 1L, (int) blob.length() );
} else {
data = null;
}
} else {
data = resultSet.getBytes( index + 1 );
}
break;
case ValueMetaInterface.TYPE_DATE:
if ( getPrecision() != 1 && databaseInterface.supportsTimeStampToDateConversion() ) {
data = resultSet.getTimestamp( index + 1 );
break; // Timestamp extends java.util.Date
} else if ( databaseInterface instanceof NetezzaDatabaseMeta ) {
// PDI-10877 workaround for IBM netezza jdbc 'special' implementation
data = getNetezzaDateValueWorkaround( databaseInterface, resultSet, index + 1 );
break;
} else {
data = resultSet.getDate( index + 1 );
break;
}
default:
break;
}
if ( resultSet.wasNull() ) {
data = null;
}
return data;
} catch ( SQLException e ) {
throw new KettleDatabaseException( "Unable to get value '" + toStringMeta() + "' from database resultset, index "
+ index, e );
}
}
private Object getNetezzaDateValueWorkaround( DatabaseInterface databaseInterface, ResultSet resultSet, int index )
throws SQLException, KettleDatabaseException {
Object data = null;
int type = resultSet.getMetaData().getColumnType( index );
switch ( type ) {
case Types.TIME: {
data = resultSet.getTime( index );
break;
}
default: {
data = resultSet.getDate( index );
}
}
return data;
}
@Override
public void setPreparedStatementValue( DatabaseMeta databaseMeta, PreparedStatement preparedStatement, int index,
Object data ) throws KettleDatabaseException {
try {
switch ( getType() ) {
case ValueMetaInterface.TYPE_NUMBER:
if ( !isNull( data ) ) {
double num = getNumber( data ).doubleValue();
if ( databaseMeta.supportsFloatRoundingOnUpdate() && getPrecision() >= 0 ) {
num = Const.round( num, getPrecision() );
}
preparedStatement.setDouble( index, num );
} else {
preparedStatement.setNull( index, java.sql.Types.DOUBLE );
}
break;
case ValueMetaInterface.TYPE_INTEGER:
if ( !isNull( data ) ) {
if ( databaseMeta.supportsSetLong() ) {
preparedStatement.setLong( index, getInteger( data ).longValue() );
} else {
double d = getNumber( data ).doubleValue();
if ( databaseMeta.supportsFloatRoundingOnUpdate() && getPrecision() >= 0 ) {
preparedStatement.setDouble( index, d );
} else {
preparedStatement.setDouble( index, Const.round( d, getPrecision() ) );
}
}
} else {
preparedStatement.setNull( index, java.sql.Types.INTEGER );
}
break;
case ValueMetaInterface.TYPE_STRING:
if ( getLength() < databaseMeta.getMaxTextFieldLength() ) {
if ( !isNull( data ) ) {
preparedStatement.setString( index, getString( data ) );
} else {
preparedStatement.setNull( index, java.sql.Types.VARCHAR );
}
} else {
if ( !isNull( data ) ) {
String string = getString( data );
int maxlen = databaseMeta.getMaxTextFieldLength();
int len = string.length();
// Take the last maxlen characters of the string...
int begin = Math.max( len - maxlen, 0 );
if ( begin > 0 ) {
// Truncate if logging result if it exceeds database maximum string field length
log.logMinimal( String.format( "Truncating %d symbols of original message in '%s' field", begin, getName() ) );
string = string.substring( begin );
}
if ( databaseMeta.supportsSetCharacterStream() ) {
StringReader sr = new StringReader( string );
preparedStatement.setCharacterStream( index, sr, string.length() );
} else {
preparedStatement.setString( index, string );
}
} else {
preparedStatement.setNull( index, java.sql.Types.VARCHAR );
}
}
break;
case ValueMetaInterface.TYPE_DATE:
if ( !isNull( data ) ) {
// Environment variable to disable timezone setting for the database updates
// When it is set, timezone will not be taken into account and the value will be converted
// into the local java timezone
if ( getPrecision() == 1 || !databaseMeta.supportsTimeStampToDateConversion() ) {
// Convert to DATE!
long dat = getInteger( data ).longValue(); // converts using Date.getTime()
java.sql.Date ddate = new java.sql.Date( dat );
if ( ignoreTimezone || this.getDateFormatTimeZone() == null ) {
preparedStatement.setDate( index, ddate );
} else {
preparedStatement.setDate( index, ddate, Calendar.getInstance( this.getDateFormatTimeZone() ) );
}
} else {
if ( data instanceof java.sql.Timestamp ) {
// Preserve ns precision!
//
if ( ignoreTimezone || this.getDateFormatTimeZone() == null ) {
preparedStatement.setTimestamp( index, (java.sql.Timestamp) data );
} else {
preparedStatement.setTimestamp( index, (java.sql.Timestamp) data, Calendar.getInstance( this
.getDateFormatTimeZone() ) );
}
} else {
long dat = getInteger( data ).longValue(); // converts using Date.getTime()
java.sql.Timestamp sdate = new java.sql.Timestamp( dat );
if ( ignoreTimezone || this.getDateFormatTimeZone() == null ) {
preparedStatement.setTimestamp( index, sdate );
} else {
preparedStatement.setTimestamp( index, sdate, Calendar.getInstance( this.getDateFormatTimeZone() ) );
}
}
}
} else {
if ( getPrecision() == 1 || !databaseMeta.supportsTimeStampToDateConversion() ) {
preparedStatement.setNull( index, java.sql.Types.DATE );
} else {
preparedStatement.setNull( index, java.sql.Types.TIMESTAMP );
}
}
break;
case ValueMetaInterface.TYPE_BOOLEAN:
if ( databaseMeta.supportsBooleanDataType() ) {
if ( !isNull( data ) ) {
preparedStatement.setBoolean( index, getBoolean( data ).booleanValue() );
} else {
preparedStatement.setNull( index, java.sql.Types.BOOLEAN );
}
} else {
if ( !isNull( data ) ) {
preparedStatement.setString( index, getBoolean( data ).booleanValue() ? "Y" : "N" );
} else {
preparedStatement.setNull( index, java.sql.Types.CHAR );
}
}
break;
case ValueMetaInterface.TYPE_BIGNUMBER:
if ( !isNull( data ) ) {
preparedStatement.setBigDecimal( index, getBigNumber( data ) );
} else {
preparedStatement.setNull( index, java.sql.Types.DECIMAL );
}
break;
case ValueMetaInterface.TYPE_BINARY:
if ( !isNull( data ) ) {
preparedStatement.setBytes( index, getBinary( data ) );
} else {
preparedStatement.setNull( index, java.sql.Types.BINARY );
}
break;
default:
// placeholder
preparedStatement.setNull( index, java.sql.Types.VARCHAR );
break;
}
} catch ( Exception e ) {
throw new KettleDatabaseException( "Error setting value #" + index + " [" + toStringMeta()
+ "] on prepared statement", e );
}
}
@Override
public Object getNativeDataType( Object object ) throws KettleValueException {
switch ( getStorageType() ) {
case STORAGE_TYPE_BINARY_STRING:
return convertBinaryStringToNativeType( (byte[]) object );
case STORAGE_TYPE_INDEXED:
return index[(Integer) object];
case STORAGE_TYPE_NORMAL:
default:
return object;
}
}
@Override
public String getDatabaseColumnTypeDefinition( DatabaseInterface databaseInterface, String tk, String pk,
boolean use_autoinc, boolean add_fieldname, boolean add_cr ) {
return null; // No default suggestions...
}
protected int getQuotesBeforeSymbol( String df, String symbols ) {
int quotes = 0;
int stopPos = df.indexOf( symbols );
if ( stopPos > 0 ) {
int curPos = -1;
do {
curPos = df.indexOf( "'", curPos + 1 );
if ( curPos >= 0 && curPos < stopPos ) {
quotes++;
}
} while ( curPos >= 0 && curPos < stopPos );
}
return quotes;
}
@Override
public Class<?> getNativeDataTypeClass() throws KettleValueException {
// Not implemented for base class
throw new KettleValueException( getTypeDesc() + " does not implement this method" );
}
}