org.dspace.app.rest.utils.RestRepositoryUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.dspace.app.rest.utils.RestRepositoryUtils.java

Source

/**
 * The contents of this file are subject to the license and copyright
 * detailed in the LICENSE and NOTICE files at the root of the source
 * tree and available online at
 *
 * http://www.dspace.org/license/
 */
package org.dspace.app.rest.utils;

import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;

import org.apache.commons.lang3.StringUtils;
import org.dspace.app.rest.SearchRestMethod;
import org.dspace.app.rest.repository.DSpaceRestRepository;
import org.dspace.app.rest.repository.LinkRestRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.Param;
import org.springframework.data.repository.support.QueryMethodParameterConversionException;
import org.springframework.data.web.PagedResourcesAssembler;
import org.springframework.hateoas.core.AnnotationAttribute;
import org.springframework.hateoas.core.MethodParameters;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ReflectionUtils;

/**
 * Collection of utility methods to work with the Rest Repositories
 * 
 * @author Andrea Bollini (andrea.bollini at 4science.it)
 * 
 */
@Component
public class RestRepositoryUtils {
    private static final AnnotationAttribute PARAM_ANNOTATION = new AnnotationAttribute(Param.class);
    private static final String NAME_NOT_FOUND = "Unable to detect parameter names for query method %s! Use @Param or compile with -parameters on JDK 8.";

    @Autowired(required = true)
    @Qualifier(value = "mvcConversionService")
    private ConversionService conversionService;

    /**
     * 
     * @param repository
     * @return if the repository have at least one search method
     */
    public boolean haveSearchMethods(DSpaceRestRepository repository) {
        for (Method method : repository.getClass().getMethods()) {
            SearchRestMethod ann = method.getAnnotation(SearchRestMethod.class);
            if (ann != null) {
                return true;
            }
        }
        return false;
    }

    /**
     * 
     * @param repository
     * @return the names of the search methods if any. Otherwise an empty list
     */
    public List<String> listSearchMethods(DSpaceRestRepository repository) {
        List<String> searchMethods = new LinkedList<String>();
        for (Method method : repository.getClass().getMethods()) {
            SearchRestMethod ann = method.getAnnotation(SearchRestMethod.class);
            if (ann != null) {
                String name = ann.name();
                if (name.isEmpty()) {
                    name = method.getName();
                }
                searchMethods.add(name);
            }
        }

        return searchMethods;
    }

    /**
     * 
     * @param searchMethodName
     * @param repository
     * @return the search method in the repository with the specified name or
     *         null if it is not found
     */
    public Method getSearchMethod(String searchMethodName, DSpaceRestRepository repository) {
        Method searchMethod = null;
        for (Method method : repository.getClass().getMethods()) {
            SearchRestMethod ann = method.getAnnotation(SearchRestMethod.class);
            if (ann != null) {
                String name = ann.name();
                if (name.isEmpty()) {
                    name = method.getName();
                }
                if (StringUtils.equals(name, searchMethodName)) {
                    searchMethod = method;
                    break;
                }
            }
        }
        return searchMethod;
    }

    /*
     * Adapted from
     * org.springframework.data.rest.webmvc.RepositorySearchController.
     * executeQueryMethod(RepositoryInvoker, MultiValueMap<String, Object>,
     * Method, DefaultedPageable, Sort, PersistentEntityResourceAssembler)
     */
    public Object executeQueryMethod(DSpaceRestRepository repository, MultiValueMap<String, Object> parameters,
            Method method, Pageable pageable, Sort sort, PagedResourcesAssembler assembler) {

        MultiValueMap<String, Object> result = new LinkedMultiValueMap<String, Object>(parameters);
        MethodParameters methodParameters = new MethodParameters(method, new AnnotationAttribute(Param.class));

        for (Entry<String, List<Object>> entry : parameters.entrySet()) {

            MethodParameter parameter = methodParameters.getParameter(entry.getKey());

            if (parameter == null) {
                continue;
            }

            result.put(parameter.getParameterName(), entry.getValue());
        }

        return invokeQueryMethod(repository, method, result, pageable, sort);
    }

    /*
     * Adapted from
     * org.springframework.data.repository.support.ReflectionRepositoryInvoker.
     * invokeQueryMethod(Method, MultiValueMap<String, ? extends Object>,
     * Pageable, Sort)
     */
    public Object invokeQueryMethod(DSpaceRestRepository repository, Method method,
            MultiValueMap<String, ? extends Object> parameters, Pageable pageable, Sort sort) {

        Assert.notNull(method, "Method must not be null!");
        Assert.notNull(parameters, "Parameters must not be null!");

        ReflectionUtils.makeAccessible(method);

        return ReflectionUtils.invokeMethod(method, repository,
                prepareParameters(method, parameters, pageable, sort));
    }

    /*
     * Taken from
     * org.springframework.data.repository.support.ReflectionRepositoryInvoker.
     * prepareParameters(Method, MultiValueMap<String, ? extends Object>,
     * Pageable, Sort)
     */
    private Object[] prepareParameters(Method method, MultiValueMap<String, ? extends Object> rawParameters,
            Pageable pageable, Sort sort) {

        List<MethodParameter> parameters = new MethodParameters(method, PARAM_ANNOTATION).getParameters();

        if (parameters.isEmpty()) {
            return new Object[0];
        }

        Object[] result = new Object[parameters.size()];
        Sort sortToUse = pageable == null ? sort : pageable.getSort();

        for (int i = 0; i < result.length; i++) {

            MethodParameter param = parameters.get(i);
            Class<?> targetType = param.getParameterType();

            if (Pageable.class.isAssignableFrom(targetType)) {
                result[i] = pageable;
            } else if (Sort.class.isAssignableFrom(targetType)) {
                result[i] = sortToUse;
            } else {

                String parameterName = param.getParameterName();

                if (StringUtils.isBlank(parameterName)) {
                    throw new IllegalArgumentException(
                            String.format(NAME_NOT_FOUND, ClassUtils.getQualifiedMethodName(method)));
                }

                Object value = unwrapSingleElement(rawParameters.get(parameterName));

                result[i] = targetType.isInstance(value) ? value : convert(value, param);
            }
        }

        return result;
    }

    /**
     * Unwraps the first item if the given source has exactly one element. Taken
     * from
     * org.springframework.data.repository.support.ReflectionRepositoryInvoker.unwrapSingleElement(List<?
     * extends Object>)
     * 
     * @param source
     *            can be {@literal null}.
     * @return
     */
    private static Object unwrapSingleElement(List<? extends Object> source) {
        return source == null ? null : source.size() == 1 ? source.get(0) : source;
    }

    /**
     * Taken from
     * org.springframework.data.repository.support.ReflectionRepositoryInvoker.convert(Object,
     * MethodParameter)
     * 
     * @param value
     * @param parameter
     * @return
     */
    private Object convert(Object value, MethodParameter parameter) {

        try {
            return conversionService.convert(value, TypeDescriptor.forObject(value), new TypeDescriptor(parameter));
        } catch (ConversionException o_O) {
            throw new QueryMethodParameterConversionException(value, parameter, o_O);
        }
    }

    public Method getLinkMethod(String methodName, LinkRestRepository linkRepository) {
        Method linkMethod = null;
        Method[] methods = linkRepository.getClass().getMethods();
        for (Method m : methods) {
            if (StringUtils.equals(m.getName(), methodName)) {
                linkMethod = m;
                break;
            }
        }
        return linkMethod;
    }

}