/*************************************************************************** * Copyright (C) 2010 by Fabrizio Montesi <famontesi@gmail.com> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * * * For details about the authors of this software, see the AUTHORS file. * ***************************************************************************/ package joliex.db.impl; import java.sql.Connection; import java.sql.Date; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Time; import java.sql.Timestamp; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import jolie.runtime.Value; /** * Commodity class for converting a parametrized * query in a valid JDBC {@link PreparedStatement}. * * @author Fabrizio Montesi * @see PreparedStatement */ public class NamedStatementParser { private static class TypeKeywords { private final static String DATE = "Date"; private final static String TIMESTAMP = "Timestamp"; private final static String TIME = "Time"; } private final Map< String, List< Integer > > parameterPositions = new HashMap< String, List< Integer > >(); private final PreparedStatement statement; public NamedStatementParser( Connection connection, String sql, Value parameters ) throws SQLException { String jdbcSql = parse( sql ); statement = connection.prepareStatement( jdbcSql ); Value v; for( Entry< String, List< Integer > > entry : parameterPositions.entrySet() ) { v = parameters.getFirstChild( entry.getKey() ); if ( v.isInt() ) { for( Integer index : entry.getValue() ) { statement.setInt( index, v.intValue() ); } } else if ( v.isDouble() ) { for( Integer index : entry.getValue() ) { statement.setDouble( index, v.doubleValue() ); } } else if ( v.isLong() ) { for( Integer index : entry.getValue() ) { statement.setLong( index, v.longValue() ); } } else if ( v.isBool() ) { for( Integer index : entry.getValue() ) { statement.setBoolean( index, v.boolValue() ); } } else if ( v.isByteArray() ) { for( Integer index : entry.getValue() ) { statement.setBytes( index, v.byteArrayValue().getBytes() ); } } else { if ( v.hasChildren( TypeKeywords.DATE ) ) { Value date = v.getFirstChild( TypeKeywords.DATE ); for( Integer index : entry.getValue() ) { String month = String.valueOf( date.getFirstChild( "month").intValue() ); String day = String.valueOf( date.getFirstChild( "day").intValue() ); if ( month.length() < 2 ) { month = "0" + month; } if ( day.length() < 2 ) { day = "0" + day; } statement.setDate( index, Date.valueOf( date.getFirstChild( "year").intValue() + "-" + month + "-" +day ) ); } } else if ( v.hasChildren( TypeKeywords.TIME ) ) { Value time = v.getFirstChild( TypeKeywords.TIME ); for( Integer index : entry.getValue() ) { String hour = String.valueOf( time.getFirstChild( "hour" ).intValue() ); String minute = String.valueOf( time.getFirstChild( "minute" ).intValue() ); String second = String.valueOf( time.getFirstChild( "second" ).intValue() ); if ( hour.length() < 2 ) { hour = "0" + hour; } statement.setTime( index, Time.valueOf( hour + ":" + minute + ":" + second ) ); } } else if ( v.hasChildren( TypeKeywords.TIMESTAMP ) ) { Value timestampValue = v.getFirstChild( TypeKeywords.TIMESTAMP ); Timestamp timestamp = new Timestamp( timestampValue.getFirstChild( "epoch" ).longValue() ); for( Integer index : entry.getValue() ) { statement.setTimestamp( index, timestamp ); } } else { for( Integer index : entry.getValue() ) { statement.setString( index, v.strValue() ); } } } } } private String parse( String sql ) { int length = sql.length(); int index = 1; boolean inDoubleQuote = false; boolean inSingleQuote = false; char c; StringBuilder builder = new StringBuilder( length ); int j; String name; List< Integer > positions; for( int i = 0; i < length; i++ ) { c = sql.charAt( i ); if ( inSingleQuote ) { if ( c == '\'' ) { inSingleQuote = false; } } else if ( inDoubleQuote ) { if ( c == '"' ) { inDoubleQuote = false; } } else { if ( c == '\'' ) { inSingleQuote = true; } else if ( c == '"' ) { inDoubleQuote = true; } else if ( c == ':' && i+1 < length && Character.isJavaIdentifierPart( sql.charAt( i+1 ) ) && ( i == 0 || sql.charAt( i - 1 ) != ':' ) ) { j = i + 2; while( j < length && Character.isJavaIdentifierPart( sql.charAt( j ) ) ) { j++; } name = sql.substring( i + 1, j ); c = '?'; i += name.length(); positions = getParameterPositions( name ); positions.add( index++ ); } } builder.append( c ); } return builder.toString(); } private List< Integer > getParameterPositions( String parameterName ) { List< Integer > ret = parameterPositions.get( parameterName ); if ( ret == null ) { ret = new ArrayList< Integer >(); parameterPositions.put( parameterName, ret ); } return ret; } public PreparedStatement getPreparedStatement() { return statement; } }