Reformatting source code to use tabs instead of spaces.
This commit is contained in:
@@ -1,124 +1,124 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-commons-parent</artifactId>
|
||||
<version>1.1.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>../spring-data-commons-parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>spring-data-commons-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Data Commons Core</name>
|
||||
|
||||
<properties>
|
||||
<querydsl.version>2.2.0-beta4</querydsl.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-commons-parent</artifactId>
|
||||
<version>1.1.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>../spring-data-commons-parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>spring-data-commons-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Data Commons Core</name>
|
||||
|
||||
<!-- Spring -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
<version>2.5</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<properties>
|
||||
<querydsl.version>2.2.0-beta4</querydsl.version>
|
||||
</properties>
|
||||
|
||||
<!-- Logging -->
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>jsr250-api</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- Spring -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
<version>2.5</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- Logging -->
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
<version>1.6</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mysema.querydsl</groupId>
|
||||
<artifactId>querydsl-mongodb</artifactId>
|
||||
<version>${querydsl.version}</version>
|
||||
<optional>true</optional>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.morphia</groupId>
|
||||
<artifactId>morphia</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mysema.querydsl</groupId>
|
||||
<artifactId>querydsl-apt</artifactId>
|
||||
<version>${querydsl.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>jsr250-api</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.springsource.bundlor</groupId>
|
||||
<artifactId>com.springsource.bundlor.maven</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.mysema.maven</groupId>
|
||||
<artifactId>maven-apt-plugin</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>generate-test-sources</phase>
|
||||
<goals>
|
||||
<goal>test-process</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>target/generated-sources/test-annotations</outputDirectory>
|
||||
<processor>com.mysema.query.apt.QuerydslAnnotationProcessor</processor>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
<version>1.6</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mysema.querydsl</groupId>
|
||||
<artifactId>querydsl-mongodb</artifactId>
|
||||
<version>${querydsl.version}</version>
|
||||
<optional>true</optional>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.morphia</groupId>
|
||||
<artifactId>morphia</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mysema.querydsl</groupId>
|
||||
<artifactId>querydsl-apt</artifactId>
|
||||
<version>${querydsl.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.springsource.bundlor</groupId>
|
||||
<artifactId>com.springsource.bundlor.maven</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.mysema.maven</groupId>
|
||||
<artifactId>maven-apt-plugin</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>generate-test-sources</phase>
|
||||
<goals>
|
||||
<goal>test-process</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>target/generated-sources/test-annotations</outputDirectory>
|
||||
<processor>com.mysema.query.apt.QuerydslAnnotationProcessor</processor>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
@@ -26,10 +26,10 @@ import java.lang.annotation.Target;
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value = {
|
||||
ElementType.TYPE,
|
||||
ElementType.ANNOTATION_TYPE,
|
||||
ElementType.FIELD,
|
||||
ElementType.PARAMETER
|
||||
ElementType.TYPE,
|
||||
ElementType.ANNOTATION_TYPE,
|
||||
ElementType.FIELD,
|
||||
ElementType.PARAMETER
|
||||
})
|
||||
public @interface Persistent {
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@ import java.lang.annotation.Target;
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({
|
||||
ElementType.ANNOTATION_TYPE,
|
||||
ElementType.FIELD
|
||||
ElementType.ANNOTATION_TYPE,
|
||||
ElementType.FIELD
|
||||
})
|
||||
public @interface Reference {
|
||||
}
|
||||
|
||||
@@ -24,73 +24,73 @@ import org.joda.time.DateTime;
|
||||
* Interface for auditable entities. Allows storing and retrieving creation and
|
||||
* modification information. The changing instance (typically some user) is to
|
||||
* be defined by a generics definition.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @param <U> the auditing type. Typically some kind of user.
|
||||
*
|
||||
* @param <U> the auditing type. Typically some kind of user.
|
||||
* @param <ID> the type of the auditing type's idenifier
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface Auditable<U, ID extends Serializable> extends Persistable<ID> {
|
||||
|
||||
/**
|
||||
* Returns the user who created this entity.
|
||||
*
|
||||
* @return the createdBy
|
||||
*/
|
||||
U getCreatedBy();
|
||||
/**
|
||||
* Returns the user who created this entity.
|
||||
*
|
||||
* @return the createdBy
|
||||
*/
|
||||
U getCreatedBy();
|
||||
|
||||
|
||||
/**
|
||||
* Sets the user who created this entity.
|
||||
*
|
||||
* @param createdBy the creating entity to set
|
||||
*/
|
||||
void setCreatedBy(final U createdBy);
|
||||
/**
|
||||
* Sets the user who created this entity.
|
||||
*
|
||||
* @param createdBy the creating entity to set
|
||||
*/
|
||||
void setCreatedBy(final U createdBy);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the creation date of the entity.
|
||||
*
|
||||
* @return the createdDate
|
||||
*/
|
||||
DateTime getCreatedDate();
|
||||
/**
|
||||
* Returns the creation date of the entity.
|
||||
*
|
||||
* @return the createdDate
|
||||
*/
|
||||
DateTime getCreatedDate();
|
||||
|
||||
|
||||
/**
|
||||
* Sets the creation date of the entity.
|
||||
*
|
||||
* @param creationDate the creation date to set
|
||||
*/
|
||||
void setCreatedDate(final DateTime creationDate);
|
||||
/**
|
||||
* Sets the creation date of the entity.
|
||||
*
|
||||
* @param creationDate the creation date to set
|
||||
*/
|
||||
void setCreatedDate(final DateTime creationDate);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the user who modified the entity lastly.
|
||||
*
|
||||
* @return the lastModifiedBy
|
||||
*/
|
||||
U getLastModifiedBy();
|
||||
/**
|
||||
* Returns the user who modified the entity lastly.
|
||||
*
|
||||
* @return the lastModifiedBy
|
||||
*/
|
||||
U getLastModifiedBy();
|
||||
|
||||
|
||||
/**
|
||||
* Sets the user who modified the entity lastly.
|
||||
*
|
||||
* @param lastModifiedBy the last modifying entity to set
|
||||
*/
|
||||
void setLastModifiedBy(final U lastModifiedBy);
|
||||
/**
|
||||
* Sets the user who modified the entity lastly.
|
||||
*
|
||||
* @param lastModifiedBy the last modifying entity to set
|
||||
*/
|
||||
void setLastModifiedBy(final U lastModifiedBy);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the date of the last modification.
|
||||
*
|
||||
* @return the lastModifiedDate
|
||||
*/
|
||||
DateTime getLastModifiedDate();
|
||||
/**
|
||||
* Returns the date of the last modification.
|
||||
*
|
||||
* @return the lastModifiedDate
|
||||
*/
|
||||
DateTime getLastModifiedDate();
|
||||
|
||||
|
||||
/**
|
||||
* Sets the date of the last modification.
|
||||
*
|
||||
* @param lastModifiedDate the date of the last modification to set
|
||||
*/
|
||||
void setLastModifiedDate(final DateTime lastModifiedDate);
|
||||
/**
|
||||
* Sets the date of the last modification.
|
||||
*
|
||||
* @param lastModifiedDate the date of the last modification to set
|
||||
*/
|
||||
void setLastModifiedDate(final DateTime lastModifiedDate);
|
||||
}
|
||||
|
||||
@@ -18,16 +18,16 @@ package org.springframework.data.domain;
|
||||
/**
|
||||
* Interface for components that are aware of the application's current auditor.
|
||||
* This will be some kind of user mostly.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*
|
||||
* @param <T> the type of the auditing instance
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface AuditorAware<T> {
|
||||
|
||||
/**
|
||||
* Returns the current auditor of the application.
|
||||
*
|
||||
* @return the current auditor
|
||||
*/
|
||||
T getCurrentAuditor();
|
||||
/**
|
||||
* Returns the current auditor of the application.
|
||||
*
|
||||
* @return the current auditor
|
||||
*/
|
||||
T getCurrentAuditor();
|
||||
}
|
||||
|
||||
@@ -22,113 +22,113 @@ import java.util.List;
|
||||
/**
|
||||
* A page is a sublist of a list of objects. It allows gain information about
|
||||
* the position of it in the containing entire list.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*
|
||||
* @param <T>
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface Page<T> extends Iterable<T> {
|
||||
|
||||
/**
|
||||
* Returns the number of the current page. Is always positive and less that
|
||||
* {@code Page#getTotalPages()}.
|
||||
*
|
||||
* @return the number of the current page
|
||||
*/
|
||||
int getNumber();
|
||||
/**
|
||||
* Returns the number of the current page. Is always positive and less that
|
||||
* {@code Page#getTotalPages()}.
|
||||
*
|
||||
* @return the number of the current page
|
||||
*/
|
||||
int getNumber();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the size of the page.
|
||||
*
|
||||
* @return the size of the page
|
||||
*/
|
||||
int getSize();
|
||||
/**
|
||||
* Returns the size of the page.
|
||||
*
|
||||
* @return the size of the page
|
||||
*/
|
||||
int getSize();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of total pages.
|
||||
*
|
||||
* @return the number of toral pages
|
||||
*/
|
||||
int getTotalPages();
|
||||
/**
|
||||
* Returns the number of total pages.
|
||||
*
|
||||
* @return the number of toral pages
|
||||
*/
|
||||
int getTotalPages();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of elements currently on this page.
|
||||
*
|
||||
* @return the number of elements currently on this page
|
||||
*/
|
||||
int getNumberOfElements();
|
||||
/**
|
||||
* Returns the number of elements currently on this page.
|
||||
*
|
||||
* @return the number of elements currently on this page
|
||||
*/
|
||||
int getNumberOfElements();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the total amount of elements.
|
||||
*
|
||||
* @return the total amount of elements
|
||||
*/
|
||||
long getTotalElements();
|
||||
/**
|
||||
* Returns the total amount of elements.
|
||||
*
|
||||
* @return the total amount of elements
|
||||
*/
|
||||
long getTotalElements();
|
||||
|
||||
|
||||
/**
|
||||
* Returns if there is a previous page.
|
||||
*
|
||||
* @return if there is a previous page
|
||||
*/
|
||||
boolean hasPreviousPage();
|
||||
/**
|
||||
* Returns if there is a previous page.
|
||||
*
|
||||
* @return if there is a previous page
|
||||
*/
|
||||
boolean hasPreviousPage();
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the current page is the first one.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isFirstPage();
|
||||
/**
|
||||
* Returns whether the current page is the first one.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isFirstPage();
|
||||
|
||||
|
||||
/**
|
||||
* Returns if there is a next page.
|
||||
*
|
||||
* @return if there is a next page
|
||||
*/
|
||||
boolean hasNextPage();
|
||||
/**
|
||||
* Returns if there is a next page.
|
||||
*
|
||||
* @return if there is a next page
|
||||
*/
|
||||
boolean hasNextPage();
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the current page is the last one.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isLastPage();
|
||||
/**
|
||||
* Returns whether the current page is the last one.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isLastPage();
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Iterable#iterator()
|
||||
*/
|
||||
Iterator<T> iterator();
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Iterable#iterator()
|
||||
*/
|
||||
Iterator<T> iterator();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the page content as {@link List}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
List<T> getContent();
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the {@link Page} has content at all.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean hasContent();
|
||||
/**
|
||||
* Returns the page content as {@link List}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
List<T> getContent();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the sorting parameters for the page.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Sort getSort();
|
||||
/**
|
||||
* Returns whether the {@link Page} has content at all.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean hasContent();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the sorting parameters for the page.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Sort getSort();
|
||||
}
|
||||
@@ -24,251 +24,251 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* Basic {@code Page} implementation.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*
|
||||
* @param <T> the type of which the page consists.
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class PageImpl<T> implements Page<T>, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 867755909294344406L;
|
||||
|
||||
private final List<T> content = new ArrayList<T>();
|
||||
private final Pageable pageable;
|
||||
private final long total;
|
||||
private static final long serialVersionUID = 867755909294344406L;
|
||||
|
||||
private final List<T> content = new ArrayList<T>();
|
||||
private final Pageable pageable;
|
||||
private final long total;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor of {@code PageImpl}.
|
||||
*
|
||||
* @param content the content of this page
|
||||
* @param pageable the paging information
|
||||
* @param total the total amount of items available
|
||||
*/
|
||||
public PageImpl(List<T> content, Pageable pageable, long total) {
|
||||
|
||||
if (null == content) {
|
||||
throw new IllegalArgumentException("Content must not be null!");
|
||||
}
|
||||
|
||||
this.content.addAll(content);
|
||||
this.total = total;
|
||||
this.pageable = pageable;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link PageImpl} with the given content. This will result
|
||||
* in the created {@link Page} being identical to the entire {@link List}.
|
||||
*
|
||||
* @param content
|
||||
*/
|
||||
public PageImpl(List<T> content) {
|
||||
|
||||
this(content, null, (null == content) ? 0 : content.size());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#getNumber()
|
||||
*/
|
||||
public int getNumber() {
|
||||
|
||||
return pageable == null ? 0 : pageable.getPageNumber();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#getSize()
|
||||
*/
|
||||
public int getSize() {
|
||||
|
||||
return pageable == null ? 0 : pageable.getPageSize();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#getTotalPages()
|
||||
*/
|
||||
public int getTotalPages() {
|
||||
|
||||
return getSize() == 0 ? 0 : (int) Math.ceil((double) total / (double) getSize());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#getNumberOfElements()
|
||||
*/
|
||||
public int getNumberOfElements() {
|
||||
|
||||
return content.size();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#getTotalElements()
|
||||
*/
|
||||
public long getTotalElements() {
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#hasPreviousPage()
|
||||
*/
|
||||
public boolean hasPreviousPage() {
|
||||
|
||||
return getNumber() > 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#isFirstPage()
|
||||
*/
|
||||
public boolean isFirstPage() {
|
||||
|
||||
return !hasPreviousPage();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#hasNextPage()
|
||||
*/
|
||||
public boolean hasNextPage() {
|
||||
|
||||
return ((getNumber() + 1) * getSize()) < total;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#isLastPage()
|
||||
*/
|
||||
public boolean isLastPage() {
|
||||
|
||||
return !hasNextPage();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#iterator()
|
||||
*/
|
||||
public Iterator<T> iterator() {
|
||||
|
||||
return content.iterator();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#asList()
|
||||
*/
|
||||
public List<T> getContent() {
|
||||
|
||||
return Collections.unmodifiableList(content);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#hasContent()
|
||||
/**
|
||||
* Constructor of {@code PageImpl}.
|
||||
*
|
||||
* @param content the content of this page
|
||||
* @param pageable the paging information
|
||||
* @param total the total amount of items available
|
||||
*/
|
||||
public boolean hasContent() {
|
||||
|
||||
return !content.isEmpty();
|
||||
}
|
||||
public PageImpl(List<T> content, Pageable pageable, long total) {
|
||||
|
||||
if (null == content) {
|
||||
throw new IllegalArgumentException("Content must not be null!");
|
||||
}
|
||||
|
||||
this.content.addAll(content);
|
||||
this.total = total;
|
||||
this.pageable = pageable;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#getSort()
|
||||
*/
|
||||
public Sort getSort() {
|
||||
/**
|
||||
* Creates a new {@link PageImpl} with the given content. This will result
|
||||
* in the created {@link Page} being identical to the entire {@link List}.
|
||||
*
|
||||
* @param content
|
||||
*/
|
||||
public PageImpl(List<T> content) {
|
||||
|
||||
return pageable == null ? null : pageable.getSort();
|
||||
}
|
||||
this(content, null, (null == content) ? 0 : content.size());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#getNumber()
|
||||
*/
|
||||
public int getNumber() {
|
||||
|
||||
String contentType = "UNKNOWN";
|
||||
|
||||
if (content.size() > 0) {
|
||||
contentType = content.get(0).getClass().getName();
|
||||
}
|
||||
|
||||
return String.format("Page %s of %d containing %s instances",
|
||||
getNumber(), getTotalPages(), contentType);
|
||||
}
|
||||
return pageable == null ? 0 : pageable.getPageNumber();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#getSize()
|
||||
*/
|
||||
public int getSize() {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
return pageable == null ? 0 : pageable.getPageSize();
|
||||
}
|
||||
|
||||
if (!(obj instanceof PageImpl<?>)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PageImpl<?> that = (PageImpl<?>) obj;
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#getTotalPages()
|
||||
*/
|
||||
public int getTotalPages() {
|
||||
|
||||
boolean totalEqual = this.total == that.total;
|
||||
boolean contentEqual = this.content.equals(that.content);
|
||||
return getSize() == 0 ? 0 : (int) Math.ceil((double) total / (double) getSize());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#getNumberOfElements()
|
||||
*/
|
||||
public int getNumberOfElements() {
|
||||
|
||||
return content.size();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#getTotalElements()
|
||||
*/
|
||||
public long getTotalElements() {
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#hasPreviousPage()
|
||||
*/
|
||||
public boolean hasPreviousPage() {
|
||||
|
||||
return getNumber() > 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#isFirstPage()
|
||||
*/
|
||||
public boolean isFirstPage() {
|
||||
|
||||
return !hasPreviousPage();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#hasNextPage()
|
||||
*/
|
||||
public boolean hasNextPage() {
|
||||
|
||||
return ((getNumber() + 1) * getSize()) < total;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#isLastPage()
|
||||
*/
|
||||
public boolean isLastPage() {
|
||||
|
||||
return !hasNextPage();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#iterator()
|
||||
*/
|
||||
public Iterator<T> iterator() {
|
||||
|
||||
return content.iterator();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#asList()
|
||||
*/
|
||||
public List<T> getContent() {
|
||||
|
||||
return Collections.unmodifiableList(content);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#hasContent()
|
||||
*/
|
||||
public boolean hasContent() {
|
||||
|
||||
return !content.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Page#getSort()
|
||||
*/
|
||||
public Sort getSort() {
|
||||
|
||||
return pageable == null ? null : pageable.getSort();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
String contentType = "UNKNOWN";
|
||||
|
||||
if (content.size() > 0) {
|
||||
contentType = content.get(0).getClass().getName();
|
||||
}
|
||||
|
||||
return String.format("Page %s of %d containing %s instances",
|
||||
getNumber(), getTotalPages(), contentType);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj instanceof PageImpl<?>)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PageImpl<?> that = (PageImpl<?>) obj;
|
||||
|
||||
boolean totalEqual = this.total == that.total;
|
||||
boolean contentEqual = this.content.equals(that.content);
|
||||
boolean pageableEqual = this.pageable == null ? that.pageable == null : this.pageable.equals(that.pageable);
|
||||
|
||||
return totalEqual && contentEqual && pageableEqual;
|
||||
}
|
||||
return totalEqual && contentEqual && pageableEqual;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = 17;
|
||||
int result = 17;
|
||||
|
||||
result = 31 * result + (int) (total ^ total >>> 32);
|
||||
result = 31 * result + (pageable == null ? 0 : pageable.hashCode());
|
||||
result = 31 * result + content.hashCode();
|
||||
result = 31 * result + (int) (total ^ total >>> 32);
|
||||
result = 31 * result + (pageable == null ? 0 : pageable.hashCode());
|
||||
result = 31 * result + content.hashCode();
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,158 +22,158 @@ import org.springframework.data.domain.Sort.Direction;
|
||||
|
||||
/**
|
||||
* Basic Java Bean implementation of {@code Pageable}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class PageRequest implements Pageable, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 8280485938848398236L;
|
||||
|
||||
private final int page;
|
||||
private final int size;
|
||||
private final Sort sort;
|
||||
private static final long serialVersionUID = 8280485938848398236L;
|
||||
|
||||
private final int page;
|
||||
private final int size;
|
||||
private final Sort sort;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link PageRequest}. Pages are zero indexed, thus providing
|
||||
* 0 for {@code page} will return the first page.
|
||||
*
|
||||
* @param size
|
||||
* @param page
|
||||
*/
|
||||
public PageRequest(int page, int size) {
|
||||
/**
|
||||
* Creates a new {@link PageRequest}. Pages are zero indexed, thus providing
|
||||
* 0 for {@code page} will return the first page.
|
||||
*
|
||||
* @param size
|
||||
* @param page
|
||||
*/
|
||||
public PageRequest(int page, int size) {
|
||||
|
||||
this(page, size, null);
|
||||
}
|
||||
this(page, size, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link PageRequest} with sort parameters applied.
|
||||
*
|
||||
* @param page
|
||||
* @param size
|
||||
* @param direction
|
||||
* @param properties
|
||||
*/
|
||||
public PageRequest(int page, int size, Direction direction,
|
||||
String... properties) {
|
||||
/**
|
||||
* Creates a new {@link PageRequest} with sort parameters applied.
|
||||
*
|
||||
* @param page
|
||||
* @param size
|
||||
* @param direction
|
||||
* @param properties
|
||||
*/
|
||||
public PageRequest(int page, int size, Direction direction,
|
||||
String... properties) {
|
||||
|
||||
this(page, size, new Sort(direction, properties));
|
||||
}
|
||||
this(page, size, new Sort(direction, properties));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link PageRequest} with sort parameters applied.
|
||||
*
|
||||
* @param page
|
||||
* @param size
|
||||
* @param sort
|
||||
*/
|
||||
public PageRequest(int page, int size, Sort sort) {
|
||||
/**
|
||||
* Creates a new {@link PageRequest} with sort parameters applied.
|
||||
*
|
||||
* @param page
|
||||
* @param size
|
||||
* @param sort
|
||||
*/
|
||||
public PageRequest(int page, int size, Sort sort) {
|
||||
|
||||
if (0 > page) {
|
||||
throw new IllegalArgumentException(
|
||||
"Page index must not be less than zero!");
|
||||
}
|
||||
if (0 > page) {
|
||||
throw new IllegalArgumentException(
|
||||
"Page index must not be less than zero!");
|
||||
}
|
||||
|
||||
if (0 >= size) {
|
||||
throw new IllegalArgumentException(
|
||||
"Page size must not be less than or equal to zero!");
|
||||
}
|
||||
if (0 >= size) {
|
||||
throw new IllegalArgumentException(
|
||||
"Page size must not be less than or equal to zero!");
|
||||
}
|
||||
|
||||
this.page = page;
|
||||
this.size = size;
|
||||
this.sort = sort;
|
||||
}
|
||||
this.page = page;
|
||||
this.size = size;
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Pageable#getPageSize()
|
||||
*/
|
||||
public int getPageSize() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Pageable#getPageSize()
|
||||
*/
|
||||
public int getPageSize() {
|
||||
|
||||
return size;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Pageable#getPageNumber()
|
||||
*/
|
||||
public int getPageNumber() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Pageable#getPageNumber()
|
||||
*/
|
||||
public int getPageNumber() {
|
||||
|
||||
return page;
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Pageable#getFirstItem()
|
||||
*/
|
||||
public int getOffset() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Pageable#getFirstItem()
|
||||
*/
|
||||
public int getOffset() {
|
||||
|
||||
return page * size;
|
||||
}
|
||||
return page * size;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Pageable#getSort()
|
||||
*/
|
||||
public Sort getSort() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.domain.Pageable#getSort()
|
||||
*/
|
||||
public Sort getSort() {
|
||||
|
||||
return sort;
|
||||
}
|
||||
return sort;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj instanceof PageRequest)) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof PageRequest)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PageRequest that = (PageRequest) obj;
|
||||
PageRequest that = (PageRequest) obj;
|
||||
|
||||
boolean pageEqual = this.page == that.page;
|
||||
boolean sizeEqual = this.size == that.size;
|
||||
boolean pageEqual = this.page == that.page;
|
||||
boolean sizeEqual = this.size == that.size;
|
||||
|
||||
boolean sortEqual =
|
||||
this.sort == null ? that.sort == null : this.sort
|
||||
.equals(that.sort);
|
||||
boolean sortEqual =
|
||||
this.sort == null ? that.sort == null : this.sort
|
||||
.equals(that.sort);
|
||||
|
||||
return pageEqual && sizeEqual && sortEqual;
|
||||
}
|
||||
return pageEqual && sizeEqual && sortEqual;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = 17;
|
||||
int result = 17;
|
||||
|
||||
result = 31 * result + page;
|
||||
result = 31 * result + size;
|
||||
result = 31 * result + (null == sort ? 0 : sort.hashCode());
|
||||
result = 31 * result + page;
|
||||
result = 31 * result + size;
|
||||
result = 31 * result + (null == sort ? 0 : sort.hashCode());
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,40 +17,40 @@ package org.springframework.data.domain;
|
||||
|
||||
/**
|
||||
* Abstract interface for pagination information.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface Pageable {
|
||||
|
||||
/**
|
||||
* Returns the page to be returned.
|
||||
*
|
||||
* @return the page to be returned.
|
||||
*/
|
||||
int getPageNumber();
|
||||
/**
|
||||
* Returns the page to be returned.
|
||||
*
|
||||
* @return the page to be returned.
|
||||
*/
|
||||
int getPageNumber();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of items to be returned.
|
||||
*
|
||||
* @return the number of items of that page
|
||||
*/
|
||||
int getPageSize();
|
||||
/**
|
||||
* Returns the number of items to be returned.
|
||||
*
|
||||
* @return the number of items of that page
|
||||
*/
|
||||
int getPageSize();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the offset to be taken according to the underlying page and page
|
||||
* size.
|
||||
*
|
||||
* @return the offset to be taken
|
||||
*/
|
||||
int getOffset();
|
||||
/**
|
||||
* Returns the offset to be taken according to the underlying page and page
|
||||
* size.
|
||||
*
|
||||
* @return the offset to be taken
|
||||
*/
|
||||
int getOffset();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the sorting parameters.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Sort getSort();
|
||||
/**
|
||||
* Returns the sorting parameters.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Sort getSort();
|
||||
}
|
||||
|
||||
@@ -20,24 +20,24 @@ import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Simple interface for entities.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*
|
||||
* @param <ID> the type of the identifier
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface Persistable<ID extends Serializable> extends Serializable {
|
||||
|
||||
/**
|
||||
* Returns the id of the entity.
|
||||
*
|
||||
* @return the id
|
||||
*/
|
||||
ID getId();
|
||||
/**
|
||||
* Returns the id of the entity.
|
||||
*
|
||||
* @return the id
|
||||
*/
|
||||
ID getId();
|
||||
|
||||
|
||||
/**
|
||||
* Returns if the {@code Persistable} is new or was persisted already.
|
||||
*
|
||||
* @return if the object is new
|
||||
*/
|
||||
boolean isNew();
|
||||
/**
|
||||
* Returns if the {@code Persistable} is new or was persisted already.
|
||||
*
|
||||
* @return if the object is new
|
||||
*/
|
||||
boolean isNew();
|
||||
}
|
||||
|
||||
@@ -29,355 +29,355 @@ import org.springframework.util.StringUtils;
|
||||
* Sort option for queries. You have to provide at least a list of properties to
|
||||
* sort for that must not include {@code null} or empty strings. The direction
|
||||
* defaults to {@value Sort#DEFAULT_DIRECTION}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class Sort implements
|
||||
Iterable<org.springframework.data.domain.Sort.Order>, Serializable {
|
||||
Iterable<org.springframework.data.domain.Sort.Order>, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 5737186511678863905L;
|
||||
public static final Direction DEFAULT_DIRECTION = Direction.ASC;
|
||||
private static final long serialVersionUID = 5737186511678863905L;
|
||||
public static final Direction DEFAULT_DIRECTION = Direction.ASC;
|
||||
|
||||
private List<Order> orders;
|
||||
private List<Order> orders;
|
||||
|
||||
|
||||
public Sort(Order... orders) {
|
||||
public Sort(Order... orders) {
|
||||
|
||||
this(Arrays.asList(orders));
|
||||
}
|
||||
this(Arrays.asList(orders));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link Sort} instance.
|
||||
*
|
||||
* @param orders must not be {@literal null} or contain {@literal null} or
|
||||
* empty strings
|
||||
*/
|
||||
public Sort(List<Order> orders) {
|
||||
/**
|
||||
* Creates a new {@link Sort} instance.
|
||||
*
|
||||
* @param orders must not be {@literal null} or contain {@literal null} or
|
||||
* empty strings
|
||||
*/
|
||||
public Sort(List<Order> orders) {
|
||||
|
||||
if (null == orders || orders.isEmpty()) {
|
||||
throw new IllegalArgumentException(
|
||||
"You have to provide at least one sort property to sort by!");
|
||||
}
|
||||
if (null == orders || orders.isEmpty()) {
|
||||
throw new IllegalArgumentException(
|
||||
"You have to provide at least one sort property to sort by!");
|
||||
}
|
||||
|
||||
this.orders = orders;
|
||||
}
|
||||
this.orders = orders;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link Sort} instance. Order defaults to
|
||||
* {@value Direction#ASC}.
|
||||
*
|
||||
* @param properties must not be {@literal null} or contain {@literal null}
|
||||
* or empty strings
|
||||
*/
|
||||
public Sort(String... properties) {
|
||||
/**
|
||||
* Creates a new {@link Sort} instance. Order defaults to
|
||||
* {@value Direction#ASC}.
|
||||
*
|
||||
* @param properties must not be {@literal null} or contain {@literal null}
|
||||
* or empty strings
|
||||
*/
|
||||
public Sort(String... properties) {
|
||||
|
||||
this(DEFAULT_DIRECTION, properties);
|
||||
}
|
||||
this(DEFAULT_DIRECTION, properties);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link Sort} instance.
|
||||
*
|
||||
* @param direction defaults to {@value Sort#DEFAULT_DIRECTION} (for
|
||||
* {@literal null} cases, too)
|
||||
* @param properties must not be {@literal null} or contain {@literal null}
|
||||
* or empty strings
|
||||
*/
|
||||
public Sort(Direction direction, String... properties) {
|
||||
/**
|
||||
* Creates a new {@link Sort} instance.
|
||||
*
|
||||
* @param direction defaults to {@value Sort#DEFAULT_DIRECTION} (for
|
||||
* {@literal null} cases, too)
|
||||
* @param properties must not be {@literal null} or contain {@literal null}
|
||||
* or empty strings
|
||||
*/
|
||||
public Sort(Direction direction, String... properties) {
|
||||
|
||||
this(direction, properties == null ? new ArrayList<String>() : Arrays
|
||||
.asList(properties));
|
||||
}
|
||||
this(direction, properties == null ? new ArrayList<String>() : Arrays
|
||||
.asList(properties));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link Sort} instance.
|
||||
*
|
||||
* @param direction
|
||||
* @param properties
|
||||
*/
|
||||
public Sort(Direction direction, List<String> properties) {
|
||||
/**
|
||||
* Creates a new {@link Sort} instance.
|
||||
*
|
||||
* @param direction
|
||||
* @param properties
|
||||
*/
|
||||
public Sort(Direction direction, List<String> properties) {
|
||||
|
||||
if (properties == null || properties.isEmpty()) {
|
||||
throw new IllegalArgumentException(
|
||||
"You have to provide at least one property to sort by!");
|
||||
}
|
||||
|
||||
this.orders = new ArrayList<Order>(properties.size());
|
||||
if (properties == null || properties.isEmpty()) {
|
||||
throw new IllegalArgumentException(
|
||||
"You have to provide at least one property to sort by!");
|
||||
}
|
||||
|
||||
this.orders = new ArrayList<Order>(properties.size());
|
||||
|
||||
for (String property : properties) {
|
||||
this.orders.add(new Order(direction, property));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the order registered for the given property.
|
||||
*
|
||||
* @param property
|
||||
* @return
|
||||
*/
|
||||
public Order getOrderFor(String property) {
|
||||
|
||||
for (Order order : this) {
|
||||
if (order.getProperty().equals(property)) {
|
||||
return order;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Iterable#iterator()
|
||||
*/
|
||||
public Iterator<Order> iterator() {
|
||||
|
||||
return this.orders.iterator();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj instanceof Sort)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Sort that = (Sort) obj;
|
||||
|
||||
return this.orders.equals(that.orders);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = 17;
|
||||
result = 31 * result + orders.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return StringUtils.collectionToCommaDelimitedString(orders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumeration for sort directions.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public static enum Direction {
|
||||
|
||||
ASC, DESC;
|
||||
|
||||
/**
|
||||
* Returns the {@link Direction} enum for the given {@link String}
|
||||
* value.
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public static Direction fromString(String value) {
|
||||
|
||||
try {
|
||||
return Direction.valueOf(value.toUpperCase(Locale.US));
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"Invalid value '%s' for orders given! Has to be either 'desc' or 'asc' (case insensitive).",
|
||||
value), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Property implements the pairing of an {@code Order} and a property. It is
|
||||
* used to provide input for {@link Sort}
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public static class Order {
|
||||
|
||||
private final Direction direction;
|
||||
private final String property;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link Order} instance. if order is {@literal null}
|
||||
* then order defaults to {@value Sort#DEFAULT_DIRECTION}
|
||||
*
|
||||
* @param direction can be {@code null}
|
||||
* @param property must not be {@code null} or empty
|
||||
*/
|
||||
public Order(Direction direction, String property) {
|
||||
|
||||
if (property == null || "".equals(property.trim())) {
|
||||
throw new IllegalArgumentException(
|
||||
"Property must not null or empty!");
|
||||
}
|
||||
|
||||
this.direction = direction == null ? DEFAULT_DIRECTION : direction;
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link Order} instance. Takes a single property. Order
|
||||
* defaults to {@value Sort.DEFAULT_ORDER}
|
||||
*
|
||||
* @param property - must not be {@code null} or empty
|
||||
*/
|
||||
public Order(String property) {
|
||||
|
||||
this(DEFAULT_DIRECTION, property);
|
||||
}
|
||||
|
||||
|
||||
public static List<Order> create(Direction direction,
|
||||
Iterable<String> properties) {
|
||||
|
||||
List<Order> orders = new ArrayList<Sort.Order>();
|
||||
for (String property : properties) {
|
||||
orders.add(new Order(direction, property));
|
||||
}
|
||||
return orders;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the order the property shall be sorted for.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Direction getDirection() {
|
||||
|
||||
return direction;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the property to order for.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getProperty() {
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether sorting for this property shall be ascending.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isAscending() {
|
||||
|
||||
return this.direction.equals(Direction.ASC);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a new {@link Order} with the given {@link Order}.
|
||||
*
|
||||
* @param order
|
||||
* @return
|
||||
*/
|
||||
public Order with(Direction order) {
|
||||
|
||||
return new Order(order, this.property);
|
||||
}
|
||||
for (String property : properties) {
|
||||
this.orders.add(new Order(direction, property));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the order registered for the given property.
|
||||
*
|
||||
* @param property
|
||||
* @return
|
||||
*/
|
||||
public Order getOrderFor(String property) {
|
||||
|
||||
for (Order order : this) {
|
||||
if (order.getProperty().equals(property)) {
|
||||
return order;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Iterable#iterator()
|
||||
*/
|
||||
public Iterator<Order> iterator() {
|
||||
|
||||
return this.orders.iterator();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj instanceof Sort)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Sort that = (Sort) obj;
|
||||
|
||||
return this.orders.equals(that.orders);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = 17;
|
||||
result = 31 * result + orders.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return StringUtils.collectionToCommaDelimitedString(orders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumeration for sort directions.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public static enum Direction {
|
||||
|
||||
ASC, DESC;
|
||||
|
||||
/**
|
||||
* Returns the {@link Direction} enum for the given {@link String}
|
||||
* value.
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public static Direction fromString(String value) {
|
||||
|
||||
try {
|
||||
return Direction.valueOf(value.toUpperCase(Locale.US));
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"Invalid value '%s' for orders given! Has to be either 'desc' or 'asc' (case insensitive).",
|
||||
value), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Property implements the pairing of an {@code Order} and a property. It is
|
||||
* used to provide input for {@link Sort}
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public static class Order {
|
||||
|
||||
private final Direction direction;
|
||||
private final String property;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link Order} instance. if order is {@literal null}
|
||||
* then order defaults to {@value Sort#DEFAULT_DIRECTION}
|
||||
*
|
||||
* @param direction can be {@code null}
|
||||
* @param property must not be {@code null} or empty
|
||||
*/
|
||||
public Order(Direction direction, String property) {
|
||||
|
||||
if (property == null || "".equals(property.trim())) {
|
||||
throw new IllegalArgumentException(
|
||||
"Property must not null or empty!");
|
||||
}
|
||||
|
||||
this.direction = direction == null ? DEFAULT_DIRECTION : direction;
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link Order} instance. Takes a single property. Order
|
||||
* defaults to {@value Sort.DEFAULT_ORDER}
|
||||
*
|
||||
* @param property - must not be {@code null} or empty
|
||||
*/
|
||||
public Order(String property) {
|
||||
|
||||
this(DEFAULT_DIRECTION, property);
|
||||
}
|
||||
|
||||
|
||||
public static List<Order> create(Direction direction,
|
||||
Iterable<String> properties) {
|
||||
|
||||
List<Order> orders = new ArrayList<Sort.Order>();
|
||||
for (String property : properties) {
|
||||
orders.add(new Order(direction, property));
|
||||
}
|
||||
return orders;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the order the property shall be sorted for.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Direction getDirection() {
|
||||
|
||||
return direction;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the property to order for.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getProperty() {
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether sorting for this property shall be ascending.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isAscending() {
|
||||
|
||||
return this.direction.equals(Direction.ASC);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a new {@link Order} with the given {@link Order}.
|
||||
*
|
||||
* @param order
|
||||
* @return
|
||||
*/
|
||||
public Order with(Direction order) {
|
||||
|
||||
return new Order(order, this.property);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a new {@link Sort} instance for the given properties.
|
||||
*
|
||||
* @param properties
|
||||
* @return
|
||||
*/
|
||||
public Sort withProperties(String... properties) {
|
||||
/**
|
||||
* Returns a new {@link Sort} instance for the given properties.
|
||||
*
|
||||
* @param properties
|
||||
* @return
|
||||
*/
|
||||
public Sort withProperties(String... properties) {
|
||||
|
||||
return new Sort(this.direction, properties);
|
||||
}
|
||||
return new Sort(this.direction, properties);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = 17;
|
||||
int result = 17;
|
||||
|
||||
result = 31 * result + direction.hashCode();
|
||||
result = 31 * result + property.hashCode();
|
||||
result = 31 * result + direction.hashCode();
|
||||
result = 31 * result + property.hashCode();
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj instanceof Order)) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof Order)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Order that = (Order) obj;
|
||||
Order that = (Order) obj;
|
||||
|
||||
return this.direction.equals(that.direction)
|
||||
&& this.property.equals(that.property);
|
||||
}
|
||||
return this.direction.equals(that.direction)
|
||||
&& this.property.equals(that.property);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return String.format("%s: %s", property, direction);
|
||||
}
|
||||
}
|
||||
return String.format("%s: %s", property, direction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Central domain abstractions especially to be used in combination with the {@link org.springframework.data.repository.Repository} abstraction.
|
||||
*
|
||||
*
|
||||
* @see org.springframework.data.repository.Repository
|
||||
*/
|
||||
package org.springframework.data.domain;
|
||||
|
||||
@@ -49,7 +49,7 @@ import org.springframework.validation.Validator;
|
||||
|
||||
/**
|
||||
* Base class to build mapping metadata and thus create instances of {@link PersistentEntity} and {@link PersistentProperty}.
|
||||
*
|
||||
*
|
||||
* @param E the concrete {@link PersistentEntity} type the {@link MappingContext} implementation creates
|
||||
* @param P the concrete {@link PersistentProperty} type the {@link MappingContext} implementation creates
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
@@ -57,223 +57,222 @@ import org.springframework.validation.Validator;
|
||||
*/
|
||||
public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?, P>, P extends PersistentProperty<P>> implements MappingContext<E, P>, InitializingBean, ApplicationEventPublisherAware {
|
||||
|
||||
private static final Set<String> UNMAPPED_FIELDS = new HashSet<String>(Arrays.asList("class", "this$0"));
|
||||
private static final Set<String> UNMAPPED_FIELDS = new HashSet<String>(Arrays.asList("class", "this$0"));
|
||||
|
||||
private ApplicationEventPublisher applicationEventPublisher;
|
||||
private ConcurrentMap<TypeInformation<?>, E> persistentEntities = new ConcurrentHashMap<TypeInformation<?>, E>();
|
||||
private ConcurrentMap<E, List<Validator>> validators = new ConcurrentHashMap<E, List<Validator>>();
|
||||
private List<Class<?>> customSimpleTypes = new ArrayList<Class<?>>();
|
||||
private Set<? extends Class<?>> initialEntitySet = new HashSet<Class<?>>();
|
||||
private boolean strict = false;
|
||||
private ApplicationEventPublisher applicationEventPublisher;
|
||||
private ConcurrentMap<TypeInformation<?>, E> persistentEntities = new ConcurrentHashMap<TypeInformation<?>, E>();
|
||||
private ConcurrentMap<E, List<Validator>> validators = new ConcurrentHashMap<E, List<Validator>>();
|
||||
private List<Class<?>> customSimpleTypes = new ArrayList<Class<?>>();
|
||||
private Set<? extends Class<?>> initialEntitySet = new HashSet<Class<?>>();
|
||||
private boolean strict = false;
|
||||
|
||||
/**
|
||||
* @param customSimpleTypes the customSimpleTypes to set
|
||||
*/
|
||||
public void setCustomSimpleTypes(List<Class<?>> customSimpleTypes) {
|
||||
this.customSimpleTypes = customSimpleTypes;
|
||||
}
|
||||
/**
|
||||
* @param customSimpleTypes the customSimpleTypes to set
|
||||
*/
|
||||
public void setCustomSimpleTypes(List<Class<?>> customSimpleTypes) {
|
||||
this.customSimpleTypes = customSimpleTypes;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher(org.springframework.context.ApplicationEventPublisher)
|
||||
*/
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
}
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher(org.springframework.context.ApplicationEventPublisher)
|
||||
*/
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Set} of types to populate the context initially.
|
||||
*
|
||||
* @param initialEntitySet
|
||||
*/
|
||||
public void setInitialEntitySet(Set<? extends Class<?>> initialEntitySet) {
|
||||
this.initialEntitySet = initialEntitySet;
|
||||
}
|
||||
/**
|
||||
* Sets the {@link Set} of types to populate the context initially.
|
||||
*
|
||||
* @param initialEntitySet
|
||||
*/
|
||||
public void setInitialEntitySet(Set<? extends Class<?>> initialEntitySet) {
|
||||
this.initialEntitySet = initialEntitySet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures whether the {@link MappingContext} is in strict mode which means, that it will throw
|
||||
* {@link MappingException}s in case one tries to lookup a {@link PersistentEntity} not already in the context. This
|
||||
* defaults to {@literal false} so that unknown types will be transparently added to the MappingContext if not known
|
||||
* in advance.
|
||||
*
|
||||
* @param strict
|
||||
*/
|
||||
public void setStrict(boolean strict) {
|
||||
this.strict = strict;
|
||||
}
|
||||
/**
|
||||
* Configures whether the {@link MappingContext} is in strict mode which means, that it will throw
|
||||
* {@link MappingException}s in case one tries to lookup a {@link PersistentEntity} not already in the context. This
|
||||
* defaults to {@literal false} so that unknown types will be transparently added to the MappingContext if not known
|
||||
* in advance.
|
||||
*
|
||||
* @param strict
|
||||
*/
|
||||
public void setStrict(boolean strict) {
|
||||
this.strict = strict;
|
||||
}
|
||||
|
||||
public Collection<E> getPersistentEntities() {
|
||||
return persistentEntities.values();
|
||||
}
|
||||
public Collection<E> getPersistentEntities() {
|
||||
return persistentEntities.values();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.model.MappingContext#getPersistentEntity(java.lang.Class)
|
||||
*/
|
||||
public E getPersistentEntity(Class<?> type) {
|
||||
|
||||
return getPersistentEntity(ClassTypeInformation.from(type));
|
||||
}
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.model.MappingContext#getPersistentEntity(java.lang.Class)
|
||||
*/
|
||||
public E getPersistentEntity(Class<?> type) {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.model.MappingContext#getPersistentEntity(org.springframework.data.util.TypeInformation)
|
||||
*/
|
||||
public E getPersistentEntity(TypeInformation<?> type) {
|
||||
|
||||
E entity = persistentEntities.get(type);
|
||||
|
||||
if (entity != null) {
|
||||
return entity;
|
||||
}
|
||||
|
||||
if (strict) {
|
||||
throw new MappingException("Unknown persistent entity " + type);
|
||||
}
|
||||
|
||||
return addPersistentEntity(type);
|
||||
}
|
||||
return getPersistentEntity(ClassTypeInformation.from(type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given type to the {@link MappingContext}.
|
||||
*
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
protected E addPersistentEntity(Class<?> type) {
|
||||
|
||||
return addPersistentEntity(ClassTypeInformation.from(type));
|
||||
}
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.model.MappingContext#getPersistentEntity(org.springframework.data.util.TypeInformation)
|
||||
*/
|
||||
public E getPersistentEntity(TypeInformation<?> type) {
|
||||
|
||||
/**
|
||||
* Adds the given {@link TypeInformation} to the {@link MappingContext}.
|
||||
*
|
||||
* @param typeInformation
|
||||
* @return
|
||||
*/
|
||||
protected E addPersistentEntity(TypeInformation<?> typeInformation) {
|
||||
E entity = persistentEntities.get(type);
|
||||
|
||||
E persistentEntity = persistentEntities.get(typeInformation);
|
||||
if (entity != null) {
|
||||
return entity;
|
||||
}
|
||||
|
||||
if (persistentEntity != null) {
|
||||
return persistentEntity;
|
||||
}
|
||||
if (strict) {
|
||||
throw new MappingException("Unknown persistent entity " + type);
|
||||
}
|
||||
|
||||
Class<?> type = typeInformation.getType();
|
||||
return addPersistentEntity(type);
|
||||
}
|
||||
|
||||
try {
|
||||
final E entity = createPersistentEntity(typeInformation);
|
||||
BeanInfo info = Introspector.getBeanInfo(type);
|
||||
/**
|
||||
* Adds the given type to the {@link MappingContext}.
|
||||
*
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
protected E addPersistentEntity(Class<?> type) {
|
||||
|
||||
final Map<String, PropertyDescriptor> descriptors = new HashMap<String, PropertyDescriptor>();
|
||||
for (PropertyDescriptor descriptor : info.getPropertyDescriptors()) {
|
||||
descriptors.put(descriptor.getName(), descriptor);
|
||||
}
|
||||
return addPersistentEntity(ClassTypeInformation.from(type));
|
||||
}
|
||||
|
||||
ReflectionUtils.doWithFields(type, new FieldCallback() {
|
||||
/**
|
||||
* Adds the given {@link TypeInformation} to the {@link MappingContext}.
|
||||
*
|
||||
* @param typeInformation
|
||||
* @return
|
||||
*/
|
||||
protected E addPersistentEntity(TypeInformation<?> typeInformation) {
|
||||
|
||||
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
|
||||
|
||||
PropertyDescriptor descriptor = descriptors.get(field.getName());
|
||||
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
P property = createPersistentProperty(field, descriptor, entity);
|
||||
E persistentEntity = persistentEntities.get(typeInformation);
|
||||
|
||||
if (property.isTransient()) {
|
||||
return;
|
||||
}
|
||||
|
||||
entity.addPersistentProperty(property);
|
||||
|
||||
if (property.isAssociation()) {
|
||||
entity.addAssociation(property.getAssociation());
|
||||
}
|
||||
if (persistentEntity != null) {
|
||||
return persistentEntity;
|
||||
}
|
||||
|
||||
if (property.isIdProperty()) {
|
||||
entity.setIdProperty(property);
|
||||
}
|
||||
Class<?> type = typeInformation.getType();
|
||||
|
||||
TypeInformation<?> nestedType = getNestedTypeToAdd(property, entity);
|
||||
if (nestedType != null) {
|
||||
addPersistentEntity(nestedType);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}, new ReflectionUtils.FieldFilter() {
|
||||
public boolean matches(Field field) {
|
||||
return !Modifier.isStatic(field.getModifiers()) && !UNMAPPED_FIELDS.contains(field.getName());
|
||||
}
|
||||
});
|
||||
|
||||
entity.verify();
|
||||
try {
|
||||
final E entity = createPersistentEntity(typeInformation);
|
||||
BeanInfo info = Introspector.getBeanInfo(type);
|
||||
|
||||
// Inform listeners
|
||||
if (null != applicationEventPublisher) {
|
||||
applicationEventPublisher.publishEvent(new MappingContextEvent<E, P>(entity, typeInformation));
|
||||
}
|
||||
final Map<String, PropertyDescriptor> descriptors = new HashMap<String, PropertyDescriptor>();
|
||||
for (PropertyDescriptor descriptor : info.getPropertyDescriptors()) {
|
||||
descriptors.put(descriptor.getName(), descriptor);
|
||||
}
|
||||
|
||||
// Cache
|
||||
persistentEntities.put(entity.getTypeInformation(), (E) entity);
|
||||
ReflectionUtils.doWithFields(type, new FieldCallback() {
|
||||
|
||||
return entity;
|
||||
} catch (IntrospectionException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
|
||||
|
||||
/**
|
||||
* Returns a potential nested type tha needs to be added when adding the given property in the course of adding a
|
||||
* {@link PersistentEntity}. Will return the property's {@link TypeInformation} directly if it is a potential entity,
|
||||
* a collections component type if it's a collection as well as the value type of a {@link Map} if it's a map
|
||||
* property.
|
||||
*
|
||||
* @param property
|
||||
* @return the TypeInformation to be added as {@link PersistentEntity} or {@literal
|
||||
*/
|
||||
private TypeInformation<?> getNestedTypeToAdd(P property, PersistentEntity<?, P> entity) {
|
||||
|
||||
if (entity.getType().equals(property.getRawType())) {
|
||||
return null;
|
||||
}
|
||||
PropertyDescriptor descriptor = descriptors.get(field.getName());
|
||||
|
||||
TypeInformation<?> typeInformation = property.getTypeInformation();
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
P property = createPersistentProperty(field, descriptor, entity);
|
||||
|
||||
if (customSimpleTypes.contains(typeInformation.getType())) {
|
||||
return null;
|
||||
}
|
||||
if (property.isTransient()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (property.isEntity()) {
|
||||
return typeInformation;
|
||||
}
|
||||
entity.addPersistentProperty(property);
|
||||
|
||||
if (property.isCollection()) {
|
||||
return getTypeInformationIfNotSimpleType(typeInformation.getComponentType());
|
||||
}
|
||||
if (property.isAssociation()) {
|
||||
entity.addAssociation(property.getAssociation());
|
||||
}
|
||||
|
||||
if (property.isMap()) {
|
||||
return getTypeInformationIfNotSimpleType(typeInformation.getMapValueType());
|
||||
}
|
||||
if (property.isIdProperty()) {
|
||||
entity.setIdProperty(property);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private TypeInformation<?> getTypeInformationIfNotSimpleType(TypeInformation<?> information) {
|
||||
return information == null || MappingBeanHelper.isSimpleType(information.getType()) ? null : information;
|
||||
}
|
||||
|
||||
public List<Validator> getEntityValidators(E entity) {
|
||||
return validators.get(entity);
|
||||
}
|
||||
|
||||
protected abstract <T> E createPersistentEntity(TypeInformation<T> typeInformation);
|
||||
|
||||
protected abstract P createPersistentProperty(Field field, PropertyDescriptor descriptor, E owner);
|
||||
TypeInformation<?> nestedType = getNestedTypeToAdd(property, entity);
|
||||
if (nestedType != null) {
|
||||
addPersistentEntity(nestedType);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}, new ReflectionUtils.FieldFilter() {
|
||||
public boolean matches(Field field) {
|
||||
return !Modifier.isStatic(field.getModifiers()) && !UNMAPPED_FIELDS.contains(field.getName());
|
||||
}
|
||||
});
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
for (Class<?> initialEntity : initialEntitySet) {
|
||||
addPersistentEntity(initialEntity);
|
||||
}
|
||||
}
|
||||
entity.verify();
|
||||
|
||||
// Inform listeners
|
||||
if (null != applicationEventPublisher) {
|
||||
applicationEventPublisher.publishEvent(new MappingContextEvent<E, P>(entity, typeInformation));
|
||||
}
|
||||
|
||||
// Cache
|
||||
persistentEntities.put(entity.getTypeInformation(), (E) entity);
|
||||
|
||||
return entity;
|
||||
} catch (IntrospectionException e) {
|
||||
throw new MappingException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a potential nested type tha needs to be added when adding the given property in the course of adding a
|
||||
* {@link PersistentEntity}. Will return the property's {@link TypeInformation} directly if it is a potential entity,
|
||||
* a collections component type if it's a collection as well as the value type of a {@link Map} if it's a map
|
||||
* property.
|
||||
*
|
||||
* @param property
|
||||
* @return the TypeInformation to be added as {@link PersistentEntity} or {@literal
|
||||
*/
|
||||
private TypeInformation<?> getNestedTypeToAdd(P property, PersistentEntity<?, P> entity) {
|
||||
|
||||
if (entity.getType().equals(property.getRawType())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TypeInformation<?> typeInformation = property.getTypeInformation();
|
||||
|
||||
if (customSimpleTypes.contains(typeInformation.getType())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (property.isEntity()) {
|
||||
return typeInformation;
|
||||
}
|
||||
|
||||
if (property.isCollection()) {
|
||||
return getTypeInformationIfNotSimpleType(typeInformation.getComponentType());
|
||||
}
|
||||
|
||||
if (property.isMap()) {
|
||||
return getTypeInformationIfNotSimpleType(typeInformation.getMapValueType());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private TypeInformation<?> getTypeInformationIfNotSimpleType(TypeInformation<?> information) {
|
||||
return information == null || MappingBeanHelper.isSimpleType(information.getType()) ? null : information;
|
||||
}
|
||||
|
||||
public List<Validator> getEntityValidators(E entity) {
|
||||
return validators.get(entity);
|
||||
}
|
||||
|
||||
protected abstract <T> E createPersistentEntity(TypeInformation<T> typeInformation);
|
||||
|
||||
protected abstract P createPersistentProperty(Field field, PropertyDescriptor descriptor, E owner);
|
||||
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
for (Class<?> initialEntity : initialEntitySet) {
|
||||
addPersistentEntity(initialEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>
|
||||
this.association = isAssociation() ? createAssociation() : null;
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
|
||||
protected abstract Association<P> createAssociation();
|
||||
|
||||
public PersistentEntity<?, P> getOwner() {
|
||||
@@ -94,16 +94,16 @@ public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>
|
||||
}
|
||||
|
||||
public boolean isAssociation() {
|
||||
if (field.isAnnotationPresent(Reference.class)) {
|
||||
return true;
|
||||
}
|
||||
for (Annotation annotation : field.getDeclaredAnnotations()) {
|
||||
if (annotation.annotationType().isAnnotationPresent(Reference.class)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
if (field.isAnnotationPresent(Reference.class)) {
|
||||
return true;
|
||||
}
|
||||
for (Annotation annotation : field.getDeclaredAnnotations()) {
|
||||
if (annotation.annotationType().isAnnotationPresent(Reference.class)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Association<P> getAssociation() {
|
||||
|
||||
@@ -31,78 +31,78 @@ import org.springframework.data.mapping.model.PersistentProperty;
|
||||
|
||||
/**
|
||||
* Special {@link PersistentProperty} that takes annotations at a property into account.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public abstract class AnnotationBasedPersistentProperty<P extends PersistentProperty<P>> extends AbstractPersistentProperty<P> {
|
||||
|
||||
private final Value value;
|
||||
|
||||
/**
|
||||
* Creates a new {@link AnnotationBasedPersistentProperty}.
|
||||
*
|
||||
* @param field
|
||||
* @param propertyDescriptor
|
||||
* @param owner
|
||||
*/
|
||||
public AnnotationBasedPersistentProperty(Field field,
|
||||
PropertyDescriptor propertyDescriptor, PersistentEntity<?, P> owner) {
|
||||
|
||||
super(field, propertyDescriptor, owner);
|
||||
this.value = field.getAnnotation(Value.class);
|
||||
field.isAnnotationPresent(Autowired.class);
|
||||
}
|
||||
private final Value value;
|
||||
|
||||
/**
|
||||
* Inspects a potentially available {@link Value} annotation at the property and returns the {@link String} value of
|
||||
* it.
|
||||
*
|
||||
* @see org.springframework.data.mapping.AbstractPersistentProperty#getSpelExpression()
|
||||
*/
|
||||
public String getSpelExpression() {
|
||||
return value == null ? null :value.value();
|
||||
}
|
||||
/**
|
||||
* Creates a new {@link AnnotationBasedPersistentProperty}.
|
||||
*
|
||||
* @param field
|
||||
* @param propertyDescriptor
|
||||
* @param owner
|
||||
*/
|
||||
public AnnotationBasedPersistentProperty(Field field,
|
||||
PropertyDescriptor propertyDescriptor, PersistentEntity<?, P> owner) {
|
||||
|
||||
/**
|
||||
* Considers plain transient fields, fields annotated with {@link Transient}, {@link Value} or {@link Autowired} as
|
||||
* transien.
|
||||
*
|
||||
* @see org.springframework.data.mapping.BasicPersistentProperty#isTransient()
|
||||
*/
|
||||
public boolean isTransient() {
|
||||
|
||||
boolean isTransient = super.isTransient() || field.isAnnotationPresent(Transient.class);
|
||||
|
||||
return isTransient || field.isAnnotationPresent(Value.class) || field.isAnnotationPresent(Autowired.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Regards the property as ID if there is an {@link Id} annotation found on it.
|
||||
*/
|
||||
public boolean isIdProperty() {
|
||||
return field.isAnnotationPresent(Id.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Considers the property an {@link Association} if it is annotated with {@link Reference}.
|
||||
*/
|
||||
@Override
|
||||
public boolean isAssociation() {
|
||||
|
||||
if (isTransient()) {
|
||||
return false;
|
||||
}
|
||||
if (field.isAnnotationPresent(Reference.class)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: do we need this? Shouldn't the section above already find that annotation?
|
||||
for (Annotation annotation : field.getDeclaredAnnotations()) {
|
||||
if (annotation.annotationType().isAnnotationPresent(Reference.class)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
super(field, propertyDescriptor, owner);
|
||||
this.value = field.getAnnotation(Value.class);
|
||||
field.isAnnotationPresent(Autowired.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspects a potentially available {@link Value} annotation at the property and returns the {@link String} value of
|
||||
* it.
|
||||
*
|
||||
* @see org.springframework.data.mapping.AbstractPersistentProperty#getSpelExpression()
|
||||
*/
|
||||
public String getSpelExpression() {
|
||||
return value == null ? null : value.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Considers plain transient fields, fields annotated with {@link Transient}, {@link Value} or {@link Autowired} as
|
||||
* transien.
|
||||
*
|
||||
* @see org.springframework.data.mapping.BasicPersistentProperty#isTransient()
|
||||
*/
|
||||
public boolean isTransient() {
|
||||
|
||||
boolean isTransient = super.isTransient() || field.isAnnotationPresent(Transient.class);
|
||||
|
||||
return isTransient || field.isAnnotationPresent(Value.class) || field.isAnnotationPresent(Autowired.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Regards the property as ID if there is an {@link Id} annotation found on it.
|
||||
*/
|
||||
public boolean isIdProperty() {
|
||||
return field.isAnnotationPresent(Id.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Considers the property an {@link Association} if it is annotated with {@link Reference}.
|
||||
*/
|
||||
@Override
|
||||
public boolean isAssociation() {
|
||||
|
||||
if (isTransient()) {
|
||||
return false;
|
||||
}
|
||||
if (field.isAnnotationPresent(Reference.class)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: do we need this? Shouldn't the section above already find that annotation?
|
||||
for (Annotation annotation : field.getDeclaredAnnotations()) {
|
||||
if (annotation.annotationType().isAnnotationPresent(Reference.class)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,5 +23,5 @@ import org.springframework.data.mapping.model.PersistentProperty;
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public interface AssociationHandler<P extends PersistentProperty<P>> {
|
||||
void doWithAssociation(Association<P> association);
|
||||
void doWithAssociation(Association<P> association);
|
||||
}
|
||||
|
||||
@@ -27,103 +27,103 @@ import org.springframework.data.util.TypeInformation;
|
||||
|
||||
/**
|
||||
* Simple value object to capture information of {@link PersistentEntity}s.
|
||||
*
|
||||
*
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implements MutablePersistentEntity<T, P> {
|
||||
|
||||
protected final PreferredConstructor<T> preferredConstructor;
|
||||
protected final TypeInformation<T> information;
|
||||
protected final Map<String, P> persistentProperties = new HashMap<String, P>();
|
||||
protected final Map<String, Association<P>> associations = new HashMap<String, Association<P>>();
|
||||
protected P idProperty;
|
||||
protected final PreferredConstructor<T> preferredConstructor;
|
||||
protected final TypeInformation<T> information;
|
||||
protected final Map<String, P> persistentProperties = new HashMap<String, P>();
|
||||
protected final Map<String, Association<P>> associations = new HashMap<String, Association<P>>();
|
||||
protected P idProperty;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link BasicPersistentEntity} from the given {@link TypeInformation}.
|
||||
*
|
||||
* @param information
|
||||
*/
|
||||
public BasicPersistentEntity(TypeInformation<T> information) {
|
||||
this.information = information;
|
||||
this.preferredConstructor = new PreferredConstructorDiscoverer<T>(information).getConstructor();
|
||||
}
|
||||
/**
|
||||
* Creates a new {@link BasicPersistentEntity} from the given {@link TypeInformation}.
|
||||
*
|
||||
* @param information
|
||||
*/
|
||||
public BasicPersistentEntity(TypeInformation<T> information) {
|
||||
this.information = information;
|
||||
this.preferredConstructor = new PreferredConstructorDiscoverer<T>(information).getConstructor();
|
||||
}
|
||||
|
||||
public PreferredConstructor<T> getPreferredConstructor() {
|
||||
return preferredConstructor;
|
||||
}
|
||||
public PreferredConstructor<T> getPreferredConstructor() {
|
||||
return preferredConstructor;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return getType().getName();
|
||||
}
|
||||
public String getName() {
|
||||
return getType().getName();
|
||||
}
|
||||
|
||||
public P getIdProperty() {
|
||||
return idProperty;
|
||||
}
|
||||
public P getIdProperty() {
|
||||
return idProperty;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.MutablePersistentEntity#setIdProperty(P)
|
||||
*/
|
||||
public void setIdProperty(P property) {
|
||||
idProperty = property;
|
||||
}
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.MutablePersistentEntity#setIdProperty(P)
|
||||
*/
|
||||
public void setIdProperty(P property) {
|
||||
idProperty = property;
|
||||
}
|
||||
|
||||
public Collection<P> getPersistentProperties() {
|
||||
return persistentProperties.values();
|
||||
}
|
||||
public Collection<P> getPersistentProperties() {
|
||||
return persistentProperties.values();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.MutablePersistentEntity#addPersistentProperty(P)
|
||||
*/
|
||||
public void addPersistentProperty(P property) {
|
||||
persistentProperties.put(property.getName(), property);
|
||||
}
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.MutablePersistentEntity#addPersistentProperty(P)
|
||||
*/
|
||||
public void addPersistentProperty(P property) {
|
||||
persistentProperties.put(property.getName(), property);
|
||||
}
|
||||
|
||||
public Collection<Association<P>> getAssociations() {
|
||||
return associations.values();
|
||||
}
|
||||
public Collection<Association<P>> getAssociations() {
|
||||
return associations.values();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.MutablePersistentEntity#addAssociation(org.springframework.data.mapping.model.Association)
|
||||
*/
|
||||
public void addAssociation(Association<P> association) {
|
||||
associations.put(association.getInverse().getName(), association);
|
||||
}
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.MutablePersistentEntity#addAssociation(org.springframework.data.mapping.model.Association)
|
||||
*/
|
||||
public void addAssociation(Association<P> association) {
|
||||
associations.put(association.getInverse().getName(), association);
|
||||
}
|
||||
|
||||
public P getPersistentProperty(String name) {
|
||||
return persistentProperties.get(name);
|
||||
}
|
||||
public P getPersistentProperty(String name) {
|
||||
return persistentProperties.get(name);
|
||||
}
|
||||
|
||||
public Class<T> getType() {
|
||||
return information.getType();
|
||||
}
|
||||
public Class<T> getType() {
|
||||
return information.getType();
|
||||
}
|
||||
|
||||
public TypeInformation<T> getTypeInformation() {
|
||||
return information;
|
||||
}
|
||||
public TypeInformation<T> getTypeInformation() {
|
||||
return information;
|
||||
}
|
||||
|
||||
public Collection<String> getPersistentPropertyNames() {
|
||||
return persistentProperties.keySet();
|
||||
}
|
||||
public Collection<String> getPersistentPropertyNames() {
|
||||
return persistentProperties.keySet();
|
||||
}
|
||||
|
||||
public void doWithProperties(PropertyHandler<P> handler) {
|
||||
for (P property : persistentProperties.values()) {
|
||||
if (!property.isTransient() && !property.isAssociation()) {
|
||||
handler.doWithPersistentProperty(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
public void doWithProperties(PropertyHandler<P> handler) {
|
||||
for (P property : persistentProperties.values()) {
|
||||
if (!property.isTransient() && !property.isAssociation()) {
|
||||
handler.doWithPersistentProperty(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void doWithAssociations(AssociationHandler<P> handler) {
|
||||
for (Association<P> association : associations.values()) {
|
||||
handler.doWithAssociation(association);
|
||||
}
|
||||
}
|
||||
public void doWithAssociations(AssociationHandler<P> handler) {
|
||||
for (Association<P> association : associations.values()) {
|
||||
handler.doWithAssociation(association);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.MutablePersistentEntity#verify()
|
||||
*/
|
||||
public void verify() {
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.MutablePersistentEntity#verify()
|
||||
*/
|
||||
public void verify() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,32 +174,32 @@ public abstract class MappingBeanHelper {
|
||||
|
||||
Method setter = property.getPropertyDescriptor() != null ? property.getPropertyDescriptor().getWriteMethod() : null;
|
||||
|
||||
if (fieldAccessOnly || null == setter) {
|
||||
Object valueToSet = getPotentiallyConvertedValue(value, property.getType());
|
||||
ReflectionUtils.makeAccessible(property.getField());
|
||||
ReflectionUtils.setField(property.getField(), on, valueToSet);
|
||||
return;
|
||||
}
|
||||
if (fieldAccessOnly || null == setter) {
|
||||
Object valueToSet = getPotentiallyConvertedValue(value, property.getType());
|
||||
ReflectionUtils.makeAccessible(property.getField());
|
||||
ReflectionUtils.setField(property.getField(), on, valueToSet);
|
||||
return;
|
||||
}
|
||||
|
||||
Class<?>[] paramTypes = setter.getParameterTypes();
|
||||
Object valueToSet = getPotentiallyConvertedValue(value, paramTypes[0]);
|
||||
ReflectionUtils.makeAccessible(setter);
|
||||
ReflectionUtils.invokeMethod(setter, on, valueToSet);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts the given source value if it is not assignable to the given target type.
|
||||
*
|
||||
*
|
||||
* @param source
|
||||
* @param targetType
|
||||
* @return
|
||||
*/
|
||||
private static Object getPotentiallyConvertedValue(Object source, Class<?> targetType) {
|
||||
if (source != null && source.getClass().isAssignableFrom(targetType)) {
|
||||
return source;
|
||||
}
|
||||
|
||||
return conversionService.convert(source, targetType);
|
||||
if (source != null && source.getClass().isAssignableFrom(targetType)) {
|
||||
return source;
|
||||
}
|
||||
|
||||
return conversionService.convert(source, targetType);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
|
||||
@@ -22,37 +22,37 @@ import org.springframework.data.mapping.model.PersistentProperty;
|
||||
|
||||
/**
|
||||
* Interface capturing mutator methods for {@link PersistentEntity}s.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface MutablePersistentEntity<T, P extends PersistentProperty<P>>
|
||||
extends PersistentEntity<T, P> {
|
||||
extends PersistentEntity<T, P> {
|
||||
|
||||
/**
|
||||
* Sets the id property for the entity.
|
||||
*
|
||||
* @param property
|
||||
*/
|
||||
void setIdProperty(P property);
|
||||
/**
|
||||
* Sets the id property for the entity.
|
||||
*
|
||||
* @param property
|
||||
*/
|
||||
void setIdProperty(P property);
|
||||
|
||||
/**
|
||||
* Adds a {@link PersistentProperty} to the entity.
|
||||
*
|
||||
* @param property
|
||||
*/
|
||||
void addPersistentProperty(P property);
|
||||
/**
|
||||
* Adds a {@link PersistentProperty} to the entity.
|
||||
*
|
||||
* @param property
|
||||
*/
|
||||
void addPersistentProperty(P property);
|
||||
|
||||
/**
|
||||
* Adds an {@link Association} to the entity.
|
||||
*
|
||||
* @param association
|
||||
*/
|
||||
void addAssociation(Association<P> association);
|
||||
/**
|
||||
* Adds an {@link Association} to the entity.
|
||||
*
|
||||
* @param association
|
||||
*/
|
||||
void addAssociation(Association<P> association);
|
||||
|
||||
/**
|
||||
* Callback method to trigger validation of the {@link PersistentEntity}. As
|
||||
* {@link MutablePersistentEntity} is not immutable there might be some
|
||||
* verification steps necessary after the object has reached is final state.
|
||||
*/
|
||||
void verify();
|
||||
/**
|
||||
* Callback method to trigger validation of the {@link PersistentEntity}. As
|
||||
* {@link MutablePersistentEntity} is not immutable there might be some
|
||||
* verification steps necessary after the object has reached is final state.
|
||||
*/
|
||||
void verify();
|
||||
}
|
||||
@@ -34,85 +34,85 @@ import org.springframework.data.util.TypeInformation;
|
||||
*/
|
||||
public class PreferredConstructorDiscoverer<T> {
|
||||
|
||||
private final ParameterNameDiscoverer nameDiscoverer =
|
||||
new LocalVariableTableParameterNameDiscoverer();
|
||||
private final ParameterNameDiscoverer nameDiscoverer =
|
||||
new LocalVariableTableParameterNameDiscoverer();
|
||||
|
||||
private PreferredConstructor<T> constructor;
|
||||
|
||||
public PreferredConstructorDiscoverer(Class<T> type) {
|
||||
this(ClassTypeInformation.from(type));
|
||||
}
|
||||
private PreferredConstructor<T> constructor;
|
||||
|
||||
/**
|
||||
* Creates a new {@link PreferredConstructorDiscoverer} for the given type.
|
||||
*
|
||||
* @param owningType
|
||||
*/
|
||||
protected PreferredConstructorDiscoverer(TypeInformation<T> owningType) {
|
||||
|
||||
boolean noArgConstructorFound = false;
|
||||
int numberOfArgConstructors = 0;
|
||||
Class<?> rawOwningType = owningType.getType();
|
||||
public PreferredConstructorDiscoverer(Class<T> type) {
|
||||
this(ClassTypeInformation.from(type));
|
||||
}
|
||||
|
||||
for (Constructor<?> constructor : rawOwningType.getDeclaredConstructors()) {
|
||||
/**
|
||||
* Creates a new {@link PreferredConstructorDiscoverer} for the given type.
|
||||
*
|
||||
* @param owningType
|
||||
*/
|
||||
protected PreferredConstructorDiscoverer(TypeInformation<T> owningType) {
|
||||
|
||||
PreferredConstructor<T> preferredConstructor =
|
||||
buildPreferredConstructor(constructor, owningType);
|
||||
boolean noArgConstructorFound = false;
|
||||
int numberOfArgConstructors = 0;
|
||||
Class<?> rawOwningType = owningType.getType();
|
||||
|
||||
// Explicitly defined constructor trumps all
|
||||
if (preferredConstructor.isExplicitlyAnnotated()) {
|
||||
this.constructor = preferredConstructor;
|
||||
return;
|
||||
}
|
||||
for (Constructor<?> constructor : rawOwningType.getDeclaredConstructors()) {
|
||||
|
||||
// No-arg constructor trumps custom ones
|
||||
if (this.constructor == null || preferredConstructor.isNoArgConstructor()) {
|
||||
this.constructor = preferredConstructor;
|
||||
}
|
||||
|
||||
if (preferredConstructor.isNoArgConstructor()) {
|
||||
noArgConstructorFound = true;
|
||||
} else {
|
||||
numberOfArgConstructors++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!noArgConstructorFound && numberOfArgConstructors > 1) {
|
||||
throw new IllegalArgumentException(
|
||||
"Multiple no-arg constructors found! Annotate one with @PreferedConstructor explicitly to select it to be used in persistence operations.");
|
||||
}
|
||||
}
|
||||
PreferredConstructor<T> preferredConstructor =
|
||||
buildPreferredConstructor(constructor, owningType);
|
||||
|
||||
// Explicitly defined constructor trumps all
|
||||
if (preferredConstructor.isExplicitlyAnnotated()) {
|
||||
this.constructor = preferredConstructor;
|
||||
return;
|
||||
}
|
||||
|
||||
// No-arg constructor trumps custom ones
|
||||
if (this.constructor == null || preferredConstructor.isNoArgConstructor()) {
|
||||
this.constructor = preferredConstructor;
|
||||
}
|
||||
|
||||
if (preferredConstructor.isNoArgConstructor()) {
|
||||
noArgConstructorFound = true;
|
||||
} else {
|
||||
numberOfArgConstructors++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!noArgConstructorFound && numberOfArgConstructors > 1) {
|
||||
throw new IllegalArgumentException(
|
||||
"Multiple no-arg constructors found! Annotate one with @PreferedConstructor explicitly to select it to be used in persistence operations.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private PreferredConstructor<T> buildPreferredConstructor(
|
||||
Constructor<?> constructor, TypeInformation<T> typeInformation) {
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private PreferredConstructor<T> buildPreferredConstructor(
|
||||
Constructor<?> constructor, TypeInformation<T> typeInformation) {
|
||||
|
||||
List<TypeInformation<?>> parameterTypes = typeInformation.getParameterTypes(constructor);
|
||||
|
||||
if (parameterTypes.isEmpty()) {
|
||||
return new PreferredConstructor<T>((Constructor<T>) constructor);
|
||||
}
|
||||
|
||||
String[] parameterNames = nameDiscoverer.getParameterNames(constructor);
|
||||
Parameter<?>[] parameters = new Parameter[parameterTypes.size()];
|
||||
Annotation[][] parameterAnnotations = constructor.getParameterAnnotations();
|
||||
List<TypeInformation<?>> parameterTypes = typeInformation.getParameterTypes(constructor);
|
||||
|
||||
for (int i = 0; i < parameterTypes.size(); i++) {
|
||||
|
||||
String name = parameterNames == null ? null : parameterNames[i];
|
||||
TypeInformation<?> type = parameterTypes.get(i);
|
||||
Annotation[] annotations = parameterAnnotations[i];
|
||||
|
||||
parameters[i] = new Parameter(name, type, annotations);
|
||||
}
|
||||
if (parameterTypes.isEmpty()) {
|
||||
return new PreferredConstructor<T>((Constructor<T>) constructor);
|
||||
}
|
||||
|
||||
return new PreferredConstructor<T>((Constructor<T>) constructor,
|
||||
parameters);
|
||||
}
|
||||
String[] parameterNames = nameDiscoverer.getParameterNames(constructor);
|
||||
Parameter<?>[] parameters = new Parameter[parameterTypes.size()];
|
||||
Annotation[][] parameterAnnotations = constructor.getParameterAnnotations();
|
||||
|
||||
for (int i = 0; i < parameterTypes.size(); i++) {
|
||||
|
||||
String name = parameterNames == null ? null : parameterNames[i];
|
||||
TypeInformation<?> type = parameterTypes.get(i);
|
||||
Annotation[] annotations = parameterAnnotations[i];
|
||||
|
||||
parameters[i] = new Parameter(name, type, annotations);
|
||||
}
|
||||
|
||||
return new PreferredConstructor<T>((Constructor<T>) constructor,
|
||||
parameters);
|
||||
}
|
||||
|
||||
|
||||
public PreferredConstructor<T> getConstructor() {
|
||||
return constructor;
|
||||
}
|
||||
public PreferredConstructor<T> getConstructor() {
|
||||
return constructor;
|
||||
}
|
||||
}
|
||||
@@ -21,10 +21,10 @@ import org.springframework.data.mapping.model.PersistentProperty;
|
||||
/**
|
||||
* Callback interface to do something with all plain {@link PersistentProperty}
|
||||
* instances <em>except</em> associations, transient properties and the id-property.
|
||||
*
|
||||
*
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public interface PropertyHandler<P extends PersistentProperty<P>> {
|
||||
|
||||
void doWithPersistentProperty(P persistentProperty);
|
||||
void doWithPersistentProperty(P persistentProperty);
|
||||
}
|
||||
|
||||
@@ -25,11 +25,11 @@ import org.springframework.data.mapping.model.MappingContext;
|
||||
*/
|
||||
public interface MappingContextAware {
|
||||
|
||||
/**
|
||||
* The active MappingContext for the environment.
|
||||
*
|
||||
* @param mappingContext
|
||||
*/
|
||||
void setMappingContext(MappingContext<?, ?> mappingContext);
|
||||
/**
|
||||
* The active MappingContext for the environment.
|
||||
*
|
||||
* @param mappingContext
|
||||
*/
|
||||
void setMappingContext(MappingContext<?, ?> mappingContext);
|
||||
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ public class MappingContextAwareBeanPostProcessor implements BeanPostProcessor,
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof MappingContextAware) {
|
||||
if (null == mappingContext) {
|
||||
Map<String, MappingContext> mappingContexts = applicationContext.getBeansOfType(MappingContext.class);
|
||||
|
||||
@@ -26,20 +26,20 @@ import org.springframework.data.util.TypeInformation;
|
||||
*/
|
||||
public class MappingContextEvent<E extends PersistentEntity<?, P>, P extends PersistentProperty<P>> extends ApplicationEvent {
|
||||
|
||||
private static final long serialVersionUID = 1336466833846092490L;
|
||||
private TypeInformation<?> typeInformation;
|
||||
private static final long serialVersionUID = 1336466833846092490L;
|
||||
private TypeInformation<?> typeInformation;
|
||||
|
||||
public MappingContextEvent(E source, TypeInformation<?> typeInformation) {
|
||||
super(source);
|
||||
this.typeInformation = typeInformation;
|
||||
}
|
||||
public MappingContextEvent(E source, TypeInformation<?> typeInformation) {
|
||||
super(source);
|
||||
this.typeInformation = typeInformation;
|
||||
}
|
||||
|
||||
public TypeInformation<?> getTypeInformation() {
|
||||
return typeInformation;
|
||||
}
|
||||
public TypeInformation<?> getTypeInformation() {
|
||||
return typeInformation;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public E getPersistentEntity() {
|
||||
return (E) source;
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
public E getPersistentEntity() {
|
||||
return (E) source;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,19 +21,19 @@ package org.springframework.data.mapping.model;
|
||||
*/
|
||||
public class Association<P extends PersistentProperty<P>> {
|
||||
|
||||
protected P inverse;
|
||||
protected P obverse;
|
||||
protected P inverse;
|
||||
protected P obverse;
|
||||
|
||||
public Association(P inverse, P obverse) {
|
||||
this.inverse = inverse;
|
||||
this.obverse = obverse;
|
||||
}
|
||||
public Association(P inverse, P obverse) {
|
||||
this.inverse = inverse;
|
||||
this.obverse = obverse;
|
||||
}
|
||||
|
||||
public P getInverse() {
|
||||
return inverse;
|
||||
}
|
||||
public P getInverse() {
|
||||
return inverse;
|
||||
}
|
||||
|
||||
public P getObverse() {
|
||||
return obverse;
|
||||
}
|
||||
public P getObverse() {
|
||||
return obverse;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,16 +22,16 @@ package org.springframework.data.mapping.model;
|
||||
*/
|
||||
public class IllegalMappingException extends RuntimeException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public IllegalMappingException(String s, Throwable throwable) {
|
||||
super(s, throwable);
|
||||
}
|
||||
public IllegalMappingException(String s, Throwable throwable) {
|
||||
super(s, throwable);
|
||||
}
|
||||
|
||||
public IllegalMappingException(String s) {
|
||||
super(s);
|
||||
}
|
||||
public IllegalMappingException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,16 +20,16 @@ package org.springframework.data.mapping.model;
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class MappingConfigurationException extends Throwable {
|
||||
/**
|
||||
*
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public MappingConfigurationException(String s) {
|
||||
super(s);
|
||||
}
|
||||
public MappingConfigurationException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
public MappingConfigurationException(String s, Throwable throwable) {
|
||||
super(s, throwable);
|
||||
}
|
||||
public MappingConfigurationException(String s, Throwable throwable) {
|
||||
super(s, throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,24 +36,24 @@ import org.springframework.validation.Validator;
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface MappingContext<E extends PersistentEntity<?, P>, P extends PersistentProperty<P>> {
|
||||
|
||||
/**
|
||||
* Obtains a list of PersistentEntity instances
|
||||
*
|
||||
* @return A list of PersistentEntity instances
|
||||
*/
|
||||
Collection<E> getPersistentEntities();
|
||||
|
||||
E getPersistentEntity(Class<?> type);
|
||||
/**
|
||||
* Obtains a list of PersistentEntity instances
|
||||
*
|
||||
* @return A list of PersistentEntity instances
|
||||
*/
|
||||
Collection<E> getPersistentEntities();
|
||||
|
||||
E getPersistentEntity(TypeInformation<?> type);
|
||||
E getPersistentEntity(Class<?> type);
|
||||
|
||||
/**
|
||||
* Obtains a validator for the given entity
|
||||
* TODO: Why do we need validators at the {@link MappingContext}?
|
||||
*
|
||||
* @param entity The entity
|
||||
* @return A validator or null if none exists for the given entity
|
||||
*/
|
||||
List<Validator> getEntityValidators(E entity);
|
||||
E getPersistentEntity(TypeInformation<?> type);
|
||||
|
||||
/**
|
||||
* Obtains a validator for the given entity
|
||||
* TODO: Why do we need validators at the {@link MappingContext}?
|
||||
*
|
||||
* @param entity The entity
|
||||
* @return A validator or null if none exists for the given entity
|
||||
*/
|
||||
List<Validator> getEntityValidators(E entity);
|
||||
}
|
||||
|
||||
@@ -20,17 +20,17 @@ package org.springframework.data.mapping.model;
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class MappingException extends RuntimeException {
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public MappingException(String s) {
|
||||
super(s);
|
||||
}
|
||||
super(s);
|
||||
}
|
||||
|
||||
public MappingException(String s, Throwable throwable) {
|
||||
super(s, throwable);
|
||||
}
|
||||
public MappingException(String s, Throwable throwable) {
|
||||
super(s, throwable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,14 +20,14 @@ package org.springframework.data.mapping.model;
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class MappingInstantiationException extends RuntimeException {
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public MappingInstantiationException(String s, Throwable throwable) {
|
||||
super(s, throwable);
|
||||
}
|
||||
|
||||
super(s, throwable);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package org.springframework.data.mapping.model;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.data.mapping.AssociationHandler;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a persistent entity
|
||||
@@ -16,62 +16,62 @@ import java.util.Collection;
|
||||
*/
|
||||
public interface PersistentEntity<T, P extends PersistentProperty<P>> {
|
||||
|
||||
/**
|
||||
* The entity name including any package prefix
|
||||
*
|
||||
* @return The entity name
|
||||
*/
|
||||
String getName();
|
||||
/**
|
||||
* The entity name including any package prefix
|
||||
*
|
||||
* @return The entity name
|
||||
*/
|
||||
String getName();
|
||||
|
||||
PreferredConstructor<T> getPreferredConstructor();
|
||||
PreferredConstructor<T> getPreferredConstructor();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the id property of the {@link PersistentEntity}. Must never
|
||||
* return {@literal null} as a {@link PersistentEntity} instance must not be
|
||||
* created if there is no id property.
|
||||
*
|
||||
* @return the id property of the {@link PersistentEntity}.
|
||||
*/
|
||||
P getIdProperty();
|
||||
/**
|
||||
* Returns the id property of the {@link PersistentEntity}. Must never
|
||||
* return {@literal null} as a {@link PersistentEntity} instance must not be
|
||||
* created if there is no id property.
|
||||
*
|
||||
* @return the id property of the {@link PersistentEntity}.
|
||||
*/
|
||||
P getIdProperty();
|
||||
|
||||
/**
|
||||
* A list of properties to be persisted
|
||||
*
|
||||
* @return A list of PersistentProperty instances
|
||||
*/
|
||||
Collection<P> getPersistentProperties();
|
||||
/**
|
||||
* A list of properties to be persisted
|
||||
*
|
||||
* @return A list of PersistentProperty instances
|
||||
*/
|
||||
Collection<P> getPersistentProperties();
|
||||
|
||||
/**
|
||||
* A list of the associations for this entity. This is typically a subset of the list returned by {@link #getPersistentProperties()}
|
||||
*
|
||||
* @return A list of associations
|
||||
*/
|
||||
Collection<Association<P>> getAssociations();
|
||||
/**
|
||||
* A list of the associations for this entity. This is typically a subset of the list returned by {@link #getPersistentProperties()}
|
||||
*
|
||||
* @return A list of associations
|
||||
*/
|
||||
Collection<Association<P>> getAssociations();
|
||||
|
||||
/**
|
||||
* Obtains a PersistentProperty instance by name
|
||||
*
|
||||
* @param name The name of the property
|
||||
* @return The PersistentProperty or null if it doesn't exist
|
||||
*/
|
||||
P getPersistentProperty(String name);
|
||||
/**
|
||||
* Obtains a PersistentProperty instance by name
|
||||
*
|
||||
* @param name The name of the property
|
||||
* @return The PersistentProperty or null if it doesn't exist
|
||||
*/
|
||||
P getPersistentProperty(String name);
|
||||
|
||||
/**
|
||||
* @return The underlying Java class for this entity
|
||||
*/
|
||||
Class<T> getType();
|
||||
|
||||
TypeInformation<T> getTypeInformation();
|
||||
/**
|
||||
* @return The underlying Java class for this entity
|
||||
*/
|
||||
Class<T> getType();
|
||||
|
||||
/**
|
||||
* A list of property names
|
||||
*
|
||||
* @return A List of strings
|
||||
*/
|
||||
Collection<String> getPersistentPropertyNames();
|
||||
TypeInformation<T> getTypeInformation();
|
||||
|
||||
void doWithProperties(PropertyHandler<P> handler);
|
||||
/**
|
||||
* A list of property names
|
||||
*
|
||||
* @return A List of strings
|
||||
*/
|
||||
Collection<String> getPersistentPropertyNames();
|
||||
|
||||
void doWithAssociations(AssociationHandler<P> handler);
|
||||
void doWithProperties(PropertyHandler<P> handler);
|
||||
|
||||
void doWithAssociations(AssociationHandler<P> handler);
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Value object to encapsulate the constructor to be used when mapping persistent data to objects.
|
||||
*
|
||||
* Value object to encapsulate the constructor to be used when mapping persistent data to objects.
|
||||
*
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@@ -39,7 +39,7 @@ public class PreferredConstructor<T> {
|
||||
private final List<Parameter<?>> parameters;
|
||||
|
||||
public PreferredConstructor(Constructor<T> constructor, Parameter<?>... parameters) {
|
||||
ReflectionUtils.makeAccessible(constructor);
|
||||
ReflectionUtils.makeAccessible(constructor);
|
||||
this.constructor = constructor;
|
||||
this.parameters = Arrays.asList(parameters);
|
||||
}
|
||||
@@ -51,23 +51,23 @@ public class PreferredConstructor<T> {
|
||||
public List<Parameter<?>> getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the constructor does not have any arguments.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isNoArgConstructor() {
|
||||
return parameters.isEmpty();
|
||||
return parameters.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the constructor was explicitly selected (by {@link PersistenceConstructor}).
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isExplicitlyAnnotated() {
|
||||
return constructor.isAnnotationPresent(PersistenceConstructor.class);
|
||||
return constructor.isAnnotationPresent(PersistenceConstructor.class);
|
||||
}
|
||||
|
||||
public static class Parameter<T> {
|
||||
@@ -76,22 +76,22 @@ public class PreferredConstructor<T> {
|
||||
private final String key;
|
||||
|
||||
public Parameter(String name, TypeInformation<T> type, Annotation[] annotations) {
|
||||
|
||||
Assert.notNull(type);
|
||||
Assert.notNull(annotations);
|
||||
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.key = getValue(annotations);
|
||||
|
||||
Assert.notNull(type);
|
||||
Assert.notNull(annotations);
|
||||
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.key = getValue(annotations);
|
||||
}
|
||||
|
||||
|
||||
private String getValue(Annotation[] annotations) {
|
||||
for (Annotation anno : annotations) {
|
||||
if (anno.annotationType() == Value.class) {
|
||||
return ((Value) anno).value();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
for (Annotation anno : annotations) {
|
||||
if (anno.annotationType() == Value.class) {
|
||||
return ((Value) anno).value();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@@ -101,9 +101,9 @@ public class PreferredConstructor<T> {
|
||||
public TypeInformation<T> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
public Class<T> getRawType() {
|
||||
return type.getType();
|
||||
return type.getType();
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
|
||||
@@ -1,141 +1,142 @@
|
||||
package org.springframework.data.persistence;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import sun.reflect.ReflectionFactory;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Try for a constructor taking state: failing that, try a no-arg
|
||||
* constructor and then setUnderlyingNode().
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
public abstract class AbstractConstructorEntityInstantiator<BACKING_INTERFACE, STATE> implements EntityInstantiator<BACKING_INTERFACE, STATE> {
|
||||
|
||||
private final Log log = LogFactory.getLog(getClass());
|
||||
private final Map<Class<? extends BACKING_INTERFACE>,StateBackedCreator<? extends BACKING_INTERFACE,STATE>> cache = new HashMap<Class<? extends BACKING_INTERFACE>,StateBackedCreator<? extends BACKING_INTERFACE,STATE>>();
|
||||
|
||||
public <T extends BACKING_INTERFACE> T createEntityFromState(STATE n, Class<T> c) {
|
||||
try {
|
||||
StateBackedCreator<T, STATE> creator = (StateBackedCreator<T, STATE>) cache.get(c);
|
||||
if (creator !=null) return creator.create(n,c);
|
||||
synchronized (cache) {
|
||||
creator = (StateBackedCreator<T, STATE>) cache.get(c);
|
||||
if (creator !=null) return creator.create(n,c);
|
||||
Class<STATE> stateClass = (Class<STATE>) n.getClass();
|
||||
creator =createInstantiator(c, stateClass);
|
||||
cache.put(c, creator);
|
||||
return creator.create(n,c);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw e;
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new IllegalArgumentException(e.getTargetException());
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setInstantiators(Map<Class<? extends BACKING_INTERFACE>,StateBackedCreator<? extends BACKING_INTERFACE,STATE>> instantiators) {
|
||||
this.cache.putAll(instantiators);
|
||||
}
|
||||
|
||||
protected <T extends BACKING_INTERFACE> StateBackedCreator<T, STATE> createInstantiator(Class<T> type, final Class<STATE> stateType) {
|
||||
StateBackedCreator<T,STATE> creator = stateTakingConstructorInstantiator(type,stateType);
|
||||
if (creator !=null) return creator;
|
||||
creator = emptyConstructorStateSettingInstantiator(type,stateType);
|
||||
if (creator !=null) return creator;
|
||||
return createFailingInstantiator(stateType);
|
||||
}
|
||||
|
||||
protected <T extends BACKING_INTERFACE> StateBackedCreator<T, STATE> createFailingInstantiator(final Class<STATE> stateType) {
|
||||
return new StateBackedCreator<T, STATE>() {
|
||||
public T create(STATE n, Class<T> c) throws Exception {
|
||||
throw new IllegalArgumentException(getFailingMessageForClass(c, stateType));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected String getFailingMessageForClass(Class<?> entityClass, Class<STATE> stateClass) {
|
||||
return getClass().getSimpleName() + ": entity " + entityClass +
|
||||
" must have either a constructor taking [" + stateClass + "] or a no-arg constructor and state setter.";
|
||||
}
|
||||
|
||||
private <T extends BACKING_INTERFACE> StateBackedCreator<T, STATE> emptyConstructorStateSettingInstantiator(Class<T> type, Class<STATE> stateType) {
|
||||
final Constructor<T> constructor = getNoArgConstructor(type);
|
||||
if (constructor == null) return null;
|
||||
|
||||
log.info("Using " + type + " no-arg constructor");
|
||||
|
||||
return new StateBackedCreator<T, STATE>() {
|
||||
public T create(STATE state, Class<T> c) throws Exception {
|
||||
try {
|
||||
StateProvider.setUnderlyingState(state);
|
||||
T newInstance = constructor.newInstance();
|
||||
setState(newInstance, state);
|
||||
return newInstance;
|
||||
} finally {
|
||||
StateProvider.retrieveState();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected <T extends BACKING_INTERFACE> StateBackedCreator<T, STATE> createWithoutConstructorInvocation(final Class<T> type, Class<STATE> stateType) {
|
||||
ReflectionFactory rf = ReflectionFactory.getReflectionFactory();
|
||||
Constructor<?> objectConstructor = getDeclaredConstructor(Object.class);
|
||||
final Constructor<?> serializationConstructor = rf.newConstructorForSerialization(type, objectConstructor);
|
||||
return new StateBackedCreator<T, STATE>() {
|
||||
public T create(STATE state, Class<T> c) throws Exception {
|
||||
final T result = type.cast(serializationConstructor.newInstance());
|
||||
setState(result,state);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
protected <T extends BACKING_INTERFACE> Constructor<T> getNoArgConstructor(Class<T> type) {
|
||||
Constructor<T> constructor = ClassUtils.getConstructorIfAvailable(type);
|
||||
if (constructor != null) return constructor;
|
||||
return getDeclaredConstructor(type);
|
||||
}
|
||||
|
||||
protected <T extends BACKING_INTERFACE> StateBackedCreator<T, STATE> stateTakingConstructorInstantiator(Class<T> type, Class<STATE> stateType) {
|
||||
Class<? extends STATE> stateInterface = (Class<? extends STATE>) stateType.getInterfaces()[0];
|
||||
final Constructor<T> constructor = ClassUtils.getConstructorIfAvailable(type, stateInterface);
|
||||
if (constructor == null) return null;
|
||||
|
||||
log.info("Using " + type + " constructor taking " + stateInterface);
|
||||
return new StateBackedCreator<T, STATE>() {
|
||||
public T create(STATE n, Class<T> c) throws Exception {
|
||||
return constructor.newInstance(n);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected <T> Constructor<T> getDeclaredConstructor(Class<T> c) {
|
||||
try {
|
||||
final Constructor<T> declaredConstructor = c.getDeclaredConstructor();
|
||||
declaredConstructor.setAccessible(true);
|
||||
return declaredConstructor;
|
||||
} catch (NoSuchMethodException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement to set state
|
||||
* @param entity
|
||||
* @param s
|
||||
*/
|
||||
protected abstract void setState(BACKING_INTERFACE entity, STATE s);
|
||||
|
||||
}
|
||||
package org.springframework.data.persistence;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import sun.reflect.ReflectionFactory;
|
||||
|
||||
/**
|
||||
* Try for a constructor taking state: failing that, try a no-arg
|
||||
* constructor and then setUnderlyingNode().
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
public abstract class AbstractConstructorEntityInstantiator<BACKING_INTERFACE, STATE> implements EntityInstantiator<BACKING_INTERFACE, STATE> {
|
||||
|
||||
private final Log log = LogFactory.getLog(getClass());
|
||||
private final Map<Class<? extends BACKING_INTERFACE>, StateBackedCreator<? extends BACKING_INTERFACE, STATE>> cache = new HashMap<Class<? extends BACKING_INTERFACE>, StateBackedCreator<? extends BACKING_INTERFACE, STATE>>();
|
||||
|
||||
public <T extends BACKING_INTERFACE> T createEntityFromState(STATE n, Class<T> c) {
|
||||
try {
|
||||
StateBackedCreator<T, STATE> creator = (StateBackedCreator<T, STATE>) cache.get(c);
|
||||
if (creator != null) return creator.create(n, c);
|
||||
synchronized (cache) {
|
||||
creator = (StateBackedCreator<T, STATE>) cache.get(c);
|
||||
if (creator != null) return creator.create(n, c);
|
||||
Class<STATE> stateClass = (Class<STATE>) n.getClass();
|
||||
creator = createInstantiator(c, stateClass);
|
||||
cache.put(c, creator);
|
||||
return creator.create(n, c);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw e;
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new IllegalArgumentException(e.getTargetException());
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setInstantiators(Map<Class<? extends BACKING_INTERFACE>, StateBackedCreator<? extends BACKING_INTERFACE, STATE>> instantiators) {
|
||||
this.cache.putAll(instantiators);
|
||||
}
|
||||
|
||||
protected <T extends BACKING_INTERFACE> StateBackedCreator<T, STATE> createInstantiator(Class<T> type, final Class<STATE> stateType) {
|
||||
StateBackedCreator<T, STATE> creator = stateTakingConstructorInstantiator(type, stateType);
|
||||
if (creator != null) return creator;
|
||||
creator = emptyConstructorStateSettingInstantiator(type, stateType);
|
||||
if (creator != null) return creator;
|
||||
return createFailingInstantiator(stateType);
|
||||
}
|
||||
|
||||
protected <T extends BACKING_INTERFACE> StateBackedCreator<T, STATE> createFailingInstantiator(final Class<STATE> stateType) {
|
||||
return new StateBackedCreator<T, STATE>() {
|
||||
public T create(STATE n, Class<T> c) throws Exception {
|
||||
throw new IllegalArgumentException(getFailingMessageForClass(c, stateType));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected String getFailingMessageForClass(Class<?> entityClass, Class<STATE> stateClass) {
|
||||
return getClass().getSimpleName() + ": entity " + entityClass +
|
||||
" must have either a constructor taking [" + stateClass + "] or a no-arg constructor and state setter.";
|
||||
}
|
||||
|
||||
private <T extends BACKING_INTERFACE> StateBackedCreator<T, STATE> emptyConstructorStateSettingInstantiator(Class<T> type, Class<STATE> stateType) {
|
||||
final Constructor<T> constructor = getNoArgConstructor(type);
|
||||
if (constructor == null) return null;
|
||||
|
||||
log.info("Using " + type + " no-arg constructor");
|
||||
|
||||
return new StateBackedCreator<T, STATE>() {
|
||||
public T create(STATE state, Class<T> c) throws Exception {
|
||||
try {
|
||||
StateProvider.setUnderlyingState(state);
|
||||
T newInstance = constructor.newInstance();
|
||||
setState(newInstance, state);
|
||||
return newInstance;
|
||||
} finally {
|
||||
StateProvider.retrieveState();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected <T extends BACKING_INTERFACE> StateBackedCreator<T, STATE> createWithoutConstructorInvocation(final Class<T> type, Class<STATE> stateType) {
|
||||
ReflectionFactory rf = ReflectionFactory.getReflectionFactory();
|
||||
Constructor<?> objectConstructor = getDeclaredConstructor(Object.class);
|
||||
final Constructor<?> serializationConstructor = rf.newConstructorForSerialization(type, objectConstructor);
|
||||
return new StateBackedCreator<T, STATE>() {
|
||||
public T create(STATE state, Class<T> c) throws Exception {
|
||||
final T result = type.cast(serializationConstructor.newInstance());
|
||||
setState(result, state);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
protected <T extends BACKING_INTERFACE> Constructor<T> getNoArgConstructor(Class<T> type) {
|
||||
Constructor<T> constructor = ClassUtils.getConstructorIfAvailable(type);
|
||||
if (constructor != null) return constructor;
|
||||
return getDeclaredConstructor(type);
|
||||
}
|
||||
|
||||
protected <T extends BACKING_INTERFACE> StateBackedCreator<T, STATE> stateTakingConstructorInstantiator(Class<T> type, Class<STATE> stateType) {
|
||||
Class<? extends STATE> stateInterface = (Class<? extends STATE>) stateType.getInterfaces()[0];
|
||||
final Constructor<T> constructor = ClassUtils.getConstructorIfAvailable(type, stateInterface);
|
||||
if (constructor == null) return null;
|
||||
|
||||
log.info("Using " + type + " constructor taking " + stateInterface);
|
||||
return new StateBackedCreator<T, STATE>() {
|
||||
public T create(STATE n, Class<T> c) throws Exception {
|
||||
return constructor.newInstance(n);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected <T> Constructor<T> getDeclaredConstructor(Class<T> c) {
|
||||
try {
|
||||
final Constructor<T> declaredConstructor = c.getDeclaredConstructor();
|
||||
declaredConstructor.setAccessible(true);
|
||||
return declaredConstructor;
|
||||
} catch (NoSuchMethodException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement to set state
|
||||
*
|
||||
* @param entity
|
||||
* @param s
|
||||
*/
|
||||
protected abstract void setState(BACKING_INTERFACE entity, STATE s);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
package org.springframework.data.persistence;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
|
||||
/**
|
||||
* Interface representing the set of changes in an entity.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Thomas Risberg
|
||||
*
|
||||
*/
|
||||
public interface ChangeSet {
|
||||
|
||||
<T> T get(String key, Class<T> requiredClass, ConversionService cs);
|
||||
|
||||
void set(String key, Object o);
|
||||
|
||||
Map<String, Object> getValues();
|
||||
|
||||
Object removeProperty(String k);
|
||||
|
||||
}
|
||||
package org.springframework.data.persistence;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
|
||||
/**
|
||||
* Interface representing the set of changes in an entity.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Thomas Risberg
|
||||
*/
|
||||
public interface ChangeSet {
|
||||
|
||||
<T> T get(String key, Class<T> requiredClass, ConversionService cs);
|
||||
|
||||
void set(String key, Object o);
|
||||
|
||||
Map<String, Object> getValues();
|
||||
|
||||
Object removeProperty(String k);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package org.springframework.data.persistence;
|
||||
|
||||
|
||||
/**
|
||||
* Interface introduced to objects exposing ChangeSet information
|
||||
* @author Rod Johnson
|
||||
* @author Thomas Risberg
|
||||
*/
|
||||
public interface ChangeSetBacked {
|
||||
|
||||
ChangeSet getChangeSet();
|
||||
|
||||
}
|
||||
package org.springframework.data.persistence;
|
||||
|
||||
|
||||
/**
|
||||
* Interface introduced to objects exposing ChangeSet information
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Thomas Risberg
|
||||
*/
|
||||
public interface ChangeSetBacked {
|
||||
|
||||
ChangeSet getChangeSet();
|
||||
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ public class ChangeSetConfiguration<T> {
|
||||
ChangeSetSynchronizer<ChangeSetBacked> changeSetManager) {
|
||||
this.changeSetManager = changeSetManager;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -5,45 +5,47 @@ import org.springframework.dao.DataAccessException;
|
||||
/**
|
||||
* Interface to be implemented by classes that can synchronize
|
||||
* between data stores and ChangeSets.
|
||||
* @author Rod Johnson
|
||||
*
|
||||
* @param <K> entity key
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
public interface ChangeSetPersister<K> {
|
||||
|
||||
|
||||
String ID_KEY = "_id";
|
||||
|
||||
String CLASS_KEY = "_class";
|
||||
|
||||
|
||||
/**
|
||||
* TODO how to tell when not found? throw exception?
|
||||
*/
|
||||
void getPersistentState(Class<? extends ChangeSetBacked> entityClass, K key, ChangeSet changeSet) throws DataAccessException, NotFoundException;
|
||||
|
||||
|
||||
/**
|
||||
* Return id
|
||||
* @param entity
|
||||
* @param cs
|
||||
*
|
||||
* @param entity
|
||||
* @param cs
|
||||
* @return
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
K getPersistentId(ChangeSetBacked entity, ChangeSet cs) throws DataAccessException;
|
||||
|
||||
|
||||
/**
|
||||
* Return key
|
||||
* @param entity
|
||||
* @param cs Key may be null if not persistent
|
||||
*
|
||||
* @param entity
|
||||
* @param cs Key may be null if not persistent
|
||||
* @return
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
K persistState(ChangeSetBacked entity, ChangeSet cs) throws DataAccessException;
|
||||
|
||||
|
||||
/**
|
||||
* Exception thrown in alternate control flow if getPersistentState
|
||||
* finds no entity data.
|
||||
*/
|
||||
class NotFoundException extends Exception {
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
package org.springframework.data.persistence;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by classes that can synchronize
|
||||
* between entities and ChangeSets.
|
||||
* @author Rod Johnson
|
||||
*
|
||||
* @param <E>
|
||||
*/
|
||||
public interface ChangeSetSynchronizer<E extends ChangeSetBacked> {
|
||||
|
||||
Map<String, Class<?>> persistentFields(Class<? extends E> entityClassClass);
|
||||
|
||||
/**
|
||||
* Take all entity fields into a changeSet.
|
||||
* @param entity
|
||||
* @return
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
void populateChangeSet(ChangeSet changeSet, E entity) throws DataAccessException;
|
||||
|
||||
void populateEntity(ChangeSet changeSet, E entity) throws DataAccessException;
|
||||
|
||||
}
|
||||
package org.springframework.data.persistence;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by classes that can synchronize
|
||||
* between entities and ChangeSets.
|
||||
*
|
||||
* @param <E>
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
public interface ChangeSetSynchronizer<E extends ChangeSetBacked> {
|
||||
|
||||
Map<String, Class<?>> persistentFields(Class<? extends E> entityClassClass);
|
||||
|
||||
/**
|
||||
* Take all entity fields into a changeSet.
|
||||
*
|
||||
* @param entity
|
||||
* @return
|
||||
* @throws DataAccessException
|
||||
*/
|
||||
void populateChangeSet(ChangeSet changeSet, E entity) throws DataAccessException;
|
||||
|
||||
void populateEntity(ChangeSet changeSet, E entity) throws DataAccessException;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
package org.springframework.data.persistence;
|
||||
|
||||
|
||||
/**
|
||||
* Interface to be implemented by classes that can instantiate and
|
||||
* configure entities.
|
||||
* The framework must do this when creating objects resulting from finders,
|
||||
* even when there may be no no-arg constructor supplied by the user.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
public interface EntityInstantiator<BACKING_INTERFACE,STATE> {
|
||||
|
||||
/*
|
||||
* The best solution if available is to add a constructor that takes Node
|
||||
* to each GraphEntity. This means generating an aspect beside every
|
||||
* class as Roo presently does.
|
||||
*
|
||||
* An alternative that does not require Roo
|
||||
* is a user-authored constructor taking Node and calling setUnderlyingNode()
|
||||
* but this is less elegant and pollutes the domain object.
|
||||
*
|
||||
* If the user supplies a no-arg constructor, instantiation can occur by invoking it
|
||||
* prior to calling setUnderlyingNode().
|
||||
*
|
||||
* If the user does NOT supply a no-arg constructor, we must rely on Sun-specific
|
||||
* code to instantiate entities without invoking a constructor.
|
||||
*/
|
||||
|
||||
<T extends BACKING_INTERFACE> T createEntityFromState(STATE s, Class<T> c);
|
||||
|
||||
}
|
||||
package org.springframework.data.persistence;
|
||||
|
||||
|
||||
/**
|
||||
* Interface to be implemented by classes that can instantiate and
|
||||
* configure entities.
|
||||
* The framework must do this when creating objects resulting from finders,
|
||||
* even when there may be no no-arg constructor supplied by the user.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
public interface EntityInstantiator<BACKING_INTERFACE, STATE> {
|
||||
|
||||
/*
|
||||
* The best solution if available is to add a constructor that takes Node
|
||||
* to each GraphEntity. This means generating an aspect beside every
|
||||
* class as Roo presently does.
|
||||
*
|
||||
* An alternative that does not require Roo
|
||||
* is a user-authored constructor taking Node and calling setUnderlyingNode()
|
||||
* but this is less elegant and pollutes the domain object.
|
||||
*
|
||||
* If the user supplies a no-arg constructor, instantiation can occur by invoking it
|
||||
* prior to calling setUnderlyingNode().
|
||||
*
|
||||
* If the user does NOT supply a no-arg constructor, we must rely on Sun-specific
|
||||
* code to instantiate entities without invoking a constructor.
|
||||
*/
|
||||
|
||||
<T extends BACKING_INTERFACE> T createEntityFromState(STATE s, Class<T> c);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,46 +1,47 @@
|
||||
package org.springframework.data.persistence;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
|
||||
/**
|
||||
* Simple ChangeSet implementation backed by a HashMap.
|
||||
* @author Thomas Risberg
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
public class HashMapChangeSet implements ChangeSet {
|
||||
|
||||
private Map<String, Object> values;
|
||||
|
||||
public HashMapChangeSet(Map<String,Object> values) {
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
public HashMapChangeSet() {
|
||||
this(new HashMap<String, Object>());
|
||||
}
|
||||
|
||||
public void set(String key, Object o) {
|
||||
values.put(key, o);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "HashMapChangeSet: values=[" + values + "]";
|
||||
}
|
||||
|
||||
public Map<String, Object> getValues() {
|
||||
return Collections.unmodifiableMap(values);
|
||||
}
|
||||
|
||||
public Object removeProperty(String k) {
|
||||
return this.values.remove(k);
|
||||
}
|
||||
|
||||
public <T> T get(String key, Class<T> requiredClass, ConversionService conversionService) {
|
||||
return conversionService.convert(values.get(key), requiredClass);
|
||||
}
|
||||
|
||||
}
|
||||
package org.springframework.data.persistence;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
|
||||
/**
|
||||
* Simple ChangeSet implementation backed by a HashMap.
|
||||
*
|
||||
* @author Thomas Risberg
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
public class HashMapChangeSet implements ChangeSet {
|
||||
|
||||
private Map<String, Object> values;
|
||||
|
||||
public HashMapChangeSet(Map<String, Object> values) {
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
public HashMapChangeSet() {
|
||||
this(new HashMap<String, Object>());
|
||||
}
|
||||
|
||||
public void set(String key, Object o) {
|
||||
values.put(key, o);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "HashMapChangeSet: values=[" + values + "]";
|
||||
}
|
||||
|
||||
public Map<String, Object> getValues() {
|
||||
return Collections.unmodifiableMap(values);
|
||||
}
|
||||
|
||||
public Object removeProperty(String k) {
|
||||
return this.values.remove(k);
|
||||
}
|
||||
|
||||
public <T> T get(String key, Class<T> requiredClass, ConversionService conversionService) {
|
||||
return conversionService.convert(values.get(key), requiredClass);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package org.springframework.data.persistence;
|
||||
|
||||
/**
|
||||
* encapsulates the instantiator of state-backed classes and populating them with the provided state.
|
||||
*
|
||||
* Can be implemented and registered with the concrete AbstractConstructorEntityInstantiator to provide
|
||||
* non reflection bases instantiaton for domain classes
|
||||
*/
|
||||
* encapsulates the instantiator of state-backed classes and populating them with the provided state.
|
||||
* <p/>
|
||||
* Can be implemented and registered with the concrete AbstractConstructorEntityInstantiator to provide
|
||||
* non reflection bases instantiaton for domain classes
|
||||
*/
|
||||
public interface StateBackedCreator<T, STATE> {
|
||||
T create(STATE n, Class<T> c) throws Exception;
|
||||
T create(STATE n, Class<T> c) throws Exception;
|
||||
}
|
||||
|
||||
@@ -5,17 +5,20 @@ package org.springframework.data.persistence;
|
||||
* @since 24.09.2010
|
||||
*/
|
||||
public abstract class StateProvider {
|
||||
private final static ThreadLocal stateHolder=new ThreadLocal();
|
||||
private final static ThreadLocal stateHolder = new ThreadLocal();
|
||||
|
||||
private StateProvider() {}
|
||||
private StateProvider() {
|
||||
}
|
||||
|
||||
public static <STATE> void setUnderlyingState(STATE state) {
|
||||
if (stateHolder.get()!=null) throw new IllegalStateException("StateHolder already contains state "+stateHolder.get()+" in thread "+Thread.currentThread());
|
||||
stateHolder.set(state);
|
||||
}
|
||||
public static <STATE> STATE retrieveState() {
|
||||
STATE result= (STATE) stateHolder.get();
|
||||
stateHolder.remove();
|
||||
return result;
|
||||
}
|
||||
public static <STATE> void setUnderlyingState(STATE state) {
|
||||
if (stateHolder.get() != null)
|
||||
throw new IllegalStateException("StateHolder already contains state " + stateHolder.get() + " in thread " + Thread.currentThread());
|
||||
stateHolder.set(state);
|
||||
}
|
||||
|
||||
public static <STATE> STATE retrieveState() {
|
||||
STATE result = (STATE) stateHolder.get();
|
||||
stateHolder.remove();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,10 @@ import com.mysema.query.types.EntityPath;
|
||||
|
||||
/**
|
||||
* Strategy interface to abstract the ways to translate an plain domain class into a {@link EntityPath}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface EntityPathResolver {
|
||||
|
||||
<T> EntityPath<T> createPath(Class<T> domainClass);
|
||||
<T> EntityPath<T> createPath(Class<T> domainClass);
|
||||
}
|
||||
@@ -17,11 +17,10 @@ package org.springframework.data.querydsl;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class QueryDslUtils {
|
||||
|
||||
public static final boolean QUERY_DSL_PRESENT = org.springframework.util.ClassUtils.isPresent(
|
||||
"com.mysema.query.types.Predicate", QueryDslUtils.class.getClassLoader());
|
||||
public static final boolean QUERY_DSL_PRESENT = org.springframework.util.ClassUtils.isPresent(
|
||||
"com.mysema.query.types.Predicate", QueryDslUtils.class.getClassLoader());
|
||||
}
|
||||
|
||||
@@ -18,118 +18,117 @@ package org.springframework.data.querydsl;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import com.mysema.query.types.EntityPath;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import com.mysema.query.types.EntityPath;
|
||||
|
||||
/**
|
||||
* Simple implementation of {@link EntityPathResolver} to lookup a query
|
||||
* class by reflection and using the static field of the same type.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public enum SimpleEntityPathResolver implements EntityPathResolver {
|
||||
|
||||
INSTANCE;
|
||||
INSTANCE;
|
||||
|
||||
private static final String NO_CLASS_FOUND_TEMPLATE =
|
||||
"Did not find a query class %s for domain class %s!";
|
||||
private static final String NO_FIELD_FOUND_TEMPLATE =
|
||||
"Did not find a static field of the same type in %s!";
|
||||
private static final String NO_CLASS_FOUND_TEMPLATE =
|
||||
"Did not find a query class %s for domain class %s!";
|
||||
private static final String NO_FIELD_FOUND_TEMPLATE =
|
||||
"Did not find a static field of the same type in %s!";
|
||||
|
||||
|
||||
/**
|
||||
* Creates an {@link EntityPath} instance for the given domain class.
|
||||
* Tries to lookup a class matching the naming convention (prepend Q to
|
||||
* the simple name of the class, same package) and find a static field
|
||||
* of the same type in it.
|
||||
*
|
||||
* @param domainClass
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> EntityPath<T> createPath(Class<T> domainClass) {
|
||||
/**
|
||||
* Creates an {@link EntityPath} instance for the given domain class.
|
||||
* Tries to lookup a class matching the naming convention (prepend Q to
|
||||
* the simple name of the class, same package) and find a static field
|
||||
* of the same type in it.
|
||||
*
|
||||
* @param domainClass
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> EntityPath<T> createPath(Class<T> domainClass) {
|
||||
|
||||
String pathClassName = getQueryClassName(domainClass);
|
||||
String pathClassName = getQueryClassName(domainClass);
|
||||
|
||||
try {
|
||||
Class<?> pathClass =
|
||||
ClassUtils.forName(pathClassName,
|
||||
SimpleEntityPathResolver.class.getClassLoader());
|
||||
Field field = getStaticFieldOfType(pathClass);
|
||||
try {
|
||||
Class<?> pathClass =
|
||||
ClassUtils.forName(pathClassName,
|
||||
SimpleEntityPathResolver.class.getClassLoader());
|
||||
Field field = getStaticFieldOfType(pathClass);
|
||||
|
||||
if (field == null) {
|
||||
throw new IllegalStateException(String.format(
|
||||
NO_FIELD_FOUND_TEMPLATE, pathClass));
|
||||
} else {
|
||||
return (EntityPath<T>) ReflectionUtils
|
||||
.getField(field, null);
|
||||
}
|
||||
if (field == null) {
|
||||
throw new IllegalStateException(String.format(
|
||||
NO_FIELD_FOUND_TEMPLATE, pathClass));
|
||||
} else {
|
||||
return (EntityPath<T>) ReflectionUtils
|
||||
.getField(field, null);
|
||||
}
|
||||
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
NO_CLASS_FOUND_TEMPLATE, pathClassName,
|
||||
domainClass.getName()), e);
|
||||
}
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
NO_CLASS_FOUND_TEMPLATE, pathClassName,
|
||||
domainClass.getName()), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the first static field of the given type inside the given
|
||||
* type.
|
||||
*
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
private Field getStaticFieldOfType(Class<?> type) {
|
||||
/**
|
||||
* Returns the first static field of the given type inside the given
|
||||
* type.
|
||||
*
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
private Field getStaticFieldOfType(Class<?> type) {
|
||||
|
||||
for (Field field : type.getDeclaredFields()) {
|
||||
for (Field field : type.getDeclaredFields()) {
|
||||
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
boolean hasSameType = type.equals(field.getType());
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
boolean hasSameType = type.equals(field.getType());
|
||||
|
||||
if (isStatic && hasSameType) {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
if (isStatic && hasSameType) {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
|
||||
Class<?> superclass = type.getSuperclass();
|
||||
return Object.class.equals(superclass) ? null
|
||||
: getStaticFieldOfType(superclass);
|
||||
}
|
||||
Class<?> superclass = type.getSuperclass();
|
||||
return Object.class.equals(superclass) ? null
|
||||
: getStaticFieldOfType(superclass);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the name of the query class for the given domain class.
|
||||
*
|
||||
* @param domainClass
|
||||
* @return
|
||||
*/
|
||||
private String getQueryClassName(Class<?> domainClass) {
|
||||
/**
|
||||
* Returns the name of the query class for the given domain class.
|
||||
*
|
||||
* @param domainClass
|
||||
* @return
|
||||
*/
|
||||
private String getQueryClassName(Class<?> domainClass) {
|
||||
|
||||
String simpleClassName = ClassUtils.getShortName(domainClass);
|
||||
return String.format("%s.Q%s%s",
|
||||
domainClass.getPackage().getName(),
|
||||
getClassBase(simpleClassName), domainClass.getSimpleName());
|
||||
}
|
||||
String simpleClassName = ClassUtils.getShortName(domainClass);
|
||||
return String.format("%s.Q%s%s",
|
||||
domainClass.getPackage().getName(),
|
||||
getClassBase(simpleClassName), domainClass.getSimpleName());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Analyzes the short class name and potentially returns the outer
|
||||
* class.
|
||||
*
|
||||
* @param shortName
|
||||
* @return
|
||||
*/
|
||||
private String getClassBase(String shortName) {
|
||||
/**
|
||||
* Analyzes the short class name and potentially returns the outer
|
||||
* class.
|
||||
*
|
||||
* @param shortName
|
||||
* @return
|
||||
*/
|
||||
private String getClassBase(String shortName) {
|
||||
|
||||
String[] parts = shortName.split("\\.");
|
||||
String[] parts = shortName.split("\\.");
|
||||
|
||||
if (parts.length < 2) {
|
||||
return "";
|
||||
}
|
||||
if (parts.length < 2) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return parts[0] + "_";
|
||||
}
|
||||
return parts[0] + "_";
|
||||
}
|
||||
}
|
||||
@@ -25,13 +25,13 @@ import java.lang.annotation.Target;
|
||||
/**
|
||||
* Annotation to exclude repository interfaces from being picked up and thus in
|
||||
* consequence getting an instance being created.
|
||||
* <p>
|
||||
* <p/>
|
||||
* This will typically be used when providing an extended base interface for all
|
||||
* repositories in combination with a custom repository base class to implement
|
||||
* methods declared in that intermediate interface. In this case you typically
|
||||
* derive your concrete repository interfaces from the intermediate one but
|
||||
* don't want to create a Spring bean for the intermediate interface.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
|
||||
@@ -25,30 +25,30 @@ import org.springframework.data.domain.Sort;
|
||||
/**
|
||||
* Extension of {@link Repository} to provide additional methods to retrieve
|
||||
* entities using the pagination and sorting abstraction.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @see Sort
|
||||
* @see Pageable
|
||||
* @see Page
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface PagingAndSortingRepository<T, ID extends Serializable> extends
|
||||
Repository<T, ID> {
|
||||
Repository<T, ID> {
|
||||
|
||||
/**
|
||||
* Returns all entities sorted by the given options.
|
||||
*
|
||||
* @param sort
|
||||
* @return all entities sorted by the given options
|
||||
*/
|
||||
Iterable<T> findAll(Sort sort);
|
||||
/**
|
||||
* Returns all entities sorted by the given options.
|
||||
*
|
||||
* @param sort
|
||||
* @return all entities sorted by the given options
|
||||
*/
|
||||
Iterable<T> findAll(Sort sort);
|
||||
|
||||
|
||||
/**
|
||||
* Returns a {@link Page} of entities meeting the paging restriction
|
||||
* provided in the {@code Pageable} object.
|
||||
*
|
||||
* @param pageable
|
||||
* @return a page of entities
|
||||
*/
|
||||
Page<T> findAll(Pageable pageable);
|
||||
/**
|
||||
* Returns a {@link Page} of entities meeting the paging restriction
|
||||
* provided in the {@code Pageable} object.
|
||||
*
|
||||
* @param pageable
|
||||
* @return a page of entities
|
||||
*/
|
||||
Page<T> findAll(Pageable pageable);
|
||||
}
|
||||
|
||||
@@ -20,86 +20,86 @@ import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Interface for generic CRUD operations on a repository for a specific type.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Eberhard Wolff
|
||||
*/
|
||||
public interface Repository<T, ID extends Serializable> {
|
||||
|
||||
/**
|
||||
* Saves a given entity. Use the returned instance for further operations as
|
||||
* the save operation might have changed the entity instance completely.
|
||||
*
|
||||
* @param entity
|
||||
* @return the saved entity
|
||||
*/
|
||||
T save(T entity);
|
||||
/**
|
||||
* Saves a given entity. Use the returned instance for further operations as
|
||||
* the save operation might have changed the entity instance completely.
|
||||
*
|
||||
* @param entity
|
||||
* @return the saved entity
|
||||
*/
|
||||
T save(T entity);
|
||||
|
||||
|
||||
/**
|
||||
* Saves all given entities.
|
||||
*
|
||||
* @param entities
|
||||
* @return
|
||||
*/
|
||||
Iterable<T> save(Iterable<? extends T> entities);
|
||||
/**
|
||||
* Saves all given entities.
|
||||
*
|
||||
* @param entities
|
||||
* @return
|
||||
*/
|
||||
Iterable<T> save(Iterable<? extends T> entities);
|
||||
|
||||
|
||||
/**
|
||||
* Retrives an entity by its primary key.
|
||||
*
|
||||
* @param id
|
||||
* @return the entity with the given primary key or {@code null} if none
|
||||
* found
|
||||
* @throws IllegalArgumentException if primaryKey is {@code null}
|
||||
*/
|
||||
T findOne(ID id);
|
||||
/**
|
||||
* Retrives an entity by its primary key.
|
||||
*
|
||||
* @param id
|
||||
* @return the entity with the given primary key or {@code null} if none
|
||||
* found
|
||||
* @throws IllegalArgumentException if primaryKey is {@code null}
|
||||
*/
|
||||
T findOne(ID id);
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether an entity with the given id exists.
|
||||
*
|
||||
* @param id
|
||||
* @return true if an entity with the given id exists, alse otherwise
|
||||
* @throws IllegalArgumentException if primaryKey is {@code null}
|
||||
*/
|
||||
boolean exists(ID id);
|
||||
/**
|
||||
* Returns whether an entity with the given id exists.
|
||||
*
|
||||
* @param id
|
||||
* @return true if an entity with the given id exists, alse otherwise
|
||||
* @throws IllegalArgumentException if primaryKey is {@code null}
|
||||
*/
|
||||
boolean exists(ID id);
|
||||
|
||||
|
||||
/**
|
||||
* Returns all instances of the type.
|
||||
*
|
||||
* @return all entities
|
||||
*/
|
||||
Iterable<T> findAll();
|
||||
/**
|
||||
* Returns all instances of the type.
|
||||
*
|
||||
* @return all entities
|
||||
*/
|
||||
Iterable<T> findAll();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of entities available.
|
||||
*
|
||||
* @return the number of entities
|
||||
*/
|
||||
long count();
|
||||
/**
|
||||
* Returns the number of entities available.
|
||||
*
|
||||
* @return the number of entities
|
||||
*/
|
||||
long count();
|
||||
|
||||
|
||||
/**
|
||||
* Deletes a given entity.
|
||||
*
|
||||
* @param entity
|
||||
*/
|
||||
void delete(T entity);
|
||||
/**
|
||||
* Deletes a given entity.
|
||||
*
|
||||
* @param entity
|
||||
*/
|
||||
void delete(T entity);
|
||||
|
||||
|
||||
/**
|
||||
* Deletes the given entities.
|
||||
*
|
||||
* @param entities
|
||||
*/
|
||||
void delete(Iterable<? extends T> entities);
|
||||
/**
|
||||
* Deletes the given entities.
|
||||
*
|
||||
* @param entities
|
||||
*/
|
||||
void delete(Iterable<? extends T> entities);
|
||||
|
||||
|
||||
/**
|
||||
* Deletes all entities managed by the repository.
|
||||
*/
|
||||
void deleteAll();
|
||||
/**
|
||||
* Deletes all entities managed by the repository.
|
||||
*/
|
||||
void deleteAll();
|
||||
}
|
||||
|
||||
@@ -52,429 +52,429 @@ import org.w3c.dom.Element;
|
||||
* the XML element into a {@link GlobalRepositoryConfigInformation} object and
|
||||
* allow either manual configuration or automatic detection of repository
|
||||
* interfaces.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public abstract class AbstractRepositoryConfigDefinitionParser<S extends GlobalRepositoryConfigInformation<T>, T extends SingleRepositoryConfigInformation<S>>
|
||||
implements BeanDefinitionParser {
|
||||
implements BeanDefinitionParser {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(
|
||||
AbstractRepositoryConfigDefinitionParser.class);
|
||||
private static final Log LOG = LogFactory.getLog(
|
||||
AbstractRepositoryConfigDefinitionParser.class);
|
||||
|
||||
private static final String REPOSITORY_INTERFACE_POST_PROCESSOR =
|
||||
"org.springframework.data.repository.support.RepositoryInterfaceAwareBeanPostProcessor";
|
||||
private static final String REPOSITORY_INTERFACE_POST_PROCESSOR =
|
||||
"org.springframework.data.repository.support.RepositoryInterfaceAwareBeanPostProcessor";
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.factory.xml.BeanDefinitionParser#parse(org.
|
||||
* w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)
|
||||
*/
|
||||
public BeanDefinition parse(Element element, ParserContext parser) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.factory.xml.BeanDefinitionParser#parse(org.
|
||||
* w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)
|
||||
*/
|
||||
public BeanDefinition parse(Element element, ParserContext parser) {
|
||||
|
||||
try {
|
||||
S configContext = getGlobalRepositoryConfigInformation(element);
|
||||
try {
|
||||
S configContext = getGlobalRepositoryConfigInformation(element);
|
||||
|
||||
if (configContext.configureManually()) {
|
||||
doManualConfiguration(configContext, parser);
|
||||
} else {
|
||||
doAutoConfiguration(configContext, parser);
|
||||
}
|
||||
if (configContext.configureManually()) {
|
||||
doManualConfiguration(configContext, parser);
|
||||
} else {
|
||||
doAutoConfiguration(configContext, parser);
|
||||
}
|
||||
|
||||
Object beanSource = parser.extractSource(element);
|
||||
registerBeansForRoot(parser.getRegistry(), beanSource);
|
||||
Object beanSource = parser.extractSource(element);
|
||||
registerBeansForRoot(parser.getRegistry(), beanSource);
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
handleError(e, element, parser.getReaderContext());
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
handleError(e, element, parser.getReaderContext());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes repository auto configuration by scanning the provided base
|
||||
* package for repository interfaces.
|
||||
*
|
||||
* @param config
|
||||
* @param parser
|
||||
*/
|
||||
private void doAutoConfiguration(S config, ParserContext parser) {
|
||||
/**
|
||||
* Executes repository auto configuration by scanning the provided base
|
||||
* package for repository interfaces.
|
||||
*
|
||||
* @param config
|
||||
* @param parser
|
||||
*/
|
||||
private void doAutoConfiguration(S config, ParserContext parser) {
|
||||
|
||||
LOG.debug("Triggering auto repository detection");
|
||||
LOG.debug("Triggering auto repository detection");
|
||||
|
||||
ResourceLoader resourceLoader =
|
||||
parser.getReaderContext().getResourceLoader();
|
||||
ResourceLoader resourceLoader =
|
||||
parser.getReaderContext().getResourceLoader();
|
||||
|
||||
// Detect available repository interfaces
|
||||
Set<String> repositoryInterfaces =
|
||||
getRepositoryInterfacesForAutoConfig(config, resourceLoader,
|
||||
parser.getReaderContext());
|
||||
// Detect available repository interfaces
|
||||
Set<String> repositoryInterfaces =
|
||||
getRepositoryInterfacesForAutoConfig(config, resourceLoader,
|
||||
parser.getReaderContext());
|
||||
|
||||
for (String repositoryInterface : repositoryInterfaces) {
|
||||
registerGenericRepositoryFactoryBean(
|
||||
parser,
|
||||
config.getAutoconfigRepositoryInformation(repositoryInterface));
|
||||
}
|
||||
}
|
||||
for (String repositoryInterface : repositoryInterfaces) {
|
||||
registerGenericRepositoryFactoryBean(
|
||||
parser,
|
||||
config.getAutoconfigRepositoryInformation(repositoryInterface));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Set<String> getRepositoryInterfacesForAutoConfig(S config,
|
||||
ResourceLoader loader, ReaderContext reader) {
|
||||
private Set<String> getRepositoryInterfacesForAutoConfig(S config,
|
||||
ResourceLoader loader, ReaderContext reader) {
|
||||
|
||||
ClassPathScanningCandidateComponentProvider scanner =
|
||||
new RepositoryComponentProvider(
|
||||
config.getRepositoryBaseInterface());
|
||||
scanner.setResourceLoader(loader);
|
||||
ClassPathScanningCandidateComponentProvider scanner =
|
||||
new RepositoryComponentProvider(
|
||||
config.getRepositoryBaseInterface());
|
||||
scanner.setResourceLoader(loader);
|
||||
|
||||
TypeFilterParser parser =
|
||||
new TypeFilterParser(loader.getClassLoader(), reader);
|
||||
parser.parseFilters(config.getSource(), scanner);
|
||||
TypeFilterParser parser =
|
||||
new TypeFilterParser(loader.getClassLoader(), reader);
|
||||
parser.parseFilters(config.getSource(), scanner);
|
||||
|
||||
Set<BeanDefinition> findCandidateComponents =
|
||||
scanner.findCandidateComponents(config.getBasePackage());
|
||||
Set<BeanDefinition> findCandidateComponents =
|
||||
scanner.findCandidateComponents(config.getBasePackage());
|
||||
|
||||
Set<String> interfaceNames = new HashSet<String>();
|
||||
for (BeanDefinition definition : findCandidateComponents) {
|
||||
interfaceNames.add(definition.getBeanClassName());
|
||||
}
|
||||
Set<String> interfaceNames = new HashSet<String>();
|
||||
for (BeanDefinition definition : findCandidateComponents) {
|
||||
interfaceNames.add(definition.getBeanClassName());
|
||||
}
|
||||
|
||||
return interfaceNames;
|
||||
}
|
||||
return interfaceNames;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a {@link GlobalRepositoryConfigInformation} implementation for
|
||||
* the given element.
|
||||
*
|
||||
* @param element
|
||||
* @return
|
||||
*/
|
||||
protected abstract S getGlobalRepositoryConfigInformation(Element element);
|
||||
/**
|
||||
* Returns a {@link GlobalRepositoryConfigInformation} implementation for
|
||||
* the given element.
|
||||
*
|
||||
* @param element
|
||||
* @return
|
||||
*/
|
||||
protected abstract S getGlobalRepositoryConfigInformation(Element element);
|
||||
|
||||
|
||||
/**
|
||||
* Proceeds manual configuration by traversing the context's
|
||||
* {@link SingleRepositoryConfigInformation}s.
|
||||
*
|
||||
* @param context
|
||||
* @param parser
|
||||
*/
|
||||
private void doManualConfiguration(S context, ParserContext parser) {
|
||||
|
||||
LOG.debug("Triggering manual repository detection");
|
||||
|
||||
for (T repositoryContext : context
|
||||
.getSingleRepositoryConfigInformations()) {
|
||||
registerGenericRepositoryFactoryBean(parser, repositoryContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void handleError(Exception e, Element source, ReaderContext reader) {
|
||||
|
||||
reader.error(e.getMessage(), reader.extractSource(source), e.getCause());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Registers a generic repository factory bean for a bean with the given
|
||||
* name and the provided configuration context.
|
||||
*
|
||||
* @param parser
|
||||
* @param name
|
||||
* @param context
|
||||
*/
|
||||
private void registerGenericRepositoryFactoryBean(ParserContext parser,
|
||||
T context) {
|
||||
|
||||
try {
|
||||
|
||||
Object beanSource = parser.extractSource(context.getSource());
|
||||
|
||||
BeanDefinitionBuilder builder =
|
||||
BeanDefinitionBuilder.rootBeanDefinition(context
|
||||
.getRepositoryFactoryBeanClassName());
|
||||
|
||||
builder.addPropertyValue("repositoryInterface",
|
||||
context.getInterfaceName());
|
||||
builder.addPropertyValue("queryLookupStrategyKey",
|
||||
context.getQueryLookupStrategyKey());
|
||||
|
||||
String transactionManagerRef = context.getTransactionManagerRef();
|
||||
|
||||
if (StringUtils.hasText(transactionManagerRef)) {
|
||||
builder.addPropertyValue("transactionManager",
|
||||
transactionManagerRef);
|
||||
}
|
||||
|
||||
String customImplementationBeanName =
|
||||
registerCustomImplementation(context, parser, beanSource);
|
||||
|
||||
if (customImplementationBeanName != null) {
|
||||
builder.addPropertyReference("customImplementation",
|
||||
customImplementationBeanName);
|
||||
}
|
||||
|
||||
postProcessBeanDefinition(context, builder, parser.getRegistry(), beanSource);
|
||||
|
||||
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
|
||||
beanDefinition.setSource(beanSource);
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(
|
||||
"Registering repository: " + context.getBeanId() +
|
||||
" - Interface: " + context.getInterfaceName() +
|
||||
" - Factory: " + context.getRepositoryFactoryBeanClassName() +
|
||||
", - Custom implementation: " + customImplementationBeanName);
|
||||
}
|
||||
|
||||
BeanComponentDefinition definition =
|
||||
new BeanComponentDefinition(beanDefinition,
|
||||
context.getBeanId());
|
||||
parser.registerBeanComponent(definition);
|
||||
} catch (RuntimeException e) {
|
||||
handleError(e, context.getSource(), parser.getReaderContext());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callback to post process a repository bean definition prior to actual
|
||||
* registration.
|
||||
*
|
||||
* @param context
|
||||
* @param builder
|
||||
* @param beanSource
|
||||
*/
|
||||
protected void postProcessBeanDefinition(T context,
|
||||
BeanDefinitionBuilder builder, BeanDefinitionRegistry registry, Object beanSource) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Registers a possibly available custom repository implementation on the
|
||||
* repository bean. Tries to find an already registered bean to reference or
|
||||
* tries to detect a custom implementation itself.
|
||||
*
|
||||
* @param config
|
||||
* @param parser
|
||||
* @param source
|
||||
* @return the bean name of the custom implementation or {@code null} if
|
||||
* none available
|
||||
*/
|
||||
private String registerCustomImplementation(T config, ParserContext parser,
|
||||
Object source) {
|
||||
|
||||
String beanName = config.getImplementationBeanName();
|
||||
|
||||
// Already a bean configured?
|
||||
if (parser.getRegistry().containsBeanDefinition(beanName)) {
|
||||
return beanName;
|
||||
}
|
||||
|
||||
// Autodetect implementation
|
||||
if (config.autodetectCustomImplementation()) {
|
||||
|
||||
AbstractBeanDefinition beanDefinition =
|
||||
detectCustomImplementation(config, parser);
|
||||
|
||||
if (null == beanDefinition) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Registering custom repository implementation: " +
|
||||
config.getImplementationBeanName() + " " +
|
||||
beanDefinition.getBeanClassName());
|
||||
}
|
||||
|
||||
beanDefinition.setSource(source);
|
||||
parser.registerBeanComponent(new BeanComponentDefinition(
|
||||
beanDefinition, beanName));
|
||||
|
||||
} else {
|
||||
beanName = config.getCustomImplementationRef();
|
||||
}
|
||||
|
||||
return beanName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tries to detect a custom implementation for a repository bean by
|
||||
* classpath scanning.
|
||||
*
|
||||
* @param config
|
||||
* @param parser
|
||||
* @return the {@code AbstractBeanDefinition} of the custom implementation
|
||||
* or {@literal null} if none found
|
||||
*/
|
||||
private AbstractBeanDefinition detectCustomImplementation(T config,
|
||||
ParserContext parser) {
|
||||
|
||||
// Build pattern to lookup implementation class
|
||||
Pattern pattern =
|
||||
Pattern.compile(".*" + config.getImplementationClassName());
|
||||
|
||||
// Build classpath scanner and lookup bean definition
|
||||
ClassPathScanningCandidateComponentProvider provider =
|
||||
new ClassPathScanningCandidateComponentProvider(false);
|
||||
provider.setResourceLoader(parser.getReaderContext()
|
||||
.getResourceLoader());
|
||||
provider.addIncludeFilter(new RegexPatternTypeFilter(pattern));
|
||||
Set<BeanDefinition> definitions =
|
||||
provider.findCandidateComponents(config.getBasePackage());
|
||||
|
||||
return (0 == definitions.size() ? null
|
||||
: (AbstractBeanDefinition) definitions.iterator().next());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callback to register additional bean definitions for a
|
||||
* {@literal repositories} root node. This usually includes beans you have
|
||||
* to set up once independently of the number of repositories to be created.
|
||||
* Will be called before any repositories bean definitions have been
|
||||
* registered.
|
||||
*
|
||||
* @param registry
|
||||
* @param source
|
||||
*/
|
||||
protected void registerBeansForRoot(BeanDefinitionRegistry registry,
|
||||
Object source) {
|
||||
|
||||
AbstractBeanDefinition definition =
|
||||
BeanDefinitionBuilder.rootBeanDefinition(
|
||||
REPOSITORY_INTERFACE_POST_PROCESSOR)
|
||||
.getBeanDefinition();
|
||||
|
||||
registerWithSourceAndGeneratedBeanName(registry, definition, source);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the given {@link BeanDefinitionRegistry} already contains
|
||||
* a bean of the given type assuming the bean name has been autogenerated.
|
||||
*
|
||||
* @param type
|
||||
* @param registry
|
||||
* @return
|
||||
*/
|
||||
protected static boolean hasBean(Class<?> type,
|
||||
BeanDefinitionRegistry registry) {
|
||||
|
||||
String name =
|
||||
String.format("%s%s0", type.getName(),
|
||||
GENERATED_BEAN_NAME_SEPARATOR);
|
||||
return registry.containsBeanDefinition(name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the given source on the given {@link AbstractBeanDefinition} and
|
||||
* registers it inside the given {@link BeanDefinitionRegistry}.
|
||||
*
|
||||
* @param registry
|
||||
* @param bean
|
||||
* @param source
|
||||
* @return
|
||||
*/
|
||||
protected static String registerWithSourceAndGeneratedBeanName(
|
||||
BeanDefinitionRegistry registry, AbstractBeanDefinition bean,
|
||||
Object source) {
|
||||
|
||||
bean.setSource(source);
|
||||
|
||||
String beanName = generateBeanName(bean, registry);
|
||||
registry.registerBeanDefinition(beanName, bean);
|
||||
|
||||
return beanName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom {@link ClassPathScanningCandidateComponentProvider} scanning for
|
||||
* interfaces extending the given base interface. Skips interfaces annotated
|
||||
* with {@link NoRepositoryBean}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
static class RepositoryComponentProvider extends
|
||||
ClassPathScanningCandidateComponentProvider {
|
||||
|
||||
/**
|
||||
* Creates a new {@link RepositoryComponentProvider}.
|
||||
*
|
||||
* @param repositoryInterface the interface to scan for
|
||||
*/
|
||||
public RepositoryComponentProvider(Class<?> repositoryInterface) {
|
||||
|
||||
super(false);
|
||||
addIncludeFilter(new InterfaceTypeFilter(repositoryInterface));
|
||||
addExcludeFilter(new AnnotationTypeFilter(NoRepositoryBean.class));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @seeorg.springframework.context.annotation.
|
||||
* ClassPathScanningCandidateComponentProvider
|
||||
* #isCandidateComponent(org.springframework
|
||||
* .beans.factory.annotation.AnnotatedBeanDefinition)
|
||||
*/
|
||||
@Override
|
||||
protected boolean isCandidateComponent(
|
||||
AnnotatedBeanDefinition beanDefinition) {
|
||||
|
||||
boolean isNonRepositoryInterface =
|
||||
!isGenericRepositoryInterface(beanDefinition
|
||||
.getBeanClassName());
|
||||
boolean isTopLevelType =
|
||||
!beanDefinition.getMetadata().hasEnclosingClass();
|
||||
|
||||
return isNonRepositoryInterface && isTopLevelType;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link org.springframework.core.type.filter.TypeFilter} that only
|
||||
* matches interfaces. Thus setting this up makes only sense providing
|
||||
* an interface type as {@code targetType}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static class InterfaceTypeFilter extends AssignableTypeFilter {
|
||||
|
||||
/**
|
||||
* Creates a new {@link InterfaceTypeFilter}.
|
||||
*
|
||||
* @param targetType
|
||||
*/
|
||||
public InterfaceTypeFilter(Class<?> targetType) {
|
||||
|
||||
super(targetType);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @seeorg.springframework.core.type.filter.
|
||||
* AbstractTypeHierarchyTraversingFilter
|
||||
* #match(org.springframework.core.type.classreading.MetadataReader,
|
||||
* org.springframework.core.type.classreading.MetadataReaderFactory)
|
||||
*/
|
||||
@Override
|
||||
public boolean match(MetadataReader metadataReader,
|
||||
MetadataReaderFactory metadataReaderFactory)
|
||||
throws IOException {
|
||||
|
||||
return metadataReader.getClassMetadata().isInterface()
|
||||
&& super.match(metadataReader, metadataReaderFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Proceeds manual configuration by traversing the context's
|
||||
* {@link SingleRepositoryConfigInformation}s.
|
||||
*
|
||||
* @param context
|
||||
* @param parser
|
||||
*/
|
||||
private void doManualConfiguration(S context, ParserContext parser) {
|
||||
|
||||
LOG.debug("Triggering manual repository detection");
|
||||
|
||||
for (T repositoryContext : context
|
||||
.getSingleRepositoryConfigInformations()) {
|
||||
registerGenericRepositoryFactoryBean(parser, repositoryContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void handleError(Exception e, Element source, ReaderContext reader) {
|
||||
|
||||
reader.error(e.getMessage(), reader.extractSource(source), e.getCause());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Registers a generic repository factory bean for a bean with the given
|
||||
* name and the provided configuration context.
|
||||
*
|
||||
* @param parser
|
||||
* @param name
|
||||
* @param context
|
||||
*/
|
||||
private void registerGenericRepositoryFactoryBean(ParserContext parser,
|
||||
T context) {
|
||||
|
||||
try {
|
||||
|
||||
Object beanSource = parser.extractSource(context.getSource());
|
||||
|
||||
BeanDefinitionBuilder builder =
|
||||
BeanDefinitionBuilder.rootBeanDefinition(context
|
||||
.getRepositoryFactoryBeanClassName());
|
||||
|
||||
builder.addPropertyValue("repositoryInterface",
|
||||
context.getInterfaceName());
|
||||
builder.addPropertyValue("queryLookupStrategyKey",
|
||||
context.getQueryLookupStrategyKey());
|
||||
|
||||
String transactionManagerRef = context.getTransactionManagerRef();
|
||||
|
||||
if (StringUtils.hasText(transactionManagerRef)) {
|
||||
builder.addPropertyValue("transactionManager",
|
||||
transactionManagerRef);
|
||||
}
|
||||
|
||||
String customImplementationBeanName =
|
||||
registerCustomImplementation(context, parser, beanSource);
|
||||
|
||||
if (customImplementationBeanName != null) {
|
||||
builder.addPropertyReference("customImplementation",
|
||||
customImplementationBeanName);
|
||||
}
|
||||
|
||||
postProcessBeanDefinition(context, builder, parser.getRegistry(), beanSource);
|
||||
|
||||
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
|
||||
beanDefinition.setSource(beanSource);
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(
|
||||
"Registering repository: " + context.getBeanId() +
|
||||
" - Interface: " + context.getInterfaceName() +
|
||||
" - Factory: " + context.getRepositoryFactoryBeanClassName() +
|
||||
", - Custom implementation: " + customImplementationBeanName);
|
||||
}
|
||||
|
||||
BeanComponentDefinition definition =
|
||||
new BeanComponentDefinition(beanDefinition,
|
||||
context.getBeanId());
|
||||
parser.registerBeanComponent(definition);
|
||||
} catch (RuntimeException e) {
|
||||
handleError(e, context.getSource(), parser.getReaderContext());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callback to post process a repository bean definition prior to actual
|
||||
* registration.
|
||||
*
|
||||
* @param context
|
||||
* @param builder
|
||||
* @param beanSource
|
||||
*/
|
||||
protected void postProcessBeanDefinition(T context,
|
||||
BeanDefinitionBuilder builder, BeanDefinitionRegistry registry, Object beanSource) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Registers a possibly available custom repository implementation on the
|
||||
* repository bean. Tries to find an already registered bean to reference or
|
||||
* tries to detect a custom implementation itself.
|
||||
*
|
||||
* @param config
|
||||
* @param parser
|
||||
* @param source
|
||||
* @return the bean name of the custom implementation or {@code null} if
|
||||
* none available
|
||||
*/
|
||||
private String registerCustomImplementation(T config, ParserContext parser,
|
||||
Object source) {
|
||||
|
||||
String beanName = config.getImplementationBeanName();
|
||||
|
||||
// Already a bean configured?
|
||||
if (parser.getRegistry().containsBeanDefinition(beanName)) {
|
||||
return beanName;
|
||||
}
|
||||
|
||||
// Autodetect implementation
|
||||
if (config.autodetectCustomImplementation()) {
|
||||
|
||||
AbstractBeanDefinition beanDefinition =
|
||||
detectCustomImplementation(config, parser);
|
||||
|
||||
if (null == beanDefinition) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Registering custom repository implementation: " +
|
||||
config.getImplementationBeanName() + " " +
|
||||
beanDefinition.getBeanClassName());
|
||||
}
|
||||
|
||||
beanDefinition.setSource(source);
|
||||
parser.registerBeanComponent(new BeanComponentDefinition(
|
||||
beanDefinition, beanName));
|
||||
|
||||
} else {
|
||||
beanName = config.getCustomImplementationRef();
|
||||
}
|
||||
|
||||
return beanName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tries to detect a custom implementation for a repository bean by
|
||||
* classpath scanning.
|
||||
*
|
||||
* @param config
|
||||
* @param parser
|
||||
* @return the {@code AbstractBeanDefinition} of the custom implementation
|
||||
* or {@literal null} if none found
|
||||
*/
|
||||
private AbstractBeanDefinition detectCustomImplementation(T config,
|
||||
ParserContext parser) {
|
||||
|
||||
// Build pattern to lookup implementation class
|
||||
Pattern pattern =
|
||||
Pattern.compile(".*" + config.getImplementationClassName());
|
||||
|
||||
// Build classpath scanner and lookup bean definition
|
||||
ClassPathScanningCandidateComponentProvider provider =
|
||||
new ClassPathScanningCandidateComponentProvider(false);
|
||||
provider.setResourceLoader(parser.getReaderContext()
|
||||
.getResourceLoader());
|
||||
provider.addIncludeFilter(new RegexPatternTypeFilter(pattern));
|
||||
Set<BeanDefinition> definitions =
|
||||
provider.findCandidateComponents(config.getBasePackage());
|
||||
|
||||
return (0 == definitions.size() ? null
|
||||
: (AbstractBeanDefinition) definitions.iterator().next());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callback to register additional bean definitions for a
|
||||
* {@literal repositories} root node. This usually includes beans you have
|
||||
* to set up once independently of the number of repositories to be created.
|
||||
* Will be called before any repositories bean definitions have been
|
||||
* registered.
|
||||
*
|
||||
* @param registry
|
||||
* @param source
|
||||
*/
|
||||
protected void registerBeansForRoot(BeanDefinitionRegistry registry,
|
||||
Object source) {
|
||||
|
||||
AbstractBeanDefinition definition =
|
||||
BeanDefinitionBuilder.rootBeanDefinition(
|
||||
REPOSITORY_INTERFACE_POST_PROCESSOR)
|
||||
.getBeanDefinition();
|
||||
|
||||
registerWithSourceAndGeneratedBeanName(registry, definition, source);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the given {@link BeanDefinitionRegistry} already contains
|
||||
* a bean of the given type assuming the bean name has been autogenerated.
|
||||
*
|
||||
* @param type
|
||||
* @param registry
|
||||
* @return
|
||||
*/
|
||||
protected static boolean hasBean(Class<?> type,
|
||||
BeanDefinitionRegistry registry) {
|
||||
|
||||
String name =
|
||||
String.format("%s%s0", type.getName(),
|
||||
GENERATED_BEAN_NAME_SEPARATOR);
|
||||
return registry.containsBeanDefinition(name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the given source on the given {@link AbstractBeanDefinition} and
|
||||
* registers it inside the given {@link BeanDefinitionRegistry}.
|
||||
*
|
||||
* @param registry
|
||||
* @param bean
|
||||
* @param source
|
||||
* @return
|
||||
*/
|
||||
protected static String registerWithSourceAndGeneratedBeanName(
|
||||
BeanDefinitionRegistry registry, AbstractBeanDefinition bean,
|
||||
Object source) {
|
||||
|
||||
bean.setSource(source);
|
||||
|
||||
String beanName = generateBeanName(bean, registry);
|
||||
registry.registerBeanDefinition(beanName, bean);
|
||||
|
||||
return beanName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom {@link ClassPathScanningCandidateComponentProvider} scanning for
|
||||
* interfaces extending the given base interface. Skips interfaces annotated
|
||||
* with {@link NoRepositoryBean}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
static class RepositoryComponentProvider extends
|
||||
ClassPathScanningCandidateComponentProvider {
|
||||
|
||||
/**
|
||||
* Creates a new {@link RepositoryComponentProvider}.
|
||||
*
|
||||
* @param repositoryInterface the interface to scan for
|
||||
*/
|
||||
public RepositoryComponentProvider(Class<?> repositoryInterface) {
|
||||
|
||||
super(false);
|
||||
addIncludeFilter(new InterfaceTypeFilter(repositoryInterface));
|
||||
addExcludeFilter(new AnnotationTypeFilter(NoRepositoryBean.class));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @seeorg.springframework.context.annotation.
|
||||
* ClassPathScanningCandidateComponentProvider
|
||||
* #isCandidateComponent(org.springframework
|
||||
* .beans.factory.annotation.AnnotatedBeanDefinition)
|
||||
*/
|
||||
@Override
|
||||
protected boolean isCandidateComponent(
|
||||
AnnotatedBeanDefinition beanDefinition) {
|
||||
|
||||
boolean isNonRepositoryInterface =
|
||||
!isGenericRepositoryInterface(beanDefinition
|
||||
.getBeanClassName());
|
||||
boolean isTopLevelType =
|
||||
!beanDefinition.getMetadata().hasEnclosingClass();
|
||||
|
||||
return isNonRepositoryInterface && isTopLevelType;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link org.springframework.core.type.filter.TypeFilter} that only
|
||||
* matches interfaces. Thus setting this up makes only sense providing
|
||||
* an interface type as {@code targetType}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static class InterfaceTypeFilter extends AssignableTypeFilter {
|
||||
|
||||
/**
|
||||
* Creates a new {@link InterfaceTypeFilter}.
|
||||
*
|
||||
* @param targetType
|
||||
*/
|
||||
public InterfaceTypeFilter(Class<?> targetType) {
|
||||
|
||||
super(targetType);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @seeorg.springframework.core.type.filter.
|
||||
* AbstractTypeHierarchyTraversingFilter
|
||||
* #match(org.springframework.core.type.classreading.MetadataReader,
|
||||
* org.springframework.core.type.classreading.MetadataReaderFactory)
|
||||
*/
|
||||
@Override
|
||||
public boolean match(MetadataReader metadataReader,
|
||||
MetadataReaderFactory metadataReaderFactory)
|
||||
throws IOException {
|
||||
|
||||
return metadataReader.getClassMetadata().isInterface()
|
||||
&& super.match(metadataReader, metadataReaderFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,52 +25,52 @@ import org.springframework.util.Assert;
|
||||
* A {@link SingleRepositoryConfigInformation} implementation that is not backed
|
||||
* by an XML element but by a scanned interface. As this is derived from the
|
||||
* parent, most of the lookup logic is delegated to the parent as well.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class AutomaticRepositoryConfigInformation<S extends CommonRepositoryConfigInformation>
|
||||
extends ParentDelegatingRepositoryConfigInformation<S> {
|
||||
extends ParentDelegatingRepositoryConfigInformation<S> {
|
||||
|
||||
private final String interfaceName;
|
||||
private final String interfaceName;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link AutomaticRepositoryConfigInformation} for the given
|
||||
* interface name and {@link CommonRepositoryConfigInformation} parent.
|
||||
*
|
||||
* @param interfaceName
|
||||
* @param parent
|
||||
*/
|
||||
public AutomaticRepositoryConfigInformation(String interfaceName, S parent) {
|
||||
/**
|
||||
* Creates a new {@link AutomaticRepositoryConfigInformation} for the given
|
||||
* interface name and {@link CommonRepositoryConfigInformation} parent.
|
||||
*
|
||||
* @param interfaceName
|
||||
* @param parent
|
||||
*/
|
||||
public AutomaticRepositoryConfigInformation(String interfaceName, S parent) {
|
||||
|
||||
super(parent);
|
||||
Assert.notNull(interfaceName);
|
||||
this.interfaceName = interfaceName;
|
||||
}
|
||||
super(parent);
|
||||
Assert.notNull(interfaceName);
|
||||
this.interfaceName = interfaceName;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.config.SingleRepositoryConfigInformation
|
||||
* #getBeanId()
|
||||
*/
|
||||
public String getBeanId() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.config.SingleRepositoryConfigInformation
|
||||
* #getBeanId()
|
||||
*/
|
||||
public String getBeanId() {
|
||||
|
||||
return uncapitalize(getShortName(interfaceName));
|
||||
}
|
||||
return uncapitalize(getShortName(interfaceName));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.config.SingleRepositoryConfigInformation
|
||||
* #getInterfaceName()
|
||||
*/
|
||||
public String getInterfaceName() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.config.SingleRepositoryConfigInformation
|
||||
* #getInterfaceName()
|
||||
*/
|
||||
public String getInterfaceName() {
|
||||
|
||||
return interfaceName;
|
||||
}
|
||||
return interfaceName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,59 +22,59 @@ import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Interface for shared repository information.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface CommonRepositoryConfigInformation {
|
||||
|
||||
/**
|
||||
* Returns the element the repository information is derived from.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Element getSource();
|
||||
/**
|
||||
* Returns the element the repository information is derived from.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Element getSource();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the base package.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getBasePackage();
|
||||
/**
|
||||
* Returns the base package.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getBasePackage();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the suffix to use for implementation bean lookup or class
|
||||
* detection.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getRepositoryImplementationSuffix();
|
||||
/**
|
||||
* Returns the suffix to use for implementation bean lookup or class
|
||||
* detection.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getRepositoryImplementationSuffix();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the configured repository factory class.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getRepositoryFactoryBeanClassName();
|
||||
/**
|
||||
* Returns the configured repository factory class.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getRepositoryFactoryBeanClassName();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the bean name of the {@link PlatformTransactionManager} to be
|
||||
* used. Returns {@literal null} if no reference has been configured
|
||||
* explicitly.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getTransactionManagerRef();
|
||||
/**
|
||||
* Returns the bean name of the {@link PlatformTransactionManager} to be
|
||||
* used. Returns {@literal null} if no reference has been configured
|
||||
* explicitly.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getTransactionManagerRef();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the strategy finder methods should be resolved.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Key getQueryLookupStrategyKey();
|
||||
/**
|
||||
* Returns the strategy finder methods should be resolved.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Key getQueryLookupStrategyKey();
|
||||
|
||||
}
|
||||
@@ -19,40 +19,40 @@ package org.springframework.data.repository.config;
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface GlobalRepositoryConfigInformation<T extends SingleRepositoryConfigInformation<?>>
|
||||
extends CommonRepositoryConfigInformation {
|
||||
extends CommonRepositoryConfigInformation {
|
||||
|
||||
/**
|
||||
* Returns the
|
||||
*
|
||||
* @param interfaceName
|
||||
* @return
|
||||
*/
|
||||
T getAutoconfigRepositoryInformation(String interfaceName);
|
||||
/**
|
||||
* Returns the
|
||||
*
|
||||
* @param interfaceName
|
||||
* @return
|
||||
*/
|
||||
T getAutoconfigRepositoryInformation(String interfaceName);
|
||||
|
||||
|
||||
/**
|
||||
* Returns all {@link SingleRepositoryConfigInformation} instances used for
|
||||
* manual configuration.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Iterable<T> getSingleRepositoryConfigInformations();
|
||||
/**
|
||||
* Returns all {@link SingleRepositoryConfigInformation} instances used for
|
||||
* manual configuration.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Iterable<T> getSingleRepositoryConfigInformations();
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether to consider manual configuration. If this returns true,
|
||||
* clients should use {@link #getSingleRepositoryConfigInformations()} to
|
||||
* lookup configuration information for individual repository beans.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean configureManually();
|
||||
/**
|
||||
* Returns whether to consider manual configuration. If this returns true,
|
||||
* clients should use {@link #getSingleRepositoryConfigInformations()} to
|
||||
* lookup configuration information for individual repository beans.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean configureManually();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the base interface to use
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<?> getRepositoryBaseInterface();
|
||||
/**
|
||||
* Returns the base interface to use
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<?> getRepositoryBaseInterface();
|
||||
}
|
||||
@@ -23,165 +23,165 @@ import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Configuration information for manual repository configuration.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class ManualRepositoryConfigInformation<T extends CommonRepositoryConfigInformation>
|
||||
extends ParentDelegatingRepositoryConfigInformation<T> {
|
||||
extends ParentDelegatingRepositoryConfigInformation<T> {
|
||||
|
||||
private static final String CUSTOM_IMPL_REF = "custom-impl-ref";
|
||||
private static final String CUSTOM_IMPL_REF = "custom-impl-ref";
|
||||
|
||||
private Element element;
|
||||
private Element element;
|
||||
|
||||
|
||||
/**
|
||||
* @param parent
|
||||
*/
|
||||
public ManualRepositoryConfigInformation(Element element, T parent) {
|
||||
/**
|
||||
* @param parent
|
||||
*/
|
||||
public ManualRepositoryConfigInformation(Element element, T parent) {
|
||||
|
||||
super(parent);
|
||||
this.element = element;
|
||||
}
|
||||
super(parent);
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.RepositoryInformation#
|
||||
* getBeanName()
|
||||
*/
|
||||
public String getBeanId() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.RepositoryInformation#
|
||||
* getBeanName()
|
||||
*/
|
||||
public String getBeanId() {
|
||||
|
||||
return element.getAttribute("id");
|
||||
}
|
||||
return element.getAttribute("id");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.RepositoryInformation#
|
||||
* getInterfaceName()
|
||||
*/
|
||||
public String getInterfaceName() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.RepositoryInformation#
|
||||
* getInterfaceName()
|
||||
*/
|
||||
public String getInterfaceName() {
|
||||
|
||||
return getBasePackage() + "." + capitalize(getBeanId());
|
||||
}
|
||||
return getBasePackage() + "." + capitalize(getBeanId());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.RepositoryInformation#
|
||||
* getCustomImplementationRef()
|
||||
*/
|
||||
@Override
|
||||
public String getCustomImplementationRef() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.RepositoryInformation#
|
||||
* getCustomImplementationRef()
|
||||
*/
|
||||
@Override
|
||||
public String getCustomImplementationRef() {
|
||||
|
||||
return element.getAttribute(CUSTOM_IMPL_REF);
|
||||
}
|
||||
return element.getAttribute(CUSTOM_IMPL_REF);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns if a custom implementation shall be autodetected.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean autodetectCustomImplementation() {
|
||||
/**
|
||||
* Returns if a custom implementation shall be autodetected.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean autodetectCustomImplementation() {
|
||||
|
||||
return !hasText(getCustomImplementationRef());
|
||||
}
|
||||
return !hasText(getCustomImplementationRef());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.AbstractRepositoryInformation
|
||||
* #getRepositoryImplementationSuffix()
|
||||
*/
|
||||
@Override
|
||||
public String getRepositoryImplementationSuffix() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.AbstractRepositoryInformation
|
||||
* #getRepositoryImplementationSuffix()
|
||||
*/
|
||||
@Override
|
||||
public String getRepositoryImplementationSuffix() {
|
||||
|
||||
String value =
|
||||
element.getAttribute(RepositoryConfig.REPOSITORY_IMPL_POSTFIX);
|
||||
return hasText(value) ? value : getParent()
|
||||
.getRepositoryImplementationSuffix();
|
||||
}
|
||||
String value =
|
||||
element.getAttribute(RepositoryConfig.REPOSITORY_IMPL_POSTFIX);
|
||||
return hasText(value) ? value : getParent()
|
||||
.getRepositoryImplementationSuffix();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getTransactionManagerRef() {
|
||||
@Override
|
||||
public String getTransactionManagerRef() {
|
||||
|
||||
return getAttribute(RepositoryConfig.TRANSACTION_MANAGER_REF);
|
||||
}
|
||||
return getAttribute(RepositoryConfig.TRANSACTION_MANAGER_REF);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.CommonRepositoryInformation
|
||||
* #getSource()
|
||||
*/
|
||||
@Override
|
||||
public Element getSource() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.CommonRepositoryInformation
|
||||
* #getSource()
|
||||
*/
|
||||
@Override
|
||||
public Element getSource() {
|
||||
|
||||
return element;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the attribute of the current context. If it's not set the method
|
||||
* will fall back to the parent's source.
|
||||
*
|
||||
* @param attribute
|
||||
* @return
|
||||
*/
|
||||
protected String getAttribute(String attribute) {
|
||||
/**
|
||||
* Returns the attribute of the current context. If it's not set the method
|
||||
* will fall back to the parent's source.
|
||||
*
|
||||
* @param attribute
|
||||
* @return
|
||||
*/
|
||||
protected String getAttribute(String attribute) {
|
||||
|
||||
String value = getSource().getAttribute(attribute);
|
||||
String value = getSource().getAttribute(attribute);
|
||||
|
||||
if (hasText(value)) {
|
||||
return value;
|
||||
}
|
||||
if (hasText(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
value = getParent().getSource().getAttribute(attribute);
|
||||
value = getParent().getSource().getAttribute(attribute);
|
||||
|
||||
return hasText(value) ? value : null;
|
||||
}
|
||||
return hasText(value) ? value : null;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.CommonRepositoryInformation
|
||||
* #getRepositoryFactoryClassName()
|
||||
*/
|
||||
@Override
|
||||
public String getRepositoryFactoryBeanClassName() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.CommonRepositoryInformation
|
||||
* #getRepositoryFactoryClassName()
|
||||
*/
|
||||
@Override
|
||||
public String getRepositoryFactoryBeanClassName() {
|
||||
|
||||
String value =
|
||||
element.getAttribute(RepositoryConfig.REPOSITORY_FACTORY_CLASS_NAME);
|
||||
return hasText(value) ? value : getParent()
|
||||
.getRepositoryFactoryBeanClassName();
|
||||
}
|
||||
String value =
|
||||
element.getAttribute(RepositoryConfig.REPOSITORY_FACTORY_CLASS_NAME);
|
||||
return hasText(value) ? value : getParent()
|
||||
.getRepositoryFactoryBeanClassName();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.CommonRepositoryInformation
|
||||
* #getQueryLookupStrategyKey()
|
||||
*/
|
||||
@Override
|
||||
public Key getQueryLookupStrategyKey() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.CommonRepositoryInformation
|
||||
* #getQueryLookupStrategyKey()
|
||||
*/
|
||||
@Override
|
||||
public Key getQueryLookupStrategyKey() {
|
||||
|
||||
return Key.create(getAttribute(RepositoryConfig.QUERY_LOOKUP_STRATEGY));
|
||||
}
|
||||
return Key.create(getAttribute(RepositoryConfig.QUERY_LOOKUP_STRATEGY));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,167 +26,167 @@ import org.w3c.dom.Element;
|
||||
* Base class for {@link SingleRepositoryConfigInformation} implementations. So
|
||||
* these implementations will capture information for XML elements manually
|
||||
* configuring a single repository bean.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public abstract class ParentDelegatingRepositoryConfigInformation<T extends CommonRepositoryConfigInformation>
|
||||
implements SingleRepositoryConfigInformation<T> {
|
||||
implements SingleRepositoryConfigInformation<T> {
|
||||
|
||||
private final T parent;
|
||||
private final T parent;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link ParentDelegatingRepositoryConfigInformation} with
|
||||
* the given {@link CommonRepositoryConfigInformation} as parent.
|
||||
*
|
||||
* @param parent
|
||||
*/
|
||||
public ParentDelegatingRepositoryConfigInformation(T parent) {
|
||||
/**
|
||||
* Creates a new {@link ParentDelegatingRepositoryConfigInformation} with
|
||||
* the given {@link CommonRepositoryConfigInformation} as parent.
|
||||
*
|
||||
* @param parent
|
||||
*/
|
||||
public ParentDelegatingRepositoryConfigInformation(T parent) {
|
||||
|
||||
Assert.notNull(parent);
|
||||
this.parent = parent;
|
||||
}
|
||||
Assert.notNull(parent);
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.RepositoryInformation#
|
||||
* getParent()
|
||||
*/
|
||||
protected T getParent() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.RepositoryInformation#
|
||||
* getParent()
|
||||
*/
|
||||
protected T getParent() {
|
||||
|
||||
return parent;
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.CommonRepositoryInformation
|
||||
* #getBasePackage()
|
||||
*/
|
||||
public String getBasePackage() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.CommonRepositoryInformation
|
||||
* #getBasePackage()
|
||||
*/
|
||||
public String getBasePackage() {
|
||||
|
||||
return parent.getBasePackage();
|
||||
}
|
||||
return parent.getBasePackage();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.RepositoryInformation#
|
||||
* getImplementationClassName()
|
||||
*/
|
||||
public String getImplementationClassName() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.RepositoryInformation#
|
||||
* getImplementationClassName()
|
||||
*/
|
||||
public String getImplementationClassName() {
|
||||
|
||||
return capitalize(getBeanId()) + getRepositoryImplementationSuffix();
|
||||
}
|
||||
return capitalize(getBeanId()) + getRepositoryImplementationSuffix();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.RepositoryInformation#
|
||||
* getImplementationBeanName()
|
||||
*/
|
||||
public String getImplementationBeanName() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.RepositoryInformation#
|
||||
* getImplementationBeanName()
|
||||
*/
|
||||
public String getImplementationBeanName() {
|
||||
|
||||
return getBeanId() + getRepositoryImplementationSuffix();
|
||||
}
|
||||
return getBeanId() + getRepositoryImplementationSuffix();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.RepositoryInformation#
|
||||
* autodetectCustomImplementation()
|
||||
*/
|
||||
public boolean autodetectCustomImplementation() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.RepositoryInformation#
|
||||
* autodetectCustomImplementation()
|
||||
*/
|
||||
public boolean autodetectCustomImplementation() {
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.RepositoryInformation#
|
||||
* getCustomImplementationRef()
|
||||
*/
|
||||
public String getCustomImplementationRef() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.RepositoryInformation#
|
||||
* getCustomImplementationRef()
|
||||
*/
|
||||
public String getCustomImplementationRef() {
|
||||
|
||||
return getBeanId() + getRepositoryImplementationSuffix();
|
||||
}
|
||||
return getBeanId() + getRepositoryImplementationSuffix();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.CommonRepositoryInformation
|
||||
* #getSource()
|
||||
*/
|
||||
public Element getSource() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.CommonRepositoryInformation
|
||||
* #getSource()
|
||||
*/
|
||||
public Element getSource() {
|
||||
|
||||
return parent.getSource();
|
||||
}
|
||||
return parent.getSource();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.config.CommonRepositoryConfigInformation
|
||||
* #getRepositoryImplementationSuffix()
|
||||
*/
|
||||
public String getRepositoryImplementationSuffix() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.config.CommonRepositoryConfigInformation
|
||||
* #getRepositoryImplementationSuffix()
|
||||
*/
|
||||
public String getRepositoryImplementationSuffix() {
|
||||
|
||||
return parent.getRepositoryImplementationSuffix();
|
||||
}
|
||||
return parent.getRepositoryImplementationSuffix();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.config.CommonRepositoryConfigInformation
|
||||
* #getRepositoryFactoryBeanClassName()
|
||||
*/
|
||||
public String getRepositoryFactoryBeanClassName() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.config.CommonRepositoryConfigInformation
|
||||
* #getRepositoryFactoryBeanClassName()
|
||||
*/
|
||||
public String getRepositoryFactoryBeanClassName() {
|
||||
|
||||
return parent.getRepositoryFactoryBeanClassName();
|
||||
}
|
||||
return parent.getRepositoryFactoryBeanClassName();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.RepositoryInformation#
|
||||
* getTransactionManagerRef()
|
||||
*/
|
||||
public String getTransactionManagerRef() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.RepositoryInformation#
|
||||
* getTransactionManagerRef()
|
||||
*/
|
||||
public String getTransactionManagerRef() {
|
||||
|
||||
return parent.getTransactionManagerRef();
|
||||
}
|
||||
return parent.getTransactionManagerRef();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.RepositoryInformation#
|
||||
* getQueryLookupStrategyKey()
|
||||
*/
|
||||
public Key getQueryLookupStrategyKey() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.RepositoryInformation#
|
||||
* getQueryLookupStrategyKey()
|
||||
*/
|
||||
public Key getQueryLookupStrategyKey() {
|
||||
|
||||
return parent.getQueryLookupStrategyKey();
|
||||
}
|
||||
return parent.getQueryLookupStrategyKey();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,191 +31,191 @@ import org.w3c.dom.NodeList;
|
||||
* Class defining access to the repository configuration abstracting the content
|
||||
* of the {@code repositories} element in XML namespcae configuration. Defines
|
||||
* default values to populate resulting repository beans with.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public abstract class RepositoryConfig<T extends SingleRepositoryConfigInformation<S>, S extends CommonRepositoryConfigInformation>
|
||||
implements GlobalRepositoryConfigInformation<T> {
|
||||
implements GlobalRepositoryConfigInformation<T> {
|
||||
|
||||
public static final String DEFAULT_REPOSITORY_IMPL_POSTFIX = "Impl";
|
||||
public static final String QUERY_LOOKUP_STRATEGY = "query-lookup-strategy";
|
||||
public static final String BASE_PACKAGE = "base-package";
|
||||
public static final String REPOSITORY_IMPL_POSTFIX =
|
||||
"repository-impl-postfix";
|
||||
public static final String REPOSITORY_FACTORY_CLASS_NAME = "factory-class";
|
||||
public static final String TRANSACTION_MANAGER_REF =
|
||||
"transaction-manager-ref";
|
||||
public static final String DEFAULT_REPOSITORY_IMPL_POSTFIX = "Impl";
|
||||
public static final String QUERY_LOOKUP_STRATEGY = "query-lookup-strategy";
|
||||
public static final String BASE_PACKAGE = "base-package";
|
||||
public static final String REPOSITORY_IMPL_POSTFIX =
|
||||
"repository-impl-postfix";
|
||||
public static final String REPOSITORY_FACTORY_CLASS_NAME = "factory-class";
|
||||
public static final String TRANSACTION_MANAGER_REF =
|
||||
"transaction-manager-ref";
|
||||
|
||||
private final Element element;
|
||||
private final String defaultRepositoryFactoryBeanClassName;
|
||||
private final Element element;
|
||||
private final String defaultRepositoryFactoryBeanClassName;
|
||||
|
||||
|
||||
/**
|
||||
* Creates an instance of {@code RepositoryConfig}.
|
||||
*
|
||||
* @param repositoriesElement
|
||||
*/
|
||||
protected RepositoryConfig(Element repositoriesElement,
|
||||
String defaultRepositoryFactoryBeanClassName) {
|
||||
/**
|
||||
* Creates an instance of {@code RepositoryConfig}.
|
||||
*
|
||||
* @param repositoriesElement
|
||||
*/
|
||||
protected RepositoryConfig(Element repositoriesElement,
|
||||
String defaultRepositoryFactoryBeanClassName) {
|
||||
|
||||
Assert.notNull(repositoriesElement, "Element must not be null!");
|
||||
Assert.notNull(defaultRepositoryFactoryBeanClassName,
|
||||
"Default repository factory bean class name must not be null!");
|
||||
Assert.notNull(repositoriesElement, "Element must not be null!");
|
||||
Assert.notNull(defaultRepositoryFactoryBeanClassName,
|
||||
"Default repository factory bean class name must not be null!");
|
||||
|
||||
this.element = repositoriesElement;
|
||||
this.defaultRepositoryFactoryBeanClassName =
|
||||
defaultRepositoryFactoryBeanClassName;
|
||||
this.element = repositoriesElement;
|
||||
this.defaultRepositoryFactoryBeanClassName =
|
||||
defaultRepositoryFactoryBeanClassName;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.config.CommonRepositoryConfigInformation
|
||||
* #getSource()
|
||||
*/
|
||||
public Element getSource() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.config.CommonRepositoryConfigInformation
|
||||
* #getSource()
|
||||
*/
|
||||
public Element getSource() {
|
||||
|
||||
return element;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.config.GlobalRepositoryConfigInformation
|
||||
* #configureManually()
|
||||
*/
|
||||
public boolean configureManually() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.config.GlobalRepositoryConfigInformation
|
||||
* #configureManually()
|
||||
*/
|
||||
public boolean configureManually() {
|
||||
|
||||
return getRepositoryElements().size() > 0;
|
||||
}
|
||||
return getRepositoryElements().size() > 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.config.CommonRepositoryConfigInformation
|
||||
* #getQueryLookupStrategyKey()
|
||||
*/
|
||||
public Key getQueryLookupStrategyKey() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.config.CommonRepositoryConfigInformation
|
||||
* #getQueryLookupStrategyKey()
|
||||
*/
|
||||
public Key getQueryLookupStrategyKey() {
|
||||
|
||||
String createFinderQueries =
|
||||
element.getAttribute(QUERY_LOOKUP_STRATEGY);
|
||||
String createFinderQueries =
|
||||
element.getAttribute(QUERY_LOOKUP_STRATEGY);
|
||||
|
||||
return StringUtils.hasText(createFinderQueries) ? Key
|
||||
.create(createFinderQueries) : null;
|
||||
}
|
||||
return StringUtils.hasText(createFinderQueries) ? Key
|
||||
.create(createFinderQueries) : null;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.config.CommonRepositoryConfigInformation
|
||||
* #getBasePackage()
|
||||
*/
|
||||
public String getBasePackage() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.config.CommonRepositoryConfigInformation
|
||||
* #getBasePackage()
|
||||
*/
|
||||
public String getBasePackage() {
|
||||
|
||||
return element.getAttribute(BASE_PACKAGE);
|
||||
}
|
||||
return element.getAttribute(BASE_PACKAGE);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.config.CommonRepositoryConfigInformation
|
||||
* #getRepositoryFactoryClassName()
|
||||
*/
|
||||
public String getRepositoryFactoryBeanClassName() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.config.CommonRepositoryConfigInformation
|
||||
* #getRepositoryFactoryClassName()
|
||||
*/
|
||||
public String getRepositoryFactoryBeanClassName() {
|
||||
|
||||
String factoryClassName =
|
||||
getSource().getAttribute(REPOSITORY_FACTORY_CLASS_NAME);
|
||||
return StringUtils.hasText(factoryClassName) ? factoryClassName
|
||||
: defaultRepositoryFactoryBeanClassName;
|
||||
}
|
||||
String factoryClassName =
|
||||
getSource().getAttribute(REPOSITORY_FACTORY_CLASS_NAME);
|
||||
return StringUtils.hasText(factoryClassName) ? factoryClassName
|
||||
: defaultRepositoryFactoryBeanClassName;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.config.CommonRepositoryConfigInformation
|
||||
* #getRepositoryImplementationSuffix()
|
||||
*/
|
||||
public String getRepositoryImplementationSuffix() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.config.CommonRepositoryConfigInformation
|
||||
* #getRepositoryImplementationSuffix()
|
||||
*/
|
||||
public String getRepositoryImplementationSuffix() {
|
||||
|
||||
String postfix = element.getAttribute(REPOSITORY_IMPL_POSTFIX);
|
||||
return StringUtils.hasText(postfix) ? postfix
|
||||
: DEFAULT_REPOSITORY_IMPL_POSTFIX;
|
||||
}
|
||||
String postfix = element.getAttribute(REPOSITORY_IMPL_POSTFIX);
|
||||
return StringUtils.hasText(postfix) ? postfix
|
||||
: DEFAULT_REPOSITORY_IMPL_POSTFIX;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.config.CommonRepositoryConfigInformation
|
||||
* #getTransactionManagerRef()
|
||||
*/
|
||||
public String getTransactionManagerRef() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.config.CommonRepositoryConfigInformation
|
||||
* #getTransactionManagerRef()
|
||||
*/
|
||||
public String getTransactionManagerRef() {
|
||||
|
||||
String ref = element.getAttribute(TRANSACTION_MANAGER_REF);
|
||||
return StringUtils.hasText(ref) ? ref : null;
|
||||
}
|
||||
String ref = element.getAttribute(TRANSACTION_MANAGER_REF);
|
||||
return StringUtils.hasText(ref) ? ref : null;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.GlobalRepositoryInformation
|
||||
* #getManualRepositoryInformations()
|
||||
*/
|
||||
public Iterable<T> getSingleRepositoryConfigInformations() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.config.GlobalRepositoryInformation
|
||||
* #getManualRepositoryInformations()
|
||||
*/
|
||||
public Iterable<T> getSingleRepositoryConfigInformations() {
|
||||
|
||||
Set<T> infos = new HashSet<T>();
|
||||
for (Element repositoryElement : getRepositoryElements()) {
|
||||
infos.add(createSingleRepositoryConfigInformationFor(repositoryElement));
|
||||
}
|
||||
Set<T> infos = new HashSet<T>();
|
||||
for (Element repositoryElement : getRepositoryElements()) {
|
||||
infos.add(createSingleRepositoryConfigInformationFor(repositoryElement));
|
||||
}
|
||||
|
||||
return infos;
|
||||
}
|
||||
return infos;
|
||||
}
|
||||
|
||||
|
||||
private Collection<Element> getRepositoryElements() {
|
||||
private Collection<Element> getRepositoryElements() {
|
||||
|
||||
NodeList nodes = element.getChildNodes();
|
||||
Set<Element> result = new HashSet<Element>();
|
||||
NodeList nodes = element.getChildNodes();
|
||||
Set<Element> result = new HashSet<Element>();
|
||||
|
||||
for (int i = 0; i < nodes.getLength(); i++) {
|
||||
for (int i = 0; i < nodes.getLength(); i++) {
|
||||
|
||||
Node node = nodes.item(i);
|
||||
Node node = nodes.item(i);
|
||||
|
||||
boolean isElement = Node.ELEMENT_NODE == node.getNodeType();
|
||||
boolean isRepository = "repository".equals(node.getLocalName());
|
||||
boolean isElement = Node.ELEMENT_NODE == node.getNodeType();
|
||||
boolean isRepository = "repository".equals(node.getLocalName());
|
||||
|
||||
if (isElement && isRepository) {
|
||||
result.add((Element) node);
|
||||
}
|
||||
}
|
||||
if (isElement && isRepository) {
|
||||
result.add((Element) node);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a {@link SingleRepositoryConfigInformation} for the given
|
||||
* {@link Element}.
|
||||
*
|
||||
* @param element
|
||||
* @return
|
||||
*/
|
||||
protected abstract T createSingleRepositoryConfigInformationFor(
|
||||
Element element);
|
||||
/**
|
||||
* Creates a {@link SingleRepositoryConfigInformation} for the given
|
||||
* {@link Element}.
|
||||
*
|
||||
* @param element
|
||||
* @return
|
||||
*/
|
||||
protected abstract T createSingleRepositoryConfigInformationFor(
|
||||
Element element);
|
||||
}
|
||||
|
||||
@@ -18,58 +18,58 @@ package org.springframework.data.repository.config;
|
||||
/**
|
||||
* Interface to capture configuration information necessary to set up a single
|
||||
* repository instance.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface SingleRepositoryConfigInformation<T extends CommonRepositoryConfigInformation>
|
||||
extends CommonRepositoryConfigInformation {
|
||||
extends CommonRepositoryConfigInformation {
|
||||
|
||||
/**
|
||||
* Returns the bean name to be used for the repository.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getBeanId();
|
||||
/**
|
||||
* Returns the bean name to be used for the repository.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getBeanId();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the name of the repository interface.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getInterfaceName();
|
||||
/**
|
||||
* Returns the name of the repository interface.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getInterfaceName();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the class name of a possible custom repository implementation
|
||||
* class to detect.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getImplementationClassName();
|
||||
/**
|
||||
* Returns the class name of a possible custom repository implementation
|
||||
* class to detect.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getImplementationClassName();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the bean name a possibly found custom implementation shall be
|
||||
* registered under.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getImplementationBeanName();
|
||||
/**
|
||||
* Returns the bean name a possibly found custom implementation shall be
|
||||
* registered under.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getImplementationBeanName();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the bean reference to the custom repository implementation.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getCustomImplementationRef();
|
||||
/**
|
||||
* Returns the bean reference to the custom repository implementation.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getCustomImplementationRef();
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether to try to autodetect a custom implementation.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean autodetectCustomImplementation();
|
||||
/**
|
||||
* Returns whether to try to autodetect a custom implementation.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean autodetectCustomImplementation();
|
||||
}
|
||||
@@ -36,251 +36,244 @@ import org.w3c.dom.NodeList;
|
||||
* Parser to populate the given
|
||||
* {@link ClassPathScanningCandidateComponentProvider} with {@link TypeFilter}s
|
||||
* parsed from the given {@link Element}'s children.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
class TypeFilterParser {
|
||||
|
||||
private static final String FILTER_TYPE_ATTRIBUTE = "type";
|
||||
private static final String FILTER_EXPRESSION_ATTRIBUTE = "expression";
|
||||
private static final String FILTER_TYPE_ATTRIBUTE = "type";
|
||||
private static final String FILTER_EXPRESSION_ATTRIBUTE = "expression";
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
private final ReaderContext readerContext;
|
||||
private final ClassLoader classLoader;
|
||||
private final ReaderContext readerContext;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link TypeFilterParser} with the given {@link ClassLoader}
|
||||
* and {@link ReaderContext}.
|
||||
*
|
||||
* @param classLoader
|
||||
* @param readerContext
|
||||
*/
|
||||
public TypeFilterParser(ClassLoader classLoader, ReaderContext readerContext) {
|
||||
/**
|
||||
* Creates a new {@link TypeFilterParser} with the given {@link ClassLoader}
|
||||
* and {@link ReaderContext}.
|
||||
*
|
||||
* @param classLoader
|
||||
* @param readerContext
|
||||
*/
|
||||
public TypeFilterParser(ClassLoader classLoader, ReaderContext readerContext) {
|
||||
|
||||
this.classLoader = classLoader;
|
||||
this.readerContext = readerContext;
|
||||
}
|
||||
this.classLoader = classLoader;
|
||||
this.readerContext = readerContext;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses include and exclude filters form the given {@link Element}'s child
|
||||
* elements and populates the given
|
||||
* {@link ClassPathScanningCandidateComponentProvider} with the according
|
||||
* {@link TypeFilter}s.
|
||||
*
|
||||
* @param element
|
||||
* @param scanner
|
||||
*/
|
||||
public void parseFilters(Element element,
|
||||
ClassPathScanningCandidateComponentProvider scanner) {
|
||||
/**
|
||||
* Parses include and exclude filters form the given {@link Element}'s child
|
||||
* elements and populates the given
|
||||
* {@link ClassPathScanningCandidateComponentProvider} with the according
|
||||
* {@link TypeFilter}s.
|
||||
*
|
||||
* @param element
|
||||
* @param scanner
|
||||
*/
|
||||
public void parseFilters(Element element,
|
||||
ClassPathScanningCandidateComponentProvider scanner) {
|
||||
|
||||
parseTypeFilters(element, scanner, Type.INCLUDE);
|
||||
parseTypeFilters(element, scanner, Type.EXCLUDE);
|
||||
}
|
||||
parseTypeFilters(element, scanner, Type.INCLUDE);
|
||||
parseTypeFilters(element, scanner, Type.EXCLUDE);
|
||||
}
|
||||
|
||||
|
||||
private void parseTypeFilters(Element element,
|
||||
ClassPathScanningCandidateComponentProvider scanner, Type type) {
|
||||
private void parseTypeFilters(Element element,
|
||||
ClassPathScanningCandidateComponentProvider scanner, Type type) {
|
||||
|
||||
NodeList nodeList = element.getChildNodes();
|
||||
for (int i = 0; i < nodeList.getLength(); i++) {
|
||||
Node node = nodeList.item(i);
|
||||
NodeList nodeList = element.getChildNodes();
|
||||
for (int i = 0; i < nodeList.getLength(); i++) {
|
||||
Node node = nodeList.item(i);
|
||||
|
||||
Element childElement = type.getElement(node);
|
||||
Element childElement = type.getElement(node);
|
||||
|
||||
if (childElement != null) {
|
||||
if (childElement != null) {
|
||||
|
||||
try {
|
||||
try {
|
||||
|
||||
type.addFilter(
|
||||
createTypeFilter((Element) node, classLoader),
|
||||
scanner);
|
||||
type.addFilter(
|
||||
createTypeFilter((Element) node, classLoader),
|
||||
scanner);
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
readerContext.error(e.getMessage(),
|
||||
readerContext.extractSource(element), e.getCause());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
readerContext.error(e.getMessage(),
|
||||
readerContext.extractSource(element), e.getCause());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected TypeFilter createTypeFilter(Element element,
|
||||
ClassLoader classLoader) {
|
||||
protected TypeFilter createTypeFilter(Element element,
|
||||
ClassLoader classLoader) {
|
||||
|
||||
String filterType = element.getAttribute(FILTER_TYPE_ATTRIBUTE);
|
||||
String expression = element.getAttribute(FILTER_EXPRESSION_ATTRIBUTE);
|
||||
String filterType = element.getAttribute(FILTER_TYPE_ATTRIBUTE);
|
||||
String expression = element.getAttribute(FILTER_EXPRESSION_ATTRIBUTE);
|
||||
|
||||
try {
|
||||
try {
|
||||
|
||||
FilterType filter = FilterType.fromString(filterType);
|
||||
return filter.getFilter(expression, classLoader);
|
||||
FilterType filter = FilterType.fromString(filterType);
|
||||
return filter.getFilter(expression, classLoader);
|
||||
|
||||
} catch (ClassNotFoundException ex) {
|
||||
throw new FatalBeanException("Type filter class not found: "
|
||||
+ expression, ex);
|
||||
}
|
||||
}
|
||||
} catch (ClassNotFoundException ex) {
|
||||
throw new FatalBeanException("Type filter class not found: "
|
||||
+ expression, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum representing all the filter types available for {@code include} and
|
||||
* {@code exclude} elements. This acts as factory for {@link TypeFilter}
|
||||
* instances.
|
||||
*
|
||||
* @see #getFilter(String, ClassLoader)
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static enum FilterType {
|
||||
/**
|
||||
* Enum representing all the filter types available for {@code include} and
|
||||
* {@code exclude} elements. This acts as factory for {@link TypeFilter}
|
||||
* instances.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @see #getFilter(String, ClassLoader)
|
||||
*/
|
||||
private static enum FilterType {
|
||||
|
||||
ANNOTATION {
|
||||
ANNOTATION {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public TypeFilter getFilter(String expression,
|
||||
ClassLoader classLoader) throws ClassNotFoundException {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public TypeFilter getFilter(String expression,
|
||||
ClassLoader classLoader) throws ClassNotFoundException {
|
||||
return new AnnotationTypeFilter(
|
||||
(Class<Annotation>) classLoader.loadClass(expression));
|
||||
}
|
||||
},
|
||||
|
||||
return new AnnotationTypeFilter(
|
||||
(Class<Annotation>) classLoader.loadClass(expression));
|
||||
}
|
||||
},
|
||||
ASSIGNABLE {
|
||||
@Override
|
||||
public TypeFilter getFilter(String expression,
|
||||
ClassLoader classLoader) throws ClassNotFoundException {
|
||||
|
||||
ASSIGNABLE {
|
||||
return new AssignableTypeFilter(
|
||||
classLoader.loadClass(expression));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeFilter getFilter(String expression,
|
||||
ClassLoader classLoader) throws ClassNotFoundException {
|
||||
},
|
||||
|
||||
return new AssignableTypeFilter(
|
||||
classLoader.loadClass(expression));
|
||||
}
|
||||
ASPECTJ {
|
||||
@Override
|
||||
public TypeFilter getFilter(String expression,
|
||||
ClassLoader classLoader) {
|
||||
|
||||
},
|
||||
return new AspectJTypeFilter(expression, classLoader);
|
||||
}
|
||||
|
||||
ASPECTJ {
|
||||
},
|
||||
|
||||
@Override
|
||||
public TypeFilter getFilter(String expression,
|
||||
ClassLoader classLoader) {
|
||||
REGEX {
|
||||
@Override
|
||||
public TypeFilter getFilter(String expression,
|
||||
ClassLoader classLoader) {
|
||||
|
||||
return new AspectJTypeFilter(expression, classLoader);
|
||||
}
|
||||
return new RegexPatternTypeFilter(Pattern.compile(expression));
|
||||
}
|
||||
|
||||
},
|
||||
},
|
||||
|
||||
REGEX {
|
||||
CUSTOM {
|
||||
@Override
|
||||
public TypeFilter getFilter(String expression,
|
||||
ClassLoader classLoader) throws ClassNotFoundException {
|
||||
|
||||
@Override
|
||||
public TypeFilter getFilter(String expression,
|
||||
ClassLoader classLoader) {
|
||||
Class<?> filterClass = classLoader.loadClass(expression);
|
||||
if (!TypeFilter.class.isAssignableFrom(filterClass)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Class is not assignable to ["
|
||||
+ TypeFilter.class.getName() + "]: "
|
||||
+ expression);
|
||||
}
|
||||
return (TypeFilter) BeanUtils.instantiateClass(filterClass);
|
||||
}
|
||||
};
|
||||
|
||||
return new RegexPatternTypeFilter(Pattern.compile(expression));
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
CUSTOM {
|
||||
|
||||
@Override
|
||||
public TypeFilter getFilter(String expression,
|
||||
ClassLoader classLoader) throws ClassNotFoundException {
|
||||
|
||||
Class<?> filterClass = classLoader.loadClass(expression);
|
||||
if (!TypeFilter.class.isAssignableFrom(filterClass)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Class is not assignable to ["
|
||||
+ TypeFilter.class.getName() + "]: "
|
||||
+ expression);
|
||||
}
|
||||
return (TypeFilter) BeanUtils.instantiateClass(filterClass);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the {@link TypeFilter} for the given expression and
|
||||
* {@link ClassLoader}.
|
||||
*
|
||||
* @param expression
|
||||
* @param classLoader
|
||||
* @return
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
abstract TypeFilter getFilter(String expression, ClassLoader classLoader)
|
||||
throws ClassNotFoundException;
|
||||
/**
|
||||
* Returns the {@link TypeFilter} for the given expression and
|
||||
* {@link ClassLoader}.
|
||||
*
|
||||
* @param expression
|
||||
* @param classLoader
|
||||
* @return
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
abstract TypeFilter getFilter(String expression, ClassLoader classLoader)
|
||||
throws ClassNotFoundException;
|
||||
|
||||
|
||||
/**
|
||||
* Returns the {@link FilterType} for the given type as {@link String}.
|
||||
*
|
||||
* @param typeString
|
||||
* @return
|
||||
* @throws IllegalArgumentException if no {@link FilterType} could be
|
||||
* found for the given argument.
|
||||
*/
|
||||
static FilterType fromString(String typeString) {
|
||||
/**
|
||||
* Returns the {@link FilterType} for the given type as {@link String}.
|
||||
*
|
||||
* @param typeString
|
||||
* @return
|
||||
* @throws IllegalArgumentException if no {@link FilterType} could be
|
||||
* found for the given argument.
|
||||
*/
|
||||
static FilterType fromString(String typeString) {
|
||||
|
||||
for (FilterType filter : FilterType.values()) {
|
||||
if (filter.name().equalsIgnoreCase(typeString)) {
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
for (FilterType filter : FilterType.values()) {
|
||||
if (filter.name().equalsIgnoreCase(typeString)) {
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unsupported filter type: "
|
||||
+ typeString);
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported filter type: "
|
||||
+ typeString);
|
||||
}
|
||||
}
|
||||
|
||||
private static enum Type {
|
||||
private static enum Type {
|
||||
|
||||
INCLUDE("include-filter") {
|
||||
INCLUDE("include-filter") {
|
||||
@Override
|
||||
public void addFilter(TypeFilter filter,
|
||||
ClassPathScanningCandidateComponentProvider scanner) {
|
||||
|
||||
@Override
|
||||
public void addFilter(TypeFilter filter,
|
||||
ClassPathScanningCandidateComponentProvider scanner) {
|
||||
scanner.addIncludeFilter(filter);
|
||||
}
|
||||
|
||||
scanner.addIncludeFilter(filter);
|
||||
}
|
||||
},
|
||||
EXCLUDE("exclude-filter") {
|
||||
@Override
|
||||
public void addFilter(TypeFilter filter,
|
||||
ClassPathScanningCandidateComponentProvider scanner) {
|
||||
|
||||
},
|
||||
EXCLUDE("exclude-filter") {
|
||||
scanner.addExcludeFilter(filter);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void addFilter(TypeFilter filter,
|
||||
ClassPathScanningCandidateComponentProvider scanner) {
|
||||
|
||||
scanner.addExcludeFilter(filter);
|
||||
}
|
||||
};
|
||||
|
||||
private String elementName;
|
||||
private String elementName;
|
||||
|
||||
|
||||
private Type(String elementName) {
|
||||
private Type(String elementName) {
|
||||
|
||||
this.elementName = elementName;
|
||||
}
|
||||
this.elementName = elementName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the {@link Element} if the given {@link Node} is an
|
||||
* {@link Element} and it's name equals the one of the type.
|
||||
*
|
||||
* @param node
|
||||
* @return
|
||||
*/
|
||||
Element getElement(Node node) {
|
||||
/**
|
||||
* Returns the {@link Element} if the given {@link Node} is an
|
||||
* {@link Element} and it's name equals the one of the type.
|
||||
*
|
||||
* @param node
|
||||
* @return
|
||||
*/
|
||||
Element getElement(Node node) {
|
||||
|
||||
if (node.getNodeType() == Node.ELEMENT_NODE) {
|
||||
String localName = node.getLocalName();
|
||||
if (elementName.equals(localName)) {
|
||||
return (Element) node;
|
||||
}
|
||||
}
|
||||
if (node.getNodeType() == Node.ELEMENT_NODE) {
|
||||
String localName = node.getLocalName();
|
||||
if (elementName.equals(localName)) {
|
||||
return (Element) node;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
abstract void addFilter(TypeFilter filter,
|
||||
ClassPathScanningCandidateComponentProvider scanner);
|
||||
}
|
||||
abstract void addFilter(TypeFilter filter,
|
||||
ClassPathScanningCandidateComponentProvider scanner);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import java.lang.annotation.Target;
|
||||
/**
|
||||
* Annotation to bind let method parameters be bound to a query via a named
|
||||
* parameter.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Target(ElementType.PARAMETER)
|
||||
@@ -33,5 +33,5 @@ import java.lang.annotation.Target;
|
||||
@Documented
|
||||
public @interface Param {
|
||||
|
||||
String value();
|
||||
String value();
|
||||
}
|
||||
|
||||
@@ -29,198 +29,198 @@ import org.springframework.util.Assert;
|
||||
/**
|
||||
* Class to abstract a single parameter of a query method. It is held in the
|
||||
* context of a {@link Parameters} instance.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public final class Parameter {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static final List<Class<?>> TYPES = Arrays.asList(Pageable.class,
|
||||
Sort.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
static final List<Class<?>> TYPES = Arrays.asList(Pageable.class,
|
||||
Sort.class);
|
||||
|
||||
private static final String PARAM_ON_SPECIAL = format(
|
||||
"You must not user @%s on a parameter typed %s or %s",
|
||||
Param.class.getSimpleName(), Pageable.class.getSimpleName(),
|
||||
Sort.class.getSimpleName());
|
||||
private static final String PARAM_ON_SPECIAL = format(
|
||||
"You must not user @%s on a parameter typed %s or %s",
|
||||
Param.class.getSimpleName(), Pageable.class.getSimpleName(),
|
||||
Sort.class.getSimpleName());
|
||||
|
||||
private static final String NAMED_PARAMETER_TEMPLATE = ":%s";
|
||||
private static final String POSITION_PARAMETER_TEMPLATE = "?%s";
|
||||
private static final String NAMED_PARAMETER_TEMPLATE = ":%s";
|
||||
private static final String POSITION_PARAMETER_TEMPLATE = "?%s";
|
||||
|
||||
private final Class<?> type;
|
||||
private final Parameters parameters;
|
||||
private final int index;
|
||||
private final String name;
|
||||
private final Class<?> type;
|
||||
private final Parameters parameters;
|
||||
private final int index;
|
||||
private final String name;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link Parameter} for the given type, {@link Annotation}s,
|
||||
* positioned at the given index inside the given {@link Parameters}.
|
||||
*
|
||||
* @param type
|
||||
* @param parameters
|
||||
* @param index
|
||||
* @param name
|
||||
*/
|
||||
Parameter(Class<?> type, Parameters parameters, int index, String name) {
|
||||
/**
|
||||
* Creates a new {@link Parameter} for the given type, {@link Annotation}s,
|
||||
* positioned at the given index inside the given {@link Parameters}.
|
||||
*
|
||||
* @param type
|
||||
* @param parameters
|
||||
* @param index
|
||||
* @param name
|
||||
*/
|
||||
Parameter(Class<?> type, Parameters parameters, int index, String name) {
|
||||
|
||||
Assert.notNull(type);
|
||||
Assert.notNull(parameters);
|
||||
Assert.notNull(type);
|
||||
Assert.notNull(parameters);
|
||||
|
||||
this.parameters = parameters;
|
||||
this.index = index;
|
||||
this.parameters = parameters;
|
||||
this.index = index;
|
||||
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
|
||||
if (isSpecialParameter() && isNamedParameter()) {
|
||||
throw new IllegalArgumentException(PARAM_ON_SPECIAL);
|
||||
}
|
||||
}
|
||||
if (isSpecialParameter() && isNamedParameter()) {
|
||||
throw new IllegalArgumentException(PARAM_ON_SPECIAL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the {@link Parameter} is the first one.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isFirst() {
|
||||
/**
|
||||
* Returns whether the {@link Parameter} is the first one.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isFirst() {
|
||||
|
||||
return index == 0;
|
||||
}
|
||||
return index == 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the next {@link Parameter} from the surrounding
|
||||
* {@link Parameters}.
|
||||
*
|
||||
* @throws ParameterOutOfBoundsException
|
||||
* @return
|
||||
*/
|
||||
public Parameter getNext() {
|
||||
/**
|
||||
* Returns the next {@link Parameter} from the surrounding
|
||||
* {@link Parameters}.
|
||||
*
|
||||
* @return
|
||||
* @throws ParameterOutOfBoundsException
|
||||
*/
|
||||
public Parameter getNext() {
|
||||
|
||||
return parameters.getParameter(index + 1);
|
||||
}
|
||||
return parameters.getParameter(index + 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the previous {@link Parameter}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Parameter getPrevious() {
|
||||
/**
|
||||
* Returns the previous {@link Parameter}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Parameter getPrevious() {
|
||||
|
||||
return parameters.getParameter(index - 1);
|
||||
}
|
||||
return parameters.getParameter(index - 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the parameter is a special parameter.
|
||||
*
|
||||
* @see #TYPES
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
public boolean isSpecialParameter() {
|
||||
/**
|
||||
* Returns whether the parameter is a special parameter.
|
||||
*
|
||||
* @param index
|
||||
* @return
|
||||
* @see #TYPES
|
||||
*/
|
||||
public boolean isSpecialParameter() {
|
||||
|
||||
return TYPES.contains(type);
|
||||
}
|
||||
return TYPES.contains(type);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the {@link Parameter} is to be bound to a query.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isBindable() {
|
||||
/**
|
||||
* Returns whether the {@link Parameter} is to be bound to a query.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isBindable() {
|
||||
|
||||
return !isSpecialParameter();
|
||||
}
|
||||
return !isSpecialParameter();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the placeholder to be used for the parameter. Can either be a
|
||||
* named one or positional.
|
||||
*
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
public String getPlaceholder() {
|
||||
/**
|
||||
* Returns the placeholder to be used for the parameter. Can either be a
|
||||
* named one or positional.
|
||||
*
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
public String getPlaceholder() {
|
||||
|
||||
if (isNamedParameter()) {
|
||||
return format(NAMED_PARAMETER_TEMPLATE, getName());
|
||||
} else {
|
||||
return format(POSITION_PARAMETER_TEMPLATE, getIndex());
|
||||
}
|
||||
}
|
||||
if (isNamedParameter()) {
|
||||
return format(NAMED_PARAMETER_TEMPLATE, getName());
|
||||
} else {
|
||||
return format(POSITION_PARAMETER_TEMPLATE, getIndex());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the position index the parameter is bound to in the context of
|
||||
* its surrounding {@link Parameters}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getIndex() {
|
||||
/**
|
||||
* Returns the position index the parameter is bound to in the context of
|
||||
* its surrounding {@link Parameters}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getIndex() {
|
||||
|
||||
return index;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the parameter is annotated with {@link Param}.
|
||||
*
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
public boolean isNamedParameter() {
|
||||
/**
|
||||
* Returns whether the parameter is annotated with {@link Param}.
|
||||
*
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
public boolean isNamedParameter() {
|
||||
|
||||
return !isSpecialParameter() && getName() != null;
|
||||
}
|
||||
return !isSpecialParameter() && getName() != null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the name of the parameter (through {@link Param} annotation) or
|
||||
* null if none can be found.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getName() {
|
||||
/**
|
||||
* Returns the name of the parameter (through {@link Param} annotation) or
|
||||
* null if none can be found.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getName() {
|
||||
|
||||
return name;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return format("%s:%s", isNamedParameter() ? getName() : "#" + index,
|
||||
type.getName());
|
||||
}
|
||||
return format("%s:%s", isNamedParameter() ? getName() : "#" + index,
|
||||
type.getName());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the {@link Parameter} is a {@link Pageable} parameter.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isPageable() {
|
||||
/**
|
||||
* Returns whether the {@link Parameter} is a {@link Pageable} parameter.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isPageable() {
|
||||
|
||||
return Pageable.class.isAssignableFrom(type);
|
||||
}
|
||||
return Pageable.class.isAssignableFrom(type);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the {@link Parameter} is a {@link Sort} parameter.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isSort() {
|
||||
/**
|
||||
* Returns whether the {@link Parameter} is a {@link Sort} parameter.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isSort() {
|
||||
|
||||
return Sort.class.isAssignableFrom(type);
|
||||
}
|
||||
return Sort.class.isAssignableFrom(type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,50 +24,50 @@ import org.springframework.data.domain.Sort;
|
||||
/**
|
||||
* Interface to access method parameters. Allows dedicated access to parameters
|
||||
* of special types
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface ParameterAccessor extends Iterable<Object> {
|
||||
|
||||
/**
|
||||
* Returns the {@link Pageable} of the parameters, if available. Returns
|
||||
* {@code null} otherwise.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Pageable getPageable();
|
||||
/**
|
||||
* Returns the {@link Pageable} of the parameters, if available. Returns
|
||||
* {@code null} otherwise.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Pageable getPageable();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the sort instance to be used for query creation. Will use a
|
||||
* {@link Sort} parameter if available or the {@link Sort} contained in a
|
||||
* {@link Pageable} if available. Returns {@code null} if no {@link Sort}
|
||||
* can be found.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Sort getSort();
|
||||
/**
|
||||
* Returns the sort instance to be used for query creation. Will use a
|
||||
* {@link Sort} parameter if available or the {@link Sort} contained in a
|
||||
* {@link Pageable} if available. Returns {@code null} if no {@link Sort}
|
||||
* can be found.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Sort getSort();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the bindable value with the given index. Bindable means, that
|
||||
* {@link Pageable} and {@link Sort} values are skipped without noticed in
|
||||
* the index. For a method signature taking {@link String}, {@link Pageable}
|
||||
* , {@link String}, {@code #getBindableParameter(1)} would return the
|
||||
* second {@link String} value.
|
||||
*
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
Object getBindableValue(int index);
|
||||
/**
|
||||
* Returns the bindable value with the given index. Bindable means, that
|
||||
* {@link Pageable} and {@link Sort} values are skipped without noticed in
|
||||
* the index. For a method signature taking {@link String}, {@link Pageable}
|
||||
* , {@link String}, {@code #getBindableParameter(1)} would return the
|
||||
* second {@link String} value.
|
||||
*
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
Object getBindableValue(int index);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an iterator over all <em>bindable</em> parameters. This means
|
||||
* parameters implementing {@link Pageable} or {@link Sort} will not be
|
||||
* included in this {@link Iterator}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Iterator<Object> iterator();
|
||||
/**
|
||||
* Returns an iterator over all <em>bindable</em> parameters. This means
|
||||
* parameters implementing {@link Pageable} or {@link Sort} will not be
|
||||
* included in this {@link Iterator}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Iterator<Object> iterator();
|
||||
}
|
||||
@@ -18,22 +18,22 @@ package org.springframework.data.repository.query;
|
||||
/**
|
||||
* Exception to be thrown when trying to access a {@link Parameter} with an
|
||||
* invalid index inside a {@link Parameters} instance.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class ParameterOutOfBoundsException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 8433209953653278886L;
|
||||
private static final long serialVersionUID = 8433209953653278886L;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link ParameterOutOfBoundsException} with the given
|
||||
* exception as cause.
|
||||
*
|
||||
* @param cause
|
||||
*/
|
||||
public ParameterOutOfBoundsException(Throwable cause) {
|
||||
/**
|
||||
* Creates a new {@link ParameterOutOfBoundsException} with the given
|
||||
* exception as cause.
|
||||
*
|
||||
* @param cause
|
||||
*/
|
||||
public ParameterOutOfBoundsException(Throwable cause) {
|
||||
|
||||
super(cause);
|
||||
}
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,293 +32,293 @@ import org.springframework.util.Assert;
|
||||
/**
|
||||
* Abstracts method parameters that have to be bound to query parameters or
|
||||
* applied to the query independently.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public final class Parameters implements Iterable<Parameter> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final List<Class<?>> TYPES = Arrays.asList(Pageable.class,
|
||||
Sort.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final List<Class<?>> TYPES = Arrays.asList(Pageable.class,
|
||||
Sort.class);
|
||||
|
||||
private static final String ALL_OR_NOTHING =
|
||||
String.format(
|
||||
"Either use @%s "
|
||||
+ "on all parameters except %s and %s typed once, or none at all!",
|
||||
Param.class.getSimpleName(),
|
||||
Pageable.class.getSimpleName(), Sort.class.getSimpleName());
|
||||
private static final String ALL_OR_NOTHING =
|
||||
String.format(
|
||||
"Either use @%s "
|
||||
+ "on all parameters except %s and %s typed once, or none at all!",
|
||||
Param.class.getSimpleName(),
|
||||
Pageable.class.getSimpleName(), Sort.class.getSimpleName());
|
||||
|
||||
private final int pageableIndex;
|
||||
private final int sortIndex;
|
||||
private final int pageableIndex;
|
||||
private final int sortIndex;
|
||||
|
||||
private final List<Parameter> parameters;
|
||||
private final ParameterNameDiscoverer discoverer =
|
||||
new LocalVariableTableParameterNameDiscoverer();
|
||||
private final List<Parameter> parameters;
|
||||
private final ParameterNameDiscoverer discoverer =
|
||||
new LocalVariableTableParameterNameDiscoverer();
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link Parameters}.
|
||||
*
|
||||
* @param method
|
||||
*/
|
||||
public Parameters(Method method) {
|
||||
/**
|
||||
* Creates a new instance of {@link Parameters}.
|
||||
*
|
||||
* @param method
|
||||
*/
|
||||
public Parameters(Method method) {
|
||||
|
||||
Assert.notNull(method);
|
||||
Assert.notNull(method);
|
||||
|
||||
this.parameters = new ArrayList<Parameter>();
|
||||
this.parameters = new ArrayList<Parameter>();
|
||||
|
||||
List<Class<?>> types = Arrays.asList(method.getParameterTypes());
|
||||
List<Class<?>> types = Arrays.asList(method.getParameterTypes());
|
||||
|
||||
for (int i = 0; i < types.size(); i++) {
|
||||
String name = getParameterName(method, i);
|
||||
parameters.add(new Parameter(types.get(i), this, i, name));
|
||||
}
|
||||
for (int i = 0; i < types.size(); i++) {
|
||||
String name = getParameterName(method, i);
|
||||
parameters.add(new Parameter(types.get(i), this, i, name));
|
||||
}
|
||||
|
||||
this.pageableIndex = types.indexOf(Pageable.class);
|
||||
this.sortIndex = types.indexOf(Sort.class);
|
||||
this.pageableIndex = types.indexOf(Pageable.class);
|
||||
this.sortIndex = types.indexOf(Sort.class);
|
||||
|
||||
assertEitherAllParamAnnotatedOrNone();
|
||||
}
|
||||
assertEitherAllParamAnnotatedOrNone();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the name of the parameter of the given {@link Method} with the
|
||||
* given index. Inspects {@link Param} annotation before falling back to a
|
||||
* {@link ParameterNameDiscoverer}.
|
||||
*
|
||||
* @param method
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
private String getParameterName(Method method, int index) {
|
||||
/**
|
||||
* Returns the name of the parameter of the given {@link Method} with the
|
||||
* given index. Inspects {@link Param} annotation before falling back to a
|
||||
* {@link ParameterNameDiscoverer}.
|
||||
*
|
||||
* @param method
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
private String getParameterName(Method method, int index) {
|
||||
|
||||
for (Annotation annotation : method.getParameterAnnotations()[index]) {
|
||||
if (annotation instanceof Param) {
|
||||
return ((Param) annotation).value();
|
||||
}
|
||||
}
|
||||
for (Annotation annotation : method.getParameterAnnotations()[index]) {
|
||||
if (annotation instanceof Param) {
|
||||
return ((Param) annotation).value();
|
||||
}
|
||||
}
|
||||
|
||||
String[] parameterNames = discoverer.getParameterNames(method);
|
||||
String[] parameterNames = discoverer.getParameterNames(method);
|
||||
|
||||
if (parameterNames != null) {
|
||||
return parameterNames[index];
|
||||
}
|
||||
if (parameterNames != null) {
|
||||
return parameterNames[index];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link Parameters} instance with the given
|
||||
* {@link Parameter}s put into new context.
|
||||
*
|
||||
* @param originals
|
||||
*/
|
||||
private Parameters(List<Parameter> originals) {
|
||||
/**
|
||||
* Creates a new {@link Parameters} instance with the given
|
||||
* {@link Parameter}s put into new context.
|
||||
*
|
||||
* @param originals
|
||||
*/
|
||||
private Parameters(List<Parameter> originals) {
|
||||
|
||||
this.parameters = new ArrayList<Parameter>();
|
||||
this.parameters = new ArrayList<Parameter>();
|
||||
|
||||
int pageableIndexTemp = -1;
|
||||
int sortIndexTemp = -1;
|
||||
|
||||
for (int i = 0; i < originals.size(); i++) {
|
||||
|
||||
Parameter original = originals.get(i);
|
||||
this.parameters.add(original);
|
||||
|
||||
pageableIndexTemp = original.isPageable() ? i : -1;
|
||||
sortIndexTemp = original.isSort() ? i : -1;
|
||||
}
|
||||
int pageableIndexTemp = -1;
|
||||
int sortIndexTemp = -1;
|
||||
|
||||
for (int i = 0; i < originals.size(); i++) {
|
||||
|
||||
Parameter original = originals.get(i);
|
||||
this.parameters.add(original);
|
||||
|
||||
pageableIndexTemp = original.isPageable() ? i : -1;
|
||||
sortIndexTemp = original.isSort() ? i : -1;
|
||||
}
|
||||
|
||||
this.pageableIndex = pageableIndexTemp;
|
||||
this.sortIndex = sortIndexTemp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the method the {@link Parameters} was created for
|
||||
* contains a {@link Pageable} argument.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean hasPageableParameter() {
|
||||
this.pageableIndex = pageableIndexTemp;
|
||||
this.sortIndex = sortIndexTemp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the method the {@link Parameters} was created for
|
||||
* contains a {@link Pageable} argument.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean hasPageableParameter() {
|
||||
|
||||
return pageableIndex != -1;
|
||||
}
|
||||
return pageableIndex != -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the index of the {@link Pageable} {@link Method} parameter if
|
||||
* available. Will return {@literal -1} if there is no {@link Pageable}
|
||||
* argument in the {@link Method}'s parameter list.
|
||||
*
|
||||
* @return the pageableIndex
|
||||
*/
|
||||
public int getPageableIndex() {
|
||||
/**
|
||||
* Returns the index of the {@link Pageable} {@link Method} parameter if
|
||||
* available. Will return {@literal -1} if there is no {@link Pageable}
|
||||
* argument in the {@link Method}'s parameter list.
|
||||
*
|
||||
* @return the pageableIndex
|
||||
*/
|
||||
public int getPageableIndex() {
|
||||
|
||||
return pageableIndex;
|
||||
}
|
||||
return pageableIndex;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the index of the {@link Sort} {@link Method} parameter if
|
||||
* available. Will return {@literal -1} if there is no {@link Sort} argument
|
||||
* in the {@link Method}'s parameter list.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getSortIndex() {
|
||||
|
||||
return sortIndex;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the method the {@link Parameters} was created for
|
||||
* contains a {@link Sort} argument.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean hasSortParameter() {
|
||||
|
||||
return sortIndex != -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the parameter with the given index.
|
||||
*
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
public Parameter getParameter(int index) {
|
||||
|
||||
try {
|
||||
return parameters.get(index);
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
throw new ParameterOutOfBoundsException(e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns the index of the {@link Sort} {@link Method} parameter if
|
||||
* available. Will return {@literal -1} if there is no {@link Sort} argument
|
||||
* in the {@link Method}'s parameter list.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getSortIndex() {
|
||||
|
||||
return sortIndex;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the method the {@link Parameters} was created for
|
||||
* contains a {@link Sort} argument.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean hasSortParameter() {
|
||||
|
||||
return sortIndex != -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the parameter with the given index.
|
||||
*
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
public Parameter getParameter(int index) {
|
||||
|
||||
try {
|
||||
return parameters.get(index);
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
throw new ParameterOutOfBoundsException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether we have a parameter at the given position.
|
||||
*
|
||||
* @param position
|
||||
* @return
|
||||
*/
|
||||
public boolean hasParameterAt(int position) {
|
||||
|
||||
try {
|
||||
return null != getParameter(position);
|
||||
} catch (ParameterOutOfBoundsException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the method signature contains one of the special
|
||||
* parameters ({@link Pageable}, {@link Sort}).
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean hasSpecialParameter() {
|
||||
|
||||
return hasSortParameter() || hasPageableParameter();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of parameters.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getNumberOfParameters() {
|
||||
|
||||
return parameters.size();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a {@link Parameters} instance with effectively all special
|
||||
* parameters removed.
|
||||
*
|
||||
* @see Parameter#TYPES
|
||||
* @see Parameter#isSpecialParameter()
|
||||
* @return
|
||||
*/
|
||||
public Parameters getBindableParameters() {
|
||||
|
||||
List<Parameter> bindables = new ArrayList<Parameter>();
|
||||
|
||||
for (Parameter candidate : this) {
|
||||
|
||||
if (candidate.isBindable()) {
|
||||
bindables.add(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
return new Parameters(bindables);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a bindable parameter with the given index. So for a method with a
|
||||
* signature of {@code (Pageable pageable, String name)} a call to
|
||||
* {@code #getBindableParameter(0)} will return the {@link String}
|
||||
* parameter.
|
||||
*
|
||||
* @param bindableIndex
|
||||
* @return
|
||||
*/
|
||||
public Parameter getBindableParameter(int bindableIndex) {
|
||||
|
||||
return getBindableParameters().getParameter(bindableIndex);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that either all of the non special parameters ({@link Pageable},
|
||||
* {@link Sort}) are annotated with {@link Param} or none of them is.
|
||||
*
|
||||
* @param method
|
||||
*/
|
||||
private void assertEitherAllParamAnnotatedOrNone() {
|
||||
|
||||
boolean nameFound = false;
|
||||
|
||||
for (Parameter parameter : this.getBindableParameters()) {
|
||||
|
||||
if (parameter.isNamedParameter()) {
|
||||
Assert.isTrue(nameFound || parameter.isFirst(), ALL_OR_NOTHING);
|
||||
nameFound = true;
|
||||
} else {
|
||||
Assert.isTrue(!nameFound, ALL_OR_NOTHING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the given type is a bindable parameter.
|
||||
*
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public static boolean isBindable(Class<?> type) {
|
||||
|
||||
return !TYPES.contains(type);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Iterable#iterator()
|
||||
*/
|
||||
public Iterator<Parameter> iterator() {
|
||||
|
||||
return parameters.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether we have a parameter at the given position.
|
||||
*
|
||||
* @param position
|
||||
* @return
|
||||
*/
|
||||
public boolean hasParameterAt(int position) {
|
||||
|
||||
try {
|
||||
return null != getParameter(position);
|
||||
} catch (ParameterOutOfBoundsException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the method signature contains one of the special
|
||||
* parameters ({@link Pageable}, {@link Sort}).
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean hasSpecialParameter() {
|
||||
|
||||
return hasSortParameter() || hasPageableParameter();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of parameters.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getNumberOfParameters() {
|
||||
|
||||
return parameters.size();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a {@link Parameters} instance with effectively all special
|
||||
* parameters removed.
|
||||
*
|
||||
* @return
|
||||
* @see Parameter#TYPES
|
||||
* @see Parameter#isSpecialParameter()
|
||||
*/
|
||||
public Parameters getBindableParameters() {
|
||||
|
||||
List<Parameter> bindables = new ArrayList<Parameter>();
|
||||
|
||||
for (Parameter candidate : this) {
|
||||
|
||||
if (candidate.isBindable()) {
|
||||
bindables.add(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
return new Parameters(bindables);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a bindable parameter with the given index. So for a method with a
|
||||
* signature of {@code (Pageable pageable, String name)} a call to
|
||||
* {@code #getBindableParameter(0)} will return the {@link String}
|
||||
* parameter.
|
||||
*
|
||||
* @param bindableIndex
|
||||
* @return
|
||||
*/
|
||||
public Parameter getBindableParameter(int bindableIndex) {
|
||||
|
||||
return getBindableParameters().getParameter(bindableIndex);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that either all of the non special parameters ({@link Pageable},
|
||||
* {@link Sort}) are annotated with {@link Param} or none of them is.
|
||||
*
|
||||
* @param method
|
||||
*/
|
||||
private void assertEitherAllParamAnnotatedOrNone() {
|
||||
|
||||
boolean nameFound = false;
|
||||
|
||||
for (Parameter parameter : this.getBindableParameters()) {
|
||||
|
||||
if (parameter.isNamedParameter()) {
|
||||
Assert.isTrue(nameFound || parameter.isFirst(), ALL_OR_NOTHING);
|
||||
nameFound = true;
|
||||
} else {
|
||||
Assert.isTrue(!nameFound, ALL_OR_NOTHING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the given type is a bindable parameter.
|
||||
*
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public static boolean isBindable(Class<?> type) {
|
||||
|
||||
return !TYPES.contains(type);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Iterable#iterator()
|
||||
*/
|
||||
public Iterator<Parameter> iterator() {
|
||||
|
||||
return parameters.iterator();
|
||||
}
|
||||
}
|
||||
@@ -25,135 +25,135 @@ import org.springframework.util.Assert;
|
||||
/**
|
||||
* {@link ParameterAccessor} implementation using a {@link Parameters} instance
|
||||
* to find special parameters.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class ParametersParameterAccessor implements ParameterAccessor {
|
||||
|
||||
private final Parameters parameters;
|
||||
private final Object[] values;
|
||||
private final Parameters parameters;
|
||||
private final Object[] values;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link ParametersParameterAccessor}.
|
||||
*
|
||||
* @param parameters
|
||||
* @param values
|
||||
*/
|
||||
public ParametersParameterAccessor(Parameters parameters, Object[] values) {
|
||||
/**
|
||||
* Creates a new {@link ParametersParameterAccessor}.
|
||||
*
|
||||
* @param parameters
|
||||
* @param values
|
||||
*/
|
||||
public ParametersParameterAccessor(Parameters parameters, Object[] values) {
|
||||
|
||||
Assert.notNull(parameters);
|
||||
Assert.notNull(values);
|
||||
Assert.notNull(parameters);
|
||||
Assert.notNull(values);
|
||||
|
||||
Assert.isTrue(parameters.getNumberOfParameters() == values.length,
|
||||
"Invalid number of parameters given!");
|
||||
Assert.isTrue(parameters.getNumberOfParameters() == values.length,
|
||||
"Invalid number of parameters given!");
|
||||
|
||||
this.parameters = parameters;
|
||||
this.values = values.clone();
|
||||
}
|
||||
this.parameters = parameters;
|
||||
this.values = values.clone();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.query.ParameterAccessor#getPageable()
|
||||
*/
|
||||
public Pageable getPageable() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.query.ParameterAccessor#getPageable()
|
||||
*/
|
||||
public Pageable getPageable() {
|
||||
|
||||
if (!parameters.hasPageableParameter()) {
|
||||
return null;
|
||||
}
|
||||
if (!parameters.hasPageableParameter()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (Pageable) values[parameters.getPageableIndex()];
|
||||
}
|
||||
return (Pageable) values[parameters.getPageableIndex()];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.query.ParameterAccessor#getSort()
|
||||
*/
|
||||
public Sort getSort() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.query.ParameterAccessor#getSort()
|
||||
*/
|
||||
public Sort getSort() {
|
||||
|
||||
if (parameters.hasSortParameter()) {
|
||||
return (Sort) values[parameters.getSortIndex()];
|
||||
}
|
||||
if (parameters.hasSortParameter()) {
|
||||
return (Sort) values[parameters.getSortIndex()];
|
||||
}
|
||||
|
||||
if (parameters.hasPageableParameter() && getPageable() != null) {
|
||||
return getPageable().getSort();
|
||||
}
|
||||
if (parameters.hasPageableParameter() && getPageable() != null) {
|
||||
return getPageable().getSort();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.query.ParameterAccessor#getBindableValue
|
||||
* (int)
|
||||
*/
|
||||
public Object getBindableValue(int index) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.query.ParameterAccessor#getBindableValue
|
||||
* (int)
|
||||
*/
|
||||
public Object getBindableValue(int index) {
|
||||
|
||||
return values[parameters.getBindableParameter(index).getIndex()];
|
||||
}
|
||||
return values[parameters.getBindableParameter(index).getIndex()];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.query.ParameterAccessor#iterator()
|
||||
*/
|
||||
public BindableParameterIterator iterator() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.query.ParameterAccessor#iterator()
|
||||
*/
|
||||
public BindableParameterIterator iterator() {
|
||||
|
||||
return new BindableParameterIterator();
|
||||
}
|
||||
return new BindableParameterIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator class to allow traversing all bindable parameters inside the
|
||||
* accessor.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private class BindableParameterIterator implements Iterator<Object> {
|
||||
/**
|
||||
* Iterator class to allow traversing all bindable parameters inside the
|
||||
* accessor.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private class BindableParameterIterator implements Iterator<Object> {
|
||||
|
||||
private int currentIndex = 0;
|
||||
private int currentIndex = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Returns the next bindable parameter.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Object next() {
|
||||
/**
|
||||
* Returns the next bindable parameter.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Object next() {
|
||||
|
||||
return getBindableValue(currentIndex++);
|
||||
}
|
||||
return getBindableValue(currentIndex++);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.util.Iterator#hasNext()
|
||||
*/
|
||||
public boolean hasNext() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.util.Iterator#hasNext()
|
||||
*/
|
||||
public boolean hasNext() {
|
||||
|
||||
return values.length <= currentIndex;
|
||||
}
|
||||
return values.length <= currentIndex;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.util.Iterator#remove()
|
||||
*/
|
||||
public void remove() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.util.Iterator#remove()
|
||||
*/
|
||||
public void remove() {
|
||||
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,68 +18,68 @@ package org.springframework.data.repository.query;
|
||||
/**
|
||||
* Exception to be thrown if a query cannot be created from a
|
||||
* {@link QueryMethod}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public final class QueryCreationException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = -1238456123580L;
|
||||
private static final String MESSAGE_TEMPLATE =
|
||||
"Could not create query for method %s! Could not find property %s on domain class %s.";
|
||||
private static final long serialVersionUID = -1238456123580L;
|
||||
private static final String MESSAGE_TEMPLATE =
|
||||
"Could not create query for method %s! Could not find property %s on domain class %s.";
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link QueryCreationException}.
|
||||
*
|
||||
* @param method
|
||||
*/
|
||||
private QueryCreationException(String message) {
|
||||
/**
|
||||
* Creates a new {@link QueryCreationException}.
|
||||
*
|
||||
* @param method
|
||||
*/
|
||||
private QueryCreationException(String message) {
|
||||
|
||||
super(message);
|
||||
}
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rejects the given domain class property.
|
||||
*
|
||||
* @param method
|
||||
* @param propertyName
|
||||
* @return
|
||||
*/
|
||||
public static QueryCreationException invalidProperty(QueryMethod method,
|
||||
String propertyName) {
|
||||
/**
|
||||
* Rejects the given domain class property.
|
||||
*
|
||||
* @param method
|
||||
* @param propertyName
|
||||
* @return
|
||||
*/
|
||||
public static QueryCreationException invalidProperty(QueryMethod method,
|
||||
String propertyName) {
|
||||
|
||||
return new QueryCreationException(String.format(MESSAGE_TEMPLATE,
|
||||
method, propertyName, method.getDomainClass().getName()));
|
||||
}
|
||||
return new QueryCreationException(String.format(MESSAGE_TEMPLATE,
|
||||
method, propertyName, method.getDomainClass().getName()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link QueryCreationException}.
|
||||
*
|
||||
* @param method
|
||||
* @param message
|
||||
* @return
|
||||
*/
|
||||
public static QueryCreationException create(QueryMethod method,
|
||||
String message) {
|
||||
/**
|
||||
* Creates a new {@link QueryCreationException}.
|
||||
*
|
||||
* @param method
|
||||
* @param message
|
||||
* @return
|
||||
*/
|
||||
public static QueryCreationException create(QueryMethod method,
|
||||
String message) {
|
||||
|
||||
return new QueryCreationException(String.format(
|
||||
"Could not create query for %s! Reason: %s", method, message));
|
||||
}
|
||||
return new QueryCreationException(String.format(
|
||||
"Could not create query for %s! Reason: %s", method, message));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link QueryCreationException} for the given
|
||||
* {@link QueryMethod} and {@link Throwable} as cause.
|
||||
*
|
||||
* @param method
|
||||
* @param cause
|
||||
* @return
|
||||
*/
|
||||
public static QueryCreationException create(QueryMethod method,
|
||||
Throwable cause) {
|
||||
/**
|
||||
* Creates a new {@link QueryCreationException} for the given
|
||||
* {@link QueryMethod} and {@link Throwable} as cause.
|
||||
*
|
||||
* @param method
|
||||
* @param cause
|
||||
* @return
|
||||
*/
|
||||
public static QueryCreationException create(QueryMethod method,
|
||||
Throwable cause) {
|
||||
|
||||
return create(method, cause.getMessage());
|
||||
}
|
||||
return create(method, cause.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,39 +24,39 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Strategy interface for which way to lookup {@link RepositoryQuery}s.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface QueryLookupStrategy {
|
||||
|
||||
public static enum Key {
|
||||
public static enum Key {
|
||||
|
||||
CREATE, USE_DECLARED_QUERY, CREATE_IF_NOT_FOUND;
|
||||
CREATE, USE_DECLARED_QUERY, CREATE_IF_NOT_FOUND;
|
||||
|
||||
/**
|
||||
* Returns a strategy key from the given XML value.
|
||||
*
|
||||
* @param xml
|
||||
* @return a strategy key from the given XML value
|
||||
*/
|
||||
public static Key create(String xml) {
|
||||
/**
|
||||
* Returns a strategy key from the given XML value.
|
||||
*
|
||||
* @param xml
|
||||
* @return a strategy key from the given XML value
|
||||
*/
|
||||
public static Key create(String xml) {
|
||||
|
||||
if (!StringUtils.hasText(xml)) {
|
||||
return null;
|
||||
}
|
||||
if (!StringUtils.hasText(xml)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return valueOf(xml.toUpperCase(Locale.US).replace("-", "_"));
|
||||
}
|
||||
}
|
||||
return valueOf(xml.toUpperCase(Locale.US).replace("-", "_"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolves a {@link RepositoryQuery} from the given {@link QueryMethod}
|
||||
* that can be executed afterwards.
|
||||
*
|
||||
* @param method
|
||||
* @param metadata
|
||||
* @return
|
||||
*/
|
||||
RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata);
|
||||
/**
|
||||
* Resolves a {@link RepositoryQuery} from the given {@link QueryMethod}
|
||||
* that can be executed afterwards.
|
||||
*
|
||||
* @param method
|
||||
* @param metadata
|
||||
* @return
|
||||
*/
|
||||
RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata);
|
||||
}
|
||||
@@ -33,159 +33,159 @@ import org.springframework.util.Assert;
|
||||
* Abstraction of a method that is designated to execute a finder query.
|
||||
* Enriches the standard {@link Method} interface with specific information that
|
||||
* is necessary to construct {@link RepositoryQuery}s for the method.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class QueryMethod {
|
||||
|
||||
public static enum Type {
|
||||
public static enum Type {
|
||||
|
||||
SINGLE_ENTITY, PAGING, COLLECTION, MODIFYING;
|
||||
}
|
||||
SINGLE_ENTITY, PAGING, COLLECTION, MODIFYING;
|
||||
}
|
||||
|
||||
private final RepositoryMetadata metadata;
|
||||
private final Method method;
|
||||
private final Parameters parameters;
|
||||
private final RepositoryMetadata metadata;
|
||||
private final Method method;
|
||||
private final Parameters parameters;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link QueryMethod} from the given parameters. Looks up the
|
||||
* correct query to use for following invocations of the method given.
|
||||
*
|
||||
* @param method must not be {@literal null}
|
||||
*/
|
||||
public QueryMethod(Method method, RepositoryMetadata metadata) {
|
||||
/**
|
||||
* Creates a new {@link QueryMethod} from the given parameters. Looks up the
|
||||
* correct query to use for following invocations of the method given.
|
||||
*
|
||||
* @param method must not be {@literal null}
|
||||
*/
|
||||
public QueryMethod(Method method, RepositoryMetadata metadata) {
|
||||
|
||||
Assert.notNull(method, "Method must not be null!");
|
||||
Assert.notNull(method, "Method must not be null!");
|
||||
|
||||
for (Class<?> type : Parameters.TYPES) {
|
||||
if (getNumberOfOccurences(method, type) > 1) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Method must only one argument of type %s!",
|
||||
type.getSimpleName()));
|
||||
}
|
||||
}
|
||||
for (Class<?> type : Parameters.TYPES) {
|
||||
if (getNumberOfOccurences(method, type) > 1) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Method must only one argument of type %s!",
|
||||
type.getSimpleName()));
|
||||
}
|
||||
}
|
||||
|
||||
if (hasParameterOfType(method, Pageable.class)) {
|
||||
assertReturnType(method, Page.class, List.class);
|
||||
if (hasParameterOfType(method, Sort.class)) {
|
||||
throw new IllegalStateException(
|
||||
"Method must not have Pageable *and* Sort parameter. "
|
||||
+ "Use sorting capabilities on Pageble instead!");
|
||||
}
|
||||
}
|
||||
if (hasParameterOfType(method, Pageable.class)) {
|
||||
assertReturnType(method, Page.class, List.class);
|
||||
if (hasParameterOfType(method, Sort.class)) {
|
||||
throw new IllegalStateException(
|
||||
"Method must not have Pageable *and* Sort parameter. "
|
||||
+ "Use sorting capabilities on Pageble instead!");
|
||||
}
|
||||
}
|
||||
|
||||
this.method = method;
|
||||
this.parameters = new Parameters(method);
|
||||
this.metadata = metadata;
|
||||
}
|
||||
this.method = method;
|
||||
this.parameters = new Parameters(method);
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the method's name.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getName() {
|
||||
/**
|
||||
* Returns the method's name.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getName() {
|
||||
|
||||
return method.getName();
|
||||
}
|
||||
return method.getName();
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public EntityMetadata<?> getEntityInformation() {
|
||||
@SuppressWarnings("rawtypes")
|
||||
public EntityMetadata<?> getEntityInformation() {
|
||||
|
||||
return new EntityMetadata() {
|
||||
return new EntityMetadata() {
|
||||
|
||||
public Class<?> getJavaType() {
|
||||
public Class<?> getJavaType() {
|
||||
|
||||
return getDomainClass();
|
||||
}
|
||||
};
|
||||
}
|
||||
return getDomainClass();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
protected Class<?> getDomainClass() {
|
||||
|
||||
Class<?> repositoryDomainClass = metadata.getDomainClass();
|
||||
Class<?> methodDomainClass = ClassUtils.getReturnedDomainClass(method);
|
||||
protected Class<?> getDomainClass() {
|
||||
|
||||
return repositoryDomainClass == null || repositoryDomainClass.isAssignableFrom(methodDomainClass) ? methodDomainClass
|
||||
: repositoryDomainClass;
|
||||
}
|
||||
Class<?> repositoryDomainClass = metadata.getDomainClass();
|
||||
Class<?> methodDomainClass = ClassUtils.getReturnedDomainClass(method);
|
||||
|
||||
return repositoryDomainClass == null || repositoryDomainClass.isAssignableFrom(methodDomainClass) ? methodDomainClass
|
||||
: repositoryDomainClass;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the finder will actually return a collection of entities
|
||||
* or a single one.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected boolean isCollectionQuery() {
|
||||
/**
|
||||
* Returns whether the finder will actually return a collection of entities
|
||||
* or a single one.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected boolean isCollectionQuery() {
|
||||
|
||||
Class<?> returnType = method.getReturnType();
|
||||
return org.springframework.util.ClassUtils.isAssignable(List.class,
|
||||
returnType);
|
||||
}
|
||||
Class<?> returnType = method.getReturnType();
|
||||
return org.springframework.util.ClassUtils.isAssignable(List.class,
|
||||
returnType);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the finder will return a {@link Page} of results.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected boolean isPageQuery() {
|
||||
/**
|
||||
* Returns whether the finder will return a {@link Page} of results.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected boolean isPageQuery() {
|
||||
|
||||
Class<?> returnType = method.getReturnType();
|
||||
return org.springframework.util.ClassUtils.isAssignable(Page.class,
|
||||
returnType);
|
||||
}
|
||||
Class<?> returnType = method.getReturnType();
|
||||
return org.springframework.util.ClassUtils.isAssignable(Page.class,
|
||||
returnType);
|
||||
}
|
||||
|
||||
|
||||
public Type getType() {
|
||||
public Type getType() {
|
||||
|
||||
if (isModifyingQuery()) {
|
||||
return Type.MODIFYING;
|
||||
}
|
||||
if (isModifyingQuery()) {
|
||||
return Type.MODIFYING;
|
||||
}
|
||||
|
||||
if (isPageQuery()) {
|
||||
return Type.PAGING;
|
||||
}
|
||||
if (isPageQuery()) {
|
||||
return Type.PAGING;
|
||||
}
|
||||
|
||||
if (isCollectionQuery()) {
|
||||
return Type.COLLECTION;
|
||||
}
|
||||
if (isCollectionQuery()) {
|
||||
return Type.COLLECTION;
|
||||
}
|
||||
|
||||
return Type.SINGLE_ENTITY;
|
||||
}
|
||||
return Type.SINGLE_ENTITY;
|
||||
}
|
||||
|
||||
|
||||
protected boolean isModifyingQuery() {
|
||||
protected boolean isModifyingQuery() {
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the {@link Parameters} wrapper to gain additional information
|
||||
* about {@link Method} parameters.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Parameters getParameters() {
|
||||
/**
|
||||
* Returns the {@link Parameters} wrapper to gain additional information
|
||||
* about {@link Method} parameters.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Parameters getParameters() {
|
||||
|
||||
return parameters;
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return method.toString();
|
||||
}
|
||||
return method.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,28 +16,27 @@
|
||||
package org.springframework.data.repository.query;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Interface for a query abstraction.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface RepositoryQuery {
|
||||
|
||||
/**
|
||||
* Executes the {@link RepositoryQuery} with the given parameters.
|
||||
*
|
||||
* @param store
|
||||
* @param parameters
|
||||
* @return
|
||||
*/
|
||||
public Object execute(Object[] parameters);
|
||||
/**
|
||||
* Executes the {@link RepositoryQuery} with the given parameters.
|
||||
*
|
||||
* @param store
|
||||
* @param parameters
|
||||
* @return
|
||||
*/
|
||||
public Object execute(Object[] parameters);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public QueryMethod getQueryMethod();
|
||||
/**
|
||||
* Returns the
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public QueryMethod getQueryMethod();
|
||||
}
|
||||
@@ -27,117 +27,117 @@ import org.springframework.util.Assert;
|
||||
/**
|
||||
* Base class for query creators that create criteria based queries from a
|
||||
* {@link PartTree}.
|
||||
*
|
||||
*
|
||||
* @param T the actual query type to be created
|
||||
* @param S the intermediate criteria type
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public abstract class AbstractQueryCreator<T, S> {
|
||||
|
||||
private final ParameterAccessor parameters;
|
||||
private final PartTree tree;
|
||||
private final ParameterAccessor parameters;
|
||||
private final PartTree tree;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link AbstractQueryCreator} for the given {@link PartTree}
|
||||
* and {@link ParametersParameterAccessor}.
|
||||
*
|
||||
* @param tree
|
||||
* @param parameters
|
||||
*/
|
||||
public AbstractQueryCreator(PartTree tree, ParameterAccessor parameters) {
|
||||
/**
|
||||
* Creates a new {@link AbstractQueryCreator} for the given {@link PartTree}
|
||||
* and {@link ParametersParameterAccessor}.
|
||||
*
|
||||
* @param tree
|
||||
* @param parameters
|
||||
*/
|
||||
public AbstractQueryCreator(PartTree tree, ParameterAccessor parameters) {
|
||||
|
||||
Assert.notNull(tree);
|
||||
Assert.notNull(parameters);
|
||||
Assert.notNull(tree);
|
||||
Assert.notNull(parameters);
|
||||
|
||||
this.tree = tree;
|
||||
this.parameters = parameters;
|
||||
}
|
||||
this.tree = tree;
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates the actual query object.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public T createQuery() {
|
||||
/**
|
||||
* Creates the actual query object.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public T createQuery() {
|
||||
|
||||
Sort treeSort = tree.getSort();
|
||||
Sort sort = treeSort != null ? treeSort : parameters.getSort();
|
||||
Sort treeSort = tree.getSort();
|
||||
Sort sort = treeSort != null ? treeSort : parameters.getSort();
|
||||
|
||||
return complete(createCriteria(tree), sort);
|
||||
}
|
||||
return complete(createCriteria(tree), sort);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Actual query building logic. Traverses the {@link PartTree} and invokes
|
||||
* callback methods to delegate actual criteria creation and concatenation.
|
||||
*
|
||||
* @param tree
|
||||
* @return
|
||||
*/
|
||||
private S createCriteria(PartTree tree) {
|
||||
/**
|
||||
* Actual query building logic. Traverses the {@link PartTree} and invokes
|
||||
* callback methods to delegate actual criteria creation and concatenation.
|
||||
*
|
||||
* @param tree
|
||||
* @return
|
||||
*/
|
||||
private S createCriteria(PartTree tree) {
|
||||
|
||||
S base = null;
|
||||
Iterator<Object> iterator = parameters.iterator();
|
||||
S base = null;
|
||||
Iterator<Object> iterator = parameters.iterator();
|
||||
|
||||
for (OrPart node : tree) {
|
||||
for (OrPart node : tree) {
|
||||
|
||||
S criteria = null;
|
||||
S criteria = null;
|
||||
|
||||
for (Part part : node) {
|
||||
for (Part part : node) {
|
||||
|
||||
criteria =
|
||||
criteria == null ? create(part, iterator) : and(part,
|
||||
criteria, iterator);
|
||||
}
|
||||
criteria =
|
||||
criteria == null ? create(part, iterator) : and(part,
|
||||
criteria, iterator);
|
||||
}
|
||||
|
||||
base = base == null ? criteria : or(base, criteria);
|
||||
}
|
||||
base = base == null ? criteria : or(base, criteria);
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new atomic instance of the criteria object.
|
||||
*
|
||||
* @param part
|
||||
* @param iterator
|
||||
* @return
|
||||
*/
|
||||
protected abstract S create(Part part, Iterator<Object> iterator);
|
||||
/**
|
||||
* Creates a new atomic instance of the criteria object.
|
||||
*
|
||||
* @param part
|
||||
* @param iterator
|
||||
* @return
|
||||
*/
|
||||
protected abstract S create(Part part, Iterator<Object> iterator);
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new criteria object from the given part and and-concatenates it
|
||||
* to the given base criteria.
|
||||
*
|
||||
* @param part
|
||||
* @param base will never be {@literal null}.
|
||||
* @param iterator
|
||||
* @return
|
||||
*/
|
||||
protected abstract S and(Part part, S base, Iterator<Object> iterator);
|
||||
/**
|
||||
* Creates a new criteria object from the given part and and-concatenates it
|
||||
* to the given base criteria.
|
||||
*
|
||||
* @param part
|
||||
* @param base will never be {@literal null}.
|
||||
* @param iterator
|
||||
* @return
|
||||
*/
|
||||
protected abstract S and(Part part, S base, Iterator<Object> iterator);
|
||||
|
||||
|
||||
/**
|
||||
* Or-concatenates the given base criteria to the given new criteria.
|
||||
*
|
||||
* @param base
|
||||
* @param criteria
|
||||
* @return
|
||||
*/
|
||||
protected abstract S or(S base, S criteria);
|
||||
/**
|
||||
* Or-concatenates the given base criteria to the given new criteria.
|
||||
*
|
||||
* @param base
|
||||
* @param criteria
|
||||
* @return
|
||||
*/
|
||||
protected abstract S or(S base, S criteria);
|
||||
|
||||
|
||||
/**
|
||||
* Actually creates the query object applying the given criteria object and
|
||||
* {@link Sort} definition.
|
||||
*
|
||||
* @param criteria
|
||||
* @param sort
|
||||
* @return
|
||||
*/
|
||||
protected abstract T complete(S criteria, Sort sort);
|
||||
/**
|
||||
* Actually creates the query object applying the given criteria object and
|
||||
* {@link Sort} definition.
|
||||
*
|
||||
* @param criteria
|
||||
* @param sort
|
||||
* @return
|
||||
*/
|
||||
protected abstract T complete(S criteria, Sort sort);
|
||||
}
|
||||
|
||||
@@ -32,69 +32,69 @@ import org.springframework.util.StringUtils;
|
||||
* up multiple properties ending with the sorting direction. So the following
|
||||
* method ends are valid: {@code LastnameUsernameDesc},
|
||||
* {@code LastnameAscUsernameDesc}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class OrderBySource {
|
||||
|
||||
private final String BLOCK_SPLIT = "(?<=Asc|Desc)(?=[A-Z])";
|
||||
private final Pattern DIRECTION_SPLIT = Pattern.compile("(.+)(Asc|Desc)$");
|
||||
private final String BLOCK_SPLIT = "(?<=Asc|Desc)(?=[A-Z])";
|
||||
private final Pattern DIRECTION_SPLIT = Pattern.compile("(.+)(Asc|Desc)$");
|
||||
|
||||
private final List<Order> orders;
|
||||
private final List<Order> orders;
|
||||
|
||||
|
||||
public OrderBySource(String clause) {
|
||||
public OrderBySource(String clause) {
|
||||
|
||||
this(clause, null);
|
||||
}
|
||||
this(clause, null);
|
||||
}
|
||||
|
||||
|
||||
public OrderBySource(String clause, Class<?> domainClass) {
|
||||
public OrderBySource(String clause, Class<?> domainClass) {
|
||||
|
||||
this.orders = new ArrayList<Sort.Order>();
|
||||
this.orders = new ArrayList<Sort.Order>();
|
||||
|
||||
for (String part : clause.split(BLOCK_SPLIT)) {
|
||||
for (String part : clause.split(BLOCK_SPLIT)) {
|
||||
|
||||
Matcher matcher = DIRECTION_SPLIT.matcher(part);
|
||||
Matcher matcher = DIRECTION_SPLIT.matcher(part);
|
||||
|
||||
if (!matcher.find()) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Invalid order syntax for part %s!", part));
|
||||
}
|
||||
if (!matcher.find()) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Invalid order syntax for part %s!", part));
|
||||
}
|
||||
|
||||
Direction direction = Direction.fromString(matcher.group(2));
|
||||
this.orders.add(createOrder(matcher.group(1), direction,
|
||||
domainClass));
|
||||
}
|
||||
}
|
||||
Direction direction = Direction.fromString(matcher.group(2));
|
||||
this.orders.add(createOrder(matcher.group(1), direction,
|
||||
domainClass));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates an {@link Order} instance from the given property source,
|
||||
* direction and domain class. If the domain class is given, we will use it
|
||||
* for nested property traversal checks.
|
||||
*
|
||||
* @see Property#from(String, Class)
|
||||
* @param propertySource
|
||||
* @param direction
|
||||
* @param domainClass can be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private Order createOrder(String propertySource, Direction direction,
|
||||
Class<?> domainClass) {
|
||||
/**
|
||||
* Creates an {@link Order} instance from the given property source,
|
||||
* direction and domain class. If the domain class is given, we will use it
|
||||
* for nested property traversal checks.
|
||||
*
|
||||
* @param propertySource
|
||||
* @param direction
|
||||
* @param domainClass can be {@literal null}.
|
||||
* @return
|
||||
* @see Property#from(String, Class)
|
||||
*/
|
||||
private Order createOrder(String propertySource, Direction direction,
|
||||
Class<?> domainClass) {
|
||||
|
||||
if (null == domainClass) {
|
||||
return new Order(direction,
|
||||
StringUtils.uncapitalize(propertySource));
|
||||
}
|
||||
if (null == domainClass) {
|
||||
return new Order(direction,
|
||||
StringUtils.uncapitalize(propertySource));
|
||||
}
|
||||
|
||||
Property property = Property.from(propertySource, domainClass);
|
||||
return new Order(direction, property.toDotPath());
|
||||
}
|
||||
Property property = Property.from(propertySource, domainClass);
|
||||
return new Order(direction, property.toDotPath());
|
||||
}
|
||||
|
||||
|
||||
public Sort toSort() {
|
||||
public Sort toSort() {
|
||||
|
||||
return this.orders.isEmpty() ? null : new Sort(this.orders);
|
||||
}
|
||||
return this.orders.isEmpty() ? null : new Sort(this.orders);
|
||||
}
|
||||
}
|
||||
@@ -26,256 +26,256 @@ import org.springframework.util.StringUtils;
|
||||
* The actual transformation is defined by a {@link Type} that is determined
|
||||
* from inspecting the given part. The query part can then be looked up via
|
||||
* {@link #getQueryPart()}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class Part {
|
||||
|
||||
private final Property property;
|
||||
private final Part.Type type;
|
||||
private final Property property;
|
||||
private final Part.Type type;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link Part} from the given method name part, the
|
||||
* {@link Class} the part originates from and the start parameter index.
|
||||
*
|
||||
* @param part
|
||||
* @param clazz
|
||||
*/
|
||||
public Part(String part, Class<?> clazz) {
|
||||
/**
|
||||
* Creates a new {@link Part} from the given method name part, the
|
||||
* {@link Class} the part originates from and the start parameter index.
|
||||
*
|
||||
* @param part
|
||||
* @param clazz
|
||||
*/
|
||||
public Part(String part, Class<?> clazz) {
|
||||
|
||||
this.type = Type.fromProperty(part, clazz);
|
||||
this.property = Property.from(type.extractProperty(part), clazz);
|
||||
}
|
||||
this.type = Type.fromProperty(part, clazz);
|
||||
this.property = Property.from(type.extractProperty(part), clazz);
|
||||
}
|
||||
|
||||
|
||||
public boolean getParameterRequired() {
|
||||
public boolean getParameterRequired() {
|
||||
|
||||
return getNumberOfArguments() > 0;
|
||||
}
|
||||
return getNumberOfArguments() > 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns how many method parameters are bound by this part.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getNumberOfArguments() {
|
||||
/**
|
||||
* Returns how many method parameters are bound by this part.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getNumberOfArguments() {
|
||||
|
||||
return type.getNumberOfArguments();
|
||||
}
|
||||
return type.getNumberOfArguments();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the part
|
||||
*/
|
||||
public Property getProperty() {
|
||||
/**
|
||||
* @return the part
|
||||
*/
|
||||
public Property getProperty() {
|
||||
|
||||
return property;
|
||||
}
|
||||
return property;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj == null || !getClass().equals(obj.getClass())) {
|
||||
return false;
|
||||
}
|
||||
if (obj == null || !getClass().equals(obj.getClass())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Part that = (Part) obj;
|
||||
Part that = (Part) obj;
|
||||
|
||||
return this.property.equals(that.property)
|
||||
&& this.type.equals(that.type);
|
||||
}
|
||||
return this.property.equals(that.property)
|
||||
&& this.type.equals(that.type);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = 37;
|
||||
result += 17 * property.hashCode();
|
||||
result += 17 * type.hashCode();
|
||||
return result;
|
||||
}
|
||||
int result = 37;
|
||||
result += 17 * property.hashCode();
|
||||
result += 17 * type.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return String.format("%s %s", property.getName(), type);
|
||||
}
|
||||
return String.format("%s %s", property.getName(), type);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the type
|
||||
*/
|
||||
public Part.Type getType() {
|
||||
/**
|
||||
* @return the type
|
||||
*/
|
||||
public Part.Type getType() {
|
||||
|
||||
return type;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of a method name part. Used to create query parts in various
|
||||
* ways.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public static enum Type {
|
||||
/**
|
||||
* The type of a method name part. Used to create query parts in various
|
||||
* ways.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public static enum Type {
|
||||
|
||||
BETWEEN(2, "Between"),
|
||||
BETWEEN(2, "Between"),
|
||||
|
||||
IS_NOT_NULL(0, "IsNotNull", "NotNull"),
|
||||
IS_NOT_NULL(0, "IsNotNull", "NotNull"),
|
||||
|
||||
IS_NULL(0, "IsNull", "Null"),
|
||||
IS_NULL(0, "IsNull", "Null"),
|
||||
|
||||
LESS_THAN("LessThan"),
|
||||
LESS_THAN("LessThan"),
|
||||
|
||||
GREATER_THAN("GreaterThan"),
|
||||
GREATER_THAN("GreaterThan"),
|
||||
|
||||
NOT_LIKE("NotLike"),
|
||||
NOT_LIKE("NotLike"),
|
||||
|
||||
LIKE("Like"),
|
||||
LIKE("Like"),
|
||||
|
||||
NOT_IN("NotIn"),
|
||||
NOT_IN("NotIn"),
|
||||
|
||||
IN("In"),
|
||||
|
||||
NEAR("Near"),
|
||||
|
||||
WITHIN("Within"),
|
||||
IN("In"),
|
||||
|
||||
NEGATING_SIMPLE_PROPERTY("Not"),
|
||||
|
||||
SIMPLE_PROPERTY;
|
||||
NEAR("Near"),
|
||||
|
||||
// Need to list them again explicitly as the order is important
|
||||
// (esp. for IS_NULL, IS_NOT_NULL)
|
||||
private static final List<Part.Type> ALL = Arrays.asList(IS_NOT_NULL,
|
||||
IS_NULL, BETWEEN, LESS_THAN, GREATER_THAN, NOT_LIKE, LIKE,
|
||||
NOT_IN, IN, NEAR, WITHIN, NEGATING_SIMPLE_PROPERTY, SIMPLE_PROPERTY);
|
||||
private List<String> keywords;
|
||||
private int numberOfArguments;
|
||||
WITHIN("Within"),
|
||||
|
||||
NEGATING_SIMPLE_PROPERTY("Not"),
|
||||
|
||||
SIMPLE_PROPERTY;
|
||||
|
||||
// Need to list them again explicitly as the order is important
|
||||
// (esp. for IS_NULL, IS_NOT_NULL)
|
||||
private static final List<Part.Type> ALL = Arrays.asList(IS_NOT_NULL,
|
||||
IS_NULL, BETWEEN, LESS_THAN, GREATER_THAN, NOT_LIKE, LIKE,
|
||||
NOT_IN, IN, NEAR, WITHIN, NEGATING_SIMPLE_PROPERTY, SIMPLE_PROPERTY);
|
||||
private List<String> keywords;
|
||||
private int numberOfArguments;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link Type} using the given keyword, number of
|
||||
* arguments to be bound and operator. Keyword and operator can be
|
||||
* {@literal null}.
|
||||
*
|
||||
* @param operator
|
||||
* @param numberOfArguments
|
||||
* @param keywords
|
||||
*/
|
||||
private Type(int numberOfArguments, String... keywords) {
|
||||
/**
|
||||
* Creates a new {@link Type} using the given keyword, number of
|
||||
* arguments to be bound and operator. Keyword and operator can be
|
||||
* {@literal null}.
|
||||
*
|
||||
* @param operator
|
||||
* @param numberOfArguments
|
||||
* @param keywords
|
||||
*/
|
||||
private Type(int numberOfArguments, String... keywords) {
|
||||
|
||||
this.numberOfArguments = numberOfArguments;
|
||||
this.keywords = Arrays.asList(keywords);
|
||||
}
|
||||
this.numberOfArguments = numberOfArguments;
|
||||
this.keywords = Arrays.asList(keywords);
|
||||
}
|
||||
|
||||
|
||||
private Type(String... keywords) {
|
||||
private Type(String... keywords) {
|
||||
|
||||
this(1, keywords);
|
||||
}
|
||||
this(1, keywords);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the {@link Type} of the {@link Part} for the given raw
|
||||
* property and the given {@link Class}. This will try to detect e.g.
|
||||
* keywords contained in the raw property that trigger special query
|
||||
* creation. Returns {@link #SIMPLE_PROPERTY} by default.
|
||||
*
|
||||
* @param rawProperty
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
public static Part.Type fromProperty(String rawProperty, Class<?> clazz) {
|
||||
/**
|
||||
* Returns the {@link Type} of the {@link Part} for the given raw
|
||||
* property and the given {@link Class}. This will try to detect e.g.
|
||||
* keywords contained in the raw property that trigger special query
|
||||
* creation. Returns {@link #SIMPLE_PROPERTY} by default.
|
||||
*
|
||||
* @param rawProperty
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
public static Part.Type fromProperty(String rawProperty, Class<?> clazz) {
|
||||
|
||||
for (Part.Type type : ALL) {
|
||||
if (type.supports(rawProperty, clazz)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
for (Part.Type type : ALL) {
|
||||
if (type.supports(rawProperty, clazz)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return SIMPLE_PROPERTY;
|
||||
}
|
||||
return SIMPLE_PROPERTY;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the the type supports the given raw property. Default
|
||||
* implementation checks whether the property ends with the registered
|
||||
* keyword. Does not support the keyword if the property is a valid
|
||||
* field as is.
|
||||
*
|
||||
* @param property
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
protected boolean supports(String property, Class<?> clazz) {
|
||||
/**
|
||||
* Returns whether the the type supports the given raw property. Default
|
||||
* implementation checks whether the property ends with the registered
|
||||
* keyword. Does not support the keyword if the property is a valid
|
||||
* field as is.
|
||||
*
|
||||
* @param property
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
protected boolean supports(String property, Class<?> clazz) {
|
||||
|
||||
if (keywords == null) {
|
||||
return true;
|
||||
}
|
||||
if (keywords == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (String keyword : keywords) {
|
||||
if (property.endsWith(keyword)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (String keyword : keywords) {
|
||||
if (property.endsWith(keyword)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of arguments the property binds. By default this
|
||||
* exactly one argument.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getNumberOfArguments() {
|
||||
/**
|
||||
* Returns the number of arguments the property binds. By default this
|
||||
* exactly one argument.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getNumberOfArguments() {
|
||||
|
||||
return numberOfArguments;
|
||||
}
|
||||
return numberOfArguments;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callback method to extract the actual property to be bound from the
|
||||
* given part. Strips the keyword from the part's end if available.
|
||||
*
|
||||
* @param part
|
||||
* @return
|
||||
*/
|
||||
public String extractProperty(String part) {
|
||||
/**
|
||||
* Callback method to extract the actual property to be bound from the
|
||||
* given part. Strips the keyword from the part's end if available.
|
||||
*
|
||||
* @param part
|
||||
* @return
|
||||
*/
|
||||
public String extractProperty(String part) {
|
||||
|
||||
String candidate = StringUtils.uncapitalize(part);
|
||||
String candidate = StringUtils.uncapitalize(part);
|
||||
|
||||
for (String keyword : keywords) {
|
||||
if (candidate.endsWith(keyword)) {
|
||||
return candidate.substring(0, candidate.indexOf(keyword));
|
||||
}
|
||||
}
|
||||
for (String keyword : keywords) {
|
||||
if (candidate.endsWith(keyword)) {
|
||||
return candidate.substring(0, candidate.indexOf(keyword));
|
||||
}
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,234 +36,234 @@ import org.springframework.util.StringUtils;
|
||||
* validate that each of the {@link Part}s are refering to a property of the
|
||||
* domain class. The {@link PartTree} can then be used to build queries based on
|
||||
* its API instead of parsing the method name for each query execution.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class PartTree implements Iterable<OrPart> {
|
||||
|
||||
private static final String ORDER_BY = "OrderBy";
|
||||
private static final String KEYWORD_TEMPLATE = "(%s)(?=[A-Z])";
|
||||
private static final String DISTINCT = "Distinct";
|
||||
private static final String ORDER_BY = "OrderBy";
|
||||
private static final String KEYWORD_TEMPLATE = "(%s)(?=[A-Z])";
|
||||
private static final String DISTINCT = "Distinct";
|
||||
|
||||
private static final Pattern PREFIX_TEMPLATE = Pattern
|
||||
.compile("^(find|read|get)(\\p{Upper}.*?)??By");
|
||||
private static final Pattern PREFIX_TEMPLATE = Pattern
|
||||
.compile("^(find|read|get)(\\p{Upper}.*?)??By");
|
||||
|
||||
private final boolean distinct;
|
||||
private final OrderBySource orderBySource;
|
||||
private final List<OrPart> nodes = new ArrayList<PartTree.OrPart>();
|
||||
private final boolean distinct;
|
||||
private final OrderBySource orderBySource;
|
||||
private final List<OrPart> nodes = new ArrayList<PartTree.OrPart>();
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link PartTree} by parsing the given {@link String}
|
||||
*
|
||||
* @param source the {@link String} to parse
|
||||
* @param domainClass the domain class to check indiviual parts against to
|
||||
* ensure they refer to a property of the class
|
||||
*/
|
||||
public PartTree(String source, Class<?> domainClass) {
|
||||
/**
|
||||
* Creates a new {@link PartTree} by parsing the given {@link String}
|
||||
*
|
||||
* @param source the {@link String} to parse
|
||||
* @param domainClass the domain class to check indiviual parts against to
|
||||
* ensure they refer to a property of the class
|
||||
*/
|
||||
public PartTree(String source, Class<?> domainClass) {
|
||||
|
||||
Assert.notNull(source);
|
||||
Assert.notNull(domainClass);
|
||||
Assert.notNull(source);
|
||||
Assert.notNull(domainClass);
|
||||
|
||||
this.distinct = detectDistinct(source);
|
||||
String foo = strip(source);
|
||||
String[] parts = split(foo, ORDER_BY);
|
||||
this.distinct = detectDistinct(source);
|
||||
String foo = strip(source);
|
||||
String[] parts = split(foo, ORDER_BY);
|
||||
|
||||
if (parts.length > 2) {
|
||||
throw new IllegalArgumentException(
|
||||
"OrderBy must not be used more than once in a method name!");
|
||||
}
|
||||
if (parts.length > 2) {
|
||||
throw new IllegalArgumentException(
|
||||
"OrderBy must not be used more than once in a method name!");
|
||||
}
|
||||
|
||||
buildTree(parts[0], domainClass);
|
||||
this.orderBySource =
|
||||
parts.length == 2 ? new OrderBySource(parts[1], domainClass)
|
||||
: null;
|
||||
buildTree(parts[0], domainClass);
|
||||
this.orderBySource =
|
||||
parts.length == 2 ? new OrderBySource(parts[1], domainClass)
|
||||
: null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Iterable#iterator()
|
||||
*/
|
||||
public Iterator<OrPart> iterator() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Iterable#iterator()
|
||||
*/
|
||||
public Iterator<OrPart> iterator() {
|
||||
|
||||
return nodes.iterator();
|
||||
}
|
||||
return nodes.iterator();
|
||||
}
|
||||
|
||||
|
||||
private void buildTree(String source, Class<?> domainClass) {
|
||||
private void buildTree(String source, Class<?> domainClass) {
|
||||
|
||||
String[] split = split(source, "Or");
|
||||
String[] split = split(source, "Or");
|
||||
|
||||
for (String part : split) {
|
||||
nodes.add(new OrPart(part, domainClass));
|
||||
}
|
||||
}
|
||||
for (String part : split) {
|
||||
nodes.add(new OrPart(part, domainClass));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the {@link Sort} specification parsed from the source.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Sort getSort() {
|
||||
/**
|
||||
* Returns the {@link Sort} specification parsed from the source.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Sort getSort() {
|
||||
|
||||
return orderBySource == null ? null : orderBySource.toSort();
|
||||
}
|
||||
return orderBySource == null ? null : orderBySource.toSort();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether we indicate distinct lookup of entities.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isDistinct() {
|
||||
/**
|
||||
* Returns whether we indicate distinct lookup of entities.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isDistinct() {
|
||||
|
||||
return distinct;
|
||||
}
|
||||
return distinct;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an {@link Iterable} of all parts contained in the
|
||||
* {@link PartTree}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Iterable<Part> getParts() {
|
||||
/**
|
||||
* Returns an {@link Iterable} of all parts contained in the
|
||||
* {@link PartTree}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Iterable<Part> getParts() {
|
||||
|
||||
List<Part> result = new ArrayList<Part>();
|
||||
List<Part> result = new ArrayList<Part>();
|
||||
|
||||
for (OrPart orPart : this) {
|
||||
for (Part part : orPart) {
|
||||
result.add(part);
|
||||
}
|
||||
}
|
||||
for (OrPart orPart : this) {
|
||||
for (Part part : orPart) {
|
||||
result.add(part);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Splits the given text at the given keywords. Expects camelcase style to
|
||||
* only match concrete keywords and not derivatives of it.
|
||||
*
|
||||
* @param text
|
||||
* @param keyword
|
||||
* @return
|
||||
*/
|
||||
private static String[] split(String text, String keyword) {
|
||||
/**
|
||||
* Splits the given text at the given keywords. Expects camelcase style to
|
||||
* only match concrete keywords and not derivatives of it.
|
||||
*
|
||||
* @param text
|
||||
* @param keyword
|
||||
* @return
|
||||
*/
|
||||
private static String[] split(String text, String keyword) {
|
||||
|
||||
String regex = format(KEYWORD_TEMPLATE, keyword);
|
||||
String regex = format(KEYWORD_TEMPLATE, keyword);
|
||||
|
||||
Pattern pattern = compile(regex);
|
||||
return pattern.split(text);
|
||||
}
|
||||
Pattern pattern = compile(regex);
|
||||
return pattern.split(text);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Strips a prefix from the given method name if it starts with one of
|
||||
* {@value #PREFIXES}.
|
||||
*
|
||||
* @param methodName
|
||||
* @return
|
||||
*/
|
||||
private String strip(String methodName) {
|
||||
/**
|
||||
* Strips a prefix from the given method name if it starts with one of
|
||||
* {@value #PREFIXES}.
|
||||
*
|
||||
* @param methodName
|
||||
* @return
|
||||
*/
|
||||
private String strip(String methodName) {
|
||||
|
||||
Matcher matcher = PREFIX_TEMPLATE.matcher(methodName);
|
||||
Matcher matcher = PREFIX_TEMPLATE.matcher(methodName);
|
||||
|
||||
if (matcher.find()) {
|
||||
return methodName.substring(matcher.group().length());
|
||||
} else {
|
||||
return methodName;
|
||||
}
|
||||
}
|
||||
if (matcher.find()) {
|
||||
return methodName.substring(matcher.group().length());
|
||||
} else {
|
||||
return methodName;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether the given source string contains the {@link #DISTINCT}
|
||||
* keyword in it's prefix.
|
||||
*
|
||||
* @param source
|
||||
* @return
|
||||
*/
|
||||
private boolean detectDistinct(String source) {
|
||||
/**
|
||||
* Checks whether the given source string contains the {@link #DISTINCT}
|
||||
* keyword in it's prefix.
|
||||
*
|
||||
* @param source
|
||||
* @return
|
||||
*/
|
||||
private boolean detectDistinct(String source) {
|
||||
|
||||
Matcher matcher = PREFIX_TEMPLATE.matcher(source);
|
||||
Matcher matcher = PREFIX_TEMPLATE.matcher(source);
|
||||
|
||||
if (!matcher.find()) {
|
||||
return false;
|
||||
}
|
||||
if (!matcher.find()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String group = matcher.group(2);
|
||||
return group != null && group.contains(DISTINCT);
|
||||
}
|
||||
String group = matcher.group(2);
|
||||
return group != null && group.contains(DISTINCT);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return String.format("%s %s",
|
||||
StringUtils.collectionToDelimitedString(nodes, " or "),
|
||||
orderBySource.toString());
|
||||
}
|
||||
return String.format("%s %s",
|
||||
StringUtils.collectionToDelimitedString(nodes, " or "),
|
||||
orderBySource.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* A part of the parsed source that results from splitting up the resource
|
||||
* ar {@literal Or} keywords. Consists of {@link Part}s that have to be
|
||||
* concatenated by {@literal And}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public static class OrPart implements Iterable<Part> {
|
||||
/**
|
||||
* A part of the parsed source that results from splitting up the resource
|
||||
* ar {@literal Or} keywords. Consists of {@link Part}s that have to be
|
||||
* concatenated by {@literal And}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public static class OrPart implements Iterable<Part> {
|
||||
|
||||
private final List<Part> children = new ArrayList<Part>();
|
||||
private final List<Part> children = new ArrayList<Part>();
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link OrPart}.
|
||||
*
|
||||
* @param source the source to split up into {@literal And} parts in
|
||||
* turn.
|
||||
* @param domainClass the domain class to check the resulting
|
||||
* {@link Part}s against.
|
||||
*/
|
||||
OrPart(String source, Class<?> domainClass) {
|
||||
/**
|
||||
* Creates a new {@link OrPart}.
|
||||
*
|
||||
* @param source the source to split up into {@literal And} parts in
|
||||
* turn.
|
||||
* @param domainClass the domain class to check the resulting
|
||||
* {@link Part}s against.
|
||||
*/
|
||||
OrPart(String source, Class<?> domainClass) {
|
||||
|
||||
String[] split = split(source, "And");
|
||||
String[] split = split(source, "And");
|
||||
|
||||
for (String part : split) {
|
||||
children.add(new Part(part, domainClass));
|
||||
}
|
||||
}
|
||||
for (String part : split) {
|
||||
children.add(new Part(part, domainClass));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return StringUtils.collectionToDelimitedString(children, " and ");
|
||||
}
|
||||
return StringUtils.collectionToDelimitedString(children, " and ");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Iterable#iterator()
|
||||
*/
|
||||
public Iterator<Part> iterator() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Iterable#iterator()
|
||||
*/
|
||||
public Iterator<Part> iterator() {
|
||||
|
||||
return children.iterator();
|
||||
}
|
||||
}
|
||||
return children.iterator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,292 +28,292 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Abstraction of a {@link Property} of a domain class.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class Property {
|
||||
|
||||
private static String ERROR_TEMPLATE = "No property %s found for type %s";
|
||||
private static String ERROR_TEMPLATE = "No property %s found for type %s";
|
||||
|
||||
private final String name;
|
||||
private final TypeInformation<?> type;
|
||||
private final boolean isCollection;
|
||||
private final String name;
|
||||
private final TypeInformation<?> type;
|
||||
private final boolean isCollection;
|
||||
|
||||
private Property next;
|
||||
private Property next;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a leaf property (no nested ones) with the given name inside the
|
||||
* given owning type.
|
||||
*
|
||||
* @param name
|
||||
* @param owningType
|
||||
*/
|
||||
Property(String name, Class<?> owningType) {
|
||||
/**
|
||||
* Creates a leaf property (no nested ones) with the given name inside the
|
||||
* given owning type.
|
||||
*
|
||||
* @param name
|
||||
* @param owningType
|
||||
*/
|
||||
Property(String name, Class<?> owningType) {
|
||||
|
||||
this(name, ClassTypeInformation.from(owningType));
|
||||
}
|
||||
|
||||
Property(String name, TypeInformation<?> owningType) {
|
||||
|
||||
Assert.hasText(name);
|
||||
Assert.notNull(owningType);
|
||||
|
||||
String propertyName = StringUtils.uncapitalize(name);
|
||||
TypeInformation<?> type = owningType.getProperty(propertyName);
|
||||
this(name, ClassTypeInformation.from(owningType));
|
||||
}
|
||||
|
||||
if (type == null) {
|
||||
throw new IllegalArgumentException(String.format(ERROR_TEMPLATE,
|
||||
propertyName, owningType.getType()));
|
||||
}
|
||||
|
||||
this.isCollection = type.isCollectionLike();
|
||||
this.type = getPropertyType(type);
|
||||
this.name = propertyName;
|
||||
}
|
||||
|
||||
private TypeInformation<?> getPropertyType(TypeInformation<?> type) {
|
||||
|
||||
if (type.isCollectionLike()) {
|
||||
return type.getComponentType();
|
||||
}
|
||||
|
||||
if (type.isMap()) {
|
||||
return type.getMapValueType();
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
Property(String name, TypeInformation<?> owningType) {
|
||||
|
||||
Assert.hasText(name);
|
||||
Assert.notNull(owningType);
|
||||
|
||||
String propertyName = StringUtils.uncapitalize(name);
|
||||
TypeInformation<?> type = owningType.getProperty(propertyName);
|
||||
|
||||
if (type == null) {
|
||||
throw new IllegalArgumentException(String.format(ERROR_TEMPLATE,
|
||||
propertyName, owningType.getType()));
|
||||
}
|
||||
|
||||
this.isCollection = type.isCollectionLike();
|
||||
this.type = getPropertyType(type);
|
||||
this.name = propertyName;
|
||||
}
|
||||
|
||||
private TypeInformation<?> getPropertyType(TypeInformation<?> type) {
|
||||
|
||||
if (type.isCollectionLike()) {
|
||||
return type.getComponentType();
|
||||
}
|
||||
|
||||
if (type.isMap()) {
|
||||
return type.getMapValueType();
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a {@link Property} with the given name inside the given owning
|
||||
* type and tries to resolve the other {@link String} to create nested
|
||||
* properties.
|
||||
*
|
||||
* @param name
|
||||
* @param owningType
|
||||
* @param toTraverse
|
||||
*/
|
||||
Property(String name, TypeInformation<?> owningType, String toTraverse) {
|
||||
/**
|
||||
* Creates a {@link Property} with the given name inside the given owning
|
||||
* type and tries to resolve the other {@link String} to create nested
|
||||
* properties.
|
||||
*
|
||||
* @param name
|
||||
* @param owningType
|
||||
* @param toTraverse
|
||||
*/
|
||||
Property(String name, TypeInformation<?> owningType, String toTraverse) {
|
||||
|
||||
this(name, owningType);
|
||||
this(name, owningType);
|
||||
|
||||
if (StringUtils.hasText(toTraverse)) {
|
||||
this.next = from(toTraverse, type);
|
||||
}
|
||||
}
|
||||
if (StringUtils.hasText(toTraverse)) {
|
||||
this.next = from(toTraverse, type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the name of the {@link Property}.
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() {
|
||||
/**
|
||||
* Returns the name of the {@link Property}.
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() {
|
||||
|
||||
return name;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the property will return the plain resolved type for
|
||||
* simple properties, the component type for any {@link Iterable} or the
|
||||
* value type of a {@link java.util.Map} if the property is one.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Class<?> getType() {
|
||||
|
||||
|
||||
return this.type.getType();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the next nested {@link Property}.
|
||||
*
|
||||
* @see #hasNext()
|
||||
* @return the next nested {@link Property} or {@literal null} if no nested
|
||||
* {@link Property} available.
|
||||
*/
|
||||
public Property next() {
|
||||
/**
|
||||
* Returns the next nested {@link Property}.
|
||||
*
|
||||
* @return the next nested {@link Property} or {@literal null} if no nested
|
||||
* {@link Property} available.
|
||||
* @see #hasNext()
|
||||
*/
|
||||
public Property next() {
|
||||
|
||||
return next;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether there is a nested {@link Property}. If this returns
|
||||
* {@literal true} you can expect {@link #next()} to return a non-
|
||||
* {@literal null} value.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean hasNext() {
|
||||
/**
|
||||
* Returns whether there is a nested {@link Property}. If this returns
|
||||
* {@literal true} you can expect {@link #next()} to return a non-
|
||||
* {@literal null} value.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean hasNext() {
|
||||
|
||||
return next != null;
|
||||
}
|
||||
return next != null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the {@link Property} path in dot notation.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String toDotPath() {
|
||||
/**
|
||||
* Returns the {@link Property} path in dot notation.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String toDotPath() {
|
||||
|
||||
if (hasNext()) {
|
||||
return getName() + "." + next().toDotPath();
|
||||
}
|
||||
if (hasNext()) {
|
||||
return getName() + "." + next().toDotPath();
|
||||
}
|
||||
|
||||
return getName();
|
||||
}
|
||||
return getName();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the {@link Property} is actually a collection.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isCollection() {
|
||||
/**
|
||||
* Returns whether the {@link Property} is actually a collection.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isCollection() {
|
||||
|
||||
return isCollection;
|
||||
}
|
||||
return isCollection;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj == null || !getClass().equals(obj.getClass())) {
|
||||
return false;
|
||||
}
|
||||
if (obj == null || !getClass().equals(obj.getClass())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Property that = (Property) obj;
|
||||
Property that = (Property) obj;
|
||||
|
||||
return this.name.equals(that.name) && this.type.equals(type);
|
||||
}
|
||||
return this.name.equals(that.name) && this.type.equals(type);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
return name.hashCode() + type.hashCode();
|
||||
}
|
||||
return name.hashCode() + type.hashCode();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extracts the {@link Property} chain from the given source {@link String}
|
||||
* and type.
|
||||
*
|
||||
* @param source
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public static Property from(String source, Class<?> type) {
|
||||
/**
|
||||
* Extracts the {@link Property} chain from the given source {@link String}
|
||||
* and type.
|
||||
*
|
||||
* @param source
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public static Property from(String source, Class<?> type) {
|
||||
|
||||
return from(source, ClassTypeInformation.from(type));
|
||||
}
|
||||
|
||||
private static Property from(String source, TypeInformation<?> type) {
|
||||
|
||||
Iterator<String> parts = Arrays.asList(source.split("_")).iterator();
|
||||
return from(source, ClassTypeInformation.from(type));
|
||||
}
|
||||
|
||||
Property result = null;
|
||||
Property current = null;
|
||||
private static Property from(String source, TypeInformation<?> type) {
|
||||
|
||||
while (parts.hasNext()) {
|
||||
if (result == null) {
|
||||
result = create(parts.next(), type);
|
||||
current = result;
|
||||
} else {
|
||||
current = create(parts.next(), current);
|
||||
}
|
||||
}
|
||||
Iterator<String> parts = Arrays.asList(source.split("_")).iterator();
|
||||
|
||||
return result;
|
||||
}
|
||||
Property result = null;
|
||||
Property current = null;
|
||||
|
||||
while (parts.hasNext()) {
|
||||
if (result == null) {
|
||||
result = create(parts.next(), type);
|
||||
current = result;
|
||||
} else {
|
||||
current = create(parts.next(), current);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link Property} as subordinary of the given
|
||||
* {@link Property}.
|
||||
*
|
||||
* @param source
|
||||
* @param base
|
||||
* @return
|
||||
*/
|
||||
private static Property create(String source, Property base) {
|
||||
/**
|
||||
* Creates a new {@link Property} as subordinary of the given
|
||||
* {@link Property}.
|
||||
*
|
||||
* @param source
|
||||
* @param base
|
||||
* @return
|
||||
*/
|
||||
private static Property create(String source, Property base) {
|
||||
|
||||
Property property = create(source, base.type);
|
||||
base.next = property;
|
||||
return property;
|
||||
}
|
||||
Property property = create(source, base.type);
|
||||
base.next = property;
|
||||
return property;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link Property} for the given
|
||||
* {@link String} and owning type. It will inspect the given source for
|
||||
* camel-case parts and traverse the {@link String} along its parts starting
|
||||
* with the entire one and chewing off parts from the right side then.
|
||||
* Whenever a valid property for the given class is found, the tail will be
|
||||
* traversed for subordinary properties of the just found one and so on.
|
||||
*
|
||||
* @param source
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
private static Property create(String source, TypeInformation<?> type) {
|
||||
/**
|
||||
* Factory method to create a new {@link Property} for the given
|
||||
* {@link String} and owning type. It will inspect the given source for
|
||||
* camel-case parts and traverse the {@link String} along its parts starting
|
||||
* with the entire one and chewing off parts from the right side then.
|
||||
* Whenever a valid property for the given class is found, the tail will be
|
||||
* traversed for subordinary properties of the just found one and so on.
|
||||
*
|
||||
* @param source
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
private static Property create(String source, TypeInformation<?> type) {
|
||||
|
||||
return create(source, type, "");
|
||||
}
|
||||
return create(source, type, "");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tries to look up a chain of {@link Property}s by trying the givne source
|
||||
* first. If that fails it will split the source apart at camel case borders
|
||||
* (starting from the right side) and try to look up a {@link Property} from
|
||||
* the calculated head and recombined new tail and additional tail.
|
||||
*
|
||||
* @param source
|
||||
* @param type
|
||||
* @param addTail
|
||||
* @return
|
||||
*/
|
||||
private static Property create(String source, TypeInformation<?> type, String addTail) {
|
||||
/**
|
||||
* Tries to look up a chain of {@link Property}s by trying the givne source
|
||||
* first. If that fails it will split the source apart at camel case borders
|
||||
* (starting from the right side) and try to look up a {@link Property} from
|
||||
* the calculated head and recombined new tail and additional tail.
|
||||
*
|
||||
* @param source
|
||||
* @param type
|
||||
* @param addTail
|
||||
* @return
|
||||
*/
|
||||
private static Property create(String source, TypeInformation<?> type, String addTail) {
|
||||
|
||||
IllegalArgumentException exception = null;
|
||||
IllegalArgumentException exception = null;
|
||||
|
||||
try {
|
||||
return new Property(source, type, addTail);
|
||||
} catch (IllegalArgumentException e) {
|
||||
exception = e;
|
||||
}
|
||||
try {
|
||||
return new Property(source, type, addTail);
|
||||
} catch (IllegalArgumentException e) {
|
||||
exception = e;
|
||||
}
|
||||
|
||||
Pattern pattern = Pattern.compile("[A-Z]?[a-z]*$");
|
||||
Matcher matcher = pattern.matcher(source);
|
||||
Pattern pattern = Pattern.compile("[A-Z]?[a-z]*$");
|
||||
Matcher matcher = pattern.matcher(source);
|
||||
|
||||
if (matcher.find() && matcher.start() != 0) {
|
||||
if (matcher.find() && matcher.start() != 0) {
|
||||
|
||||
int position = matcher.start();
|
||||
String head = source.substring(0, position);
|
||||
String tail = source.substring(position);
|
||||
int position = matcher.start();
|
||||
String head = source.substring(0, position);
|
||||
String tail = source.substring(position);
|
||||
|
||||
return create(head, type, tail + addTail);
|
||||
}
|
||||
return create(head, type, tail + addTail);
|
||||
}
|
||||
|
||||
throw exception;
|
||||
}
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,50 +23,50 @@ import org.springframework.util.Assert;
|
||||
/**
|
||||
* Base class for implementations of {@link EntityInformation}. Considers an
|
||||
* entity to be new whenever {@link #getId(Object)} returns {@literal null}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public abstract class AbstractEntityInformation<T, ID extends Serializable> implements
|
||||
EntityInformation<T, ID> {
|
||||
EntityInformation<T, ID> {
|
||||
|
||||
private final Class<T> domainClass;
|
||||
private final Class<T> domainClass;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link AbstractEntityInformation} from the given domain
|
||||
* class.
|
||||
*
|
||||
* @param domainClass
|
||||
*/
|
||||
public AbstractEntityInformation(Class<T> domainClass) {
|
||||
/**
|
||||
* Creates a new {@link AbstractEntityInformation} from the given domain
|
||||
* class.
|
||||
*
|
||||
* @param domainClass
|
||||
*/
|
||||
public AbstractEntityInformation(Class<T> domainClass) {
|
||||
|
||||
Assert.notNull(domainClass);
|
||||
this.domainClass = domainClass;
|
||||
}
|
||||
Assert.notNull(domainClass);
|
||||
this.domainClass = domainClass;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.support.IsNewAware#isNew(java.lang
|
||||
* .Object)
|
||||
*/
|
||||
public boolean isNew(T entity) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.support.IsNewAware#isNew(java.lang
|
||||
* .Object)
|
||||
*/
|
||||
public boolean isNew(T entity) {
|
||||
|
||||
return getId(entity) == null;
|
||||
}
|
||||
return getId(entity) == null;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.support.EntityInformation#getJavaType
|
||||
* ()
|
||||
*/
|
||||
public Class<T> getJavaType() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.support.EntityInformation#getJavaType
|
||||
* ()
|
||||
*/
|
||||
public Class<T> getJavaType() {
|
||||
|
||||
return this.domainClass;
|
||||
}
|
||||
return this.domainClass;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,258 +30,258 @@ import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link RepositoryInformation}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
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();
|
||||
@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 Map<Method, Method> methodCache = new ConcurrentHashMap<Method, Method>();
|
||||
|
||||
private final RepositoryMetadata metadata;
|
||||
private final Class<?> repositoryBaseClass;
|
||||
private final RepositoryMetadata metadata;
|
||||
private final Class<?> repositoryBaseClass;
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultRepositoryMetadata} for the given repository interface and repository base class.
|
||||
*
|
||||
* @param repositoryInterface
|
||||
*/
|
||||
public DefaultRepositoryInformation(RepositoryMetadata metadata, Class<?> repositoryBaseClass) {
|
||||
/**
|
||||
* Creates a new {@link DefaultRepositoryMetadata} for the given repository interface and repository base class.
|
||||
*
|
||||
* @param repositoryInterface
|
||||
*/
|
||||
public DefaultRepositoryInformation(RepositoryMetadata metadata, Class<?> repositoryBaseClass) {
|
||||
|
||||
Assert.notNull(metadata);
|
||||
Assert.notNull(repositoryBaseClass);
|
||||
this.metadata = metadata;
|
||||
this.repositoryBaseClass = repositoryBaseClass;
|
||||
}
|
||||
Assert.notNull(metadata);
|
||||
Assert.notNull(repositoryBaseClass);
|
||||
this.metadata = metadata;
|
||||
this.repositoryBaseClass = repositoryBaseClass;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.RepositoryMetadata#getRepositoryInterface()
|
||||
*/
|
||||
public Class<?> getRepositoryInterface() {
|
||||
return metadata.getRepositoryInterface();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.RepositoryMetadata#getDomainClass()
|
||||
*/
|
||||
public Class<?> getDomainClass() {
|
||||
return metadata.getDomainClass();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.RepositoryMetadata#getIdClass()
|
||||
*/
|
||||
public Class<?> getIdClass() {
|
||||
return metadata.getIdClass();
|
||||
}
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.RepositoryMetadata#getRepositoryInterface()
|
||||
*/
|
||||
public Class<?> getRepositoryInterface() {
|
||||
return metadata.getRepositoryInterface();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.repository.support.RepositoryMetadata#
|
||||
* getRepositoryBaseClass()
|
||||
*/
|
||||
public Class<?> getRepositoryBaseClass() {
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.RepositoryMetadata#getDomainClass()
|
||||
*/
|
||||
public Class<?> getDomainClass() {
|
||||
return metadata.getDomainClass();
|
||||
}
|
||||
|
||||
return this.repositoryBaseClass;
|
||||
}
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.RepositoryMetadata#getIdClass()
|
||||
*/
|
||||
public Class<?> getIdClass() {
|
||||
return metadata.getIdClass();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.repository.support.RepositoryMetadata#
|
||||
* getBaseClassMethod(java.lang.reflect.Method)
|
||||
*/
|
||||
public Method getBaseClassMethod(Method method) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.repository.support.RepositoryMetadata#
|
||||
* getRepositoryBaseClass()
|
||||
*/
|
||||
public Class<?> getRepositoryBaseClass() {
|
||||
|
||||
Assert.notNull(method);
|
||||
return this.repositoryBaseClass;
|
||||
}
|
||||
|
||||
Method result = methodCache.get(method);
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.repository.support.RepositoryMetadata#
|
||||
* getBaseClassMethod(java.lang.reflect.Method)
|
||||
*/
|
||||
public Method getBaseClassMethod(Method method) {
|
||||
|
||||
if (null != result) {
|
||||
return result;
|
||||
}
|
||||
Assert.notNull(method);
|
||||
|
||||
result = getBaseClassMethodFor(method);
|
||||
methodCache.put(method, result);
|
||||
Method result = methodCache.get(method);
|
||||
|
||||
return result;
|
||||
}
|
||||
if (null != result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given method is considered to be a repository base class method.
|
||||
*
|
||||
* @param method
|
||||
* @return
|
||||
*/
|
||||
private boolean isBaseClassMethod(Method method) {
|
||||
result = getBaseClassMethodFor(method);
|
||||
methodCache.put(method, result);
|
||||
|
||||
Assert.notNull(method);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (method.getDeclaringClass().isAssignableFrom(repositoryBaseClass)) {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Returns whether the given method is considered to be a repository base class method.
|
||||
*
|
||||
* @param method
|
||||
* @return
|
||||
*/
|
||||
private boolean isBaseClassMethod(Method method) {
|
||||
|
||||
return !method.equals(getBaseClassMethod(method));
|
||||
}
|
||||
Assert.notNull(method);
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.repository.support.RepositoryMetadata#
|
||||
* getFinderMethods()
|
||||
*/
|
||||
public Iterable<Method> getQueryMethods() {
|
||||
if (method.getDeclaringClass().isAssignableFrom(repositoryBaseClass)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Set<Method> result = new HashSet<Method>();
|
||||
return !method.equals(getBaseClassMethod(method));
|
||||
}
|
||||
|
||||
for (Method method : getRepositoryInterface().getDeclaredMethods()) {
|
||||
if (!isCustomMethod(method) && !isBaseClassMethod(method)) {
|
||||
result.add(method);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.repository.support.RepositoryMetadata#
|
||||
* getFinderMethods()
|
||||
*/
|
||||
public Iterable<Method> getQueryMethods() {
|
||||
|
||||
return result;
|
||||
}
|
||||
Set<Method> result = new HashSet<Method>();
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.support.RepositoryMetadata#isCustomMethod
|
||||
* (java.lang.reflect.Method)
|
||||
*/
|
||||
public boolean isCustomMethod(Method method) {
|
||||
for (Method method : getRepositoryInterface().getDeclaredMethods()) {
|
||||
if (!isCustomMethod(method) && !isBaseClassMethod(method)) {
|
||||
result.add(method);
|
||||
}
|
||||
}
|
||||
|
||||
Class<?> declaringClass = method.getDeclaringClass();
|
||||
return result;
|
||||
}
|
||||
|
||||
boolean isQueryMethod = declaringClass.equals(getRepositoryInterface());
|
||||
boolean isRepositoryInterface = isGenericRepositoryInterface(declaringClass);
|
||||
boolean isBaseClassMethod = isBaseClassMethod(method);
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.support.RepositoryMetadata#isCustomMethod
|
||||
* (java.lang.reflect.Method)
|
||||
*/
|
||||
public boolean isCustomMethod(Method method) {
|
||||
|
||||
return !(isRepositoryInterface || isBaseClassMethod || isQueryMethod);
|
||||
}
|
||||
Class<?> declaringClass = method.getDeclaringClass();
|
||||
|
||||
/**
|
||||
* Returns the given base class' method if the given method (declared in the repository interface) was also declared
|
||||
* at the repository base class. Returns the given method if the given base class does not declare the method given.
|
||||
* Takes generics into account.
|
||||
*
|
||||
* @param method
|
||||
* @return
|
||||
*/
|
||||
Method getBaseClassMethodFor(Method method) {
|
||||
boolean isQueryMethod = declaringClass.equals(getRepositoryInterface());
|
||||
boolean isRepositoryInterface = isGenericRepositoryInterface(declaringClass);
|
||||
boolean isBaseClassMethod = isBaseClassMethod(method);
|
||||
|
||||
for (Method baseClassMethod : repositoryBaseClass.getMethods()) {
|
||||
return !(isRepositoryInterface || isBaseClassMethod || isQueryMethod);
|
||||
}
|
||||
|
||||
// Wrong name
|
||||
if (!method.getName().equals(baseClassMethod.getName())) {
|
||||
continue;
|
||||
}
|
||||
/**
|
||||
* Returns the given base class' method if the given method (declared in the repository interface) was also declared
|
||||
* at the repository base class. Returns the given method if the given base class does not declare the method given.
|
||||
* Takes generics into account.
|
||||
*
|
||||
* @param method
|
||||
* @return
|
||||
*/
|
||||
Method getBaseClassMethodFor(Method method) {
|
||||
|
||||
// Wrong number of arguments
|
||||
if (!(method.getParameterTypes().length == baseClassMethod.getParameterTypes().length)) {
|
||||
continue;
|
||||
}
|
||||
for (Method baseClassMethod : repositoryBaseClass.getMethods()) {
|
||||
|
||||
// Check whether all parameters match
|
||||
if (!parametersMatch(method, baseClassMethod)) {
|
||||
continue;
|
||||
}
|
||||
// Wrong name
|
||||
if (!method.getName().equals(baseClassMethod.getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return baseClassMethod;
|
||||
}
|
||||
// Wrong number of arguments
|
||||
if (!(method.getParameterTypes().length == baseClassMethod.getParameterTypes().length)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return method;
|
||||
}
|
||||
// Check whether all parameters match
|
||||
if (!parametersMatch(method, baseClassMethod)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.repository.support.RepositoryMetadata#
|
||||
* hasCustomMethod()
|
||||
*/
|
||||
public boolean hasCustomMethod() {
|
||||
return baseClassMethod;
|
||||
}
|
||||
|
||||
Class<?> repositoryInterface = getRepositoryInterface();
|
||||
return method;
|
||||
}
|
||||
|
||||
// No detection required if no typing interface was configured
|
||||
if (isGenericRepositoryInterface(repositoryInterface)) {
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.repository.support.RepositoryMetadata#
|
||||
* hasCustomMethod()
|
||||
*/
|
||||
public boolean hasCustomMethod() {
|
||||
|
||||
for (Method method : repositoryInterface.getMethods()) {
|
||||
if (isCustomMethod(method) && !isBaseClassMethod(method)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Class<?> repositoryInterface = getRepositoryInterface();
|
||||
|
||||
return false;
|
||||
}
|
||||
// No detection required if no typing interface was configured
|
||||
if (isGenericRepositoryInterface(repositoryInterface)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
for (Method method : repositoryInterface.getMethods()) {
|
||||
if (isCustomMethod(method) && !isBaseClassMethod(method)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Type[] genericTypes = baseClassMethod.getGenericParameterTypes();
|
||||
Class<?>[] types = baseClassMethod.getParameterTypes();
|
||||
Class<?>[] methodParameters = method.getParameterTypes();
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < genericTypes.length; i++) {
|
||||
/**
|
||||
* 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 type = genericTypes[i];
|
||||
Type[] genericTypes = baseClassMethod.getGenericParameterTypes();
|
||||
Class<?>[] types = baseClassMethod.getParameterTypes();
|
||||
Class<?>[] methodParameters = method.getParameterTypes();
|
||||
|
||||
if (type instanceof TypeVariable<?>) {
|
||||
for (int i = 0; i < genericTypes.length; i++) {
|
||||
|
||||
String name = ((TypeVariable<?>) type).getName();
|
||||
Type type = genericTypes[i];
|
||||
|
||||
if (!matchesGenericType(name, methodParameters[i])) {
|
||||
return false;
|
||||
}
|
||||
if (type instanceof TypeVariable<?>) {
|
||||
|
||||
} else {
|
||||
String name = ((TypeVariable<?>) type).getName();
|
||||
|
||||
if (!types[i].equals(methodParameters[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!matchesGenericType(name, methodParameters[i])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
|
||||
/**
|
||||
* 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(String name, Class<?> parameterType) {
|
||||
if (!types[i].equals(methodParameters[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Class<?> entityType = getDomainClass();
|
||||
Class<?> idClass = getIdClass();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ID_TYPE_NAME.equals(name) && parameterType.equals(idClass)) {
|
||||
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(String name, Class<?> parameterType) {
|
||||
|
||||
if (DOMAIN_TYPE_NAME.equals(name) && parameterType.equals(entityType)) {
|
||||
return true;
|
||||
}
|
||||
Class<?> entityType = getDomainClass();
|
||||
Class<?> idClass = getIdClass();
|
||||
|
||||
return false;
|
||||
}
|
||||
if (ID_TYPE_NAME.equals(name) && parameterType.equals(idClass)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DOMAIN_TYPE_NAME.equals(name) && parameterType.equals(entityType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,66 +23,65 @@ import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link RepositoryMetadata}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class DefaultRepositoryMetadata implements RepositoryMetadata {
|
||||
|
||||
private final Class<?> repositoryInterface;
|
||||
private final Class<?> repositoryInterface;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link DefaultRepositoryMetadata} for the given repository
|
||||
* interface and repository base class.
|
||||
*
|
||||
* @param repositoryInterface
|
||||
*/
|
||||
public DefaultRepositoryMetadata(Class<?> repositoryInterface) {
|
||||
/**
|
||||
* Creates a new {@link DefaultRepositoryMetadata} for the given repository
|
||||
* interface and repository base class.
|
||||
*
|
||||
* @param repositoryInterface
|
||||
*/
|
||||
public DefaultRepositoryMetadata(Class<?> repositoryInterface) {
|
||||
|
||||
Assert.notNull(repositoryInterface);
|
||||
this.repositoryInterface = repositoryInterface;
|
||||
}
|
||||
Assert.notNull(repositoryInterface);
|
||||
this.repositoryInterface = repositoryInterface;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.repository.support.RepositoryMetadata#
|
||||
* getRepositoryInterface()
|
||||
*/
|
||||
public Class<?> getRepositoryInterface() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.repository.support.RepositoryMetadata#
|
||||
* getRepositoryInterface()
|
||||
*/
|
||||
public Class<?> getRepositoryInterface() {
|
||||
|
||||
return repositoryInterface;
|
||||
}
|
||||
return repositoryInterface;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.support.RepositoryMetadata#getDomainClass
|
||||
* ()
|
||||
*/
|
||||
public Class<?> getDomainClass() {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.support.RepositoryMetadata#getDomainClass
|
||||
* ()
|
||||
*/
|
||||
public Class<?> getDomainClass() {
|
||||
|
||||
Class<?>[] arguments =
|
||||
resolveTypeArguments(repositoryInterface, Repository.class);
|
||||
return arguments == null ? null : arguments[0];
|
||||
}
|
||||
Class<?>[] arguments =
|
||||
resolveTypeArguments(repositoryInterface, Repository.class);
|
||||
return arguments == null ? null : arguments[0];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.support.RepositoryMetadata#getIdClass
|
||||
* ()
|
||||
*/
|
||||
public Class<?> getIdClass() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.support.RepositoryMetadata#getIdClass
|
||||
* ()
|
||||
*/
|
||||
public Class<?> getIdClass() {
|
||||
|
||||
Class<?>[] arguments =
|
||||
resolveTypeArguments(repositoryInterface, Repository.class);
|
||||
return arguments == null ? null : arguments[1];
|
||||
}
|
||||
Class<?>[] arguments =
|
||||
resolveTypeArguments(repositoryInterface, Repository.class);
|
||||
return arguments == null ? null : arguments[1];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +29,6 @@ import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.ConditionalGenericConverter;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.support.EntityInformation;
|
||||
import org.springframework.data.repository.support.RepositoryFactoryInformation;
|
||||
|
||||
|
||||
/**
|
||||
@@ -39,121 +37,121 @@ import org.springframework.data.repository.support.RepositoryFactoryInformation;
|
||||
* s. The implementation uses a {@link ConversionService} in turn to convert the
|
||||
* source type into the domain class' id type which is then converted into a
|
||||
* domain class object by using a {@link Repository}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class DomainClassConverter implements ConditionalGenericConverter,
|
||||
ApplicationContextAware {
|
||||
ApplicationContextAware {
|
||||
|
||||
private final Map<EntityInformation<?, Serializable>, Repository<?, Serializable>> repositories =
|
||||
new HashMap<EntityInformation<?, Serializable>, Repository<?, Serializable>>();
|
||||
private final ConversionService service;
|
||||
private final Map<EntityInformation<?, Serializable>, Repository<?, Serializable>> repositories =
|
||||
new HashMap<EntityInformation<?, Serializable>, Repository<?, Serializable>>();
|
||||
private final ConversionService service;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link DomainClassConverter}.
|
||||
*
|
||||
* @param service
|
||||
*/
|
||||
public DomainClassConverter(ConversionService service) {
|
||||
/**
|
||||
* Creates a new {@link DomainClassConverter}.
|
||||
*
|
||||
* @param service
|
||||
*/
|
||||
public DomainClassConverter(ConversionService service) {
|
||||
|
||||
this.service = service;
|
||||
}
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.core.convert.converter.GenericConverter#
|
||||
* getConvertibleTypes()
|
||||
*/
|
||||
public Set<ConvertiblePair> getConvertibleTypes() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.core.convert.converter.GenericConverter#
|
||||
* getConvertibleTypes()
|
||||
*/
|
||||
public Set<ConvertiblePair> getConvertibleTypes() {
|
||||
|
||||
return Collections.singleton(new ConvertiblePair(Object.class,
|
||||
Object.class));
|
||||
}
|
||||
return Collections.singleton(new ConvertiblePair(Object.class,
|
||||
Object.class));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.core.convert.converter.GenericConverter#convert(java
|
||||
* .lang.Object, org.springframework.core.convert.TypeDescriptor,
|
||||
* org.springframework.core.convert.TypeDescriptor)
|
||||
*/
|
||||
public Object convert(Object source, TypeDescriptor sourceType,
|
||||
TypeDescriptor targetType) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.core.convert.converter.GenericConverter#convert(java
|
||||
* .lang.Object, org.springframework.core.convert.TypeDescriptor,
|
||||
* org.springframework.core.convert.TypeDescriptor)
|
||||
*/
|
||||
public Object convert(Object source, TypeDescriptor sourceType,
|
||||
TypeDescriptor targetType) {
|
||||
|
||||
EntityInformation<?, Serializable> info =
|
||||
getRepositoryForDomainType(targetType.getType());
|
||||
EntityInformation<?, Serializable> info =
|
||||
getRepositoryForDomainType(targetType.getType());
|
||||
|
||||
Repository<?, Serializable> repository = repositories.get(info);
|
||||
Serializable id = service.convert(source, info.getIdType());
|
||||
return repository.findOne(id);
|
||||
}
|
||||
Repository<?, Serializable> repository = repositories.get(info);
|
||||
Serializable id = service.convert(source, info.getIdType());
|
||||
return repository.findOne(id);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.core.convert.converter.ConditionalGenericConverter
|
||||
* #matches(org.springframework.core.convert.TypeDescriptor,
|
||||
* org.springframework.core.convert.TypeDescriptor)
|
||||
*/
|
||||
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.core.convert.converter.ConditionalGenericConverter
|
||||
* #matches(org.springframework.core.convert.TypeDescriptor,
|
||||
* org.springframework.core.convert.TypeDescriptor)
|
||||
*/
|
||||
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
|
||||
EntityInformation<?, ?> info =
|
||||
getRepositoryForDomainType(targetType.getType());
|
||||
EntityInformation<?, ?> info =
|
||||
getRepositoryForDomainType(targetType.getType());
|
||||
|
||||
if (info == null) {
|
||||
return false;
|
||||
}
|
||||
if (info == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return service.canConvert(sourceType.getType(), info.getIdType());
|
||||
}
|
||||
return service.canConvert(sourceType.getType(), info.getIdType());
|
||||
}
|
||||
|
||||
|
||||
private EntityInformation<?, Serializable> getRepositoryForDomainType(
|
||||
Class<?> domainType) {
|
||||
private EntityInformation<?, Serializable> getRepositoryForDomainType(
|
||||
Class<?> domainType) {
|
||||
|
||||
for (EntityInformation<?, Serializable> information : repositories
|
||||
.keySet()) {
|
||||
for (EntityInformation<?, Serializable> information : repositories
|
||||
.keySet()) {
|
||||
|
||||
if (domainType.equals(information.getJavaType())) {
|
||||
return information;
|
||||
}
|
||||
}
|
||||
if (domainType.equals(information.getJavaType())) {
|
||||
return information;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.context.ApplicationContextAware#setApplicationContext
|
||||
* (org.springframework.context.ApplicationContext)
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void setApplicationContext(ApplicationContext context) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.context.ApplicationContextAware#setApplicationContext
|
||||
* (org.springframework.context.ApplicationContext)
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public void setApplicationContext(ApplicationContext context) {
|
||||
|
||||
Collection<RepositoryFactoryInformation> providers =
|
||||
BeanFactoryUtils.beansOfTypeIncludingAncestors(context,
|
||||
RepositoryFactoryInformation.class).values();
|
||||
Collection<RepositoryFactoryInformation> providers =
|
||||
BeanFactoryUtils.beansOfTypeIncludingAncestors(context,
|
||||
RepositoryFactoryInformation.class).values();
|
||||
|
||||
for (RepositoryFactoryInformation entry : providers) {
|
||||
for (RepositoryFactoryInformation entry : providers) {
|
||||
|
||||
EntityInformation<Object, Serializable> metadata =
|
||||
entry.getEntityInformation();
|
||||
Class<Repository<Object, Serializable>> objectType =
|
||||
entry.getRepositoryInterface();
|
||||
Repository<Object, Serializable> repository =
|
||||
BeanFactoryUtils.beanOfType(context, objectType);
|
||||
EntityInformation<Object, Serializable> metadata =
|
||||
entry.getEntityInformation();
|
||||
Class<Repository<Object, Serializable>> objectType =
|
||||
entry.getRepositoryInterface();
|
||||
Repository<Object, Serializable> repository =
|
||||
BeanFactoryUtils.beanOfType(context, objectType);
|
||||
|
||||
this.repositories.put(metadata, repository);
|
||||
}
|
||||
}
|
||||
this.repositories.put(metadata, repository);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import java.io.Serializable;
|
||||
import org.springframework.beans.PropertyEditorRegistry;
|
||||
import org.springframework.beans.SimpleTypeConverter;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.support.EntityInformation;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -30,151 +29,151 @@ import org.springframework.util.StringUtils;
|
||||
/**
|
||||
* Generic {@link PropertyEditor} to map entities handled by a
|
||||
* {@link Repository} to their id's and vice versa.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class DomainClassPropertyEditor<T, ID extends Serializable> extends
|
||||
PropertyEditorSupport {
|
||||
PropertyEditorSupport {
|
||||
|
||||
private final Repository<T, ID> repository;
|
||||
private final EntityInformation<T, ID> information;
|
||||
private final PropertyEditorRegistry registry;
|
||||
private final Repository<T, ID> repository;
|
||||
private final EntityInformation<T, ID> information;
|
||||
private final PropertyEditorRegistry registry;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link DomainClassPropertyEditor} for the given
|
||||
* {@link Repository}.
|
||||
*
|
||||
* @param repository
|
||||
* @param registry
|
||||
*/
|
||||
public DomainClassPropertyEditor(Repository<T, ID> repository,
|
||||
EntityInformation<T, ID> information,
|
||||
PropertyEditorRegistry registry) {
|
||||
/**
|
||||
* Creates a new {@link DomainClassPropertyEditor} for the given
|
||||
* {@link Repository}.
|
||||
*
|
||||
* @param repository
|
||||
* @param registry
|
||||
*/
|
||||
public DomainClassPropertyEditor(Repository<T, ID> repository,
|
||||
EntityInformation<T, ID> information,
|
||||
PropertyEditorRegistry registry) {
|
||||
|
||||
Assert.notNull(repository);
|
||||
Assert.notNull(registry);
|
||||
Assert.notNull(repository);
|
||||
Assert.notNull(registry);
|
||||
|
||||
this.repository = repository;
|
||||
this.information = information;
|
||||
this.registry = registry;
|
||||
}
|
||||
this.repository = repository;
|
||||
this.information = information;
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.beans.PropertyEditorSupport#setAsText(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void setAsText(String idAsString) throws IllegalArgumentException {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.beans.PropertyEditorSupport#setAsText(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void setAsText(String idAsString) throws IllegalArgumentException {
|
||||
|
||||
if (!StringUtils.hasText(idAsString)) {
|
||||
setValue(null);
|
||||
return;
|
||||
}
|
||||
if (!StringUtils.hasText(idAsString)) {
|
||||
setValue(null);
|
||||
return;
|
||||
}
|
||||
|
||||
setValue(repository.findOne(getId(idAsString)));
|
||||
}
|
||||
setValue(repository.findOne(getId(idAsString)));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.beans.PropertyEditorSupport#getAsText()
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public String getAsText() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.beans.PropertyEditorSupport#getAsText()
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public String getAsText() {
|
||||
|
||||
T entity = (T) getValue();
|
||||
T entity = (T) getValue();
|
||||
|
||||
if (null == entity) {
|
||||
return null;
|
||||
}
|
||||
if (null == entity) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object id = getId(entity);
|
||||
return id == null ? null : id.toString();
|
||||
}
|
||||
Object id = getId(entity);
|
||||
return id == null ? null : id.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Looks up the id of the given entity using one of the
|
||||
* {@link org.synyx.hades.dao.orm.GenericDaoSupport.IdAware} implementations
|
||||
* of Hades.
|
||||
*
|
||||
* @param entity
|
||||
* @return
|
||||
*/
|
||||
private ID getId(T entity) {
|
||||
/**
|
||||
* Looks up the id of the given entity using one of the
|
||||
* {@link org.synyx.hades.dao.orm.GenericDaoSupport.IdAware} implementations
|
||||
* of Hades.
|
||||
*
|
||||
* @param entity
|
||||
* @return
|
||||
*/
|
||||
private ID getId(T entity) {
|
||||
|
||||
return information.getId(entity);
|
||||
}
|
||||
return information.getId(entity);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the actual typed id. Looks up an available customly registered
|
||||
* {@link PropertyEditor} from the {@link PropertyEditorRegistry} before
|
||||
* falling back on a {@link SimpleTypeConverter} to translate the
|
||||
* {@link String} id into the type one.
|
||||
*
|
||||
* @param idAsString
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private ID getId(String idAsString) {
|
||||
/**
|
||||
* Returns the actual typed id. Looks up an available customly registered
|
||||
* {@link PropertyEditor} from the {@link PropertyEditorRegistry} before
|
||||
* falling back on a {@link SimpleTypeConverter} to translate the
|
||||
* {@link String} id into the type one.
|
||||
*
|
||||
* @param idAsString
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private ID getId(String idAsString) {
|
||||
|
||||
Class<ID> idClass = information.getIdType();
|
||||
Class<ID> idClass = information.getIdType();
|
||||
|
||||
PropertyEditor idEditor = registry.findCustomEditor(idClass, null);
|
||||
PropertyEditor idEditor = registry.findCustomEditor(idClass, null);
|
||||
|
||||
if (idEditor != null) {
|
||||
idEditor.setAsText(idAsString);
|
||||
return (ID) idEditor.getValue();
|
||||
}
|
||||
if (idEditor != null) {
|
||||
idEditor.setAsText(idAsString);
|
||||
return (ID) idEditor.getValue();
|
||||
}
|
||||
|
||||
return new SimpleTypeConverter()
|
||||
.convertIfNecessary(idAsString, idClass);
|
||||
}
|
||||
return new SimpleTypeConverter()
|
||||
.convertIfNecessary(idAsString, idClass);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj == null || this.getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
if (obj == null || this.getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DomainClassPropertyEditor<?, ?> that =
|
||||
(DomainClassPropertyEditor<?, ?>) obj;
|
||||
DomainClassPropertyEditor<?, ?> that =
|
||||
(DomainClassPropertyEditor<?, ?>) obj;
|
||||
|
||||
return this.repository.equals(that.repository)
|
||||
&& this.registry.equals(that.registry)
|
||||
&& this.information.equals(that.information);
|
||||
}
|
||||
return this.repository.equals(that.repository)
|
||||
&& this.registry.equals(that.registry)
|
||||
&& this.information.equals(that.information);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int hashCode = 17;
|
||||
hashCode += repository.hashCode() * 32;
|
||||
hashCode += information.hashCode() * 32;
|
||||
hashCode += registry.hashCode() * 32;
|
||||
return hashCode;
|
||||
}
|
||||
int hashCode = 17;
|
||||
hashCode += repository.hashCode() * 32;
|
||||
hashCode += information.hashCode() * 32;
|
||||
hashCode += registry.hashCode() * 32;
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,6 @@ import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.support.EntityInformation;
|
||||
import org.springframework.data.repository.support.RepositoryFactoryInformation;
|
||||
|
||||
|
||||
/**
|
||||
@@ -40,76 +38,76 @@ import org.springframework.data.repository.support.RepositoryFactoryInformation;
|
||||
* {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter}
|
||||
* and register the {@link DomainClassPropertyEditorRegistrar} there: <code>
|
||||
* <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
|
||||
* <property name="webBindingInitializer">
|
||||
* <bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
|
||||
* <property name="propertyEditorRegistrars">
|
||||
* <bean class="org.springframework.data.extensions.beans.DomainClassPropertyEditorRegistrar" />
|
||||
* </property>
|
||||
* </bean>
|
||||
* </property>
|
||||
* <property name="webBindingInitializer">
|
||||
* <bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
|
||||
* <property name="propertyEditorRegistrars">
|
||||
* <bean class="org.springframework.data.extensions.beans.DomainClassPropertyEditorRegistrar" />
|
||||
* </property>
|
||||
* </bean>
|
||||
* </property>
|
||||
* </bean>
|
||||
* </code> Make sure this bean declaration is in the {@link ApplicationContext}
|
||||
* created by the {@link DispatcherServlet} whereas the repositories need to be
|
||||
* declared in the root
|
||||
* {@link org.springframework.web.context.WebApplicationContext}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class DomainClassPropertyEditorRegistrar implements
|
||||
PropertyEditorRegistrar, ApplicationContextAware {
|
||||
PropertyEditorRegistrar, ApplicationContextAware {
|
||||
|
||||
private final Map<EntityInformation<Object, Serializable>, Repository<Object, Serializable>> repositories =
|
||||
new HashMap<EntityInformation<Object, Serializable>, Repository<Object, Serializable>>();
|
||||
private final Map<EntityInformation<Object, Serializable>, Repository<Object, Serializable>> repositories =
|
||||
new HashMap<EntityInformation<Object, Serializable>, Repository<Object, Serializable>>();
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.PropertyEditorRegistrar#registerCustomEditors
|
||||
* (org.springframework.beans.PropertyEditorRegistry)
|
||||
*/
|
||||
public void registerCustomEditors(PropertyEditorRegistry registry) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.PropertyEditorRegistrar#registerCustomEditors
|
||||
* (org.springframework.beans.PropertyEditorRegistry)
|
||||
*/
|
||||
public void registerCustomEditors(PropertyEditorRegistry registry) {
|
||||
|
||||
for (Entry<EntityInformation<Object, Serializable>, Repository<Object, Serializable>> entry : repositories
|
||||
.entrySet()) {
|
||||
for (Entry<EntityInformation<Object, Serializable>, Repository<Object, Serializable>> entry : repositories
|
||||
.entrySet()) {
|
||||
|
||||
EntityInformation<Object, Serializable> metadata = entry.getKey();
|
||||
Repository<Object, Serializable> repository = entry.getValue();
|
||||
EntityInformation<Object, Serializable> metadata = entry.getKey();
|
||||
Repository<Object, Serializable> repository = entry.getValue();
|
||||
|
||||
DomainClassPropertyEditor<Object, Serializable> editor =
|
||||
new DomainClassPropertyEditor<Object, Serializable>(
|
||||
repository, metadata, registry);
|
||||
DomainClassPropertyEditor<Object, Serializable> editor =
|
||||
new DomainClassPropertyEditor<Object, Serializable>(
|
||||
repository, metadata, registry);
|
||||
|
||||
registry.registerCustomEditor(metadata.getJavaType(), editor);
|
||||
}
|
||||
}
|
||||
registry.registerCustomEditor(metadata.getJavaType(), editor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.context.ApplicationContextAware#setApplicationContext
|
||||
* (org.springframework.context.ApplicationContext)
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void setApplicationContext(ApplicationContext context) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.context.ApplicationContextAware#setApplicationContext
|
||||
* (org.springframework.context.ApplicationContext)
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public void setApplicationContext(ApplicationContext context) {
|
||||
|
||||
Collection<RepositoryFactoryInformation> providers =
|
||||
BeanFactoryUtils.beansOfTypeIncludingAncestors(context,
|
||||
RepositoryFactoryInformation.class).values();
|
||||
Collection<RepositoryFactoryInformation> providers =
|
||||
BeanFactoryUtils.beansOfTypeIncludingAncestors(context,
|
||||
RepositoryFactoryInformation.class).values();
|
||||
|
||||
for (RepositoryFactoryInformation information : providers) {
|
||||
for (RepositoryFactoryInformation information : providers) {
|
||||
|
||||
EntityInformation<Object, Serializable> metadata =
|
||||
information.getEntityInformation();
|
||||
Class<Repository<Object, Serializable>> objectType =
|
||||
information.getRepositoryInterface();
|
||||
Repository<Object, Serializable> repository =
|
||||
BeanFactoryUtils.beanOfType(context, objectType);
|
||||
EntityInformation<Object, Serializable> metadata =
|
||||
information.getEntityInformation();
|
||||
Class<Repository<Object, Serializable>> objectType =
|
||||
information.getRepositoryInterface();
|
||||
Repository<Object, Serializable> repository =
|
||||
BeanFactoryUtils.beanOfType(context, objectType);
|
||||
|
||||
this.repositories.put(metadata, repository);
|
||||
}
|
||||
}
|
||||
this.repositories.put(metadata, repository);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,32 +20,32 @@ import java.io.Serializable;
|
||||
/**
|
||||
* Extension of {@link EntityMetadata} to add functionality to query information
|
||||
* of entity instances.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface EntityInformation<T, ID extends Serializable> extends EntityMetadata<T> {
|
||||
|
||||
/**
|
||||
* Returns whether the given entity is considered to be new.
|
||||
*
|
||||
* @param entity must never be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
boolean isNew(T entity);
|
||||
/**
|
||||
* Returns whether the given entity is considered to be new.
|
||||
*
|
||||
* @param entity must never be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
boolean isNew(T entity);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the id of the given entity.
|
||||
*
|
||||
* @param entity must never be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
ID getId(T entity);
|
||||
|
||||
/**
|
||||
* Returns the type of the id of the entity.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<ID> getIdType();
|
||||
/**
|
||||
* Returns the id of the given entity.
|
||||
*
|
||||
* @param entity must never be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
ID getId(T entity);
|
||||
|
||||
/**
|
||||
* Returns the type of the id of the entity.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<ID> getIdType();
|
||||
}
|
||||
|
||||
@@ -17,15 +17,15 @@ package org.springframework.data.repository.support;
|
||||
|
||||
/**
|
||||
* Metadata for entity types.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface EntityMetadata<T> {
|
||||
|
||||
/**
|
||||
* Returns the actual domain class type.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<T> getJavaType();
|
||||
/**
|
||||
* Returns the actual domain class type.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<T> getJavaType();
|
||||
}
|
||||
|
||||
@@ -25,57 +25,57 @@ import org.springframework.data.domain.Persistable;
|
||||
* Implementation of {@link EntityMetadata} that assumes the entity handled
|
||||
* implements {@link Persistable} and uses {@link Persistable#isNew()} for the
|
||||
* {@link #isNew(Object)} check.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class PersistableEntityInformation<T extends Persistable<ID>, ID extends Serializable> extends
|
||||
AbstractEntityInformation<T, ID> {
|
||||
|
||||
private Class<ID> idClass;
|
||||
AbstractEntityInformation<T, ID> {
|
||||
|
||||
/**
|
||||
* Creates a new {@link PersistableEntityInformation}.
|
||||
*
|
||||
* @param domainClass
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public PersistableEntityInformation(Class<T> domainClass) {
|
||||
private Class<ID> idClass;
|
||||
|
||||
super(domainClass);
|
||||
this.idClass = (Class<ID>) GenericTypeResolver.resolveTypeArgument(domainClass, Persistable.class);
|
||||
}
|
||||
/**
|
||||
* Creates a new {@link PersistableEntityInformation}.
|
||||
*
|
||||
* @param domainClass
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public PersistableEntityInformation(Class<T> domainClass) {
|
||||
|
||||
super(domainClass);
|
||||
this.idClass = (Class<ID>) GenericTypeResolver.resolveTypeArgument(domainClass, Persistable.class);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.support.IsNewAware#isNew(java.lang
|
||||
* .Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean isNew(T entity) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.support.IsNewAware#isNew(java.lang
|
||||
* .Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean isNew(T entity) {
|
||||
|
||||
return entity.isNew();
|
||||
}
|
||||
return entity.isNew();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.support.IdAware#getId(java.lang.Object
|
||||
* )
|
||||
*/
|
||||
public ID getId(T entity) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.support.IdAware#getId(java.lang.Object
|
||||
* )
|
||||
*/
|
||||
public ID getId(T entity) {
|
||||
|
||||
return entity.getId();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.EntityInformation#getIdType()
|
||||
*/
|
||||
public Class<ID> getIdType() {
|
||||
return this.idClass;
|
||||
}
|
||||
return entity.getId();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.EntityInformation#getIdType()
|
||||
*/
|
||||
public Class<ID> getIdType() {
|
||||
return this.idClass;
|
||||
}
|
||||
}
|
||||
@@ -21,15 +21,15 @@ import org.springframework.data.repository.query.RepositoryQuery;
|
||||
/**
|
||||
* Callback for listeners that want to execute functionality on
|
||||
* {@link RepositoryQuery} creation.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface QueryCreationListener<T extends RepositoryQuery> {
|
||||
|
||||
/**
|
||||
* Will be invoked just after the {@link RepositoryQuery} was created.
|
||||
*
|
||||
* @param query
|
||||
*/
|
||||
void onCreation(T query);
|
||||
/**
|
||||
* Will be invoked just after the {@link RepositoryQuery} was created.
|
||||
*
|
||||
* @param query
|
||||
*/
|
||||
void onCreation(T query);
|
||||
}
|
||||
|
||||
@@ -29,126 +29,126 @@ import org.springframework.util.Assert;
|
||||
/**
|
||||
* Adapter for Springs {@link FactoryBean} interface to allow easy setup of
|
||||
* repository factories via Spring configuration.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*
|
||||
* @param <T> the type of the repository
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>, S, ID extends Serializable>
|
||||
implements InitializingBean, RepositoryFactoryInformation<S, ID>, FactoryBean<T> {
|
||||
implements InitializingBean, RepositoryFactoryInformation<S, ID>, FactoryBean<T> {
|
||||
|
||||
private RepositoryFactorySupport factory;
|
||||
private RepositoryFactorySupport factory;
|
||||
|
||||
private Key queryLookupStrategyKey;
|
||||
private Class<? extends T> repositoryInterface;
|
||||
private Object customImplementation;
|
||||
private Key queryLookupStrategyKey;
|
||||
private Class<? extends T> repositoryInterface;
|
||||
private Object customImplementation;
|
||||
|
||||
|
||||
/**
|
||||
* Setter to inject the repository interface to implement.
|
||||
*
|
||||
* @param repositoryInterface the repository interface to set
|
||||
*/
|
||||
@Required
|
||||
public void setRepositoryInterface(Class<? extends T> repositoryInterface) {
|
||||
/**
|
||||
* Setter to inject the repository interface to implement.
|
||||
*
|
||||
* @param repositoryInterface the repository interface to set
|
||||
*/
|
||||
@Required
|
||||
public void setRepositoryInterface(Class<? extends T> repositoryInterface) {
|
||||
|
||||
Assert.notNull(repositoryInterface);
|
||||
this.repositoryInterface = repositoryInterface;
|
||||
}
|
||||
Assert.notNull(repositoryInterface);
|
||||
this.repositoryInterface = repositoryInterface;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the {@link QueryLookupStrategy.Key} to be used.
|
||||
*
|
||||
* @param queryLookupStrategyKey
|
||||
*/
|
||||
public void setQueryLookupStrategyKey(Key queryLookupStrategyKey) {
|
||||
/**
|
||||
* Set the {@link QueryLookupStrategy.Key} to be used.
|
||||
*
|
||||
* @param queryLookupStrategyKey
|
||||
*/
|
||||
public void setQueryLookupStrategyKey(Key queryLookupStrategyKey) {
|
||||
|
||||
this.queryLookupStrategyKey = queryLookupStrategyKey;
|
||||
}
|
||||
this.queryLookupStrategyKey = queryLookupStrategyKey;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Setter to inject a custom repository implementation.
|
||||
*
|
||||
* @param customImplementation
|
||||
*/
|
||||
public void setCustomImplementation(Object customImplementation) {
|
||||
/**
|
||||
* Setter to inject a custom repository implementation.
|
||||
*
|
||||
* @param customImplementation
|
||||
*/
|
||||
public void setCustomImplementation(Object customImplementation) {
|
||||
|
||||
this.customImplementation = customImplementation;
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.EntityMetadataProvider#getEntityMetadata()
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public EntityInformation<S, ID> getEntityInformation() {
|
||||
|
||||
RepositoryMetadata repositoryMetadata = factory.getRepositoryMetadata(repositoryInterface);
|
||||
return (EntityInformation<S, ID>) factory.getEntityInformation(repositoryMetadata.getDomainClass());
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.RepositoryFactoryInformation#getRepositoryInterface()
|
||||
*/
|
||||
public Class<? extends T> getRepositoryInterface() {
|
||||
|
||||
return repositoryInterface;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObject()
|
||||
*/
|
||||
public T getObject() {
|
||||
|
||||
return factory.getRepository(repositoryInterface, customImplementation);
|
||||
}
|
||||
this.customImplementation = customImplementation;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Class<? extends T> getObjectType() {
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.EntityMetadataProvider#getEntityMetadata()
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public EntityInformation<S, ID> getEntityInformation() {
|
||||
|
||||
return (Class<? extends T>) (null == repositoryInterface ? Repository.class
|
||||
: repositoryInterface);
|
||||
}
|
||||
RepositoryMetadata repositoryMetadata = factory.getRepositoryMetadata(repositoryInterface);
|
||||
return (EntityInformation<S, ID>) factory.getEntityInformation(repositoryMetadata.getDomainClass());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
|
||||
*/
|
||||
public boolean isSingleton() {
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.support.RepositoryFactoryInformation#getRepositoryInterface()
|
||||
*/
|
||||
public Class<? extends T> getRepositoryInterface() {
|
||||
|
||||
return true;
|
||||
}
|
||||
return repositoryInterface;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObject()
|
||||
*/
|
||||
public T getObject() {
|
||||
|
||||
return factory.getRepository(repositoryInterface, customImplementation);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
public void afterPropertiesSet() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Class<? extends T> getObjectType() {
|
||||
|
||||
this.factory = createRepositoryFactory();
|
||||
this.factory.setQueryLookupStrategyKey(queryLookupStrategyKey);
|
||||
}
|
||||
return (Class<? extends T>) (null == repositoryInterface ? Repository.class
|
||||
: repositoryInterface);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create the actual {@link RepositoryFactorySupport} instance.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected abstract RepositoryFactorySupport createRepositoryFactory();
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
|
||||
*/
|
||||
public boolean isSingleton() {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
this.factory = createRepositoryFactory();
|
||||
this.factory.setQueryLookupStrategyKey(queryLookupStrategyKey);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create the actual {@link RepositoryFactorySupport} instance.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected abstract RepositoryFactorySupport createRepositoryFactory();
|
||||
}
|
||||
|
||||
@@ -23,23 +23,23 @@ import org.springframework.data.repository.Repository;
|
||||
/**
|
||||
* Interface for components that can provide {@link EntityInformation} this
|
||||
* interface
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface RepositoryFactoryInformation<T, ID extends Serializable> {
|
||||
|
||||
/**
|
||||
* Returns {@link EntityInformation} the repository factory is using.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
EntityInformation<T, ID> getEntityInformation();
|
||||
/**
|
||||
* Returns {@link EntityInformation} the repository factory is using.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
EntityInformation<T, ID> getEntityInformation();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the interface of the {@link Repository} the factory will create.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<? extends Repository<T, ID>> getRepositoryInterface();
|
||||
/**
|
||||
* Returns the interface of the {@link Repository} the factory will create.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<? extends Repository<T, ID>> getRepositoryInterface();
|
||||
}
|
||||
|
||||
@@ -43,351 +43,351 @@ import org.springframework.util.Assert;
|
||||
* handing the control to the {@code QueryExecuterMethodInterceptor}. Query
|
||||
* detection strategy can be configured by setting
|
||||
* {@link QueryLookupStrategy.Key}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public abstract class RepositoryFactorySupport {
|
||||
|
||||
private final List<RepositoryProxyPostProcessor> postProcessors =
|
||||
new ArrayList<RepositoryProxyPostProcessor>();
|
||||
private QueryLookupStrategy.Key queryLookupStrategyKey;
|
||||
private List<QueryCreationListener<?>> queryPostProcessors =
|
||||
new ArrayList<QueryCreationListener<?>>();
|
||||
private final List<RepositoryProxyPostProcessor> postProcessors =
|
||||
new ArrayList<RepositoryProxyPostProcessor>();
|
||||
private QueryLookupStrategy.Key queryLookupStrategyKey;
|
||||
private List<QueryCreationListener<?>> queryPostProcessors =
|
||||
new ArrayList<QueryCreationListener<?>>();
|
||||
|
||||
|
||||
/**
|
||||
* Sets the strategy of how to lookup a query to execute finders.
|
||||
*
|
||||
* @param queryLookupStrategy the createFinderQueries to set
|
||||
*/
|
||||
public void setQueryLookupStrategyKey(Key key) {
|
||||
/**
|
||||
* Sets the strategy of how to lookup a query to execute finders.
|
||||
*
|
||||
* @param queryLookupStrategy the createFinderQueries to set
|
||||
*/
|
||||
public void setQueryLookupStrategyKey(Key key) {
|
||||
|
||||
this.queryLookupStrategyKey = key;
|
||||
}
|
||||
this.queryLookupStrategyKey = key;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a {@link QueryCreationListener} to the factory to plug in
|
||||
* functionality triggered right after creation of {@link RepositoryQuery}
|
||||
* instances.
|
||||
*
|
||||
* @param listener
|
||||
*/
|
||||
public void addQueryCreationListener(QueryCreationListener<?> listener) {
|
||||
/**
|
||||
* Adds a {@link QueryCreationListener} to the factory to plug in
|
||||
* functionality triggered right after creation of {@link RepositoryQuery}
|
||||
* instances.
|
||||
*
|
||||
* @param listener
|
||||
*/
|
||||
public void addQueryCreationListener(QueryCreationListener<?> listener) {
|
||||
|
||||
Assert.notNull(listener);
|
||||
this.queryPostProcessors.add(listener);
|
||||
}
|
||||
Assert.notNull(listener);
|
||||
this.queryPostProcessors.add(listener);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds {@link RepositoryProxyPostProcessor}s to the factory to allow
|
||||
* manipulation of the {@link ProxyFactory} before the proxy gets created.
|
||||
* Note that the {@link QueryExecuterMethodInterceptor} will be added to the
|
||||
* proxy <em>after</em> the {@link RepositoryProxyPostProcessor}s are
|
||||
* considered.
|
||||
*
|
||||
* @param processor
|
||||
*/
|
||||
public void addRepositoryProxyPostProcessor(
|
||||
RepositoryProxyPostProcessor processor) {
|
||||
/**
|
||||
* Adds {@link RepositoryProxyPostProcessor}s to the factory to allow
|
||||
* manipulation of the {@link ProxyFactory} before the proxy gets created.
|
||||
* Note that the {@link QueryExecuterMethodInterceptor} will be added to the
|
||||
* proxy <em>after</em> the {@link RepositoryProxyPostProcessor}s are
|
||||
* considered.
|
||||
*
|
||||
* @param processor
|
||||
*/
|
||||
public void addRepositoryProxyPostProcessor(
|
||||
RepositoryProxyPostProcessor processor) {
|
||||
|
||||
Assert.notNull(processor);
|
||||
this.postProcessors.add(processor);
|
||||
}
|
||||
Assert.notNull(processor);
|
||||
this.postProcessors.add(processor);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a repository instance for the given interface.
|
||||
*
|
||||
* @param <T>
|
||||
* @param repositoryInterface
|
||||
* @return
|
||||
*/
|
||||
public <T extends Repository<?, ?>> T getRepository(
|
||||
Class<T> repositoryInterface) {
|
||||
/**
|
||||
* Returns a repository instance for the given interface.
|
||||
*
|
||||
* @param <T>
|
||||
* @param repositoryInterface
|
||||
* @return
|
||||
*/
|
||||
public <T extends Repository<?, ?>> T getRepository(
|
||||
Class<T> repositoryInterface) {
|
||||
|
||||
return getRepository(repositoryInterface, null);
|
||||
}
|
||||
return getRepository(repositoryInterface, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a repository instance for the given interface backed by an
|
||||
* instance providing implementation logic for custom logic.
|
||||
*
|
||||
* @param <T>
|
||||
* @param repositoryInterface
|
||||
* @param customImplementation
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
public <T> T getRepository(Class<T> repositoryInterface,
|
||||
Object customImplementation) {
|
||||
/**
|
||||
* Returns a repository instance for the given interface backed by an
|
||||
* instance providing implementation logic for custom logic.
|
||||
*
|
||||
* @param <T>
|
||||
* @param repositoryInterface
|
||||
* @param customImplementation
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public <T> T getRepository(Class<T> repositoryInterface,
|
||||
Object customImplementation) {
|
||||
|
||||
RepositoryMetadata metadata = getRepositoryMetadata(repositoryInterface);
|
||||
RepositoryInformation information = getRepositoryInformation(metadata);
|
||||
RepositoryMetadata metadata = getRepositoryMetadata(repositoryInterface);
|
||||
RepositoryInformation information = getRepositoryInformation(metadata);
|
||||
|
||||
validate(information, customImplementation);
|
||||
validate(information, customImplementation);
|
||||
|
||||
Object target = getTargetRepository(information);
|
||||
Object target = getTargetRepository(information);
|
||||
|
||||
// Create proxy
|
||||
ProxyFactory result = new ProxyFactory();
|
||||
result.setTarget(target);
|
||||
result.setInterfaces(new Class[] { repositoryInterface });
|
||||
// Create proxy
|
||||
ProxyFactory result = new ProxyFactory();
|
||||
result.setTarget(target);
|
||||
result.setInterfaces(new Class[]{repositoryInterface});
|
||||
|
||||
for (RepositoryProxyPostProcessor processor : postProcessors) {
|
||||
processor.postProcess(result);
|
||||
}
|
||||
for (RepositoryProxyPostProcessor processor : postProcessors) {
|
||||
processor.postProcess(result);
|
||||
}
|
||||
|
||||
result.addAdvice(new QueryExecuterMethodInterceptor(information,
|
||||
customImplementation, target));
|
||||
result.addAdvice(new QueryExecuterMethodInterceptor(information,
|
||||
customImplementation, target));
|
||||
|
||||
return (T) result.getProxy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link RepositoryMetadata} for the given repository interface.
|
||||
*
|
||||
* @param repositoryInterface
|
||||
* @return
|
||||
*/
|
||||
RepositoryMetadata getRepositoryMetadata(Class<?> repositoryInterface) {
|
||||
return new DefaultRepositoryMetadata(repositoryInterface);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the {@link RepositoryInformation} for the given repository interface.
|
||||
*
|
||||
* @param repositoryInterface
|
||||
* @return
|
||||
*/
|
||||
private RepositoryInformation getRepositoryInformation(RepositoryMetadata metadata) {
|
||||
return new DefaultRepositoryInformation(metadata, getRepositoryBaseClass(metadata));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the {@link EntityInformation} for the given domain class.
|
||||
*
|
||||
* @param <T> the entity type
|
||||
* @param <ID> the id type
|
||||
* @param domainClass
|
||||
* @return
|
||||
*/
|
||||
public abstract <T, ID extends Serializable> EntityInformation<T, ID> getEntityInformation(Class<T> domainClass);
|
||||
return (T) result.getProxy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link RepositoryMetadata} for the given repository interface.
|
||||
*
|
||||
* @param repositoryInterface
|
||||
* @return
|
||||
*/
|
||||
RepositoryMetadata getRepositoryMetadata(Class<?> repositoryInterface) {
|
||||
return new DefaultRepositoryMetadata(repositoryInterface);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a repository instance as backing for the query proxy.
|
||||
*
|
||||
* @param metadata
|
||||
* @return
|
||||
*/
|
||||
protected abstract Object getTargetRepository(RepositoryMetadata metadata);
|
||||
/**
|
||||
* Returns the {@link RepositoryInformation} for the given repository interface.
|
||||
*
|
||||
* @param repositoryInterface
|
||||
* @return
|
||||
*/
|
||||
private RepositoryInformation getRepositoryInformation(RepositoryMetadata metadata) {
|
||||
return new DefaultRepositoryInformation(metadata, getRepositoryBaseClass(metadata));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the base class backing the actual repository instance. Make sure
|
||||
* {@link #getTargetRepository(RepositoryMetadata)} returns an instance of
|
||||
* this class.
|
||||
*
|
||||
* @param metadata
|
||||
* @return
|
||||
*/
|
||||
protected abstract Class<?> getRepositoryBaseClass(
|
||||
RepositoryMetadata metadata);
|
||||
/**
|
||||
* Returns the {@link EntityInformation} for the given domain class.
|
||||
*
|
||||
* @param <T> the entity type
|
||||
* @param <ID> the id type
|
||||
* @param domainClass
|
||||
* @return
|
||||
*/
|
||||
public abstract <T, ID extends Serializable> EntityInformation<T, ID> getEntityInformation(Class<T> domainClass);
|
||||
|
||||
|
||||
/**
|
||||
* Create a repository instance as backing for the query proxy.
|
||||
*
|
||||
* @param metadata
|
||||
* @return
|
||||
*/
|
||||
protected abstract Object getTargetRepository(RepositoryMetadata metadata);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the base class backing the actual repository instance. Make sure
|
||||
* {@link #getTargetRepository(RepositoryMetadata)} returns an instance of
|
||||
* this class.
|
||||
*
|
||||
* @param metadata
|
||||
* @return
|
||||
*/
|
||||
protected abstract Class<?> getRepositoryBaseClass(
|
||||
RepositoryMetadata metadata);
|
||||
|
||||
/**
|
||||
* Returns the {@link QueryLookupStrategy} for the given {@link Key}.
|
||||
*
|
||||
*
|
||||
* @param key can be {@literal null}
|
||||
* @return the {@link QueryLookupStrategy} to use or {@literal null} if no queries should be looked up.
|
||||
*/
|
||||
protected QueryLookupStrategy getQueryLookupStrategy(Key key) {
|
||||
return null;
|
||||
}
|
||||
protected QueryLookupStrategy getQueryLookupStrategy(Key key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates the given repository interface as well as the given custom
|
||||
* implementation.
|
||||
*
|
||||
* @param repositoryInformation
|
||||
* @param customImplementation
|
||||
*/
|
||||
private void validate(RepositoryInformation repositoryInformation,
|
||||
Object customImplementation) {
|
||||
/**
|
||||
* Validates the given repository interface as well as the given custom
|
||||
* implementation.
|
||||
*
|
||||
* @param repositoryInformation
|
||||
* @param customImplementation
|
||||
*/
|
||||
private void validate(RepositoryInformation repositoryInformation,
|
||||
Object customImplementation) {
|
||||
|
||||
if (null == customImplementation
|
||||
&& repositoryInformation.hasCustomMethod()) {
|
||||
if (null == customImplementation
|
||||
&& repositoryInformation.hasCustomMethod()) {
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"You have custom methods in %s but not provided a custom implementation!",
|
||||
repositoryInformation.getRepositoryInterface()));
|
||||
}
|
||||
|
||||
validate(repositoryInformation);
|
||||
}
|
||||
|
||||
protected void validate(RepositoryMetadata repositoryMetadata) {
|
||||
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"You have custom methods in %s but not provided a custom implementation!",
|
||||
repositoryInformation.getRepositoryInterface()));
|
||||
}
|
||||
|
||||
/**
|
||||
* This {@code MethodInterceptor} intercepts calls to methods of the custom
|
||||
* implementation and delegates the to it if configured. Furthermore it
|
||||
* resolves method calls to finders and triggers execution of them. You can
|
||||
* rely on having a custom repository implementation instance set if this
|
||||
* returns true.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class QueryExecuterMethodInterceptor implements MethodInterceptor {
|
||||
validate(repositoryInformation);
|
||||
}
|
||||
|
||||
private final Map<Method, RepositoryQuery> queries =
|
||||
new ConcurrentHashMap<Method, RepositoryQuery>();
|
||||
protected void validate(RepositoryMetadata repositoryMetadata) {
|
||||
|
||||
private final Object customImplementation;
|
||||
private final RepositoryInformation repositoryInformation;
|
||||
private final Object target;
|
||||
}
|
||||
|
||||
/**
|
||||
* This {@code MethodInterceptor} intercepts calls to methods of the custom
|
||||
* implementation and delegates the to it if configured. Furthermore it
|
||||
* resolves method calls to finders and triggers execution of them. You can
|
||||
* rely on having a custom repository implementation instance set if this
|
||||
* returns true.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class QueryExecuterMethodInterceptor implements MethodInterceptor {
|
||||
|
||||
private final Map<Method, RepositoryQuery> queries =
|
||||
new ConcurrentHashMap<Method, RepositoryQuery>();
|
||||
|
||||
private final Object customImplementation;
|
||||
private final RepositoryInformation repositoryInformation;
|
||||
private final Object target;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link QueryExecuterMethodInterceptor}. Builds a model
|
||||
* of {@link QueryMethod}s to be invoked on execution of repository
|
||||
* interface methods.
|
||||
*/
|
||||
public QueryExecuterMethodInterceptor(
|
||||
RepositoryInformation repositoryInformation,
|
||||
Object customImplementation, Object target) {
|
||||
/**
|
||||
* Creates a new {@link QueryExecuterMethodInterceptor}. Builds a model
|
||||
* of {@link QueryMethod}s to be invoked on execution of repository
|
||||
* interface methods.
|
||||
*/
|
||||
public QueryExecuterMethodInterceptor(
|
||||
RepositoryInformation repositoryInformation,
|
||||
Object customImplementation, Object target) {
|
||||
|
||||
this.repositoryInformation = repositoryInformation;
|
||||
this.customImplementation = customImplementation;
|
||||
this.target = target;
|
||||
this.repositoryInformation = repositoryInformation;
|
||||
this.customImplementation = customImplementation;
|
||||
this.target = target;
|
||||
|
||||
QueryLookupStrategy lookupStrategy =
|
||||
getQueryLookupStrategy(queryLookupStrategyKey);
|
||||
|
||||
if (lookupStrategy == null) {
|
||||
|
||||
if (repositoryInformation.hasCustomMethod()) {
|
||||
QueryLookupStrategy lookupStrategy =
|
||||
getQueryLookupStrategy(queryLookupStrategyKey);
|
||||
|
||||
if (lookupStrategy == null) {
|
||||
|
||||
if (repositoryInformation.hasCustomMethod()) {
|
||||
throw new IllegalStateException(
|
||||
"You have defined query method in the repository but " +
|
||||
"you don't have no query lookup strategy defined. The " +
|
||||
"infrastructure apparently does not support query methods!");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
"you don't have no query lookup strategy defined. The " +
|
||||
"infrastructure apparently does not support query methods!");
|
||||
}
|
||||
|
||||
for (Method method : repositoryInformation.getQueryMethods()) {
|
||||
RepositoryQuery query =
|
||||
lookupStrategy.resolveQuery(method,repositoryInformation);
|
||||
invokeListeners(query);
|
||||
queries.put(method, query);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (Method method : repositoryInformation.getQueryMethods()) {
|
||||
RepositoryQuery query =
|
||||
lookupStrategy.resolveQuery(method, repositoryInformation);
|
||||
invokeListeners(query);
|
||||
queries.put(method, query);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private void invokeListeners(RepositoryQuery query) {
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
private void invokeListeners(RepositoryQuery query) {
|
||||
|
||||
for (QueryCreationListener listener : queryPostProcessors) {
|
||||
Class<?> typeArgument =
|
||||
GenericTypeResolver.resolveTypeArgument(
|
||||
listener.getClass(),
|
||||
QueryCreationListener.class);
|
||||
if (typeArgument != null
|
||||
&& typeArgument.isAssignableFrom(query.getClass())) {
|
||||
listener.onCreation(query);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (QueryCreationListener listener : queryPostProcessors) {
|
||||
Class<?> typeArgument =
|
||||
GenericTypeResolver.resolveTypeArgument(
|
||||
listener.getClass(),
|
||||
QueryCreationListener.class);
|
||||
if (typeArgument != null
|
||||
&& typeArgument.isAssignableFrom(query.getClass())) {
|
||||
listener.onCreation(query);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance
|
||||
* .intercept.MethodInvocation)
|
||||
*/
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance
|
||||
* .intercept.MethodInvocation)
|
||||
*/
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
|
||||
Method method = invocation.getMethod();
|
||||
Method method = invocation.getMethod();
|
||||
|
||||
if (isCustomMethodInvocation(invocation)) {
|
||||
if (isCustomMethodInvocation(invocation)) {
|
||||
|
||||
makeAccessible(method);
|
||||
return executeMethodOn(customImplementation, method,
|
||||
invocation.getArguments());
|
||||
}
|
||||
makeAccessible(method);
|
||||
return executeMethodOn(customImplementation, method,
|
||||
invocation.getArguments());
|
||||
}
|
||||
|
||||
if (hasQueryFor(method)) {
|
||||
return queries.get(method).execute(invocation.getArguments());
|
||||
}
|
||||
if (hasQueryFor(method)) {
|
||||
return queries.get(method).execute(invocation.getArguments());
|
||||
}
|
||||
|
||||
// Lookup actual method as it might be redeclared in the interface
|
||||
// and we have to use the repository instance nevertheless
|
||||
Method actualMethod = repositoryInformation.getBaseClassMethod(method);
|
||||
return executeMethodOn(target, actualMethod,
|
||||
invocation.getArguments());
|
||||
}
|
||||
// Lookup actual method as it might be redeclared in the interface
|
||||
// and we have to use the repository instance nevertheless
|
||||
Method actualMethod = repositoryInformation.getBaseClassMethod(method);
|
||||
return executeMethodOn(target, actualMethod,
|
||||
invocation.getArguments());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes the given method on the given target. Correctly unwraps
|
||||
* exceptions not caused by the reflection magic.
|
||||
*
|
||||
* @param target
|
||||
* @param method
|
||||
* @param parameters
|
||||
* @return
|
||||
* @throws Throwable
|
||||
*/
|
||||
private Object executeMethodOn(Object target, Method method,
|
||||
Object[] parameters) throws Throwable {
|
||||
/**
|
||||
* Executes the given method on the given target. Correctly unwraps
|
||||
* exceptions not caused by the reflection magic.
|
||||
*
|
||||
* @param target
|
||||
* @param method
|
||||
* @param parameters
|
||||
* @return
|
||||
* @throws Throwable
|
||||
*/
|
||||
private Object executeMethodOn(Object target, Method method,
|
||||
Object[] parameters) throws Throwable {
|
||||
|
||||
try {
|
||||
return method.invoke(target, parameters);
|
||||
} catch (Exception e) {
|
||||
ClassUtils.unwrapReflectionException(e);
|
||||
}
|
||||
try {
|
||||
return method.invoke(target, parameters);
|
||||
} catch (Exception e) {
|
||||
ClassUtils.unwrapReflectionException(e);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Should not occur!");
|
||||
}
|
||||
throw new IllegalStateException("Should not occur!");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether we know of a query to execute for the given
|
||||
* {@link Method};
|
||||
*
|
||||
* @param method
|
||||
* @return
|
||||
*/
|
||||
private boolean hasQueryFor(Method method) {
|
||||
/**
|
||||
* Returns whether we know of a query to execute for the given
|
||||
* {@link Method};
|
||||
*
|
||||
* @param method
|
||||
* @return
|
||||
*/
|
||||
private boolean hasQueryFor(Method method) {
|
||||
|
||||
return queries.containsKey(method);
|
||||
}
|
||||
return queries.containsKey(method);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the given {@link MethodInvocation} is considered to
|
||||
* be targeted as an invocation of a custom method.
|
||||
*
|
||||
* @param method
|
||||
* @return
|
||||
*/
|
||||
private boolean isCustomMethodInvocation(MethodInvocation invocation) {
|
||||
/**
|
||||
* Returns whether the given {@link MethodInvocation} is considered to
|
||||
* be targeted as an invocation of a custom method.
|
||||
*
|
||||
* @param method
|
||||
* @return
|
||||
*/
|
||||
private boolean isCustomMethodInvocation(MethodInvocation invocation) {
|
||||
|
||||
if (null == customImplementation) {
|
||||
return false;
|
||||
}
|
||||
if (null == customImplementation) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return repositoryInformation.isCustomMethod(invocation.getMethod());
|
||||
}
|
||||
}
|
||||
return repositoryInformation.isCustomMethod(invocation.getMethod());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,52 +24,52 @@ import java.lang.reflect.Method;
|
||||
*/
|
||||
interface RepositoryInformation extends RepositoryMetadata {
|
||||
|
||||
/**
|
||||
* Returns the base class to be used to create the proxy backing instance.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<?> getRepositoryBaseClass();
|
||||
/**
|
||||
* Returns the base class to be used to create the proxy backing instance.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<?> getRepositoryBaseClass();
|
||||
|
||||
|
||||
/**
|
||||
* Returns if the configured repository interface has custom methods, that
|
||||
* might have to be delegated to a custom implementation. This is used to
|
||||
* verify repository configuration.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean hasCustomMethod();
|
||||
/**
|
||||
* Returns if the configured repository interface has custom methods, that
|
||||
* might have to be delegated to a custom implementation. This is used to
|
||||
* verify repository configuration.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean hasCustomMethod();
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the given method is a custom repository method.
|
||||
*
|
||||
* @param method
|
||||
* @param baseClass
|
||||
* @return
|
||||
*/
|
||||
boolean isCustomMethod(Method method);
|
||||
/**
|
||||
* Returns whether the given method is a custom repository method.
|
||||
*
|
||||
* @param method
|
||||
* @param baseClass
|
||||
* @return
|
||||
*/
|
||||
boolean isCustomMethod(Method method);
|
||||
|
||||
|
||||
/**
|
||||
* Returns all methods considered to be query methods.
|
||||
*
|
||||
* @param repositoryInterface
|
||||
* @return
|
||||
*/
|
||||
Iterable<Method> getQueryMethods();
|
||||
/**
|
||||
* Returns all methods considered to be query methods.
|
||||
*
|
||||
* @param repositoryInterface
|
||||
* @return
|
||||
*/
|
||||
Iterable<Method> getQueryMethods();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the base class method that is backing the given method. This can
|
||||
* be necessary if a repository interface redeclares a method of the core
|
||||
* repository interface (e.g. for transaction behaviour customization).
|
||||
* Returns the method itself if the base class does not implement the given
|
||||
* method.
|
||||
*
|
||||
* @param method
|
||||
* @return
|
||||
*/
|
||||
Method getBaseClassMethod(Method method);
|
||||
/**
|
||||
* Returns the base class method that is backing the given method. This can
|
||||
* be necessary if a repository interface redeclares a method of the core
|
||||
* repository interface (e.g. for transaction behaviour customization).
|
||||
* Returns the method itself if the base class does not implement the given
|
||||
* method.
|
||||
*
|
||||
* @param method
|
||||
* @return
|
||||
*/
|
||||
Method getBaseClassMethod(Method method);
|
||||
}
|
||||
|
||||
@@ -33,85 +33,85 @@ import org.springframework.util.ClassUtils;
|
||||
* as shortcut to prevent the need of instantiating
|
||||
* {@link RepositoryFactoryBeanSupport}s just to find out what repository
|
||||
* interface they actually create.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
class RepositoryInterfaceAwareBeanPostProcessor extends
|
||||
InstantiationAwareBeanPostProcessorAdapter implements BeanFactoryAware {
|
||||
InstantiationAwareBeanPostProcessorAdapter implements BeanFactoryAware {
|
||||
|
||||
private static final Class<?> REPOSITORY_TYPE =
|
||||
RepositoryFactoryBeanSupport.class;
|
||||
private static final Class<?> REPOSITORY_TYPE =
|
||||
RepositoryFactoryBeanSupport.class;
|
||||
|
||||
private ConfigurableListableBeanFactory context;
|
||||
private ConfigurableListableBeanFactory context;
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org
|
||||
* .springframework.beans.factory.BeanFactory)
|
||||
*/
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org
|
||||
* .springframework.beans.factory.BeanFactory)
|
||||
*/
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
|
||||
if (beanFactory instanceof ConfigurableListableBeanFactory) {
|
||||
this.context = (ConfigurableListableBeanFactory) beanFactory;
|
||||
}
|
||||
}
|
||||
if (beanFactory instanceof ConfigurableListableBeanFactory) {
|
||||
this.context = (ConfigurableListableBeanFactory) beanFactory;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.beans.factory.config.
|
||||
* InstantiationAwareBeanPostProcessorAdapter
|
||||
* #predictBeanType(java.lang.Class, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public Class<?> predictBeanType(Class<?> beanClass, String beanName) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.beans.factory.config.
|
||||
* InstantiationAwareBeanPostProcessorAdapter
|
||||
* #predictBeanType(java.lang.Class, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public Class<?> predictBeanType(Class<?> beanClass, String beanName) {
|
||||
|
||||
if (null == context || !REPOSITORY_TYPE.isAssignableFrom(beanClass)) {
|
||||
return null;
|
||||
}
|
||||
if (null == context || !REPOSITORY_TYPE.isAssignableFrom(beanClass)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
BeanDefinition definition = context.getBeanDefinition(beanName);
|
||||
PropertyValue value =
|
||||
definition.getPropertyValues().getPropertyValue(
|
||||
"repositoryInterface");
|
||||
BeanDefinition definition = context.getBeanDefinition(beanName);
|
||||
PropertyValue value =
|
||||
definition.getPropertyValues().getPropertyValue(
|
||||
"repositoryInterface");
|
||||
|
||||
return getClassForPropertyValue(value);
|
||||
}
|
||||
return getClassForPropertyValue(value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the class which is configured in the given {@link PropertyValue}.
|
||||
* In case it is not a {@link TypedStringValue} or the value contained
|
||||
* cannot be interpreted as {@link Class} it will return null.
|
||||
*
|
||||
* @param propertyValue
|
||||
* @return
|
||||
*/
|
||||
private Class<?> getClassForPropertyValue(PropertyValue propertyValue) {
|
||||
/**
|
||||
* Returns the class which is configured in the given {@link PropertyValue}.
|
||||
* In case it is not a {@link TypedStringValue} or the value contained
|
||||
* cannot be interpreted as {@link Class} it will return null.
|
||||
*
|
||||
* @param propertyValue
|
||||
* @return
|
||||
*/
|
||||
private Class<?> getClassForPropertyValue(PropertyValue propertyValue) {
|
||||
|
||||
Object value = propertyValue.getValue();
|
||||
String className = null;
|
||||
Object value = propertyValue.getValue();
|
||||
String className = null;
|
||||
|
||||
if (value instanceof TypedStringValue) {
|
||||
className = ((TypedStringValue) value).getValue();
|
||||
} else if (value instanceof String) {
|
||||
className = (String) value;
|
||||
} else if (value instanceof Class<?>) {
|
||||
return (Class<?>) value;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
if (value instanceof TypedStringValue) {
|
||||
className = ((TypedStringValue) value).getValue();
|
||||
} else if (value instanceof String) {
|
||||
className = (String) value;
|
||||
} else if (value instanceof Class<?>) {
|
||||
return (Class<?>) value;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return ClassUtils.resolveClassName(className,
|
||||
RepositoryInterfaceAwareBeanPostProcessor.class
|
||||
.getClassLoader());
|
||||
} catch (IllegalArgumentException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
try {
|
||||
return ClassUtils.resolveClassName(className,
|
||||
RepositoryInterfaceAwareBeanPostProcessor.class
|
||||
.getClassLoader());
|
||||
} catch (IllegalArgumentException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,17 +16,16 @@
|
||||
package org.springframework.data.repository.support;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Metadata for repository interfaces.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface RepositoryMetadata {
|
||||
|
||||
|
||||
/**
|
||||
* Returns the id class the given class is declared for.
|
||||
*
|
||||
*
|
||||
* @param clazz
|
||||
* @return the id class of the entity managed by the repository for or
|
||||
* {@code null} if none found.
|
||||
@@ -35,7 +34,7 @@ public interface RepositoryMetadata {
|
||||
|
||||
/**
|
||||
* Returns the domain class the repository is declared for.
|
||||
*
|
||||
*
|
||||
* @param clazz
|
||||
* @return the domain class the repository is handling or {@code null} if
|
||||
* none found.
|
||||
@@ -44,7 +43,7 @@ public interface RepositoryMetadata {
|
||||
|
||||
/**
|
||||
* Returns the repository interface.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<?> getRepositoryInterface();
|
||||
|
||||
@@ -21,16 +21,16 @@ import org.springframework.aop.framework.ProxyFactory;
|
||||
/**
|
||||
* Callback interface used during repository proxy creation. Allows manipulating
|
||||
* the {@link ProxyFactory} creating the repository.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface RepositoryProxyPostProcessor {
|
||||
|
||||
/**
|
||||
* Manipulates the {@link ProxyFactory}, e.g. add further interceptors to
|
||||
* it.
|
||||
*
|
||||
* @param factory
|
||||
*/
|
||||
void postProcess(ProxyFactory factory);
|
||||
/**
|
||||
* Manipulates the {@link ProxyFactory}, e.g. add further interceptors to
|
||||
* it.
|
||||
*
|
||||
* @param factory
|
||||
*/
|
||||
void postProcess(ProxyFactory factory);
|
||||
}
|
||||
@@ -31,73 +31,73 @@ import org.springframework.util.Assert;
|
||||
* capabilities to the repository proxy. Will register a
|
||||
* {@link TransactionalRepositoryProxyPostProcessor} that in turn adds a
|
||||
* {@link TransactionInterceptor} to the repository proxy to be created.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public abstract class TransactionalRepositoryFactoryBeanSupport<T extends Repository<S, ID>, S, ID extends Serializable>
|
||||
extends RepositoryFactoryBeanSupport<T, S, ID> implements BeanFactoryAware {
|
||||
extends RepositoryFactoryBeanSupport<T, S, ID> implements BeanFactoryAware {
|
||||
|
||||
private String transactionManagerName = TxUtils.DEFAULT_TRANSACTION_MANAGER;
|
||||
private RepositoryProxyPostProcessor txPostProcessor;
|
||||
private String transactionManagerName = TxUtils.DEFAULT_TRANSACTION_MANAGER;
|
||||
private RepositoryProxyPostProcessor txPostProcessor;
|
||||
|
||||
|
||||
/**
|
||||
* Setter to configure which transaction manager to be used. We have to use
|
||||
* the bean name explicitly as otherwise the qualifier of the
|
||||
* {@link org.springframework.transaction.annotation.Transactional}
|
||||
* annotation is used. By explicitly defining the transaction manager bean
|
||||
* name we favour let this one be the default one chosen.
|
||||
*
|
||||
* @param transactionManager
|
||||
*/
|
||||
public void setTransactionManager(String transactionManager) {
|
||||
/**
|
||||
* Setter to configure which transaction manager to be used. We have to use
|
||||
* the bean name explicitly as otherwise the qualifier of the
|
||||
* {@link org.springframework.transaction.annotation.Transactional}
|
||||
* annotation is used. By explicitly defining the transaction manager bean
|
||||
* name we favour let this one be the default one chosen.
|
||||
*
|
||||
* @param transactionManager
|
||||
*/
|
||||
public void setTransactionManager(String transactionManager) {
|
||||
|
||||
this.transactionManagerName =
|
||||
transactionManager == null ? TxUtils.DEFAULT_TRANSACTION_MANAGER
|
||||
: transactionManager;
|
||||
}
|
||||
this.transactionManagerName =
|
||||
transactionManager == null ? TxUtils.DEFAULT_TRANSACTION_MANAGER
|
||||
: transactionManager;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delegates {@link RepositoryFactorySupport} creation to
|
||||
* {@link #doCreateRepositoryFactory()} and applies the
|
||||
* {@link TransactionalRepositoryProxyPostProcessor} to the created
|
||||
* instance.
|
||||
*
|
||||
* @see org.springframework.data.repository.support.RepositoryFactoryBeanSupport
|
||||
* #createRepositoryFactory()
|
||||
*/
|
||||
@Override
|
||||
protected final RepositoryFactorySupport createRepositoryFactory() {
|
||||
/**
|
||||
* Delegates {@link RepositoryFactorySupport} creation to
|
||||
* {@link #doCreateRepositoryFactory()} and applies the
|
||||
* {@link TransactionalRepositoryProxyPostProcessor} to the created
|
||||
* instance.
|
||||
*
|
||||
* @see org.springframework.data.repository.support.RepositoryFactoryBeanSupport
|
||||
* #createRepositoryFactory()
|
||||
*/
|
||||
@Override
|
||||
protected final RepositoryFactorySupport createRepositoryFactory() {
|
||||
|
||||
RepositoryFactorySupport factory = doCreateRepositoryFactory();
|
||||
factory.addRepositoryProxyPostProcessor(txPostProcessor);
|
||||
return factory;
|
||||
}
|
||||
RepositoryFactorySupport factory = doCreateRepositoryFactory();
|
||||
factory.addRepositoryProxyPostProcessor(txPostProcessor);
|
||||
return factory;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates the actual {@link RepositoryFactorySupport} instance.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected abstract RepositoryFactorySupport doCreateRepositoryFactory();
|
||||
/**
|
||||
* Creates the actual {@link RepositoryFactorySupport} instance.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected abstract RepositoryFactorySupport doCreateRepositoryFactory();
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org
|
||||
* .springframework.beans.factory.BeanFactory)
|
||||
*/
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org
|
||||
* .springframework.beans.factory.BeanFactory)
|
||||
*/
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
|
||||
Assert.isInstanceOf(ListableBeanFactory.class, beanFactory);
|
||||
Assert.isInstanceOf(ListableBeanFactory.class, beanFactory);
|
||||
|
||||
this.txPostProcessor =
|
||||
new TransactionalRepositoryProxyPostProcessor(
|
||||
(ListableBeanFactory) beanFactory,
|
||||
transactionManagerName);
|
||||
}
|
||||
this.txPostProcessor =
|
||||
new TransactionalRepositoryProxyPostProcessor(
|
||||
(ListableBeanFactory) beanFactory,
|
||||
transactionManagerName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,49 +27,49 @@ import org.springframework.util.Assert;
|
||||
* {@link RepositoryProxyPostProcessor} to add transactional behaviour to
|
||||
* repository proxies. Adds a {@link PersistenceExceptionTranslationInterceptor}
|
||||
* as well as an annotation based {@link TransactionInterceptor} to the proxy.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
class TransactionalRepositoryProxyPostProcessor implements
|
||||
RepositoryProxyPostProcessor {
|
||||
RepositoryProxyPostProcessor {
|
||||
|
||||
private final TransactionInterceptor transactionInterceptor;
|
||||
private final PersistenceExceptionTranslationInterceptor petInterceptor;
|
||||
private final TransactionInterceptor transactionInterceptor;
|
||||
private final PersistenceExceptionTranslationInterceptor petInterceptor;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link TransactionalRepositoryProxyPostProcessor}.
|
||||
*/
|
||||
public TransactionalRepositoryProxyPostProcessor(
|
||||
ListableBeanFactory beanFactory, String transactionManagerName) {
|
||||
/**
|
||||
* Creates a new {@link TransactionalRepositoryProxyPostProcessor}.
|
||||
*/
|
||||
public TransactionalRepositoryProxyPostProcessor(
|
||||
ListableBeanFactory beanFactory, String transactionManagerName) {
|
||||
|
||||
Assert.notNull(beanFactory);
|
||||
Assert.notNull(transactionManagerName);
|
||||
Assert.notNull(beanFactory);
|
||||
Assert.notNull(transactionManagerName);
|
||||
|
||||
this.petInterceptor = new PersistenceExceptionTranslationInterceptor();
|
||||
this.petInterceptor.setBeanFactory(beanFactory);
|
||||
this.petInterceptor.afterPropertiesSet();
|
||||
this.petInterceptor = new PersistenceExceptionTranslationInterceptor();
|
||||
this.petInterceptor.setBeanFactory(beanFactory);
|
||||
this.petInterceptor.afterPropertiesSet();
|
||||
|
||||
this.transactionInterceptor =
|
||||
new TransactionInterceptor(null,
|
||||
new AnnotationTransactionAttributeSource());
|
||||
this.transactionInterceptor
|
||||
.setTransactionManagerBeanName(transactionManagerName);
|
||||
this.transactionInterceptor.setBeanFactory(beanFactory);
|
||||
this.transactionInterceptor.afterPropertiesSet();
|
||||
}
|
||||
this.transactionInterceptor =
|
||||
new TransactionInterceptor(null,
|
||||
new AnnotationTransactionAttributeSource());
|
||||
this.transactionInterceptor
|
||||
.setTransactionManagerBeanName(transactionManagerName);
|
||||
this.transactionInterceptor.setBeanFactory(beanFactory);
|
||||
this.transactionInterceptor.afterPropertiesSet();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.support.RepositoryProxyPostProcessor
|
||||
* #postProcess(org.springframework.aop.framework.ProxyFactory)
|
||||
*/
|
||||
public void postProcess(ProxyFactory factory) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.support.RepositoryProxyPostProcessor
|
||||
* #postProcess(org.springframework.aop.framework.ProxyFactory)
|
||||
*/
|
||||
public void postProcess(ProxyFactory factory) {
|
||||
|
||||
factory.addAdvice(petInterceptor);
|
||||
factory.addAdvice(transactionInterceptor);
|
||||
}
|
||||
factory.addAdvice(petInterceptor);
|
||||
factory.addAdvice(transactionInterceptor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,175 +30,175 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Utility class to work with classes.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public abstract class ClassUtils {
|
||||
|
||||
/**
|
||||
* Private constructor to prevent instantiation.
|
||||
*/
|
||||
private ClassUtils() {
|
||||
/**
|
||||
* Private constructor to prevent instantiation.
|
||||
*/
|
||||
private ClassUtils() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the domain class returned by the given {@link Method}. Will
|
||||
* extract the type from {@link Collection}s and
|
||||
* {@link org.springframework.data.domain.Page} as well.
|
||||
*
|
||||
* @param method
|
||||
* @return
|
||||
*/
|
||||
public static Class<?> getReturnedDomainClass(Method method) {
|
||||
/**
|
||||
* Returns the domain class returned by the given {@link Method}. Will
|
||||
* extract the type from {@link Collection}s and
|
||||
* {@link org.springframework.data.domain.Page} as well.
|
||||
*
|
||||
* @param method
|
||||
* @return
|
||||
*/
|
||||
public static Class<?> getReturnedDomainClass(Method method) {
|
||||
|
||||
Class<?> type = method.getReturnType();
|
||||
Class<?> type = method.getReturnType();
|
||||
|
||||
if (Collection.class.isAssignableFrom(type)
|
||||
|| Page.class.isAssignableFrom(type)) {
|
||||
if (Collection.class.isAssignableFrom(type)
|
||||
|| Page.class.isAssignableFrom(type)) {
|
||||
|
||||
ParameterizedType returnType = (ParameterizedType) method.getGenericReturnType();
|
||||
Type componentType = returnType.getActualTypeArguments()[0];
|
||||
|
||||
return componentType instanceof ParameterizedType ? (Class<?>) ((ParameterizedType) componentType).getRawType()
|
||||
: (Class<?>) componentType;
|
||||
}
|
||||
ParameterizedType returnType = (ParameterizedType) method.getGenericReturnType();
|
||||
Type componentType = returnType.getActualTypeArguments()[0];
|
||||
|
||||
return type;
|
||||
}
|
||||
return componentType instanceof ParameterizedType ? (Class<?>) ((ParameterizedType) componentType).getRawType()
|
||||
: (Class<?>) componentType;
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the given class contains a property with the given name.
|
||||
*
|
||||
* @param fieldName
|
||||
* @return
|
||||
*/
|
||||
public static boolean hasProperty(Class<?> type, String property) {
|
||||
/**
|
||||
* Returns whether the given class contains a property with the given name.
|
||||
*
|
||||
* @param fieldName
|
||||
* @return
|
||||
*/
|
||||
public static boolean hasProperty(Class<?> type, String property) {
|
||||
|
||||
if (null != ReflectionUtils.findMethod(type, "get" + property)) {
|
||||
return true;
|
||||
}
|
||||
if (null != ReflectionUtils.findMethod(type, "get" + property)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return null != ReflectionUtils.findField(type,
|
||||
StringUtils.uncapitalize(property));
|
||||
}
|
||||
return null != ReflectionUtils.findField(type,
|
||||
StringUtils.uncapitalize(property));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns wthere the given type is the {@link Repository} interface.
|
||||
*
|
||||
* @param interfaze
|
||||
* @return
|
||||
*/
|
||||
public static boolean isGenericRepositoryInterface(Class<?> interfaze) {
|
||||
/**
|
||||
* Returns wthere the given type is the {@link Repository} interface.
|
||||
*
|
||||
* @param interfaze
|
||||
* @return
|
||||
*/
|
||||
public static boolean isGenericRepositoryInterface(Class<?> interfaze) {
|
||||
|
||||
return Repository.class.equals(interfaze);
|
||||
}
|
||||
return Repository.class.equals(interfaze);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the given type name is a repository interface name.
|
||||
*
|
||||
* @param interfaceName
|
||||
* @return
|
||||
*/
|
||||
public static boolean isGenericRepositoryInterface(String interfaceName) {
|
||||
/**
|
||||
* Returns whether the given type name is a repository interface name.
|
||||
*
|
||||
* @param interfaceName
|
||||
* @return
|
||||
*/
|
||||
public static boolean isGenericRepositoryInterface(String interfaceName) {
|
||||
|
||||
return Repository.class.getName().equals(interfaceName);
|
||||
}
|
||||
return Repository.class.getName().equals(interfaceName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of occurences of the given type in the given
|
||||
* {@link Method}s parameters.
|
||||
*
|
||||
* @param method
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public static int getNumberOfOccurences(Method method, Class<?> type) {
|
||||
/**
|
||||
* Returns the number of occurences of the given type in the given
|
||||
* {@link Method}s parameters.
|
||||
*
|
||||
* @param method
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public static int getNumberOfOccurences(Method method, Class<?> type) {
|
||||
|
||||
int result = 0;
|
||||
for (Class<?> clazz : method.getParameterTypes()) {
|
||||
if (type.equals(clazz)) {
|
||||
result++;
|
||||
}
|
||||
}
|
||||
int result = 0;
|
||||
for (Class<?> clazz : method.getParameterTypes()) {
|
||||
if (type.equals(clazz)) {
|
||||
result++;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Asserts the given {@link Method}'s return type to be one of the given
|
||||
* types.
|
||||
*
|
||||
* @param method
|
||||
* @param types
|
||||
*/
|
||||
public static void assertReturnType(Method method, Class<?>... types) {
|
||||
/**
|
||||
* Asserts the given {@link Method}'s return type to be one of the given
|
||||
* types.
|
||||
*
|
||||
* @param method
|
||||
* @param types
|
||||
*/
|
||||
public static void assertReturnType(Method method, Class<?>... types) {
|
||||
|
||||
if (!Arrays.asList(types).contains(method.getReturnType())) {
|
||||
throw new IllegalStateException(
|
||||
"Method has to have one of the following return types! "
|
||||
+ Arrays.toString(types));
|
||||
}
|
||||
}
|
||||
if (!Arrays.asList(types).contains(method.getReturnType())) {
|
||||
throw new IllegalStateException(
|
||||
"Method has to have one of the following return types! "
|
||||
+ Arrays.toString(types));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the given object is of one of the given types. Will
|
||||
* return {@literal false} for {@literal null}.
|
||||
*
|
||||
* @param object
|
||||
* @param types
|
||||
* @return
|
||||
*/
|
||||
public static boolean isOfType(Object object, Collection<Class<?>> types) {
|
||||
/**
|
||||
* Returns whether the given object is of one of the given types. Will
|
||||
* return {@literal false} for {@literal null}.
|
||||
*
|
||||
* @param object
|
||||
* @param types
|
||||
* @return
|
||||
*/
|
||||
public static boolean isOfType(Object object, Collection<Class<?>> types) {
|
||||
|
||||
if (null == object) {
|
||||
return false;
|
||||
}
|
||||
if (null == object) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Class<?> type : types) {
|
||||
if (type.isAssignableFrom(object.getClass())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (Class<?> type : types) {
|
||||
if (type.isAssignableFrom(object.getClass())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the given {@link Method} has a parameter of the given
|
||||
* type.
|
||||
*
|
||||
* @param method
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public static boolean hasParameterOfType(Method method, Class<?> type) {
|
||||
/**
|
||||
* Returns whether the given {@link Method} has a parameter of the given
|
||||
* type.
|
||||
*
|
||||
* @param method
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public static boolean hasParameterOfType(Method method, Class<?> type) {
|
||||
|
||||
return Arrays.asList(method.getParameterTypes()).contains(type);
|
||||
}
|
||||
return Arrays.asList(method.getParameterTypes()).contains(type);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper method to extract the original exception that can possibly occur
|
||||
* during a reflection call.
|
||||
*
|
||||
* @param ex
|
||||
* @throws Throwable
|
||||
*/
|
||||
public static void unwrapReflectionException(Exception ex) throws Throwable {
|
||||
/**
|
||||
* Helper method to extract the original exception that can possibly occur
|
||||
* during a reflection call.
|
||||
*
|
||||
* @param ex
|
||||
* @throws Throwable
|
||||
*/
|
||||
public static void unwrapReflectionException(Exception ex) throws Throwable {
|
||||
|
||||
if (ex instanceof InvocationTargetException) {
|
||||
throw ((InvocationTargetException) ex).getTargetException();
|
||||
}
|
||||
if (ex instanceof InvocationTargetException) {
|
||||
throw ((InvocationTargetException) ex).getTargetException();
|
||||
}
|
||||
|
||||
throw ex;
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,15 +17,15 @@ package org.springframework.data.repository.util;
|
||||
|
||||
/**
|
||||
* Simple constants holder.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public abstract class TxUtils {
|
||||
|
||||
private TxUtils() {
|
||||
private TxUtils() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static final String DEFAULT_TRANSACTION_MANAGER =
|
||||
"transactionManager";
|
||||
public static final String DEFAULT_TRANSACTION_MANAGER =
|
||||
"transactionManager";
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public class ChangeSetBackedTransactionSynchronization implements TransactionSyn
|
||||
this.changeSetPersister = changeSetPersister;
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
|
||||
public void afterCommit() {
|
||||
log.debug("After Commit called for " + entity);
|
||||
changeSetPersister.persistState(entity, entity.getChangeSet());
|
||||
@@ -33,8 +33,7 @@ public class ChangeSetBackedTransactionSynchronization implements TransactionSyn
|
||||
if (status == STATUS_COMMITTED) {
|
||||
// this is good
|
||||
log.debug("ChangedSetBackedTransactionSynchronization completed successfully for " + this.entity);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// this could be bad - TODO: compensate
|
||||
log.error("ChangedSetBackedTransactionSynchronization failed for " + this.entity);
|
||||
}
|
||||
@@ -57,5 +56,5 @@ public class ChangeSetBackedTransactionSynchronization implements TransactionSyn
|
||||
public void suspend() {
|
||||
throw new IllegalStateException("ChangedSetBackedTransactionSynchronization does not support transaction suspension currently.");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,75 +1,73 @@
|
||||
package org.springframework.data.transaction;
|
||||
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionException;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.DefaultTransactionStatus;
|
||||
|
||||
public class NaiveDoubleTransactionManager implements PlatformTransactionManager {
|
||||
Map<TransactionStatus,TransactionStatus> status=new IdentityHashMap<TransactionStatus, TransactionStatus>();
|
||||
private final PlatformTransactionManager a;
|
||||
|
||||
private final PlatformTransactionManager b;
|
||||
|
||||
public NaiveDoubleTransactionManager(PlatformTransactionManager a, PlatformTransactionManager b) {
|
||||
System.err.println("WARNING: Naive JTA/Neo4j Spring transaction manager--must implement properly");
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
public void commit(TransactionStatus ts) throws TransactionException {
|
||||
try {
|
||||
final TransactionStatus tsb = copyTransactionStatus(status.get(ts));
|
||||
try {
|
||||
a.commit(ts);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
System.err.println("Continuing to commit tx despite this:" + t);
|
||||
}
|
||||
try {
|
||||
b.commit(tsb);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
System.err.println("Can't commit tx" + t);
|
||||
throw new TransactionException(t.getMessage(), t) {};
|
||||
}
|
||||
} finally {
|
||||
status.remove(ts);
|
||||
}
|
||||
}
|
||||
|
||||
private TransactionStatus copyTransactionStatus(TransactionStatus ts) {
|
||||
Object t = (ts instanceof DefaultTransactionStatus) ? ((DefaultTransactionStatus) ts).getTransaction() : null;
|
||||
return new DefaultTransactionStatus(t,ts.isNewTransaction(), false, false, false, null);
|
||||
}
|
||||
|
||||
public TransactionStatus getTransaction(TransactionDefinition td)
|
||||
throws TransactionException {
|
||||
TransactionStatus atx = a.getTransaction(td);
|
||||
TransactionStatus btx = b.getTransaction(td);
|
||||
status.put(atx, btx);
|
||||
return atx;
|
||||
}
|
||||
|
||||
public void rollback(TransactionStatus ts) throws TransactionException {
|
||||
final TransactionStatus tsb = copyTransactionStatus(status.remove(ts));
|
||||
try {
|
||||
a.rollback(ts);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
System.err.println("Continuing to rollback tx despite this:" + t);
|
||||
}
|
||||
try {
|
||||
b.rollback(tsb);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
System.err.println("Can't rollback tx" + t);
|
||||
throw new TransactionException(t.getMessage(), t) {};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
package org.springframework.data.transaction;
|
||||
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionException;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.DefaultTransactionStatus;
|
||||
|
||||
public class NaiveDoubleTransactionManager implements PlatformTransactionManager {
|
||||
Map<TransactionStatus, TransactionStatus> status = new IdentityHashMap<TransactionStatus, TransactionStatus>();
|
||||
private final PlatformTransactionManager a;
|
||||
|
||||
private final PlatformTransactionManager b;
|
||||
|
||||
public NaiveDoubleTransactionManager(PlatformTransactionManager a, PlatformTransactionManager b) {
|
||||
System.err.println("WARNING: Naive JTA/Neo4j Spring transaction manager--must implement properly");
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
public void commit(TransactionStatus ts) throws TransactionException {
|
||||
try {
|
||||
final TransactionStatus tsb = copyTransactionStatus(status.get(ts));
|
||||
try {
|
||||
a.commit(ts);
|
||||
} catch (Throwable t) {
|
||||
System.err.println("Continuing to commit tx despite this:" + t);
|
||||
}
|
||||
try {
|
||||
b.commit(tsb);
|
||||
} catch (Throwable t) {
|
||||
System.err.println("Can't commit tx" + t);
|
||||
throw new TransactionException(t.getMessage(), t) {
|
||||
};
|
||||
}
|
||||
} finally {
|
||||
status.remove(ts);
|
||||
}
|
||||
}
|
||||
|
||||
private TransactionStatus copyTransactionStatus(TransactionStatus ts) {
|
||||
Object t = (ts instanceof DefaultTransactionStatus) ? ((DefaultTransactionStatus) ts).getTransaction() : null;
|
||||
return new DefaultTransactionStatus(t, ts.isNewTransaction(), false, false, false, null);
|
||||
}
|
||||
|
||||
public TransactionStatus getTransaction(TransactionDefinition td)
|
||||
throws TransactionException {
|
||||
TransactionStatus atx = a.getTransaction(td);
|
||||
TransactionStatus btx = b.getTransaction(td);
|
||||
status.put(atx, btx);
|
||||
return atx;
|
||||
}
|
||||
|
||||
public void rollback(TransactionStatus ts) throws TransactionException {
|
||||
final TransactionStatus tsb = copyTransactionStatus(status.remove(ts));
|
||||
try {
|
||||
a.rollback(ts);
|
||||
} catch (Throwable t) {
|
||||
System.err.println("Continuing to rollback tx despite this:" + t);
|
||||
}
|
||||
try {
|
||||
b.rollback(tsb);
|
||||
} catch (Throwable t) {
|
||||
System.err.println("Can't rollback tx" + t);
|
||||
throw new TransactionException(t.getMessage(), t) {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,35 +11,35 @@ import java.lang.reflect.Type;
|
||||
*/
|
||||
public class ArrayTypeDiscoverer<S> extends TypeDiscoverer<S> {
|
||||
|
||||
private GenericArrayType type;
|
||||
|
||||
/**
|
||||
* @param type
|
||||
* @param parent
|
||||
* @param parent
|
||||
*/
|
||||
protected ArrayTypeDiscoverer(GenericArrayType type, TypeDiscoverer<?> parent) {
|
||||
super(type, null, parent);
|
||||
this.type = type;
|
||||
}
|
||||
private GenericArrayType type;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeDiscoverer#getType()
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Class<S> getType() {
|
||||
|
||||
return (Class<S>) Array.newInstance(resolveType(type.getGenericComponentType()), 0).getClass();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeDiscoverer#getComponentType()
|
||||
*/
|
||||
@Override
|
||||
public TypeInformation<?> getComponentType() {
|
||||
|
||||
Type componentType = type.getGenericComponentType();
|
||||
return createInfo(componentType);
|
||||
}
|
||||
/**
|
||||
* @param type
|
||||
* @param parent
|
||||
* @param parent
|
||||
*/
|
||||
protected ArrayTypeDiscoverer(GenericArrayType type, TypeDiscoverer<?> parent) {
|
||||
super(type, null, parent);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeDiscoverer#getType()
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Class<S> getType() {
|
||||
|
||||
return (Class<S>) Array.newInstance(resolveType(type.getGenericComponentType()), 0).getClass();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeDiscoverer#getComponentType()
|
||||
*/
|
||||
@Override
|
||||
public TypeInformation<?> getComponentType() {
|
||||
|
||||
Type componentType = type.getGenericComponentType();
|
||||
return createInfo(componentType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,88 +8,87 @@ import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Property information for a plain {@link Class}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class ClassTypeInformation<S> extends TypeDiscoverer<S> {
|
||||
|
||||
private final Class<S> type;
|
||||
|
||||
public static <S> TypeInformation<S> from(Class<S> type) {
|
||||
return new ClassTypeInformation<S>(type);
|
||||
}
|
||||
private final Class<S> type;
|
||||
|
||||
/**
|
||||
* Creates {@link ClassTypeInformation} for the given type.
|
||||
*
|
||||
* @param type
|
||||
*/
|
||||
public ClassTypeInformation(Class<S> type) {
|
||||
this(type, GenericTypeResolver.getTypeVariableMap(type), null);
|
||||
}
|
||||
public static <S> TypeInformation<S> from(Class<S> type) {
|
||||
return new ClassTypeInformation<S>(type);
|
||||
}
|
||||
|
||||
ClassTypeInformation(Class<S> type, TypeDiscoverer<?> parent) {
|
||||
this(type, null, parent);
|
||||
}
|
||||
/**
|
||||
* Creates {@link ClassTypeInformation} for the given type.
|
||||
*
|
||||
* @param type
|
||||
*/
|
||||
public ClassTypeInformation(Class<S> type) {
|
||||
this(type, GenericTypeResolver.getTypeVariableMap(type), null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
ClassTypeInformation(Class<S> type, Map<TypeVariable, Type> typeVariableMap,
|
||||
TypeDiscoverer<?> parent) {
|
||||
super(type, typeVariableMap, parent);
|
||||
this.type = type;
|
||||
}
|
||||
ClassTypeInformation(Class<S> type, TypeDiscoverer<?> parent) {
|
||||
this(type, null, parent);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.document.mongodb.TypeDiscovererTest.FieldInformation#getType()
|
||||
*/
|
||||
@Override
|
||||
public Class<S> getType() {
|
||||
return type;
|
||||
}
|
||||
@SuppressWarnings("rawtypes") ClassTypeInformation(Class<S> type, Map<TypeVariable, Type> typeVariableMap,
|
||||
TypeDiscoverer<?> parent) {
|
||||
super(type, typeVariableMap, parent);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeDiscoverer#getComponentType()
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public TypeInformation<?> getComponentType() {
|
||||
|
||||
if (type.isArray()) {
|
||||
return createInfo(resolveArrayType(type));
|
||||
}
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.document.mongodb.TypeDiscovererTest.FieldInformation#getType()
|
||||
*/
|
||||
@Override
|
||||
public Class<S> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
TypeVariable<?>[] typeParameters = type.getTypeParameters();
|
||||
return typeParameters.length > 0 ? new TypeVariableTypeInformation(typeParameters[0], this.getType(), this) : null;
|
||||
}
|
||||
|
||||
private static Type resolveArrayType(Class<?> type) {
|
||||
Assert.isTrue(type.isArray());
|
||||
Class<?> componentType = type.getComponentType();
|
||||
return componentType.isArray() ? resolveArrayType(componentType) : componentType;
|
||||
}
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeDiscoverer#getComponentType()
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public TypeInformation<?> getComponentType() {
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeDiscoverer#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (type.isArray()) {
|
||||
return createInfo(resolveArrayType(type));
|
||||
}
|
||||
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
TypeVariable<?>[] typeParameters = type.getTypeParameters();
|
||||
return typeParameters.length > 0 ? new TypeVariableTypeInformation(typeParameters[0], this.getType(), this) : null;
|
||||
}
|
||||
|
||||
ClassTypeInformation<?> that = (ClassTypeInformation<?>) obj;
|
||||
return this.type.equals(that.type);
|
||||
}
|
||||
private static Type resolveArrayType(Class<?> type) {
|
||||
Assert.isTrue(type.isArray());
|
||||
Class<?> componentType = type.getComponentType();
|
||||
return componentType.isArray() ? resolveArrayType(componentType) : componentType;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeDiscoverer#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
return result += 31 * type.hashCode();
|
||||
}
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeDiscoverer#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ClassTypeInformation<?> that = (ClassTypeInformation<?>) obj;
|
||||
return this.type.equals(that.type);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeDiscoverer#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
return result += 31 * type.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -34,321 +34,315 @@ import org.springframework.util.Assert;
|
||||
/**
|
||||
* Copy of Spring's {@link org.springframework.core.GenericTypeResolver}. Needed
|
||||
* until {@link #getTypeVariableMap(Class)} gets public.
|
||||
*
|
||||
* <p/>
|
||||
* TODO: remove that class, as soon as Spring 3.0.6 gets released.
|
||||
*
|
||||
* @see SPR-8005
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
abstract class GenericTypeResolver {
|
||||
|
||||
/** Cache from Class to TypeVariable Map */
|
||||
private static final Map<Class, Reference<Map<TypeVariable, Type>>> typeVariableCache = Collections
|
||||
.synchronizedMap(new WeakHashMap<Class, Reference<Map<TypeVariable, Type>>>());
|
||||
/**
|
||||
* Cache from Class to TypeVariable Map
|
||||
*/
|
||||
private static final Map<Class, Reference<Map<TypeVariable, Type>>> typeVariableCache = Collections
|
||||
.synchronizedMap(new WeakHashMap<Class, Reference<Map<TypeVariable, Type>>>());
|
||||
|
||||
/**
|
||||
* Determine the target type for the given parameter specification.
|
||||
*
|
||||
* @param methodParam
|
||||
* the method parameter specification
|
||||
* @return the corresponding generic parameter type
|
||||
*/
|
||||
public static Type getTargetType(MethodParameter methodParam) {
|
||||
Assert.notNull(methodParam, "MethodParameter must not be null");
|
||||
if (methodParam.getConstructor() != null) {
|
||||
return methodParam.getConstructor().getGenericParameterTypes()[methodParam
|
||||
.getParameterIndex()];
|
||||
} else {
|
||||
if (methodParam.getParameterIndex() >= 0) {
|
||||
return methodParam.getMethod().getGenericParameterTypes()[methodParam
|
||||
.getParameterIndex()];
|
||||
} else {
|
||||
return methodParam.getMethod().getGenericReturnType();
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Determine the target type for the given parameter specification.
|
||||
*
|
||||
* @param methodParam the method parameter specification
|
||||
* @return the corresponding generic parameter type
|
||||
*/
|
||||
public static Type getTargetType(MethodParameter methodParam) {
|
||||
Assert.notNull(methodParam, "MethodParameter must not be null");
|
||||
if (methodParam.getConstructor() != null) {
|
||||
return methodParam.getConstructor().getGenericParameterTypes()[methodParam
|
||||
.getParameterIndex()];
|
||||
} else {
|
||||
if (methodParam.getParameterIndex() >= 0) {
|
||||
return methodParam.getMethod().getGenericParameterTypes()[methodParam
|
||||
.getParameterIndex()];
|
||||
} else {
|
||||
return methodParam.getMethod().getGenericReturnType();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the single type argument of the given generic interface against the
|
||||
* given target class which is assumed to implement the generic interface and
|
||||
* possibly declare a concrete type for its type variable.
|
||||
*
|
||||
* @param clazz
|
||||
* the target class to check against
|
||||
* @param genericIfc
|
||||
* the generic interface or superclass to resolve the type argument
|
||||
* from
|
||||
* @return the resolved type of the argument, or <code>null</code> if not
|
||||
* resolvable
|
||||
*/
|
||||
public static Class<?> resolveTypeArgument(Class clazz, Class genericIfc) {
|
||||
Class[] typeArgs = resolveTypeArguments(clazz, genericIfc);
|
||||
if (typeArgs == null) {
|
||||
return null;
|
||||
}
|
||||
if (typeArgs.length != 1) {
|
||||
throw new IllegalArgumentException(
|
||||
"Expected 1 type argument on generic interface ["
|
||||
+ genericIfc.getName() + "] but found " + typeArgs.length);
|
||||
}
|
||||
return typeArgs[0];
|
||||
}
|
||||
/**
|
||||
* Resolve the single type argument of the given generic interface against the
|
||||
* given target class which is assumed to implement the generic interface and
|
||||
* possibly declare a concrete type for its type variable.
|
||||
*
|
||||
* @param clazz the target class to check against
|
||||
* @param genericIfc the generic interface or superclass to resolve the type argument
|
||||
* from
|
||||
* @return the resolved type of the argument, or <code>null</code> if not
|
||||
* resolvable
|
||||
*/
|
||||
public static Class<?> resolveTypeArgument(Class clazz, Class genericIfc) {
|
||||
Class[] typeArgs = resolveTypeArguments(clazz, genericIfc);
|
||||
if (typeArgs == null) {
|
||||
return null;
|
||||
}
|
||||
if (typeArgs.length != 1) {
|
||||
throw new IllegalArgumentException(
|
||||
"Expected 1 type argument on generic interface ["
|
||||
+ genericIfc.getName() + "] but found " + typeArgs.length);
|
||||
}
|
||||
return typeArgs[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the type arguments of the given generic interface against the given
|
||||
* target class which is assumed to implement the generic interface and
|
||||
* possibly declare concrete types for its type variables.
|
||||
*
|
||||
* @param clazz
|
||||
* the target class to check against
|
||||
* @param genericIfc
|
||||
* the generic interface or superclass to resolve the type argument
|
||||
* from
|
||||
* @return the resolved type of each argument, with the array size matching
|
||||
* the number of actual type arguments, or <code>null</code> if not
|
||||
* resolvable
|
||||
*/
|
||||
public static Class[] resolveTypeArguments(Class clazz, Class genericIfc) {
|
||||
return doResolveTypeArguments(clazz, clazz, genericIfc);
|
||||
}
|
||||
/**
|
||||
* Resolve the type arguments of the given generic interface against the given
|
||||
* target class which is assumed to implement the generic interface and
|
||||
* possibly declare concrete types for its type variables.
|
||||
*
|
||||
* @param clazz the target class to check against
|
||||
* @param genericIfc the generic interface or superclass to resolve the type argument
|
||||
* from
|
||||
* @return the resolved type of each argument, with the array size matching
|
||||
* the number of actual type arguments, or <code>null</code> if not
|
||||
* resolvable
|
||||
*/
|
||||
public static Class[] resolveTypeArguments(Class clazz, Class genericIfc) {
|
||||
return doResolveTypeArguments(clazz, clazz, genericIfc);
|
||||
}
|
||||
|
||||
private static Class[] doResolveTypeArguments(Class ownerClass,
|
||||
Class classToIntrospect, Class genericIfc) {
|
||||
while (classToIntrospect != null) {
|
||||
if (genericIfc.isInterface()) {
|
||||
Type[] ifcs = classToIntrospect.getGenericInterfaces();
|
||||
for (Type ifc : ifcs) {
|
||||
Class[] result = doResolveTypeArguments(ownerClass, ifc, genericIfc);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Class[] result = doResolveTypeArguments(ownerClass,
|
||||
classToIntrospect.getGenericSuperclass(), genericIfc);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
classToIntrospect = classToIntrospect.getSuperclass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private static Class[] doResolveTypeArguments(Class ownerClass,
|
||||
Class classToIntrospect, Class genericIfc) {
|
||||
while (classToIntrospect != null) {
|
||||
if (genericIfc.isInterface()) {
|
||||
Type[] ifcs = classToIntrospect.getGenericInterfaces();
|
||||
for (Type ifc : ifcs) {
|
||||
Class[] result = doResolveTypeArguments(ownerClass, ifc, genericIfc);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Class[] result = doResolveTypeArguments(ownerClass,
|
||||
classToIntrospect.getGenericSuperclass(), genericIfc);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
classToIntrospect = classToIntrospect.getSuperclass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Class[] doResolveTypeArguments(Class ownerClass, Type ifc,
|
||||
Class genericIfc) {
|
||||
if (ifc instanceof ParameterizedType) {
|
||||
ParameterizedType paramIfc = (ParameterizedType) ifc;
|
||||
Type rawType = paramIfc.getRawType();
|
||||
if (genericIfc.equals(rawType)) {
|
||||
Type[] typeArgs = paramIfc.getActualTypeArguments();
|
||||
Class[] result = new Class[typeArgs.length];
|
||||
for (int i = 0; i < typeArgs.length; i++) {
|
||||
Type arg = typeArgs[i];
|
||||
result[i] = extractClass(ownerClass, arg);
|
||||
}
|
||||
return result;
|
||||
} else if (genericIfc.isAssignableFrom((Class) rawType)) {
|
||||
return doResolveTypeArguments(ownerClass, (Class) rawType, genericIfc);
|
||||
}
|
||||
} else if (genericIfc.isAssignableFrom((Class) ifc)) {
|
||||
return doResolveTypeArguments(ownerClass, (Class) ifc, genericIfc);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private static Class[] doResolveTypeArguments(Class ownerClass, Type ifc,
|
||||
Class genericIfc) {
|
||||
if (ifc instanceof ParameterizedType) {
|
||||
ParameterizedType paramIfc = (ParameterizedType) ifc;
|
||||
Type rawType = paramIfc.getRawType();
|
||||
if (genericIfc.equals(rawType)) {
|
||||
Type[] typeArgs = paramIfc.getActualTypeArguments();
|
||||
Class[] result = new Class[typeArgs.length];
|
||||
for (int i = 0; i < typeArgs.length; i++) {
|
||||
Type arg = typeArgs[i];
|
||||
result[i] = extractClass(ownerClass, arg);
|
||||
}
|
||||
return result;
|
||||
} else if (genericIfc.isAssignableFrom((Class) rawType)) {
|
||||
return doResolveTypeArguments(ownerClass, (Class) rawType, genericIfc);
|
||||
}
|
||||
} else if (genericIfc.isAssignableFrom((Class) ifc)) {
|
||||
return doResolveTypeArguments(ownerClass, (Class) ifc, genericIfc);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a class instance from given Type.
|
||||
*/
|
||||
private static Class extractClass(Class ownerClass, Type arg) {
|
||||
if (arg instanceof ParameterizedType) {
|
||||
return extractClass(ownerClass, ((ParameterizedType) arg).getRawType());
|
||||
} else if (arg instanceof GenericArrayType) {
|
||||
GenericArrayType gat = (GenericArrayType) arg;
|
||||
Type gt = gat.getGenericComponentType();
|
||||
Class<?> componentClass = extractClass(ownerClass, gt);
|
||||
return Array.newInstance(componentClass, 0).getClass();
|
||||
} else if (arg instanceof TypeVariable) {
|
||||
TypeVariable tv = (TypeVariable) arg;
|
||||
arg = getTypeVariableMap(ownerClass).get(tv);
|
||||
if (arg == null) {
|
||||
arg = extractBoundForTypeVariable(tv);
|
||||
} else {
|
||||
arg = extractClass(ownerClass, arg);
|
||||
}
|
||||
}
|
||||
return (arg instanceof Class ? (Class) arg : Object.class);
|
||||
}
|
||||
/**
|
||||
* Extract a class instance from given Type.
|
||||
*/
|
||||
private static Class extractClass(Class ownerClass, Type arg) {
|
||||
if (arg instanceof ParameterizedType) {
|
||||
return extractClass(ownerClass, ((ParameterizedType) arg).getRawType());
|
||||
} else if (arg instanceof GenericArrayType) {
|
||||
GenericArrayType gat = (GenericArrayType) arg;
|
||||
Type gt = gat.getGenericComponentType();
|
||||
Class<?> componentClass = extractClass(ownerClass, gt);
|
||||
return Array.newInstance(componentClass, 0).getClass();
|
||||
} else if (arg instanceof TypeVariable) {
|
||||
TypeVariable tv = (TypeVariable) arg;
|
||||
arg = getTypeVariableMap(ownerClass).get(tv);
|
||||
if (arg == null) {
|
||||
arg = extractBoundForTypeVariable(tv);
|
||||
} else {
|
||||
arg = extractClass(ownerClass, arg);
|
||||
}
|
||||
}
|
||||
return (arg instanceof Class ? (Class) arg : Object.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the specified generic type against the given TypeVariable map.
|
||||
*
|
||||
* @param genericType
|
||||
* the generic type to resolve
|
||||
* @param typeVariableMap
|
||||
* the TypeVariable Map to resolved against
|
||||
* @return the type if it resolves to a Class, or <code>Object.class</code>
|
||||
* otherwise
|
||||
*/
|
||||
static Class resolveType(Type genericType,
|
||||
Map<TypeVariable, Type> typeVariableMap) {
|
||||
Type rawType = getRawType(genericType, typeVariableMap);
|
||||
return (rawType instanceof Class ? (Class) rawType : Object.class);
|
||||
}
|
||||
/**
|
||||
* Resolve the specified generic type against the given TypeVariable map.
|
||||
*
|
||||
* @param genericType the generic type to resolve
|
||||
* @param typeVariableMap the TypeVariable Map to resolved against
|
||||
* @return the type if it resolves to a Class, or <code>Object.class</code>
|
||||
* otherwise
|
||||
*/
|
||||
static Class resolveType(Type genericType,
|
||||
Map<TypeVariable, Type> typeVariableMap) {
|
||||
Type rawType = getRawType(genericType, typeVariableMap);
|
||||
return (rawType instanceof Class ? (Class) rawType : Object.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the raw type for the given generic parameter type.
|
||||
*
|
||||
* @param genericType
|
||||
* the generic type to resolve
|
||||
* @param typeVariableMap
|
||||
* the TypeVariable Map to resolved against
|
||||
* @return the resolved raw type
|
||||
*/
|
||||
static Type getRawType(Type genericType,
|
||||
Map<TypeVariable, Type> typeVariableMap) {
|
||||
Type resolvedType = genericType;
|
||||
if (genericType instanceof TypeVariable) {
|
||||
TypeVariable tv = (TypeVariable) genericType;
|
||||
resolvedType = typeVariableMap.get(tv);
|
||||
if (resolvedType == null) {
|
||||
resolvedType = extractBoundForTypeVariable(tv);
|
||||
}
|
||||
}
|
||||
if (resolvedType instanceof ParameterizedType) {
|
||||
return ((ParameterizedType) resolvedType).getRawType();
|
||||
} else {
|
||||
return resolvedType;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Determine the raw type for the given generic parameter type.
|
||||
*
|
||||
* @param genericType the generic type to resolve
|
||||
* @param typeVariableMap the TypeVariable Map to resolved against
|
||||
* @return the resolved raw type
|
||||
*/
|
||||
static Type getRawType(Type genericType,
|
||||
Map<TypeVariable, Type> typeVariableMap) {
|
||||
Type resolvedType = genericType;
|
||||
if (genericType instanceof TypeVariable) {
|
||||
TypeVariable tv = (TypeVariable) genericType;
|
||||
resolvedType = typeVariableMap.get(tv);
|
||||
if (resolvedType == null) {
|
||||
resolvedType = extractBoundForTypeVariable(tv);
|
||||
}
|
||||
}
|
||||
if (resolvedType instanceof ParameterizedType) {
|
||||
return ((ParameterizedType) resolvedType).getRawType();
|
||||
} else {
|
||||
return resolvedType;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a mapping of {@link TypeVariable#getName TypeVariable names} to
|
||||
* concrete {@link Class} for the specified {@link Class}. Searches all super
|
||||
* types, enclosing types and interfaces.
|
||||
*/
|
||||
static Map<TypeVariable, Type> getTypeVariableMap(Class clazz) {
|
||||
Reference<Map<TypeVariable, Type>> ref = typeVariableCache.get(clazz);
|
||||
Map<TypeVariable, Type> typeVariableMap = (ref != null ? ref.get() : null);
|
||||
/**
|
||||
* Build a mapping of {@link TypeVariable#getName TypeVariable names} to
|
||||
* concrete {@link Class} for the specified {@link Class}. Searches all super
|
||||
* types, enclosing types and interfaces.
|
||||
*/
|
||||
static Map<TypeVariable, Type> getTypeVariableMap(Class clazz) {
|
||||
Reference<Map<TypeVariable, Type>> ref = typeVariableCache.get(clazz);
|
||||
Map<TypeVariable, Type> typeVariableMap = (ref != null ? ref.get() : null);
|
||||
|
||||
if (typeVariableMap == null) {
|
||||
typeVariableMap = new HashMap<TypeVariable, Type>();
|
||||
if (typeVariableMap == null) {
|
||||
typeVariableMap = new HashMap<TypeVariable, Type>();
|
||||
|
||||
// interfaces
|
||||
extractTypeVariablesFromGenericInterfaces(clazz.getGenericInterfaces(),
|
||||
typeVariableMap);
|
||||
// interfaces
|
||||
extractTypeVariablesFromGenericInterfaces(clazz.getGenericInterfaces(),
|
||||
typeVariableMap);
|
||||
|
||||
// super class
|
||||
Type genericType = clazz.getGenericSuperclass();
|
||||
Class type = clazz.getSuperclass();
|
||||
while (type != null && !Object.class.equals(type)) {
|
||||
if (genericType instanceof ParameterizedType) {
|
||||
ParameterizedType pt = (ParameterizedType) genericType;
|
||||
populateTypeMapFromParameterizedType(pt, typeVariableMap);
|
||||
}
|
||||
extractTypeVariablesFromGenericInterfaces(type.getGenericInterfaces(),
|
||||
typeVariableMap);
|
||||
genericType = type.getGenericSuperclass();
|
||||
type = type.getSuperclass();
|
||||
}
|
||||
// super class
|
||||
Type genericType = clazz.getGenericSuperclass();
|
||||
Class type = clazz.getSuperclass();
|
||||
while (type != null && !Object.class.equals(type)) {
|
||||
if (genericType instanceof ParameterizedType) {
|
||||
ParameterizedType pt = (ParameterizedType) genericType;
|
||||
populateTypeMapFromParameterizedType(pt, typeVariableMap);
|
||||
}
|
||||
extractTypeVariablesFromGenericInterfaces(type.getGenericInterfaces(),
|
||||
typeVariableMap);
|
||||
genericType = type.getGenericSuperclass();
|
||||
type = type.getSuperclass();
|
||||
}
|
||||
|
||||
// enclosing class
|
||||
type = clazz;
|
||||
while (type.isMemberClass()) {
|
||||
genericType = type.getGenericSuperclass();
|
||||
if (genericType instanceof ParameterizedType) {
|
||||
ParameterizedType pt = (ParameterizedType) genericType;
|
||||
populateTypeMapFromParameterizedType(pt, typeVariableMap);
|
||||
}
|
||||
type = type.getEnclosingClass();
|
||||
}
|
||||
// enclosing class
|
||||
type = clazz;
|
||||
while (type.isMemberClass()) {
|
||||
genericType = type.getGenericSuperclass();
|
||||
if (genericType instanceof ParameterizedType) {
|
||||
ParameterizedType pt = (ParameterizedType) genericType;
|
||||
populateTypeMapFromParameterizedType(pt, typeVariableMap);
|
||||
}
|
||||
type = type.getEnclosingClass();
|
||||
}
|
||||
|
||||
typeVariableCache.put(clazz, new WeakReference<Map<TypeVariable, Type>>(
|
||||
typeVariableMap));
|
||||
}
|
||||
typeVariableCache.put(clazz, new WeakReference<Map<TypeVariable, Type>>(
|
||||
typeVariableMap));
|
||||
}
|
||||
|
||||
return typeVariableMap;
|
||||
}
|
||||
return typeVariableMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the bound <code>Type</code> for a given {@link TypeVariable}.
|
||||
*/
|
||||
static Type extractBoundForTypeVariable(TypeVariable typeVariable) {
|
||||
Type[] bounds = typeVariable.getBounds();
|
||||
if (bounds.length == 0) {
|
||||
return Object.class;
|
||||
}
|
||||
Type bound = bounds[0];
|
||||
if (bound instanceof TypeVariable) {
|
||||
bound = extractBoundForTypeVariable((TypeVariable) bound);
|
||||
}
|
||||
return bound;
|
||||
}
|
||||
/**
|
||||
* Extracts the bound <code>Type</code> for a given {@link TypeVariable}.
|
||||
*/
|
||||
static Type extractBoundForTypeVariable(TypeVariable typeVariable) {
|
||||
Type[] bounds = typeVariable.getBounds();
|
||||
if (bounds.length == 0) {
|
||||
return Object.class;
|
||||
}
|
||||
Type bound = bounds[0];
|
||||
if (bound instanceof TypeVariable) {
|
||||
bound = extractBoundForTypeVariable((TypeVariable) bound);
|
||||
}
|
||||
return bound;
|
||||
}
|
||||
|
||||
private static void extractTypeVariablesFromGenericInterfaces(
|
||||
Type[] genericInterfaces, Map<TypeVariable, Type> typeVariableMap) {
|
||||
for (Type genericInterface : genericInterfaces) {
|
||||
if (genericInterface instanceof ParameterizedType) {
|
||||
ParameterizedType pt = (ParameterizedType) genericInterface;
|
||||
populateTypeMapFromParameterizedType(pt, typeVariableMap);
|
||||
if (pt.getRawType() instanceof Class) {
|
||||
extractTypeVariablesFromGenericInterfaces(
|
||||
((Class) pt.getRawType()).getGenericInterfaces(), typeVariableMap);
|
||||
}
|
||||
} else if (genericInterface instanceof Class) {
|
||||
extractTypeVariablesFromGenericInterfaces(
|
||||
((Class) genericInterface).getGenericInterfaces(), typeVariableMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
private static void extractTypeVariablesFromGenericInterfaces(
|
||||
Type[] genericInterfaces, Map<TypeVariable, Type> typeVariableMap) {
|
||||
for (Type genericInterface : genericInterfaces) {
|
||||
if (genericInterface instanceof ParameterizedType) {
|
||||
ParameterizedType pt = (ParameterizedType) genericInterface;
|
||||
populateTypeMapFromParameterizedType(pt, typeVariableMap);
|
||||
if (pt.getRawType() instanceof Class) {
|
||||
extractTypeVariablesFromGenericInterfaces(
|
||||
((Class) pt.getRawType()).getGenericInterfaces(), typeVariableMap);
|
||||
}
|
||||
} else if (genericInterface instanceof Class) {
|
||||
extractTypeVariablesFromGenericInterfaces(
|
||||
((Class) genericInterface).getGenericInterfaces(), typeVariableMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the {@link TypeVariable TypeVariables} from the supplied
|
||||
* {@link ParameterizedType} and add mappings corresponding to the
|
||||
* {@link TypeVariable#getName TypeVariable name} -> concrete type to the
|
||||
* supplied {@link Map}.
|
||||
* <p>
|
||||
* Consider this case:
|
||||
*
|
||||
* <pre class="code>
|
||||
* public interface Foo<S, T> {
|
||||
* ..
|
||||
* }
|
||||
*
|
||||
* public class FooImpl implements Foo<String, Integer> {
|
||||
* ..
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* For '<code>FooImpl</code>' the following mappings would be added to the
|
||||
* {@link Map}: {S=java.lang.String, T=java.lang.Integer}.
|
||||
*/
|
||||
private static void populateTypeMapFromParameterizedType(
|
||||
ParameterizedType type, Map<TypeVariable, Type> typeVariableMap) {
|
||||
if (type.getRawType() instanceof Class) {
|
||||
Type[] actualTypeArguments = type.getActualTypeArguments();
|
||||
TypeVariable[] typeVariables = ((Class) type.getRawType())
|
||||
.getTypeParameters();
|
||||
for (int i = 0; i < actualTypeArguments.length; i++) {
|
||||
Type actualTypeArgument = actualTypeArguments[i];
|
||||
TypeVariable variable = typeVariables[i];
|
||||
if (actualTypeArgument instanceof Class) {
|
||||
typeVariableMap.put(variable, actualTypeArgument);
|
||||
} else if (actualTypeArgument instanceof GenericArrayType) {
|
||||
typeVariableMap.put(variable, actualTypeArgument);
|
||||
} else if (actualTypeArgument instanceof ParameterizedType) {
|
||||
typeVariableMap.put(variable, actualTypeArgument);
|
||||
} else if (actualTypeArgument instanceof TypeVariable) {
|
||||
// We have a type that is parameterized at instantiation time
|
||||
// the nearest match on the bridge method will be the bounded type.
|
||||
TypeVariable typeVariableArgument = (TypeVariable) actualTypeArgument;
|
||||
Type resolvedType = typeVariableMap.get(typeVariableArgument);
|
||||
if (resolvedType == null) {
|
||||
resolvedType = extractBoundForTypeVariable(typeVariableArgument);
|
||||
}
|
||||
typeVariableMap.put(variable, resolvedType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Read the {@link TypeVariable TypeVariables} from the supplied
|
||||
* {@link ParameterizedType} and add mappings corresponding to the
|
||||
* {@link TypeVariable#getName TypeVariable name} -> concrete type to the
|
||||
* supplied {@link Map}.
|
||||
* <p/>
|
||||
* Consider this case:
|
||||
* <p/>
|
||||
* <pre class="code>
|
||||
* public interface Foo<S, T> {
|
||||
* ..
|
||||
* }
|
||||
* <p/>
|
||||
* public class FooImpl implements Foo<String, Integer> {
|
||||
* ..
|
||||
* }
|
||||
* </pre>
|
||||
* <p/>
|
||||
* For '<code>FooImpl</code>' the following mappings would be added to the
|
||||
* {@link Map}: {S=java.lang.String, T=java.lang.Integer}.
|
||||
*/
|
||||
private static void populateTypeMapFromParameterizedType(
|
||||
ParameterizedType type, Map<TypeVariable, Type> typeVariableMap) {
|
||||
if (type.getRawType() instanceof Class) {
|
||||
Type[] actualTypeArguments = type.getActualTypeArguments();
|
||||
TypeVariable[] typeVariables = ((Class) type.getRawType())
|
||||
.getTypeParameters();
|
||||
for (int i = 0; i < actualTypeArguments.length; i++) {
|
||||
Type actualTypeArgument = actualTypeArguments[i];
|
||||
TypeVariable variable = typeVariables[i];
|
||||
if (actualTypeArgument instanceof Class) {
|
||||
typeVariableMap.put(variable, actualTypeArgument);
|
||||
} else if (actualTypeArgument instanceof GenericArrayType) {
|
||||
typeVariableMap.put(variable, actualTypeArgument);
|
||||
} else if (actualTypeArgument instanceof ParameterizedType) {
|
||||
typeVariableMap.put(variable, actualTypeArgument);
|
||||
} else if (actualTypeArgument instanceof TypeVariable) {
|
||||
// We have a type that is parameterized at instantiation time
|
||||
// the nearest match on the bridge method will be the bounded type.
|
||||
TypeVariable typeVariableArgument = (TypeVariable) actualTypeArgument;
|
||||
Type resolvedType = typeVariableMap.get(typeVariableArgument);
|
||||
if (resolvedType == null) {
|
||||
resolvedType = extractBoundForTypeVariable(typeVariableArgument);
|
||||
}
|
||||
typeVariableMap.put(variable, resolvedType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,241 +19,241 @@ import org.springframework.util.ReflectionUtils;
|
||||
/**
|
||||
* Basic {@link TypeDiscoverer} that contains basic functionality to discover
|
||||
* property types.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
class TypeDiscoverer<S> implements TypeInformation<S> {
|
||||
|
||||
private final Type type;
|
||||
@SuppressWarnings("rawtypes")
|
||||
private final Map<TypeVariable, Type> typeVariableMap;
|
||||
private final Map<String, TypeInformation<?>> fieldTypes = new ConcurrentHashMap<String, TypeInformation<?>>();
|
||||
private final TypeDiscoverer<?> parent;
|
||||
private final Type type;
|
||||
@SuppressWarnings("rawtypes")
|
||||
private final Map<TypeVariable, Type> typeVariableMap;
|
||||
private final Map<String, TypeInformation<?>> fieldTypes = new ConcurrentHashMap<String, TypeInformation<?>>();
|
||||
private final TypeDiscoverer<?> parent;
|
||||
|
||||
/**
|
||||
* Creates a ne {@link TypeDiscoverer} for the given type, type variable map and parent.
|
||||
*
|
||||
* @param type must not be null.
|
||||
* @param typeVariableMap
|
||||
* @param parent
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
protected TypeDiscoverer(Type type, Map<TypeVariable, Type> typeVariableMap,
|
||||
TypeDiscoverer<?> parent) {
|
||||
/**
|
||||
* Creates a ne {@link TypeDiscoverer} for the given type, type variable map and parent.
|
||||
*
|
||||
* @param type must not be null.
|
||||
* @param typeVariableMap
|
||||
* @param parent
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
protected TypeDiscoverer(Type type, Map<TypeVariable, Type> typeVariableMap,
|
||||
TypeDiscoverer<?> parent) {
|
||||
|
||||
Assert.notNull(type);
|
||||
this.type = type;
|
||||
this.typeVariableMap = typeVariableMap;
|
||||
this.parent = parent;
|
||||
}
|
||||
Assert.notNull(type);
|
||||
this.type = type;
|
||||
this.typeVariableMap = typeVariableMap;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type variable map. Will traverse the parents up to the root on
|
||||
* and use it's map.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
private Map<TypeVariable, Type> getTypeVariableMap() {
|
||||
/**
|
||||
* Returns the type variable map. Will traverse the parents up to the root on
|
||||
* and use it's map.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
private Map<TypeVariable, Type> getTypeVariableMap() {
|
||||
|
||||
return parent != null ? parent.getTypeVariableMap() : typeVariableMap;
|
||||
}
|
||||
return parent != null ? parent.getTypeVariableMap() : typeVariableMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates {@link TypeInformation} for the given {@link Type}.
|
||||
*
|
||||
* @param fieldType
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
protected TypeInformation<?> createInfo(Type fieldType) {
|
||||
|
||||
if (fieldType.equals(this.type)) {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Creates {@link TypeInformation} for the given {@link Type}.
|
||||
*
|
||||
* @param fieldType
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
protected TypeInformation<?> createInfo(Type fieldType) {
|
||||
|
||||
if (fieldType instanceof ParameterizedType) {
|
||||
ParameterizedType parameterizedType = (ParameterizedType) fieldType;
|
||||
return new TypeDiscoverer(parameterizedType, null, this);
|
||||
}
|
||||
if (fieldType.equals(this.type)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (fieldType instanceof TypeVariable) {
|
||||
TypeVariable<?> variable = (TypeVariable<?>) fieldType;
|
||||
return new TypeVariableTypeInformation(variable, type, this);
|
||||
}
|
||||
if (fieldType instanceof ParameterizedType) {
|
||||
ParameterizedType parameterizedType = (ParameterizedType) fieldType;
|
||||
return new TypeDiscoverer(parameterizedType, null, this);
|
||||
}
|
||||
|
||||
if (fieldType instanceof Class) {
|
||||
return new ClassTypeInformation((Class<?>) fieldType, this);
|
||||
}
|
||||
|
||||
if (fieldType instanceof GenericArrayType) {
|
||||
return new ArrayTypeDiscoverer((GenericArrayType) fieldType, this);
|
||||
}
|
||||
if (fieldType instanceof TypeVariable) {
|
||||
TypeVariable<?> variable = (TypeVariable<?>) fieldType;
|
||||
return new TypeVariableTypeInformation(variable, type, this);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (fieldType instanceof Class) {
|
||||
return new ClassTypeInformation((Class<?>) fieldType, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the given type into a plain {@link Class}.
|
||||
*
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Class<S> resolveType(Type type) {
|
||||
if (fieldType instanceof GenericArrayType) {
|
||||
return new ArrayTypeDiscoverer((GenericArrayType) fieldType, this);
|
||||
}
|
||||
|
||||
return GenericTypeResolver.resolveType(type, getTypeVariableMap());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeInformation#getParameterTypes(java.lang.reflect.Constructor)
|
||||
*/
|
||||
public List<TypeInformation<?>> getParameterTypes(Constructor<?> constructor) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
List<TypeInformation<?>> result = new ArrayList<TypeInformation<?>>();
|
||||
|
||||
for (Class<?> type : constructor.getParameterTypes()) {
|
||||
result.add(createInfo(type));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Resolves the given type into a plain {@link Class}.
|
||||
*
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Class<S> resolveType(Type type) {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.document.mongodb.TypeDiscovererTest.FieldInformation
|
||||
* #getField(java.lang.String)
|
||||
*/
|
||||
public TypeInformation<?> getProperty(String fieldname) {
|
||||
return GenericTypeResolver.resolveType(type, getTypeVariableMap());
|
||||
}
|
||||
|
||||
int separatorIndex = fieldname.indexOf(".");
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeInformation#getParameterTypes(java.lang.reflect.Constructor)
|
||||
*/
|
||||
public List<TypeInformation<?>> getParameterTypes(Constructor<?> constructor) {
|
||||
|
||||
if (separatorIndex == -1) {
|
||||
if (fieldTypes.containsKey(fieldname)) {
|
||||
return fieldTypes.get(fieldname);
|
||||
}
|
||||
List<TypeInformation<?>> result = new ArrayList<TypeInformation<?>>();
|
||||
|
||||
TypeInformation<?> propertyInformation = getPropertyInformation(fieldname);
|
||||
if (propertyInformation != null) {
|
||||
fieldTypes.put(fieldname, propertyInformation);
|
||||
}
|
||||
return propertyInformation;
|
||||
}
|
||||
for (Class<?> type : constructor.getParameterTypes()) {
|
||||
result.add(createInfo(type));
|
||||
}
|
||||
|
||||
String head = fieldname.substring(0, separatorIndex);
|
||||
TypeInformation<?> info = fieldTypes.get(head);
|
||||
return info.getProperty(fieldname.substring(separatorIndex + 1));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private TypeInformation<?> getPropertyInformation(String fieldname) {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.document.mongodb.TypeDiscovererTest.FieldInformation
|
||||
* #getField(java.lang.String)
|
||||
*/
|
||||
public TypeInformation<?> getProperty(String fieldname) {
|
||||
|
||||
Field field = ReflectionUtils.findField(getType(), fieldname);
|
||||
int separatorIndex = fieldname.indexOf(".");
|
||||
|
||||
if (field == null) {
|
||||
return null;
|
||||
}
|
||||
if (separatorIndex == -1) {
|
||||
if (fieldTypes.containsKey(fieldname)) {
|
||||
return fieldTypes.get(fieldname);
|
||||
}
|
||||
|
||||
return createInfo(field.getGenericType());
|
||||
}
|
||||
TypeInformation<?> propertyInformation = getPropertyInformation(fieldname);
|
||||
if (propertyInformation != null) {
|
||||
fieldTypes.put(fieldname, propertyInformation);
|
||||
}
|
||||
return propertyInformation;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.document.mongodb.TypeDiscovererTest.FieldInformation
|
||||
* #getType()
|
||||
*/
|
||||
public Class<S> getType() {
|
||||
return resolveType(type);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeInformation#isMap()
|
||||
*/
|
||||
public boolean isMap() {
|
||||
Class<?> rawType = getType();
|
||||
return rawType == null ? false : Map.class.isAssignableFrom(rawType);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeInformation#getMapValueType()
|
||||
*/
|
||||
public TypeInformation<?> getMapValueType() {
|
||||
|
||||
if (!Map.class.isAssignableFrom(getType())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ParameterizedType parameterizedType = (ParameterizedType) type;
|
||||
return createInfo(parameterizedType.getActualTypeArguments()[1]);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeInformation#isCollectionLike()
|
||||
*/
|
||||
public boolean isCollectionLike() {
|
||||
|
||||
Class<?> rawType = getType();
|
||||
return rawType == null ? null : rawType.isArray() || Iterable.class.isAssignableFrom(rawType);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeInformation#getComponentType()
|
||||
*/
|
||||
public TypeInformation<?> getComponentType() {
|
||||
|
||||
if (!(Map.class.isAssignableFrom(getType()) || isCollectionLike())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ParameterizedType parameterizedType = (ParameterizedType) type;
|
||||
return createInfo(parameterizedType.getActualTypeArguments()[0]);
|
||||
}
|
||||
String head = fieldname.substring(0, separatorIndex);
|
||||
TypeInformation<?> info = fieldTypes.get(head);
|
||||
return info.getProperty(fieldname.substring(separatorIndex + 1));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
private TypeInformation<?> getPropertyInformation(String fieldname) {
|
||||
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
Field field = ReflectionUtils.findField(getType(), fieldname);
|
||||
|
||||
if (!this.getClass().equals(obj.getClass())) {
|
||||
return false;
|
||||
}
|
||||
if (field == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TypeDiscoverer<?> that = (TypeDiscoverer<?>) obj;
|
||||
return createInfo(field.getGenericType());
|
||||
}
|
||||
|
||||
boolean typeEqual = nullSafeEquals(this.type, that.type);
|
||||
boolean typeVariableMapEqual = nullSafeEquals(this.typeVariableMap,
|
||||
that.typeVariableMap);
|
||||
boolean parentEqual = nullSafeEquals(this.parent, that.parent);
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.document.mongodb.TypeDiscovererTest.FieldInformation
|
||||
* #getType()
|
||||
*/
|
||||
public Class<S> getType() {
|
||||
return resolveType(type);
|
||||
}
|
||||
|
||||
return typeEqual && typeVariableMapEqual && parentEqual;
|
||||
}
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeInformation#isMap()
|
||||
*/
|
||||
public boolean isMap() {
|
||||
Class<?> rawType = getType();
|
||||
return rawType == null ? false : Map.class.isAssignableFrom(rawType);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeInformation#getMapValueType()
|
||||
*/
|
||||
public TypeInformation<?> getMapValueType() {
|
||||
|
||||
int result = 17;
|
||||
result += nullSafeHashCode(type);
|
||||
result += nullSafeHashCode(typeVariableMap);
|
||||
result += nullSafeHashCode(parent);
|
||||
return result;
|
||||
}
|
||||
if (!Map.class.isAssignableFrom(getType())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ParameterizedType parameterizedType = (ParameterizedType) type;
|
||||
return createInfo(parameterizedType.getActualTypeArguments()[1]);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeInformation#isCollectionLike()
|
||||
*/
|
||||
public boolean isCollectionLike() {
|
||||
|
||||
Class<?> rawType = getType();
|
||||
return rawType == null ? null : rawType.isArray() || Iterable.class.isAssignableFrom(rawType);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeInformation#getComponentType()
|
||||
*/
|
||||
public TypeInformation<?> getComponentType() {
|
||||
|
||||
if (!(Map.class.isAssignableFrom(getType()) || isCollectionLike())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ParameterizedType parameterizedType = (ParameterizedType) type;
|
||||
return createInfo(parameterizedType.getActualTypeArguments()[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.getClass().equals(obj.getClass())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TypeDiscoverer<?> that = (TypeDiscoverer<?>) obj;
|
||||
|
||||
boolean typeEqual = nullSafeEquals(this.type, that.type);
|
||||
boolean typeVariableMapEqual = nullSafeEquals(this.typeVariableMap,
|
||||
that.typeVariableMap);
|
||||
boolean parentEqual = nullSafeEquals(this.parent, that.parent);
|
||||
|
||||
return typeEqual && typeVariableMapEqual && parentEqual;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = 17;
|
||||
result += nullSafeHashCode(type);
|
||||
result += nullSafeHashCode(typeVariableMap);
|
||||
result += nullSafeHashCode(parent);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -10,58 +10,58 @@ import java.util.Map;
|
||||
* Interface to access property types and resolving generics on the way.
|
||||
* Starting with a {@link ClassTypeInformation} you can travers properties using
|
||||
* {@link #getProperty(String)} to access type information.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface TypeInformation<S> {
|
||||
|
||||
List<TypeInformation<?>> getParameterTypes(Constructor<?> constructor);
|
||||
|
||||
/**
|
||||
* Returns the property information for the property with the given name.
|
||||
* Supports proeprty traversal through dot notation.
|
||||
*
|
||||
* @param fieldname
|
||||
* @return
|
||||
*/
|
||||
TypeInformation<?> getProperty(String fieldname);
|
||||
List<TypeInformation<?>> getParameterTypes(Constructor<?> constructor);
|
||||
|
||||
/**
|
||||
* Returns whether the type can be considered a collection, which means it's a container of elements, e.g. a
|
||||
* {@link Collection} and {@link Array} or anything implementing {@link Iterable}. If this returns {@literal true} you
|
||||
* can expect {@link #getComponentType()} to return a non-{@literal null} value.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isCollectionLike();
|
||||
|
||||
/**
|
||||
* Returns the component type for {@link Collection}s or the key type for {@link Map}s.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
TypeInformation<?> getComponentType();
|
||||
/**
|
||||
* Returns the property information for the property with the given name.
|
||||
* Supports proeprty traversal through dot notation.
|
||||
*
|
||||
* @param fieldname
|
||||
* @return
|
||||
*/
|
||||
TypeInformation<?> getProperty(String fieldname);
|
||||
|
||||
/**
|
||||
* Returns whether the property is a {@link Map}. If this returns {@literal true} you can expect
|
||||
* {@link #getComponentType()} as well as {@link #getMapValueType()} to return something not {@literal null}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isMap();
|
||||
|
||||
/**
|
||||
* Will return the type of the value in case the underlying type is a {@link Map}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
TypeInformation<?> getMapValueType();
|
||||
/**
|
||||
* Returns whether the type can be considered a collection, which means it's a container of elements, e.g. a
|
||||
* {@link Collection} and {@link Array} or anything implementing {@link Iterable}. If this returns {@literal true} you
|
||||
* can expect {@link #getComponentType()} to return a non-{@literal null} value.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isCollectionLike();
|
||||
|
||||
/**
|
||||
* Returns the type of the property. Will resolve generics and the generic
|
||||
* context of
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<S> getType();
|
||||
/**
|
||||
* Returns the component type for {@link Collection}s or the key type for {@link Map}s.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
TypeInformation<?> getComponentType();
|
||||
|
||||
/**
|
||||
* Returns whether the property is a {@link Map}. If this returns {@literal true} you can expect
|
||||
* {@link #getComponentType()} as well as {@link #getMapValueType()} to return something not {@literal null}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isMap();
|
||||
|
||||
/**
|
||||
* Will return the type of the value in case the underlying type is a {@link Map}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
TypeInformation<?> getMapValueType();
|
||||
|
||||
/**
|
||||
* Returns the type of the property. Will resolve generics and the generic
|
||||
* context of
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<S> getType();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user