Package org.springframework.data.repository.core.support

Source Code of org.springframework.data.repository.core.support.DefaultRepositoryInformation

/*
* Copyright 2011-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.repository.core.support;

import static org.springframework.core.GenericTypeResolver.*;
import static org.springframework.data.repository.util.ClassUtils.*;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.annotation.QueryAnnotation;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.CrudMethods;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.util.ReflectionUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

/**
* Default implementation of {@link RepositoryInformation}.
*
* @author Oliver Gierke
* @author Thomas Darimont
*/
class DefaultRepositoryInformation implements RepositoryInformation {

  @SuppressWarnings("rawtypes") private static final TypeVariable<Class<Repository>>[] PARAMETERS = Repository.class
      .getTypeParameters();
  private static final String DOMAIN_TYPE_NAME = PARAMETERS[0].getName();
  private static final String ID_TYPE_NAME = PARAMETERS[1].getName();

  private final Map<Method, Method> methodCache = new ConcurrentHashMap<Method, Method>();

  private final RepositoryMetadata metadata;
  private final Class<?> repositoryBaseClass;
  private final Class<?> customImplementationClass;

  /**
   * Creates a new {@link DefaultRepositoryMetadata} for the given repository interface and repository base class.
   *
   * @param metadata must not be {@literal null}.
   * @param repositoryBaseClass must not be {@literal null}.
   * @param customImplementationClass
   */
  public DefaultRepositoryInformation(RepositoryMetadata metadata, Class<?> repositoryBaseClass,
      Class<?> customImplementationClass) {

    Assert.notNull(metadata);
    Assert.notNull(repositoryBaseClass);

    this.metadata = metadata;
    this.repositoryBaseClass = repositoryBaseClass;
    this.customImplementationClass = customImplementationClass;
  }

  /*
   * (non-Javadoc)
   * @see org.springframework.data.repository.support.RepositoryMetadata#getDomainClass()
   */
  @Override
  public Class<?> getDomainType() {
    return metadata.getDomainType();
  }

  /*
   * (non-Javadoc)
   * @see org.springframework.data.repository.support.RepositoryMetadata#getIdClass()
   */
  @Override
  public Class<? extends Serializable> getIdType() {
    return metadata.getIdType();
  }

  /*
   * (non-Javadoc)
   * @see org.springframework.data.repository.support.RepositoryInformation#getRepositoryBaseClass()
   */
  @Override
  public Class<?> getRepositoryBaseClass() {
    return this.repositoryBaseClass;
  }

  /*
   * (non-Javadoc)
   * @see org.springframework.data.repository.support.RepositoryInformation#getTargetClassMethod(java.lang.reflect.Method)
   */
  @Override
  public Method getTargetClassMethod(Method method) {

    if (methodCache.containsKey(method)) {
      return methodCache.get(method);
    }

    Method result = getTargetClassMethod(method, customImplementationClass);

    if (!result.equals(method)) {
      methodCache.put(method, result);
      return result;
    }

    result = getTargetClassMethod(method, repositoryBaseClass);
    methodCache.put(method, result);
    return result;
  }

  /**
   * Returns whether the given method is considered to be a repository base class method.
   *
   * @param method
   * @return
   */
  private boolean isTargetClassMethod(Method method, Class<?> targetType) {

    Assert.notNull(method);

    if (targetType == null) {
      return false;
    }

    if (method.getDeclaringClass().isAssignableFrom(targetType)) {
      return true;
    }

    return !method.equals(getTargetClassMethod(method, targetType));
  }

  /*
   * (non-Javadoc)
   * @see org.springframework.data.repository.support.RepositoryInformation#getQueryMethods()
   */
  @Override
  public Set<Method> getQueryMethods() {

    Set<Method> result = new HashSet<Method>();

    for (Method method : getRepositoryInterface().getMethods()) {
      method = ClassUtils.getMostSpecificMethod(method, getRepositoryInterface());
      if (isQueryMethodCandidate(method)) {
        result.add(method);
      }
    }

    return Collections.unmodifiableSet(result);
  }

  /**
   * Checks whether the given method is a query method candidate.
   *
   * @param method
   * @return
   */
  private boolean isQueryMethodCandidate(Method method) {
    return !method.isBridge() && !ReflectionUtils.isDefaultMethod(method) //
        && (isQueryAnnotationPresentOn(method) || !isCustomMethod(method) && !isBaseClassMethod(method));
  }

  /**
   * Checks whether the given method contains a custom store specific query annotation annotated with
   * {@link QueryAnnotation}. The method-hierarchy is also considered in the search for the annotation.
   *
   * @param method
   * @return
   */
  private boolean isQueryAnnotationPresentOn(Method method) {

    return AnnotationUtils.findAnnotation(method, QueryAnnotation.class) != null;
  }

  /*
   * (non-Javadoc)
   * @see org.springframework.data.repository.support.RepositoryInformation#isCustomMethod(java.lang.reflect.Method)
   */
  @Override
  public boolean isCustomMethod(Method method) {
    return isTargetClassMethod(method, customImplementationClass);
  }

  /*
   * (non-Javadoc)
   * @see org.springframework.data.repository.core.RepositoryInformation#isQueryMethod(java.lang.reflect.Method)
   */
  @Override
  public boolean isQueryMethod(Method method) {
    return getQueryMethods().contains(method);
  }

  /*
   * (non-Javadoc)
   * @see org.springframework.data.repository.core.RepositoryInformation#isBaseClassMethod(java.lang.reflect.Method)
   */
  @Override
  public boolean isBaseClassMethod(Method method) {

    Assert.notNull(method, "Method must not be null!");
    return isTargetClassMethod(method, repositoryBaseClass);
  }

  /**
   * Returns the given target class' method if the given method (declared in the repository interface) was also declared
   * at the target class. Returns the given method if the given base class does not declare the method given. Takes
   * generics into account.
   *
   * @param method must not be {@literal null}
   * @param baseClass
   * @return
   */
  Method getTargetClassMethod(Method method, Class<?> baseClass) {

    if (baseClass == null) {
      return method;
    }

    for (Method baseClassMethod : baseClass.getMethods()) {

      // Wrong name
      if (!method.getName().equals(baseClassMethod.getName())) {
        continue;
      }

      // Wrong number of arguments
      if (!(method.getParameterTypes().length == baseClassMethod.getParameterTypes().length)) {
        continue;
      }

      // Check whether all parameters match
      if (!parametersMatch(method, baseClassMethod)) {
        continue;
      }

      return baseClassMethod;
    }

    return method;
  }

  /*
   * (non-Javadoc)
   * @see org.springframework.data.repository.support.RepositoryInformation#hasCustomMethod()
   */
  @Override
  public boolean hasCustomMethod() {

    Class<?> repositoryInterface = getRepositoryInterface();

    // No detection required if no typing interface was configured
    if (isGenericRepositoryInterface(repositoryInterface)) {
      return false;
    }

    for (Method method : repositoryInterface.getMethods()) {
      if (isCustomMethod(method) && !isBaseClassMethod(method)) {
        return true;
      }
    }

    return false;
  }

  /*
   * (non-Javadoc)
   * @see org.springframework.data.repository.core.RepositoryMetadata#getRepositoryInterface()
   */
  @Override
  public Class<?> getRepositoryInterface() {
    return metadata.getRepositoryInterface();
  }

  /*
   * (non-Javadoc)
   * @see org.springframework.data.repository.core.RepositoryMetadata#getReturnedDomainClass(java.lang.reflect.Method)
   */
  @Override
  public Class<?> getReturnedDomainClass(Method method) {
    return metadata.getReturnedDomainClass(method);
  }

  /*
   * (non-Javadoc)
   * @see org.springframework.data.repository.core.RepositoryMetadata#getCrudMethods()
   */
  @Override
  public CrudMethods getCrudMethods() {
    return metadata.getCrudMethods();
  }

  /*
   * (non-Javadoc)
   * @see org.springframework.data.repository.core.RepositoryMetadata#isPagingRepository()
   */
  @Override
  public boolean isPagingRepository() {
    return metadata.isPagingRepository();
  }

  /**
   * Checks the given method's parameters to match the ones of the given base class method. Matches generic arguments
   * agains the ones bound in the given repository interface.
   *
   * @param method
   * @param baseClassMethod
   * @return
   */
  private boolean parametersMatch(Method method, Method baseClassMethod) {

    Type[] genericTypes = baseClassMethod.getGenericParameterTypes();
    Class<?>[] types = baseClassMethod.getParameterTypes();

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

      Type type = genericTypes[i];
      MethodParameter parameter = new MethodParameter(method, i);
      Class<?> parameterType = resolveParameterType(parameter, metadata.getRepositoryInterface());

      if (type instanceof TypeVariable<?>) {
        if (!matchesGenericType((TypeVariable<?>) type, parameterType)) {
          return false;
        }
      } else {
        if (!types[i].equals(parameterType)) {
          return false;
        }
      }
    }

    return true;
  }

  /**
   * Checks whether the given parameter type matches the generic type of the given parameter. Thus when {@literal PK} is
   * declared, the method ensures that given method parameter is the primary key type declared in the given repository
   * interface e.g.
   *
   * @param name
   * @param parameterType
   * @return
   */
  private boolean matchesGenericType(TypeVariable<?> variable, Class<?> parameterType) {

    Class<?> entityType = getDomainType();
    Class<?> idClass = getIdType();

    if (ID_TYPE_NAME.equals(variable.getName()) && parameterType.isAssignableFrom(idClass)) {
      return true;
    }

    Type boundType = variable.getBounds()[0];
    String referenceName = boundType instanceof TypeVariable ? boundType.toString() : variable.toString();

    boolean isDomainTypeVariableReference = DOMAIN_TYPE_NAME.equals(referenceName);
    boolean parameterMatchesEntityType = parameterType.isAssignableFrom(entityType);

    // We need this check to besure not to match save(Iterable) for entities implementing Iterable
    boolean isNotIterable = !parameterType.equals(Iterable.class);

    if (isDomainTypeVariableReference && parameterMatchesEntityType && isNotIterable) {
      return true;
    }

    return false;
  }
}
TOP

Related Classes of org.springframework.data.repository.core.support.DefaultRepositoryInformation

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.