# PostgreSQLTypeHelper.java

    /*
     * Copyright (c) 2010, Stanislav Muhametsin. All Rights Reserved.
     *
     * 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.qi4j.index.sql.support.postgresql;

    import java.lang.reflect.ParameterizedType;
    import java.lang.reflect.Type;
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
    import java.sql.Timestamp;
    import java.util.Date;
    import org.qi4j.api.common.Optional;
    import org.qi4j.api.injection.scope.This;
    import org.qi4j.api.mixin.Mixins;
    import org.qi4j.index.sql.support.skeletons.SQLDBState;

    /**
     * A little helper interface to convert java types to SQL types when inserting objects to prepared statements. It will
     * first preprocess the object and possibly convert it to something else (like Character to Integer). Then it will use
     * the {@link SQLDBState#javaTypes2SQLTypes()} mapping to determine which SQL datatype to use when inserting the
     * object to prepared statement.
     */
    @Mixins( PostgreSQLTypeHelper.SQLTypeHelperMixin.class )
    public interface PostgreSQLTypeHelper
    {

        /**
         * Adds the specified object at specified index in specified prepared statement.
         *
         * @param ps The prepared statement.
         * @param index The index for the object to be inserted in prepared statemtent ({@code > 0}).
         * @param primitive The object to insert.
         * @param primitiveType The type of object.
         * @throws SQLException If something underlying throws it.
         */
        void addPrimitiveToPS( PreparedStatement ps, Integer index, @Optional Object primitive, Type primitiveType )
            throws SQLException;

        Integer getSQLType( Object primitive );

        public class SQLTypeHelperMixin
            implements PostgreSQLTypeHelper
        {

            @This
            private SQLDBState _state;

            public Object processJavaPrimitiveBeforeUsingInStatement( Object primitive )
            {
                if( primitive != null )
                {
                    if( primitive instanceof Character )
                    {
                        primitive = Character.codePointAt( new char[]
                        {
                            (Character) primitive
                        }, 0 );
                    }
                    else if( primitive instanceof Date )
                    {
                        primitive = new Timestamp( ( (Date) primitive ).getTime() );
                    }
                    else if( primitive instanceof Byte )
                    {
                        primitive = (short) (Byte) primitive;
                    }
                    else if( primitive instanceof Short )
                    {
                        primitive = (int) (Short) primitive;
                    }
                }
                return primitive;
            }

            @Override
            public void addPrimitiveToPS( PreparedStatement ps, Integer index, Object primitive, Type primitiveType )
                throws SQLException
            {
                primitive = processJavaPrimitiveBeforeUsingInStatement( primitive );
                Integer sqlType = this.getSQLType( primitiveType );
                ps.setObject( index, primitive, sqlType );
            }

            @Override
            public Integer getSQLType( Object primitive )
            {
                primitive = processJavaPrimitiveBeforeUsingInStatement( primitive );

                return this.getSQLType( primitive.getClass() );
            }

            private Integer getSQLType( Type primitiveType )
            {
                if( primitiveType instanceof ParameterizedType )
                {
                    primitiveType = ( (ParameterizedType) primitiveType ).getRawType();
                }

                Class<?> primitiveClass = (Class<?>) primitiveType;

                if( Enum.class.isAssignableFrom( primitiveClass ) )
                {
                    primitiveClass = Enum.class;
                }
                Integer sqlType = this._state.javaTypes2SQLTypes().get().get( primitiveClass );
                if( sqlType == null )
                {
                    throw new InternalError( "Could not find mapping from " + primitiveClass + " to SQL type." );
                }

                return sqlType;
            }
        }

    }