package org.hibernate.engine.query.spi;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hibernate.engine.query.ParameterRecognitionException;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.ArrayHelper;
public class ParamLocationRecognizer implements ParameterParser.Recognizer {
private Map<String, NamedParameterDescriptor> namedParameterDescriptors;
private Map<Integer, OrdinalParameterDescriptor> ordinalParameterDescriptors;
private Map<String, InFlightNamedParameterState> inFlightNamedStateMap;
private Map<Integer, InFlightOrdinalParameterState> inFlightOrdinalStateMap;
private Map<Integer, InFlightJpaOrdinalParameterState> inFlightJpaOrdinalStateMap;
private final int jdbcStyleOrdinalCountBase;
private int jdbcStyleOrdinalCount;
public ParamLocationRecognizer(int jdbcStyleOrdinalCountBase) {
this.jdbcStyleOrdinalCountBase = jdbcStyleOrdinalCountBase;
this.jdbcStyleOrdinalCount = jdbcStyleOrdinalCountBase;
}
public static ParamLocationRecognizer parseLocations(
String query,
SessionFactoryImplementor sessionFactory) {
final ParamLocationRecognizer recognizer = new ParamLocationRecognizer(
sessionFactory.getSessionFactoryOptions().jdbcStyleParamsZeroBased() ? 0 : 1
);
ParameterParser.parse( query, recognizer );
return recognizer;
}
@Override
public void complete() {
if ( inFlightNamedStateMap != null && ( inFlightOrdinalStateMap != null || inFlightJpaOrdinalStateMap != null ) ) {
throw mixedParamStrategy();
}
if ( inFlightOrdinalStateMap != null && inFlightJpaOrdinalStateMap != null ) {
throw mixedParamStrategy();
}
if ( inFlightNamedStateMap != null ) {
final Map<String, NamedParameterDescriptor> tmp = new HashMap<>();
for ( InFlightNamedParameterState inFlightState : inFlightNamedStateMap.values() ) {
tmp.put( inFlightState.name, inFlightState.complete() );
}
namedParameterDescriptors = Collections.unmodifiableMap( tmp );
}
else {
namedParameterDescriptors = Collections.emptyMap();
}
if ( inFlightOrdinalStateMap == null && inFlightJpaOrdinalStateMap == null ) {
ordinalParameterDescriptors = Collections.emptyMap();
}
else {
final Map<Integer, OrdinalParameterDescriptor> tmp = new HashMap<>();
if ( inFlightOrdinalStateMap != null ) {
for ( InFlightOrdinalParameterState state : inFlightOrdinalStateMap.values() ) {
tmp.put( state.identifier, state.complete() );
}
}
else {
for ( InFlightJpaOrdinalParameterState state : inFlightJpaOrdinalStateMap.values() ) {
tmp.put( state.identifier, state.complete() );
}
}
ordinalParameterDescriptors = Collections.unmodifiableMap( tmp );
}
}
private ParameterRecognitionException mixedParamStrategy() {
throw new ParameterRecognitionException( "Mixed parameter strategies - use just one of named, positional or JPA-ordinal strategy" );
}
public Map<String, NamedParameterDescriptor> getNamedParameterDescriptionMap() {
return namedParameterDescriptors;
}
public Map<Integer, OrdinalParameterDescriptor> getOrdinalParameterDescriptionMap() {
return ordinalParameterDescriptors;
}
@Override
public void ordinalParameter(int position) {
if ( inFlightOrdinalStateMap == null ) {
inFlightOrdinalStateMap = new HashMap<>();
}
final int label = jdbcStyleOrdinalCount++;
inFlightOrdinalStateMap.put(
label,
new InFlightOrdinalParameterState( label, label - jdbcStyleOrdinalCountBase, position )
);
}
@Override
public void namedParameter(String name, int position) {
getOrBuildNamedParameterDescription( name ).add( position );
}
private InFlightNamedParameterState getOrBuildNamedParameterDescription(String name) {
if ( inFlightNamedStateMap == null ) {
inFlightNamedStateMap = new HashMap<>();
}
InFlightNamedParameterState descriptor = inFlightNamedStateMap.get( name );
if ( descriptor == null ) {
descriptor = new InFlightNamedParameterState( name );
inFlightNamedStateMap.put( name, descriptor );
}
return descriptor;
}
@Override
public void jpaPositionalParameter(int name, int position) {
getOrBuildJpaOrdinalParameterDescription( name ).add( position );
}
private InFlightJpaOrdinalParameterState getOrBuildJpaOrdinalParameterDescription(int name) {
if ( inFlightJpaOrdinalStateMap == null ) {
inFlightJpaOrdinalStateMap = new HashMap<>();
}
InFlightJpaOrdinalParameterState descriptor = inFlightJpaOrdinalStateMap.get( name );
if ( descriptor == null ) {
descriptor = new InFlightJpaOrdinalParameterState( name );
inFlightJpaOrdinalStateMap.put( name, descriptor );
}
return descriptor;
}
@Override
public void other(char character) {
}
@Override
public void outParameter(int position) {
}
public static class InFlightNamedParameterState {
private final String name;
private final List<Integer> sourcePositions = new ArrayList<>();
InFlightNamedParameterState(String name) {
this.name = name;
}
private void add(int position) {
sourcePositions.add( position );
}
private NamedParameterDescriptor complete() {
return new NamedParameterDescriptor(
name,
null,
ArrayHelper.toIntArray( sourcePositions )
);
}
}
public static class InFlightOrdinalParameterState {
private final int identifier;
private final int valuePosition;
private final int sourcePosition;
InFlightOrdinalParameterState(int label, int valuePosition, int sourcePosition) {
this.identifier = label;
this.valuePosition = valuePosition;
this.sourcePosition = sourcePosition;
}
private OrdinalParameterDescriptor complete() {
return new OrdinalParameterDescriptor(
identifier,
valuePosition,
null,
new int[] { sourcePosition }
);
}
}
public static class InFlightJpaOrdinalParameterState {
private final int identifier;
private final List<Integer> sourcePositions = new ArrayList<>();
InFlightJpaOrdinalParameterState(int identifier) {
this.identifier = identifier;
}
private void add(int position) {
sourcePositions.add( position );
}
private OrdinalParameterDescriptor complete() {
return new OrdinalParameterDescriptor(
identifier,
identifier - 1,
null,
ArrayHelper.toIntArray( sourcePositions )
);
}
}
}