package org.hibernate.query.internal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import javax.persistence.Parameter;
import org.hibernate.QueryException;
import org.hibernate.QueryParameterException;
import org.hibernate.engine.query.spi.NamedParameterDescriptor;
import org.hibernate.engine.query.spi.OrdinalParameterDescriptor;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.compare.ComparableComparator;
import org.hibernate.query.ParameterMetadata;
import org.hibernate.query.QueryParameter;
import org.hibernate.type.Type;
public class ParameterMetadataImpl implements ParameterMetadata {
private final Map<Integer,OrdinalParameterDescriptor> ordinalDescriptorMap;
private final Map<String,NamedParameterDescriptor> namedDescriptorMap;
public ParameterMetadataImpl(
Map<Integer,OrdinalParameterDescriptor> ordinalDescriptorMap,
Map<String, NamedParameterDescriptor> namedDescriptorMap) {
this.ordinalDescriptorMap = ordinalDescriptorMap == null
? Collections.emptyMap()
: Collections.unmodifiableMap( ordinalDescriptorMap );
this.namedDescriptorMap = namedDescriptorMap == null
? Collections.emptyMap()
: Collections.unmodifiableMap( namedDescriptorMap );
if (ordinalDescriptorMap != null && ! ordinalDescriptorMap.isEmpty() ) {
final List<Integer> sortedPositions = new ArrayList<>( ordinalDescriptorMap.keySet() );
sortedPositions.sort( ComparableComparator.INSTANCE );
int lastPosition = -1;
for ( Integer sortedPosition : sortedPositions ) {
if ( lastPosition == -1 ) {
lastPosition = sortedPosition;
continue;
}
if ( sortedPosition != lastPosition + 1 ) {
throw new QueryException(
String.format(
Locale.ROOT,
"Unexpected gap in ordinal parameter labels [%s -> %s] : [%s]",
lastPosition,
sortedPosition,
StringHelper.join( ",", sortedPositions.iterator() )
)
);
}
lastPosition = sortedPosition;
}
}
}
@Override
public Collection<QueryParameter> getPositionalParameters() {
return Collections.unmodifiableCollection( ordinalDescriptorMap.values() );
}
@Override
public Collection<QueryParameter> getNamedParameters() {
return Collections.unmodifiableCollection( namedDescriptorMap.values() );
}
@Override
public int getParameterCount() {
return ordinalDescriptorMap.size() + namedDescriptorMap.size();
}
@Override
@SuppressWarnings("SuspiciousMethodCalls")
public boolean containsReference(QueryParameter parameter) {
return ordinalDescriptorMap.containsValue( parameter )
|| namedDescriptorMap.containsValue( parameter );
}
@Override
public boolean hasNamedParameters() {
return !namedDescriptorMap.isEmpty();
}
@Override
public boolean hasPositionalParameters() {
return getOrdinalParameterCount() > 0;
}
@Override
public int getPositionalParameterCount() {
return getOrdinalParameterCount();
}
public int getOrdinalParameterCount() {
return ordinalDescriptorMap.size();
}
@Override
public Set<String> getNamedParameterNames() {
return namedDescriptorMap.keySet();
}
public Set<Integer> getOrdinalParameterLabels() {
return ordinalDescriptorMap.keySet();
}
public OrdinalParameterDescriptor getOrdinalParameterDescriptor(int position) {
final OrdinalParameterDescriptor descriptor = ordinalDescriptorMap.get( position );
if ( descriptor == null ) {
throw new IllegalArgumentException(
String.format(
Locale.ROOT,
"Could not locate ordinal parameter [%s], expecting one of [%s]",
position,
StringHelper.join( ", ", ordinalDescriptorMap.keySet().iterator())
)
);
}
return descriptor;
}
@Deprecated
public Type getOrdinalParameterExpectedType(int position) {
return getOrdinalParameterDescriptor( position ).getExpectedType();
}
@Deprecated
public int getOrdinalParameterSourceLocation(int position) {
return getOrdinalParameterDescriptor( position ).getPosition();
}
@Override
public <T> QueryParameter<T> getQueryParameter(String name) {
return getNamedParameterDescriptor( name );
}
@Override
@SuppressWarnings("unchecked")
public <T> QueryParameter<T> getQueryParameter(Integer position) {
return getOrdinalParameterDescriptor( position );
}
@Override
public <T> QueryParameter<T> resolve(Parameter<T> param) {
if ( param instanceof QueryParameter ) {
return (QueryParameter<T>) param;
}
throw new IllegalArgumentException( "Could not resolve javax.persistence.Parameter to org.hibernate.query.QueryParameter" );
}
public NamedParameterDescriptor getNamedParameterDescriptor(String name) {
final NamedParameterDescriptor descriptor = namedDescriptorMap.get( name );
if ( descriptor == null ) {
throw new IllegalArgumentException(
String.format(
Locale.ROOT,
"Could not locate named parameter [%s], expecting one of [%s]",
name,
String.join( ", ", namedDescriptorMap.keySet() )
)
);
}
return descriptor;
}
@Override
public void visitRegistrations(Consumer<QueryParameter> action) {
if ( hasPositionalParameters() ) {
for ( OrdinalParameterDescriptor descriptor : ordinalDescriptorMap.values() ) {
action.accept( descriptor );
}
}
else if ( hasNamedParameters() ) {
for ( NamedParameterDescriptor descriptor : namedDescriptorMap.values() ) {
action.accept( descriptor );
}
}
}
@Deprecated
public Type getNamedParameterExpectedType(String name) {
return getNamedParameterDescriptor( name ).getExpectedType();
}
@Deprecated
public int[] getNamedParameterSourceLocations(String name) {
return getNamedParameterDescriptor( name ).getSourceLocations();
}
@Override
@SuppressWarnings("unchecked")
public Set<QueryParameter<?>> collectAllParameters() {
if ( hasNamedParameters() || hasPositionalParameters() ) {
final HashSet allParameters = new HashSet();
allParameters.addAll( namedDescriptorMap.values() );
allParameters.addAll( ordinalDescriptorMap.values() );
return allParameters;
}
return Collections.emptySet();
}
@Override
@SuppressWarnings("unchecked")
public Set<Parameter<?>> collectAllParametersJpa() {
if ( hasNamedParameters() || hasPositionalParameters() ) {
final HashSet allParameters = new HashSet();
allParameters.addAll( namedDescriptorMap.values() );
allParameters.addAll( ordinalDescriptorMap.values() );
return allParameters;
}
return Collections.emptySet();
}
}