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

org.springframework.data.jpa.repository.query.ParameterMetadataProvider Maven / Gradle / Ivy

There is a newer version: 3.3.3
Show newest version
package org.springframework.data.jpa.repository.query;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.ParameterExpression;

import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.query.parser.Part;
import org.springframework.data.repository.query.parser.Part.Type;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;

/**
 * Helper class to allow easy creation of {@link ParameterMetadata}s.
 * 
 * @author Oliver Gierke
 */
class ParameterMetadataProvider {

	private final CriteriaBuilder builder;
	private final Iterator parameters;
	private final List> expressions;
	private Iterator accessor;

	/**
	 * Creates a new {@link ParameterMetadataProvider} from the given {@link CriteriaBuilder} and
	 * {@link ParametersParameterAccessor}.
	 * 
	 * @param builder must not be {@literal null}.
	 * @param parameters must not be {@literal null}.
	 */
	public ParameterMetadataProvider(CriteriaBuilder builder, ParametersParameterAccessor accessor) {

		this(builder, accessor.getParameters());
		Assert.notNull(accessor);
		this.accessor = accessor.iterator();
	}

	public ParameterMetadataProvider(CriteriaBuilder builder, Parameters parameters) {

		Assert.notNull(builder);

		this.builder = builder;
		this.parameters = parameters.getBindableParameters().iterator();
		this.expressions = new ArrayList>();
		this.accessor = null;
	}

	/**
	 * Returns all {@link ParameterMetadata}s built.
	 * 
	 * @return the expressions
	 */
	public List> getExpressions() {
		return Collections.unmodifiableList(expressions);
	}

	/**
	 * Builds a new {@link ParameterMetadata} for given {@link Part} and the next {@link Parameter}.
	 * 
	 * @param 
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public  ParameterMetadata next(Part part) {

		Parameter parameter = parameters.next();
		return (ParameterMetadata) next(part, parameter.getType(), parameter.getName());
	}

	/**
	 * Builds a new {@link ParameterMetadata} of the given {@link Part} and type. Forwards the underlying
	 * {@link Parameters} as well.
	 * 
	 * @param 
	 * @param type must not be {@literal null}.
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public  ParameterMetadata next(Part part, Class type) {

		Parameter parameter = parameters.next();
		Class typeToUse = ClassUtils.isAssignable(type, parameter.getType()) ? parameter.getType() : type;
		return (ParameterMetadata) next(part, typeToUse, null);
	}

	/**
	 * Builds a new {@link ParameterMetadata} for the given type and name.
	 * 
	 * @param 
	 * @param part must not be {@literal null}.
	 * @param type must not be {@literal null}.
	 * @param name
	 * @return
	 */
	private  ParameterMetadata next(Part part, Class type, String name) {

		Assert.notNull(type);

		ParameterExpression expression = name == null ? builder.parameter(type) : builder.parameter(type, name);
		ParameterMetadata value = new ParameterMetadata(expression, part.getType(),
				accessor == null ? ParameterMetadata.PLACEHOLDER : accessor.next());
		expressions.add(value);

		return value;
	}

	static class ParameterMetadata {

		static final Object PLACEHOLDER = new Object();

		private final ParameterExpression expression;
		private final Type type;

		public ParameterMetadata(ParameterExpression expression, Type type, Object value) {

			this.expression = expression;
			this.type = value == null && Type.SIMPLE_PROPERTY.equals(type) ? Type.IS_NULL : type;
		}

		/**
		 * Returns the {@link ParameterExpression}.
		 * 
		 * @return the expression
		 */
		public ParameterExpression getExpression() {
			return expression;
		}

		/**
		 * Returns whether the parameter shall be considered an {@literal IS NULL} parameter.
		 * 
		 * @return
		 */
		public boolean isIsNullParameter() {
			return Type.IS_NULL.equals(type);
		}

		/**
		 * Prepares the object before it's actually bound to the {@link javax.persistence.Query;}.
		 * 
		 * @param parameter must not be {@literal null}.
		 * @return
		 */
		public Object prepare(Object parameter) {

			Assert.notNull(parameter);

			switch (type) {
			case STARTING_WITH:
				return String.format("%s%%", parameter.toString());
			case ENDING_WITH:
				return String.format("%%%s", parameter.toString());
			case CONTAINING:
				return String.format("%%%s%%", parameter.toString());
			default:
				return Collection.class.equals(expression.getJavaType()) ? toCollection(parameter) : parameter;
			}
		}

		/**
		 * Return sthe given argument as {@link Collection} which means it will return it as is if it's a
		 * {@link Collections}, turn an array into an {@link ArrayList} or simply wrap any other value into a single element
		 * {@link Collections}.
		 * 
		 * @param value
		 * @return
		 */
		private static Collection toCollection(Object value) {

			if (value == null) {
				return null;
			}

			if (value instanceof Collection) {
				return (Collection) value;
			}

			if (ObjectUtils.isArray(value)) {
				return Arrays.asList(ObjectUtils.toObjectArray(value));
			}

			return Collections.singleton(value);
		}
	}
}