All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.engine.query.spi.ParamLocationRecognizer Maven / Gradle / Ivy

There is a newer version: 7.0.0.Alpha1
Show newest version
/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or .
 */
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;

/**
 * Implements a parameter parser recognizer specifically for the purpose
 * of journaling parameter locations.
 *
 * @author Steve Ebersole
 */
public class ParamLocationRecognizer implements ParameterParser.Recognizer {

	private Map namedParameterDescriptors;
	private Map ordinalParameterDescriptors;

	private Map inFlightNamedStateMap;
	private Map inFlightOrdinalStateMap;
	private Map inFlightJpaOrdinalStateMap;

	private final int jdbcStyleOrdinalCountBase;
	private int jdbcStyleOrdinalCount;

	public ParamLocationRecognizer(int jdbcStyleOrdinalCountBase) {
		this.jdbcStyleOrdinalCountBase = jdbcStyleOrdinalCountBase;
		this.jdbcStyleOrdinalCount = jdbcStyleOrdinalCountBase;
	}

	/**
	 * Convenience method for creating a param location recognizer and
	 * initiating the parse.
	 *
	 * @param query The query to be parsed for parameter locations.
	 * @param sessionFactory
	 * @return The generated recognizer, with journaled location info.
	 */
	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();
		}

		// we know `inFlightNamedStateMap` is null, so no need to check it again

		if ( inFlightOrdinalStateMap != null && inFlightJpaOrdinalStateMap != null ) {
			throw mixedParamStrategy();
		}

		if ( inFlightNamedStateMap != null ) {
			final Map 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 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 getNamedParameterDescriptionMap() {
		return namedParameterDescriptors;
	}

	public Map getOrdinalParameterDescriptionMap() {
		return ordinalParameterDescriptors;
	}


	// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Recognition code ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


	// NOTE : we keep track of `inFlightOrdinalStateMap` versus `inFlightJpaOrdinalStateMap`
	//		in order to perform better validations of mixed parameter strategies

	@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) {
		// don't care...
	}

	@Override
	public void outParameter(int position) {
		// don't care...
	}


	/**
	 * Internal in-flight representation of a recognized named parameter
	 */
	public static class InFlightNamedParameterState {
		private final String name;
		private final List 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 )
			);
		}
	}


	/**
	 * Internal in-flight representation of a recognized named parameter
	 */
	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 }
			);
		}
	}


	/**
	 * Internal in-flight representation of a recognized named parameter
	 */
	public static class InFlightJpaOrdinalParameterState {
		private final int identifier;
		private final List 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 )
			);
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy