package org.springframework.data.simpledb.query;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.simpledb.annotation.Query;
import org.springframework.data.simpledb.core.SimpleDbDomain;
import org.springframework.data.simpledb.query.parser.QueryParserUtils;
import org.springframework.data.simpledb.reflection.ReflectionUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Iterator;
import java.util.List;
/**
* SimpleDB specific extension of {@link org.springframework.data.repository.query.QueryMethod}. <br/>
* Adds query extraction based on custom Query annotation and query validation helper methods.
*/
public class SimpleDbQueryMethod extends QueryMethod {
// TODO add here custom query extractor
private final Method method;
private final SimpleDbDomain simpleDbDomain;
/**
* Creates a new {@link org.springframework.data.simpledb.query.SimpleDbQueryMethod}
*
* @param method
* must not be {@literal null}
* @param metadata
* must not be {@literal null}
* @param simpleDbDomain
*/
public SimpleDbQueryMethod(Method method, RepositoryMetadata metadata, SimpleDbDomain simpleDbDomain) {
super(method, metadata);
this.method = method;
this.simpleDbDomain = simpleDbDomain;
Assert.isTrue(!(isModifyingQuery() && getParameters().hasSpecialParameter()),
String.format("Modifying method must not contain %s!", Parameters.TYPES));
}
private void assertParameterNamesInAnnotatedQuery(String annotatedQuery) {
if(!StringUtils.hasText(annotatedQuery)) {
return;
}
for(Parameter parameter : getParameters()) {
if(!parameter.isNamedParameter()) {
continue;
}
if(!annotatedQuery.contains(String.format(":%s", parameter.getName()))) {
throw new IllegalStateException(String.format(
"Using named parameters for method %s but parameter '%s' not found in annotated query '%s'!",
method, parameter.getName(), annotatedQuery));
}
}
}
/**
* Returns the query string declared in a {@link Query} annotation or {@literal null} if neither the annotation
* found nor the attribute was specified.
*
* @return a Query String
*/
public final String getAnnotatedQuery() {
String valueParameter = getValueParameters();
String whereParameters = getWhereParameters();
String[] selectParameters = getSelectParameters();
String result = QueryParserUtils.buildQueryFromQueryParameters(valueParameter, selectParameters, whereParameters,
simpleDbDomain.getDomain(getDomainClass()));
assertParameterNamesInAnnotatedQuery(result);
return result;
}
protected String[] getSelectParameters() {
return getAnnotationValue(Query.QueryClause.SELECT.getQueryClause(), String[].class);
}
protected String getWhereParameters() {
return getAnnotationValue(Query.QueryClause.WHERE.getQueryClause(), String.class);
}
protected String getValueParameters() {
return getAnnotationValue(Query.QueryClause.VALUE.getQueryClause(), String.class);
}
/**
* Returns the {@link Query} annotation's attribute casted to the given type or default value if no annotation
* available.
*
* @param attribute
* @param type
* @return
*/
private <T> T getAnnotationValue(String attribute, Class<T> type) {
Query annotation = method.getAnnotation(Query.class);
Object value = annotation == null ? AnnotationUtils.getDefaultValue(Query.class, attribute) : AnnotationUtils
.getValue(annotation, attribute);
return type.cast(value);
}
public Class<?> getDomainClazz() {
return super.getDomainClass();
}
public Class<?> getReturnType() {
return method.getReturnType();
}
public boolean returnsFieldOfTypeCollection() {
String query = getAnnotatedQuery();
if(!isCollectionQuery()) {
return false;
}
List<String> attributesFromQuery = QueryUtils.getQueryPartialFieldNames(query);
if(attributesFromQuery.size() != 1) {
return false;
}
String attributeName = attributesFromQuery.get(0);
return ReflectionUtils.isOfType(method.getGenericReturnType(), getDomainClass(), attributeName);
}
public boolean returnsListOfListOfObject() {
Type returnedGenericType = getCollectionGenericType();
return ReflectionUtils.isListOfListOfObject(returnedGenericType);
}
public boolean returnsCollectionOfDomainClass() {
Type returnedGenericType = getCollectionGenericType();
return returnedGenericType.equals(getDomainClass());
}
private Type getCollectionGenericType() {
Type returnType = method.getGenericReturnType();
if(isCollectionQuery()) {
return ((ParameterizedType) returnType).getActualTypeArguments()[0];
} else {
return returnType;
}
}
/**
* @return whether or not the query method contains a {@link Pageable} parameter in its signature.
*/
public boolean isPagedQuery() {
boolean isPaged = false;
final Iterator<Parameter> it = getParameters().iterator();
while(it.hasNext()) {
final Parameter param = it.next();
if(Pageable.class.isAssignableFrom(param.getType())) {
isPaged = true;
break;
}
}
return isPaged;
}
public static boolean isAnnotatedQuery(final Method method) {
return method.getAnnotation(Query.class) != null;
}
}