/* * Hibernate, Relational Persistence for Idiomatic Java * * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. */ package org.hibernate.spatial.dialect.postgis; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import org.geolatte.geom.ByteBuffer; import org.geolatte.geom.ByteOrder; import org.geolatte.geom.Geometry; import org.geolatte.geom.codec.Wkb; import org.geolatte.geom.codec.WkbDecoder; import org.geolatte.geom.codec.WkbEncoder; import org.geolatte.geom.codec.Wkt; import org.geolatte.geom.codec.WktDecoder; import org.postgresql.util.PGobject; import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.sql.BasicBinder; import org.hibernate.type.descriptor.sql.BasicExtractor; import org.hibernate.type.descriptor.sql.SqlTypeDescriptor; /** * Type Descriptor for the Postgis Geometry type * * @author Karel Maesen, Geovise BVBA */ public class PGGeometryTypeDescriptor implements SqlTypeDescriptor { /** * An instance of this class */ public static final PGGeometryTypeDescriptor INSTANCE = new PGGeometryTypeDescriptor(); @Override public int getSqlType() { return Types.OTHER; } @Override public boolean canBeRemapped() { return false; } @Override public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) { return new BasicBinder<X>( javaTypeDescriptor, this ) { @Override protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException { final WkbEncoder encoder = Wkb.newEncoder( Wkb.Dialect.POSTGIS_EWKB_1 ); final Geometry geometry = getJavaDescriptor().unwrap( value, Geometry.class, options ); final byte[] bytes = encoder.encode( geometry, ByteOrder.NDR ).toByteArray(); st.setBytes( index, bytes ); } @Override protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) throws SQLException { final WkbEncoder encoder = Wkb.newEncoder( Wkb.Dialect.POSTGIS_EWKB_1 ); final Geometry geometry = getJavaDescriptor().unwrap( value, Geometry.class, options ); final byte[] bytes = encoder.encode( geometry, ByteOrder.NDR ).toByteArray(); st.setBytes( name, bytes ); } }; } @Override public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) { return new BasicExtractor<X>( javaTypeDescriptor, this ) { @Override protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException { return getJavaDescriptor().wrap( toGeometry( rs.getObject( name ) ), options ); } @Override protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException { return getJavaDescriptor().wrap( toGeometry( statement.getObject( index ) ), options ); } @Override protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException { return getJavaDescriptor().wrap( toGeometry( statement.getObject( name ) ), options ); } }; } public static Geometry<?> toGeometry(Object object) { if ( object == null ) { return null; } ByteBuffer buffer = null; if ( object instanceof PGobject ) { String pgValue = ((PGobject) object).getValue(); if ( pgValue.startsWith( "00" ) || pgValue.startsWith( "01" ) ) { //we have a WKB because this pgValue starts with the bit-order byte buffer = ByteBuffer.from( pgValue ); final WkbDecoder decoder = Wkb.newDecoder( Wkb.Dialect.POSTGIS_EWKB_1 ); return decoder.decode( buffer ); } else { return parseWkt( pgValue ); } } throw new IllegalStateException( "Received object of type " + object.getClass().getCanonicalName() ); } private static Geometry<?> parseWkt(String pgValue) { final WktDecoder decoder = Wkt.newDecoder( Wkt.Dialect.POSTGIS_EWKT_1 ); return decoder.decode( pgValue ); } }