package org.hibernate.jpa.spi;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.persistence.Tuple;
import javax.persistence.TupleElement;
import org.hibernate.HibernateException;
import org.hibernate.transform.BasicTransformerAdapter;
public class NativeQueryTupleTransformer extends BasicTransformerAdapter {
@Override
public Object transformTuple(Object[] tuple, String[] aliases) {
return new NativeTupleImpl( tuple, aliases );
}
private static class NativeTupleElementImpl<X> implements TupleElement<X> {
private final Class<? extends X> javaType;
private final String alias;
public NativeTupleElementImpl(Class<? extends X> javaType, String alias) {
this.javaType = javaType;
this.alias = alias;
}
@Override
public Class<? extends X> getJavaType() {
return javaType;
}
@Override
public String getAlias() {
return alias;
}
}
private static class NativeTupleImpl implements Tuple {
private Object[] tuple;
private Map<String, Object> aliasToValue = new LinkedHashMap<>();
private Map<String, String> aliasReferences = new LinkedHashMap<>();
public NativeTupleImpl(Object[] tuple, String[] aliases) {
if ( tuple == null ) {
throw new HibernateException( "Tuple must not be null" );
}
if ( aliases == null ) {
throw new HibernateException( "Aliases must not be null" );
}
if ( tuple.length != aliases.length ) {
throw new HibernateException( "Got different size of tuples and aliases" );
}
this.tuple = tuple;
for ( int i = 0; i < tuple.length; i++ ) {
aliasToValue.put( aliases[i], tuple[i] );
aliasReferences.put( aliases[i].toLowerCase(), aliases[i] );
}
}
@Override
public <X> X get(String alias, Class<X> type) {
final Object untyped = get( alias );
return ( untyped != null ) ? type.cast( untyped ) : null;
}
@Override
public Object get(String alias) {
final String aliasReference = aliasReferences.get( alias.toLowerCase() );
if ( aliasReference != null && aliasToValue.containsKey( aliasReference ) ) {
return aliasToValue.get( aliasReference );
}
throw new IllegalArgumentException( "Unknown alias [" + alias + "]" );
}
@Override
public <X> X get(int i, Class<X> type) {
final Object untyped = get( i );
return ( untyped != null ) ? type.cast( untyped ) : null;
}
@Override
public Object get(int i) {
if ( i < 0 ) {
throw new IllegalArgumentException( "requested tuple index must be greater than zero" );
}
if ( i >= aliasToValue.size() ) {
throw new IllegalArgumentException( "requested tuple index exceeds actual tuple size" );
}
return tuple[i];
}
@Override
public Object[] toArray() {
return tuple;
}
@Override
public List<TupleElement<?>> getElements() {
List<TupleElement<?>> elements = new ArrayList<>( aliasToValue.size() );
for ( Map.Entry<String, Object> entry : aliasToValue.entrySet() ) {
elements.add( new NativeTupleElementImpl<>( getValueClass( entry.getValue() ), entry.getKey() ) );
}
return elements;
}
private Class<?> getValueClass(Object value) {
Class<?> valueClass = Object.class;
if ( value != null ) {
valueClass = value.getClass();
}
return valueClass;
}
@Override
public <X> X get(TupleElement<X> tupleElement) {
return get( tupleElement.getAlias(), tupleElement.getJavaType() );
}
}
}