>
> Seems reasonable for us to offer a PGxxx class for every core backend
> type. Send them in and we'll take a look...
>
> Kris Jurka
>
Kris,
Attached is an implementation for the proposed PGcidr & PGinet classes.
I have tried to follow the formatting conventions used in the geometric
extensions but is is a first attempt, please let me know if there is
anything else I should/could do to improve these.
I will take any comments / suggestions and incorporate them into the
PGmacaddr class also.
Cheers,
Russ
/*-------------------------------------------------------------------------
*
* Copyright (C) 2005, PostgreSQL Global Development Group
*
*--------------------------------------------------------------------------
*/
package org.postgresql.net;
import java.io.Serializable;
import java.sql.SQLException;
import org.postgresql.util.GT;
import org.postgresql.util.PGobject;
import org.postgresql.util.PGtokenizer;
import org.postgresql.util.PSQLException;
import org.postgresql.util.PSQLState;
/**
* This represents org.postgresql's cidr datatype, which is
* used to hold network addresses.
*
* <p>
* This class wraps the postgresql specific CIDR datatype. It
* supports IPV4 network addresses in the following format.
* <p>
* a[.b[.c[.d]]][/e]
*
* @author Russell Francis < rfrancis@ev.net >
*/
public class PGcidr extends PGobject implements Serializable, Cloneable
{
protected int hashValue;
/**
* This constructor takes a string in the
* cidr format a[.b[.c[.d]]][/e] and creates
* a PGcidr to represent it.
*
* @param s The representation of the cidr as a string.
* @exception SQLException if the string is not in the proper format.
*/
public PGcidr( String s )
throws SQLException
{
this();
setValue( s );
}
/**
* A simple constructor.
*/
public PGcidr()
{
setType( "cidr" );
}
/**
* Set the value of this CIDR.
*
* <p>This accepts strings in the a[.b[.c[.d]]][/netmask] format.
*
* @param v The string representation of this network address.
* @exception SQLException If it is not in a valid cidr format.
*/
public void setValue( String v )
throws SQLException
{
int a = 0;
int b = 0;
int c = 0;
int d = 0;
int netmask = -1;
if( v == null )
{
throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.",
new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH ) );
}
PGtokenizer t = new PGtokenizer( v, '/' );
int size = t.getSize();
if( ( size != 1 ) && ( size != 2 ) )
{
throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.",
new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH ) );
}
try
{
if( size == 2 )
{
// we have a netmask to read
netmask = Integer.valueOf( t.getToken( 1 ) ).intValue();
if( ( netmask < 0 ) || ( netmask > 32 ) )
{
throw( new PSQLException(
GT.tr( "Conversion to type {0} failed: {1}.", new Object[]{ type, v } ),
PSQLState.DATA_TYPE_MISMATCH ) );
}
}
// read the body a.b.c.d
t = new PGtokenizer( t.getToken( 0 ), '.' );
size = t.getSize();
if( ( size < 1 ) || ( size > 4 ) )
{
throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.",
new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH ) );
}
a = Integer.valueOf( t.getToken( 0 ) ).intValue();
if( size >= 2 )
{
b = Integer.valueOf( t.getToken( 1 ) ).intValue();
}
if( size >= 3 )
{
c = Integer.valueOf( t.getToken( 2 ) ).intValue();
}
if( size >= 4 )
{
d = Integer.valueOf( t.getToken( 3 ) ).intValue();
}
}
catch( NumberFormatException e )
{
throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.",
new Object[]{ type, v }), PSQLState.DATA_TYPE_MISMATCH, e ) );
}
if( ( a < 0 ) || ( a > 255 ) ||
( b < 0 ) || ( b > 255 ) ||
( c < 0 ) || ( c > 255 ) ||
( d < 0 ) || ( d > 255 ) )
{
throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.",
new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH ) );
}
// If the netmask is not set in the parameter,
// we will take a guess like PG does.
//
// 1.0.0.0 - 127.0.0.0 - class A, netmask 8
// 128.0.0.0 - 191.0.0.0 - class B, netmask 16
// 192.0.0.0 - 223.0.0.0 - class C, netmask 24
if( netmask == -1 )
{
// start with a safe default, we will try to trim
// this down depending on the values of a,b,c & d.
netmask = 32;
if( a >= 1 || a <= 127 )
{
if( b == 0 && c == 0 && d == 0 )
{
netmask = 8;
}
else if( c == 0 && d == 0 )
{
netmask = 16;
}
else if( d == 0 )
{
netmask = 24;
}
}
else if( a >= 128 && a <= 191 )
{
if( c == 0 && d == 0 )
{
netmask = 16;
}
else if( d == 0 )
{
netmask = 24;
}
}
else if( a >= 192 && a <= 223 )
{
if( d == 0 )
{
netmask = 24;
}
}
}
// verify that there are no bits to the right of the netmask
if( netmask < 32 )
{
int address = ( a << 24 ) | ( b << 16 ) | ( c << 8 ) | d;
address <<= netmask;
if( address != 0 )
{
// There are bits to the right of the netmask
throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.",
new Object[]{ type, v }), PSQLState.DATA_TYPE_MISMATCH ) );
}
}
// Seems like it will generate a decent hash?
this.hashValue = ((a ^ b) << 24) | ((b ^ c) << 8) | ((c ^ d) << 16) | (d ^ netmask);
// ok, the parameter cleared all of out tests,
// a.b.c.d/netmask should contain out new CIDR value.
this.value = "" + a + "." + b + "." + c + "." + d + "/" + netmask;
}
/**
* Get the hash code for this network address.
*
* @return The hash value for this object.
*/
public int hashCode()
{
return( this.hashValue );
}
/**
* Compare two PGcidr objects for equality.
*
* <p>This will return true if the parameter obj is of type PGcidr
* and represents the same network as this.
*
* @param obj The object which we wish to compare.
* @return true if it represents the same network as this, false otherwise.
*/
public boolean equals( Object obj )
{
if( obj instanceof PGcidr )
{
PGcidr cidr = (PGcidr)obj;
if( this.getValue().equals( cidr.getValue() ) )
{
return( true );
}
}
return( false );
}
/**
* This will make a duplicate of the current PGcidr object.
*
* @return null on failure, or a new PGcidr object which
* represents the same network address as the invoking
* object.
*/
public Object clone()
{
try
{
return( new PGcidr( this.getValue() ) );
}
catch( SQLException e )
{
return( null );
}
}
}
/*-------------------------------------------------------------------------
*
* Copyright (C) 2005, PostgreSQL Global Development Group
*
*--------------------------------------------------------------------------
*/
package org.postgresql.net;
import java.io.Serializable;
import java.sql.SQLException;
import org.postgresql.util.GT;
import org.postgresql.util.PGobject;
import org.postgresql.util.PGtokenizer;
import org.postgresql.util.PSQLException;
import org.postgresql.util.PSQLState;
/**
* This represents org.postgresql's inet datatype, which is used
* to hold IPV4 network addresses and ip addresses.
*
* <p>
* This class wraps the postgresql specific INET datatype. It supports
* values in the following format.
* <p>
* a.b.c.d[/netmask]
*
* @author Russell Francis < rfrancis@ev.net >
*/
public class PGinet extends PGobject implements Serializable, Cloneable
{
private int hashValue;
/**
* This constructor takes a string in the inet format
* a.b.c.d[/netmask] and creates a new PGinet to
* represent it.
*
* @param s The string representation of the inet value.
* @exception SQLException If the string is invalid.
*/
public PGinet( String s )
throws SQLException
{
this();
this.setValue( s );
}
/**
* A simple constructor.
*/
public PGinet()
{
setType( "inet" );
}
/**
* This method sets the value of this PGinet object.
*
* @param v A string representation of an inet address a.b.c.d[/netmask]
* @exception SQLException If the parameter is not a valid inet address.
*/
public void setValue( String v )
throws SQLException
{
if( v == null )
{
throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.",
new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH ) );
}
PGtokenizer t = new PGtokenizer( v, '/' );
int size = t.getSize();
if( ( size != 1 ) && ( size != 2 ) )
{
throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.",
new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH ) );
}
int a = 0;
int b = 0;
int c = 0;
int d = 0;
int netmask = 32;
try
{
if( size == 2 )
{
netmask = Integer.valueOf( t.getToken( 1 ) ).intValue();
if( ( netmask < 0 ) || ( netmask > 32 ) )
{
throw( new PSQLException(
GT.tr( "Conversion to typ {0} failed: {1}.", new Object[]{ type, v } ),
PSQLState.DATA_TYPE_MISMATCH ) );
}
}
t = new PGtokenizer( t.getToken( 0 ), '.' );
if( t.getSize() != 4 )
{
throw( new PSQLException(
GT.tr( "Conversion to typ {0} failed: {1}.", new Object[]{ type, v } ),
PSQLState.DATA_TYPE_MISMATCH ) );
}
a = Integer.valueOf( t.getToken( 0 ) ).intValue();
b = Integer.valueOf( t.getToken( 1 ) ).intValue();
c = Integer.valueOf( t.getToken( 2 ) ).intValue();
d = Integer.valueOf( t.getToken( 3 ) ).intValue();
}
catch( NumberFormatException e )
{
throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.",
new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH, e ) );
}
// ensure that the values are within a valid range.
if( ( a < 0 ) || ( a > 255 ) ||
( b < 0 ) || ( b > 255 ) ||
( c < 0 ) || ( c > 255 ) ||
( d < 0 ) || ( d > 255 ) )
{
throw( new PSQLException( GT.tr( "Conversion to type {0} failed: {1}.",
new Object[]{ type, v } ), PSQLState.DATA_TYPE_MISMATCH ) );
}
this.hashValue = ((a ^ c) << 24) | ((b ^ d) << 16) | ((b ^ c) << 8) | (d ^ netmask);
this.value = "" + a + "." + b + "." + c + "." + d + ((netmask == 32) ? "" : ("/" + netmask));
}
/**
* Get the hash code for this network address.
*
* @return The hash value for this object.
*/
public int hashCode()
{
return( this.hashValue );
}
/**
* Compare two PGinet's for equality.
*
* @param obj The object which we wish to compare.
* @return true if it represents the same network or ip address
* as this PGinet, false otherwise.
*/
public boolean equals( Object obj )
{
if( obj instanceof PGinet )
{
PGinet inet = (PGinet)obj;
if( this.toString().equals( inet.toString() ) )
{
return( true );
}
}
return( false );
}
/**
* Make a duplicate of this PGinet object.
*
* @return null on failure, or a new PGinet address
* which is equal to this object.
*/
public Object clone()
{
try
{
return( new PGinet( this.getValue() ) );
}
catch( SQLException e )
{
return( null );
}
}
}