Reformatting source code to use tabs instead of spaces.

This commit is contained in:
Jon Brisbin
2011-05-10 10:50:31 -05:00
committed by J. Brisbin
parent 870d156adb
commit e67e35ac8d
144 changed files with 8643 additions and 8672 deletions

View File

@@ -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>

View File

@@ -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 {
}

View File

@@ -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 {
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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);
}
}
}

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -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() {

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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() {
}
}
}

View File

@@ -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"})

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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() {

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -22,7 +22,6 @@ public class ChangeSetConfiguration<T> {
ChangeSetSynchronizer<ChangeSetBacked> changeSetManager) {
this.changeSetManager = changeSetManager;
}
}

View File

@@ -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 {
}
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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());
}

View File

@@ -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] + "_";
}
}

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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);
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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));
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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);
}
}

View File

@@ -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();
}

View File

@@ -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);
}
}

View File

@@ -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();
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}
}

View File

@@ -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());
}
}

View File

@@ -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);
}

View File

@@ -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();
}
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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];
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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>
* &lt;bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"&gt;
* &lt;property name="webBindingInitializer"&gt;
* &lt;bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer"&gt;
* &lt;property name="propertyEditorRegistrars"&gt;
* &lt;bean class="org.springframework.data.extensions.beans.DomainClassPropertyEditorRegistrar" /&gt;
* &lt;/property&gt;
* &lt;/bean&gt;
* &lt;/property&gt;
* &lt;property name="webBindingInitializer"&gt;
* &lt;bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer"&gt;
* &lt;property name="propertyEditorRegistrars"&gt;
* &lt;bean class="org.springframework.data.extensions.beans.DomainClassPropertyEditorRegistrar" /&gt;
* &lt;/property&gt;
* &lt;/bean&gt;
* &lt;/property&gt;
* &lt;/bean&gt;
* </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);
}
}
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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());
}
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}
}

View File

@@ -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();

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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";
}

View File

@@ -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.");
}
}

View File

@@ -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) {
};
}
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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