/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2016 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 org.pentaho.di.core.Const;
import org.pentaho.di.core.database.DatabaseInterface;
import org.pentaho.di.core.database.DatabaseMeta;
import org.pentaho.di.core.database.PostgreSQLDatabaseMeta;
import org.pentaho.di.core.exception.KettleDatabaseException;
import org.pentaho.di.core.exception.KettleValueException;
import org.pentaho.di.core.row.ValueMetaInterface;
import org.pentaho.di.core.util.Utils;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Types;
import java.util.Date;
public class ValueMetaInternetAddress extends ValueMetaDate {
@Override
public int compare( Object data1, Object data2 ) throws KettleValueException {
InetAddress inet1 = getInternetAddress( data1 );
InetAddress inet2 = getInternetAddress( data2 );
int cmp = 0;
if ( inet1 == null ) {
if ( inet2 == null ) {
cmp = 0;
} else {
cmp = -1;
}
} else if ( inet2 == null ) {
cmp = 1;
} else {
BigInteger bigint1 = new BigInteger( inet1.getAddress() );
BigInteger bigint2 = new BigInteger( inet2.getAddress() );
cmp = bigint1.compareTo( bigint2 );
}
if ( isSortedDescending() ) {
return -cmp;
} else {
return cmp;
}
}
public ValueMetaInternetAddress() {
this( null );
}
public ValueMetaInternetAddress( String name ) {
super( name, ValueMetaInterface.TYPE_INET );
}
public InetAddress getInternetAddress( Object object ) throws KettleValueException {
if ( object == null ) {
return null;
}
switch ( type ) {
case TYPE_INET:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return (InetAddress) object;
case STORAGE_TYPE_BINARY_STRING:
return (InetAddress) convertBinaryStringToNativeType( (byte[]) object );
case STORAGE_TYPE_INDEXED:
return (InetAddress) index[( (Integer) object )];
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
case TYPE_STRING:
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertStringToInternetAddress( (String) object );
case STORAGE_TYPE_BINARY_STRING:
return convertStringToInternetAddress( (String) convertBinaryStringToNativeType( (byte[]) object ) );
case STORAGE_TYPE_INDEXED:
return convertStringToInternetAddress( (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 convertNumberToInternetAddress( (Double) object );
case STORAGE_TYPE_BINARY_STRING:
return convertNumberToInternetAddress( (Double) convertBinaryStringToNativeType( (byte[]) object ) );
case STORAGE_TYPE_INDEXED:
return convertNumberToInternetAddress( (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 convertIntegerToInternetAddress( (Long) object );
case STORAGE_TYPE_BINARY_STRING:
return convertIntegerToInternetAddress( (Long) convertBinaryStringToNativeType( (byte[]) object ) );
case STORAGE_TYPE_INDEXED:
return convertIntegerToInternetAddress( (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 convertBigNumberToInternetAddress( (BigDecimal) object );
case STORAGE_TYPE_BINARY_STRING:
return convertBigNumberToInternetAddress( (BigDecimal) convertBinaryStringToNativeType( (byte[]) object ) );
case STORAGE_TYPE_INDEXED:
return convertBigNumberToInternetAddress( (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 Internet address." );
case TYPE_BINARY:
throw new KettleValueException( toString()
+ " : I don't know how to convert a binary value to Internet address." );
case TYPE_SERIALIZABLE:
throw new KettleValueException( toString()
+ " : I don't know how to convert a serializable value to Internet address." );
default:
throw new KettleValueException( toString() + " : Unknown type " + type + " specified." );
}
}
@Override
public Date getDate( Object object ) throws KettleValueException {
throw new KettleValueException( toStringMeta()
+ ": it's not possible to convert from Internet Address to a date" );
}
@Override
public Long getInteger( Object object ) throws KettleValueException {
InetAddress address = getInternetAddress( object );
if ( address == null ) {
return null;
}
long total = 0L;
byte[] addr = address.getAddress();
if ( addr.length > 8 ) {
throw new KettleValueException( "Unable to convert Internet Address v6 to an Integer: "
+ getString( object ) + " (The precision is too high to be contained in a long integer value)" );
}
for ( int i = 0; i < addr.length; i++ ) {
total += ( addr[i] & 0xFF ) * ( (long) Math.pow( 256, ( addr.length - 1 - i ) ) );
}
return total;
}
@Override
public Double getNumber( Object object ) throws KettleValueException {
Long l = getInteger( object );
if ( l == null ) {
return null;
}
return Long.valueOf( l ).doubleValue();
}
@Override
public BigDecimal getBigNumber( Object object ) throws KettleValueException {
Long l = getInteger( object );
if ( l == null ) {
return null;
}
return BigDecimal.valueOf( l );
}
@Override
public Boolean getBoolean( Object object ) throws KettleValueException {
throw new KettleValueException( toStringMeta()
+ ": it's not possible to convert from an Internet Address to a Boolean" );
}
@Override
public String getString( Object object ) throws KettleValueException {
return convertInternetAddressToString( getInternetAddress( object ) );
}
@Override
public byte[] getBinaryString( Object object ) throws KettleValueException {
if ( isStorageBinaryString() && identicalFormat ) {
return (byte[]) object; // shortcut it directly for better performance.
}
if ( object == null ) {
return null;
}
switch ( storageType ) {
case STORAGE_TYPE_NORMAL:
return convertStringToBinaryString( getString( object ) );
case STORAGE_TYPE_BINARY_STRING:
return convertStringToBinaryString( getString(
convertStringToInternetAddress( convertBinaryStringToString( (byte[]) object ) ) ) );
case STORAGE_TYPE_INDEXED:
return convertStringToBinaryString(
convertInternetAddressToString( (InetAddress) index[( (Integer) object )] ) );
default:
throw new KettleValueException( toString() + " : Unknown storage type " + storageType + " specified." );
}
}
protected InetAddress convertBigNumberToInternetAddress( BigDecimal bd ) throws KettleValueException {
if ( bd == null ) {
return null;
}
return convertIntegerToInternetAddress( bd.longValue() );
}
protected InetAddress convertNumberToInternetAddress( Double d ) throws KettleValueException {
if ( d == null ) {
return null;
}
long nanos = d.longValue();
return convertIntegerToInternetAddress( nanos );
}
protected InetAddress convertIntegerToInternetAddress( Long l ) throws KettleValueException {
if ( l == null ) {
return null;
}
byte[] addr;
if ( l >= Math.pow( 256, 4 ) ) {
addr = new byte[16];
} else {
addr = new byte[4];
}
for ( int i = 0; i < addr.length; i++ ) {
long mask = 0xFF << ( i * 8 );
addr[addr.length - 1 - i] = (byte) ( ( l & mask ) >> ( 8 * i ) );
}
try {
return InetAddress.getByAddress( addr );
} catch ( Exception e ) {
throw new KettleValueException( "Unable to convert an Integer to an internet address", e );
}
}
protected synchronized InetAddress convertStringToInternetAddress( String string ) throws KettleValueException {
// See if trimming needs to be performed before conversion
//
string = Const.trimToType( string, getTrimType() );
if ( Utils.isEmpty( string ) ) {
return null;
}
try {
return InetAddress.getByName( string );
} catch ( Exception e ) {
throw new KettleValueException( toString()
+ " : couldn't convert string [" + string + "] to an internet address", e );
}
}
protected synchronized String convertInternetAddressToString( InetAddress inetAddress ) throws KettleValueException {
if ( inetAddress == null ) {
return null;
}
return inetAddress.getHostAddress();
}
@Override
public Object convertDataFromString( String pol, ValueMetaInterface convertMeta, String nullIf, String ifNull,
int trim_type ) throws KettleValueException {
// null handling and conversion of value to null
//
String null_value = nullIf;
if ( null_value == null ) {
switch ( convertMeta.getType() ) {
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...
//
if ( Utils.isEmpty( pol ) ) {
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 null;
}
}
} else {
// Verify if there are only spaces in the polled value...
// We consider that empty as well...
//
if ( Const.onlySpaces( pol ) ) {
return null;
}
}
}
StringBuilder strpol;
// Trimming
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 );
}
/**
* 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 ( meta2.getType() ) {
case TYPE_STRING:
return convertStringToInternetAddress( meta2.getString( data2 ) );
case TYPE_INTEGER:
return convertIntegerToInternetAddress( meta2.getInteger( data2 ) );
case TYPE_NUMBER:
return convertNumberToInternetAddress( meta2.getNumber( data2 ) );
case TYPE_BIGNUMBER:
return convertBigNumberToInternetAddress( meta2.getBigNumber( data2 ) );
default:
throw new KettleValueException( meta2.toStringMeta() + " : can't be converted to an Internet Address" );
}
}
@Override
public Object cloneValueData( Object object ) throws KettleValueException {
InetAddress inetAddress = getInternetAddress( object );
if ( inetAddress == null ) {
return null;
}
try {
return InetAddress.getByAddress( inetAddress.getAddress() );
} catch ( Exception e ) {
throw new KettleValueException( "Unable to clone Internet Address", e );
}
}
@Override
public ValueMetaInterface getValueFromSQLType( DatabaseMeta databaseMeta, String name, ResultSetMetaData rm,
int index, boolean ignoreLength, boolean lazyConversion ) throws KettleDatabaseException {
try {
int type = rm.getColumnType( index );
if ( type == java.sql.Types.OTHER ) {
String columnTypeName = rm.getColumnTypeName( index );
if ( "INET".equalsIgnoreCase( columnTypeName ) ) {
ValueMetaInternetAddress valueMeta = new ValueMetaInternetAddress( name );
// Also get original column details, comment, etc.
//
getOriginalColumnMetadata( valueMeta, rm, index, ignoreLength );
return valueMeta;
}
}
return null;
} catch ( Exception e ) {
throw new KettleDatabaseException( "Error evaluating Internet address value metadata", e );
}
}
@Override
public Object getValueFromResultSet( DatabaseInterface databaseInterface, ResultSet resultSet, int index ) throws KettleDatabaseException {
try {
return convertStringToInternetAddress( resultSet.getString( index + 1 ) );
} catch ( Exception e ) {
throw new KettleDatabaseException( toStringMeta()
+ " : Unable to get Internet Address from resultset at index " + index, e );
}
}
@Override
public void setPreparedStatementValue( DatabaseMeta databaseMeta, PreparedStatement preparedStatement,
int index, Object data ) throws KettleDatabaseException {
try {
preparedStatement.setObject( index, getString( data ), Types.OTHER );
} catch ( Exception e ) {
throw new KettleDatabaseException( toStringMeta()
+ " : Unable to set Internet address value on prepared statement on index " + index, e );
}
}
@Override
public String getDatabaseColumnTypeDefinition( DatabaseInterface databaseInterface, String tk, String pk,
boolean use_autoinc, boolean add_fieldname, boolean add_cr ) {
String retval = null;
if ( databaseInterface instanceof PostgreSQLDatabaseMeta ) {
if ( add_fieldname ) {
retval = getName() + " ";
} else {
retval = "";
}
retval += "INET";
if ( add_cr ) {
retval += Const.CR;
}
}
return retval;
}
@Override
public Class<?> getNativeDataTypeClass() throws KettleValueException {
return InetAddress.class;
}
}