package org.hibernate.hql.internal.ast.tree;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.hibernate.QueryException;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.type.ComponentType;
import org.hibernate.type.Type;
import antlr.collections.AST;
public class IntoClause extends HqlSqlWalkerNode implements DisplayableNode {
private Queryable persister;
private String columnSpec = "";
private Type[] types;
private boolean discriminated;
private boolean explicitIdInsertion;
private boolean explicitVersionInsertion;
private Set componentIds;
private List explicitComponentIds;
public void initialize(Queryable persister) {
if ( persister.isAbstract() ) {
throw new QueryException( "cannot insert into abstract class (no table)" );
}
this.persister = persister;
initializeColumns();
if ( getWalker().getSessionFactoryHelper().hasPhysicalDiscriminatorColumn( persister ) ) {
discriminated = true;
columnSpec += ", " + persister.getDiscriminatorColumnName();
}
resetText();
}
private void resetText() {
setText( "into " + getTableName() + " ( " + columnSpec + " )" );
}
public String getTableName() {
return persister.getSubclassTableName( 0 );
}
public Queryable getQueryable() {
return persister;
}
public String getEntityName() {
return persister.getEntityName();
}
public Type[] getInsertionTypes() {
return types;
}
public boolean isDiscriminated() {
return discriminated;
}
public boolean isExplicitIdInsertion() {
return explicitIdInsertion;
}
public boolean isExplicitVersionInsertion() {
return explicitVersionInsertion;
}
public void prependIdColumnSpec() {
columnSpec = persister.getIdentifierColumnNames()[0] + ", " + columnSpec;
resetText();
}
public void prependVersionColumnSpec() {
columnSpec = persister.getPropertyColumnNames( persister.getVersionProperty() )[0] + ", " + columnSpec;
resetText();
}
public void validateTypes(SelectClause selectClause) throws QueryException {
Type[] selectTypes = selectClause.getQueryReturnTypes();
if ( selectTypes.length + selectClause.getTotalParameterCount() != types.length ) {
throw new QueryException( "number of select types did not match those for insert" );
}
int parameterCount = 0;
for ( int i = 0; i < types.length; i++ ) {
if( selectClause.getParameterPositions().contains(i) ) {
parameterCount++;
}
else if ( !areCompatible( types[i], selectTypes[i - parameterCount] ) ) {
throw new QueryException(
"insertion type [" + types[i] + "] and selection type [" +
selectTypes[i - parameterCount] + "] at position " + i + " are not compatible"
);
}
}
}
public String getDisplayText() {
StringBuilder buf = new StringBuilder();
buf.append( "IntoClause{" );
buf.append( "entityName=" ).append( getEntityName() );
buf.append( ",tableName=" ).append( getTableName() );
buf.append( ",columns={" ).append( columnSpec ).append( "}" );
buf.append( "}" );
return buf.toString();
}
private void initializeColumns() {
AST propertySpec = getFirstChild();
List types = new ArrayList();
visitPropertySpecNodes( propertySpec.getFirstChild(), types );
this.types = ArrayHelper.toTypeArray( types );
columnSpec = columnSpec.substring( 0, columnSpec.length() - 2 );
}
private void visitPropertySpecNodes(AST propertyNode, List types) {
if ( propertyNode == null ) {
return;
}
String name = propertyNode.getText();
if ( isSuperclassProperty( name ) ) {
throw new QueryException( "INSERT statements cannot refer to superclass/joined properties [" + name + "]" );
}
if ( !explicitIdInsertion ) {
if ( persister.getIdentifierType() instanceof ComponentType ) {
if ( componentIds == null ) {
String[] propertyNames = ( (ComponentType) persister.getIdentifierType() ).getPropertyNames();
componentIds = new HashSet();
for ( int i = 0; i < propertyNames.length; i++ ) {
componentIds.add( propertyNames[i] );
}
}
if ( componentIds.contains(name) ) {
if ( explicitComponentIds == null ) {
explicitComponentIds = new ArrayList( componentIds.size() );
}
explicitComponentIds.add( name );
explicitIdInsertion = explicitComponentIds.size() == componentIds.size();
}
} else if ( name.equals( persister.getIdentifierPropertyName() ) ) {
explicitIdInsertion = true;
}
}
if ( persister.isVersioned() ) {
if ( name.equals( persister.getPropertyNames()[ persister.getVersionProperty() ] ) ) {
explicitVersionInsertion = true;
}
}
String[] columnNames = persister.toColumns( name );
renderColumns( columnNames );
types.add( persister.toType( name ) );
visitPropertySpecNodes( propertyNode.getNextSibling(), types );
visitPropertySpecNodes( propertyNode.getFirstChild(), types );
}
private void renderColumns(String[] columnNames) {
for ( int i = 0; i < columnNames.length; i++ ) {
columnSpec += columnNames[i] + ", ";
}
}
private boolean isSuperclassProperty(String propertyName) {
return persister.getSubclassPropertyTableNumber( propertyName ) != 0;
}
private boolean areCompatible(Type target, Type source) {
if ( target.equals( source ) ) {
return true;
}
if ( !target.getReturnedClass().isAssignableFrom( source.getReturnedClass() ) ) {
return false;
}
int[] targetDatatypes = target.sqlTypes( getSessionFactoryHelper().getFactory() );
int[] sourceDatatypes = source.sqlTypes( getSessionFactoryHelper().getFactory() );
if ( targetDatatypes.length != sourceDatatypes.length ) {
return false;
}
for ( int i = 0; i < targetDatatypes.length; i++ ) {
if ( !areSqlTypesCompatible( targetDatatypes[i], sourceDatatypes[i] ) ) {
return false;
}
}
return true;
}
private boolean areSqlTypesCompatible(int target, int source) {
switch ( target ) {
case Types.TIMESTAMP:
return source == Types.DATE || source == Types.TIME || source == Types.TIMESTAMP;
case Types.DATE:
return source == Types.DATE || source == Types.TIMESTAMP;
case Types.TIME:
return source == Types.TIME || source == Types.TIMESTAMP;
default:
return target == source;
}
}
}