DATACMNS-266 - Use new common Maven build infrastructure.

Simplified project setup to be a single module build again. Using Spring Data Build parent POM to simplify project setup. See https://github.com/SpringSource/spring-data-build#spring-data-build-infrastructure
This commit is contained in:
Oliver Gierke
2013-01-11 12:13:47 +01:00
parent c908d0e023
commit ac256f9921
375 changed files with 215 additions and 3092 deletions

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Declares a field as the one representing the principal that created the entity containing the field.
*
* @author Ranie Jade Ramiso
* @author Oliver Gierke
* @since 1.5
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.FIELD, ElementType.ANNOTATION_TYPE })
public @interface CreatedBy {
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Declares a field as the one representing the date the entity containing the field was created.
*
* @author Ranie Jade Ramiso
* @author Oliver Gierke
* @since 1.5
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.FIELD, ElementType.ANNOTATION_TYPE })
public @interface CreatedDate {
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author J. Brisbin <jbrisbin@vmware.com>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.FIELD })
public @interface Id {
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Declares a field as the one representing the principal that recently modified the entity containing the field.
*
* @author Ranie Jade Ramiso
* @author Oliver Gierke
* @since 1.5
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.FIELD, ElementType.ANNOTATION_TYPE })
public @interface LastModifiedBy {
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Declares a field as the one representing the date the entity containing the field was recently modified.
*
* @author Ranie Jade Ramiso
* @author Oliver Gierke
* @since 1.5
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.FIELD, ElementType.ANNOTATION_TYPE })
public @interface LastModifiedDate {
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.CONSTRUCTOR)
public @interface PersistenceConstructor {
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author J. Brisbin <jbrisbin@vmware.com>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.TYPE, ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.PARAMETER })
public @interface Persistent {
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Meta-annotation to be used to annotate annotations that mark references to other objects.
*
* @author Oliver Gierke
* @author Jon Brisbin
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.ANNOTATION_TYPE, ElementType.FIELD })
public @interface Reference {
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 20011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marks a field to be transient for the mapping framework. Thus the property will not be persisted and not further
* inspected by the mapping framework.
*
* @author Oliver Gierke
* @author Jon Brisbin
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Transient {
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.data.mapping.PersistentEntity;
/**
* Annotation to allow {@link String} based type aliases to be used when writing type information for
* {@link PersistentEntity}s.
*
* @author Oliver Gierke
*/
@Documented
@Inherited
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeAlias {
/**
* The type alias to be used when persisting
*
* @return
*/
String value();
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.annotation;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* Demarcates a property to be used as version field to implement optimistic locking on entities.
*
* @since 1.5
* @author Patryk Wasik
* @author Oliver Gierke
*/
@Documented
@Target({ FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
public @interface Version {
}

View File

@@ -0,0 +1,4 @@
/**
* Core annotations being used by Spring Data.
*/
package org.springframework.data.annotation;

View File

@@ -0,0 +1,139 @@
package org.springframework.data.auditing;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.joda.time.DateTime;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.util.ReflectionUtils;
import org.springframework.data.util.ReflectionUtils.AnnotationFieldFilter;
import org.springframework.util.Assert;
/**
* Inspects the given {@link Class} for fields annotated by {@link CreatedBy}, {@link CreatedDate},
* {@link LastModifiedBy} , and {@link LastModifiedDate}. Only one field per annotation is stored.
*
* @author Ranie Jade Ramiso
* @author Oliver Gierke
* @since 1.5
*/
class AnnotationAuditingMetadata {
private static final AnnotationFieldFilter CREATED_BY_FILTER = new AnnotationFieldFilter(CreatedBy.class);
private static final AnnotationFieldFilter CREATED_DATE_FILTER = new AnnotationFieldFilter(CreatedDate.class);
private static final AnnotationFieldFilter LAST_MODIFIED_BY_FILTER = new AnnotationFieldFilter(LastModifiedBy.class);
private static final AnnotationFieldFilter LAST_MODIFIED_DATE_FILTER = new AnnotationFieldFilter(
LastModifiedDate.class);
private static final Map<Class<?>, AnnotationAuditingMetadata> METADATA_CACHE = new ConcurrentHashMap<Class<?>, AnnotationAuditingMetadata>();
static final List<Class<?>> SUPPORTED_DATE_TYPES;
static {
List<Class<?>> types = new ArrayList<Class<?>>(4);
types.add(DateTime.class);
types.add(Date.class);
types.add(Long.class);
types.add(long.class);
SUPPORTED_DATE_TYPES = Collections.unmodifiableList(types);
}
private final Field createdByField;
private final Field createdDateField;
private final Field lastModifiedByField;
private final Field lastModifiedDateField;
private AnnotationAuditingMetadata(Class<?> type) {
Assert.notNull(type, "Given type must not be null!");
this.createdByField = ReflectionUtils.findField(type, CREATED_BY_FILTER);
this.createdDateField = ReflectionUtils.findField(type, CREATED_DATE_FILTER);
this.lastModifiedByField = ReflectionUtils.findField(type, LAST_MODIFIED_BY_FILTER);
this.lastModifiedDateField = ReflectionUtils.findField(type, LAST_MODIFIED_DATE_FILTER);
assertValidDateFieldType(createdDateField);
assertValidDateFieldType(lastModifiedDateField);
}
/**
* Checks whether the given field has a type that is a supported date type.
*
* @param field
*/
private void assertValidDateFieldType(Field field) {
if (field == null || SUPPORTED_DATE_TYPES.contains(field.getType())) {
return;
}
throw new IllegalStateException(String.format(
"Found created/modified date field with type %s but only %s are supported!", field.getType(),
SUPPORTED_DATE_TYPES));
}
/**
* Return a {@link AnnotationAuditingMetadata} for the given {@link Class}.
*
* @param type the type to inspect, must not be {@literal null}.
*/
public static AnnotationAuditingMetadata getMetadata(Class<?> type) {
if (METADATA_CACHE.containsKey(type)) {
return METADATA_CACHE.get(type);
}
AnnotationAuditingMetadata metadata = new AnnotationAuditingMetadata(type);
METADATA_CACHE.put(type, metadata);
return metadata;
}
/**
* Returns whether the {@link Class} represented in this instance is auditable or not.
*/
public boolean isAuditable() {
if (createdByField == null && createdDateField == null && lastModifiedByField == null
&& lastModifiedDateField == null) {
return false;
}
return true;
}
/**
* Return the field annotated by {@link CreatedBy}, or {@literal null}.
*/
public Field getCreatedByField() {
return createdByField;
}
/**
* Return the field annotated by {@link CreatedDate}, or {@literal null}.
*/
public Field getCreatedDateField() {
return createdDateField;
}
/**
* Return the field annotated by {@link LastModifiedBy}, or {@literal null}.
*/
public Field getLastModifiedByField() {
return lastModifiedByField;
}
/**
* Return the field annotated by {@link LastModifiedDate}, or {@literal null}.
*/
public Field getLastModifiedDateField() {
return lastModifiedDateField;
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.auditing;
import org.joda.time.DateTime;
/**
* Interface to abstract the ways setting the auditing information can be implemented.
*
* @author Oliver Gierke
* @since 1.5
*/
public interface AuditableBeanWrapper {
/**
* Set the creator of the object.
*
* @param value
*/
void setCreatedBy(Object value);
/**
* Set the date the object was created.
*
* @param value
*/
void setCreatedDate(DateTime value);
/**
* Set the last modifier of the object.
*
* @param value
*/
void setLastModifiedBy(Object value);
/**
* Set the last modification date.
*
* @param value
*/
void setLastModifiedDate(DateTime value);
}

View File

@@ -0,0 +1,220 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.auditing;
import java.lang.reflect.Field;
import java.util.Date;
import org.joda.time.DateTime;
import org.springframework.data.domain.Auditable;
import org.springframework.data.util.ReflectionUtils;
import org.springframework.util.Assert;
/**
* A factory class to {@link AuditableBeanWrapper} instances.
*
* @author Oliver Gierke
* @since 1.5
*/
class AuditableBeanWrapperFactory {
/**
* Returns an {@link AuditableBeanWrapper} if the given object is capable of being equipped with auditing information.
*
* @param source the auditing candidate.
* @return
*/
@SuppressWarnings("unchecked")
public AuditableBeanWrapper getBeanWrapperFor(Object source) {
if (source == null) {
return null;
}
if (source instanceof Auditable) {
return new AuditableInterfaceBeanWrapper((Auditable<Object, ?>) source);
}
AnnotationAuditingMetadata metadata = AnnotationAuditingMetadata.getMetadata(source.getClass());
if (metadata.isAuditable()) {
return new ReflectionAuditingBeanWrapper(source);
}
return null;
}
/**
* An {@link AuditableBeanWrapper} that works with objects implementing
*
* @author Oliver Gierke
*/
static class AuditableInterfaceBeanWrapper implements AuditableBeanWrapper {
private final Auditable<Object, ?> auditable;
public AuditableInterfaceBeanWrapper(Auditable<Object, ?> auditable) {
this.auditable = auditable;
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedBy(java.lang.Object)
*/
public void setCreatedBy(Object value) {
auditable.setCreatedBy(value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedDate(org.joda.time.DateTime)
*/
public void setCreatedDate(DateTime value) {
auditable.setCreatedDate(value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedBy(java.lang.Object)
*/
public void setLastModifiedBy(Object value) {
auditable.setLastModifiedBy(value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(org.joda.time.DateTime)
*/
public void setLastModifiedDate(DateTime value) {
auditable.setLastModifiedDate(value);
}
}
/**
* An {@link AuditableBeanWrapper} implementation that sets values on the target object using refelction.
*
* @author Oliver Gierke
*/
static class ReflectionAuditingBeanWrapper implements AuditableBeanWrapper {
private final AnnotationAuditingMetadata metadata;
private final Object target;
/**
* Creates a new {@link ReflectionAuditingBeanWrapper} to set auditing data on the given target object.
*
* @param target must not be {@literal null}.
*/
public ReflectionAuditingBeanWrapper(Object target) {
Assert.notNull(target, "Target object must not be null!");
this.metadata = AnnotationAuditingMetadata.getMetadata(target.getClass());
this.target = target;
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedBy(java.lang.Object)
*/
public void setCreatedBy(Object value) {
setField(metadata.getCreatedByField(), value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setCreatedDate(org.joda.time.DateTime)
*/
public void setCreatedDate(DateTime value) {
setDateField(metadata.getCreatedDateField(), value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedBy(java.lang.Object)
*/
public void setLastModifiedBy(Object value) {
setField(metadata.getLastModifiedByField(), value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.auditing.AuditableBeanWrapper#setLastModifiedDate(org.joda.time.DateTime)
*/
public void setLastModifiedDate(DateTime value) {
setDateField(metadata.getLastModifiedDateField(), value);
}
/**
* Sets the given field to the given value if the field is not {@literal null}.
*
* @param field
* @param value
*/
private void setField(Field field, Object value) {
if (field != null) {
ReflectionUtils.setField(field, target, value);
}
}
/**
* Sets the given field to the given value if the field is not {@literal null}.
*
* @param field
* @param value
*/
private void setDateField(Field field, DateTime value) {
if (field == null) {
return;
}
ReflectionUtils.setField(field, target, getDateValueToSet(value, field));
}
/**
* Returns the {@link DateTime} in a type compatible to the given field.
*
* @param value
* @param field must not be {@literal null}.
* @return
*/
private Object getDateValueToSet(DateTime value, Field field) {
if (value == null) {
return null;
}
Class<?> targetType = field.getType();
if (DateTime.class.equals(targetType)) {
return value;
}
if (Date.class.equals(targetType)) {
return value.toDate();
}
if (Long.class.equals(targetType) || long.class.equals(targetType)) {
return value.getMillis();
}
throw new IllegalArgumentException(String.format("Invalid date type for field %s! Supported types are %s.",
field, AnnotationAuditingMetadata.SUPPORTED_DATE_TYPES));
}
}
}

View File

@@ -0,0 +1,174 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.auditing;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.domain.Auditable;
import org.springframework.data.domain.AuditorAware;
import org.springframework.util.Assert;
/**
* Auditing handler to mark entity objects created and modified.
*
* @author Oliver Gierke
* @since 1.5
*/
public class AuditingHandler<T> implements InitializingBean {
private static final Logger LOGGER = LoggerFactory.getLogger(AuditingHandler.class);
private final AuditableBeanWrapperFactory factory = new AuditableBeanWrapperFactory();
private DateTimeProvider dateTimeProvider = CurrentDateTimeProvider.INSTANCE;
private AuditorAware<T> auditorAware;
private boolean dateTimeForNow = true;
private boolean modifyOnCreation = true;
/**
* Setter to inject a {@code AuditorAware} component to retrieve the current auditor.
*
* @param auditorAware the auditorAware to set
*/
public void setAuditorAware(final AuditorAware<T> auditorAware) {
Assert.notNull(auditorAware);
this.auditorAware = auditorAware;
}
/**
* Setter do determine if {@link Auditable#setCreatedDate(DateTime)} and
* {@link Auditable#setLastModifiedDate(DateTime)} shall be filled with the current Java time. Defaults to
* {@code true}. One might set this to {@code false} to use database features to set entity time.
*
* @param dateTimeForNow the dateTimeForNow to set
*/
public void setDateTimeForNow(boolean dateTimeForNow) {
this.dateTimeForNow = dateTimeForNow;
}
/**
* Set this to false if you want to treat entity creation as modification and thus set the current date as
* modification date, too. Defaults to {@code true}.
*
* @param modifyOnCreation if modification information shall be set on creation, too
*/
public void setModifyOnCreation(boolean modifyOnCreation) {
this.modifyOnCreation = modifyOnCreation;
}
/**
* Sets the {@link DateTimeProvider} to be used to determine the dates to be set.
*
* @param dateTimeProvider
*/
public void setDateTimeProvider(DateTimeProvider dateTimeProvider) {
this.dateTimeProvider = dateTimeProvider == null ? CurrentDateTimeProvider.INSTANCE : dateTimeProvider;
}
/**
* Marks the given object as created.
*
* @param source
*/
public void markCreated(Object source) {
touch(source, true);
}
/**
* Marks the given object as modified.
*
* @param source
*/
public void markModified(Object source) {
touch(source, false);
}
private void touch(Object target, boolean isNew) {
AuditableBeanWrapper wrapper = factory.getBeanWrapperFor(target);
if (wrapper == null) {
return;
}
T auditor = touchAuditor(wrapper, isNew);
DateTime now = dateTimeForNow ? touchDate(wrapper, isNew) : null;
Object defaultedNow = now == null ? "not set" : now;
Object defaultedAuditor = auditor == null ? "unknown" : auditor;
LOGGER.debug("Touched {} - Last modification at {} by {}", new Object[] { target, defaultedNow, defaultedAuditor });
}
/**
* Sets modifying and creating auditioner. Creating auditioner is only set on new auditables.
*
* @param auditable
* @return
*/
private T touchAuditor(AuditableBeanWrapper wrapper, boolean isNew) {
if (null == auditorAware) {
return null;
}
T auditor = auditorAware.getCurrentAuditor();
if (isNew) {
wrapper.setCreatedBy(auditor);
if (!modifyOnCreation) {
return auditor;
}
}
wrapper.setLastModifiedBy(auditor);
return auditor;
}
/**
* Touches the auditable regarding modification and creation date. Creation date is only set on new auditables.
*
* @param wrapper
* @return
*/
private DateTime touchDate(AuditableBeanWrapper wrapper, boolean isNew) {
DateTime now = dateTimeProvider.getDateTime();
if (isNew) {
wrapper.setCreatedDate(now);
if (!modifyOnCreation) {
return now;
}
}
wrapper.setLastModifiedDate(now);
return now;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() {
if (auditorAware == null) {
LOGGER.debug("No AuditorAware set! Auditing will not be applied!");
}
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.auditing;
import org.joda.time.DateTime;
/**
* Default {@link DateTimeProvider} simply creating new {@link DateTime} instances for each method call.
*
* @author Oliver Gierke
* @since 1.5
*/
public enum CurrentDateTimeProvider implements DateTimeProvider {
INSTANCE;
/*
* (non-Javadoc)
* @see org.springframework.data.jpa.domain.support.DateTimeProvider#getDateTime()
*/
public DateTime getDateTime() {
return new DateTime();
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.auditing;
import org.joda.time.DateTime;
/**
* SPI to calculate the {@link DateTime} instance to be used when auditing.
*
* @author Oliver Gierke
* @since 1.5
*/
public interface DateTimeProvider {
/**
* Returns the {@link DateTime} to be used as modification date.
*
* @return
*/
DateTime getDateTime();
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.auditing;
import org.springframework.data.support.IsNewStrategy;
import org.springframework.data.support.IsNewStrategyFactory;
import org.springframework.util.Assert;
/**
* {@link AuditingHandler} extension that uses an {@link IsNewStrategyFactory} to expose a generic
* {@link #markAudited(Object)} method that will route calls to {@link #markCreated(Object)} or
* {@link #markModified(Object)} based on the {@link IsNewStrategy} determined from the factory.
*
* @author Oliver Gierke
* @since 1.5
*/
public class IsNewAwareAuditingHandler<T> extends AuditingHandler<T> {
private final IsNewStrategyFactory isNewStrategyFactory;
/**
* Creates a new {@link IsNewAwareAuditingHandler} using the given {@link IsNewStrategyFactory}.
*
* @param isNewStrategyFactory must not be {@literal null}.
*/
public IsNewAwareAuditingHandler(IsNewStrategyFactory isNewStrategyFactory) {
Assert.notNull(isNewStrategyFactory, "IsNewStrategy must not be null!");
this.isNewStrategyFactory = isNewStrategyFactory;
}
/**
* Marks the given object created or modified based on the {@link IsNewStrategy} returned by the
* {@link IsNewStrategyFactory} configured. Will rout the calls to {@link #markCreated(Object)} and
* {@link #markModified(Object)} accordingly.
*
* @param object
*/
public void markAudited(Object object) {
if (object == null) {
return;
}
IsNewStrategy strategy = isNewStrategyFactory.getIsNewStrategy(object.getClass());
if (strategy.isNew(object)) {
markCreated(object);
} else {
markModified(object);
}
}
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.authentication;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Class used to provide credentials for username/password authentication
*
* @author Thomas Risberg
* @author Oliver Gierke
*/
public class UserCredentials {
public static final UserCredentials NO_CREDENTIALS = new UserCredentials(null, null);
private final String username;
private final String password;
/**
* Creates a new {@link UserCredentials} instance from the given username and password. Empty {@link String}s provided
* will be treated like no username or password set.
*
* @param username
* @param password
*/
public UserCredentials(String username, String password) {
this.username = StringUtils.hasText(username) ? username : null;
this.password = StringUtils.hasText(password) ? password : null;
}
/**
* Get the username to use for authentication.
*
* @return the username
*/
public String getUsername() {
return username;
}
/**
* Get the password to use for authentication.
*
* @return the password
*/
public String getPassword() {
return password;
}
/**
* Returns whether the credentials contain a username.
*
* @return
*/
public boolean hasUsername() {
return this.username != null;
}
/**
* Returns whether the credentials contain a password.
*
* @return
*/
public boolean hasPassword() {
return this.password != null;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || !getClass().equals(obj.getClass())) {
return false;
}
UserCredentials that = (UserCredentials) obj;
return ObjectUtils.nullSafeEquals(this.username, that.username)
&& ObjectUtils.nullSafeEquals(this.password, that.password);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result += 31 * ObjectUtils.nullSafeHashCode(username);
result += 31 * ObjectUtils.nullSafeHashCode(password);
return result;
}
}

View File

@@ -0,0 +1,4 @@
/**
* Types to abstract authentication concepts.
*/
package org.springframework.data.authentication;

View File

@@ -0,0 +1,85 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.config;
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.aop.target.LazyInitTargetSource;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.data.auditing.AuditingHandler;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* {@link BeanDefinitionParser} that parses an {@link AuditingHandler} {@link BeanDefinition}
*
* @author Oliver Gierke
* @since 1.5
*/
public class AuditingHandlerBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
private static final String AUDITOR_AWARE_REF = "auditor-aware-ref";
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#getBeanClass(org.w3c.dom.Element)
*/
@Override
protected Class<?> getBeanClass(Element element) {
return AuditingHandler.class;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#shouldGenerateId()
*/
@Override
protected boolean shouldGenerateId() {
return true;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#doParse(org.w3c.dom.Element, org.springframework.beans.factory.support.BeanDefinitionBuilder)
*/
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String auditorAwareRef = element.getAttribute(AUDITOR_AWARE_REF);
if (StringUtils.hasText(auditorAwareRef)) {
builder.addPropertyValue("auditorAware", createLazyInitTargetSourceBeanDefinition(auditorAwareRef));
}
ParsingUtils.setPropertyValue(builder, element, "set-dates", "dateTimeForNow");
ParsingUtils.setPropertyReference(builder, element, "date-time-provider-ref", "dateTimeProvider");
}
private BeanDefinition createLazyInitTargetSourceBeanDefinition(String auditorAwareRef) {
BeanDefinitionBuilder targetSourceBuilder = rootBeanDefinition(LazyInitTargetSource.class);
targetSourceBuilder.addPropertyValue("targetBeanName", auditorAwareRef);
BeanDefinitionBuilder builder = rootBeanDefinition(ProxyFactoryBean.class);
builder.addPropertyValue("targetSource", targetSourceBuilder.getBeanDefinition());
return builder.getBeanDefinition();
}
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.config;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Helper to create {@link BeanComponentDefinition} more easily.
*
* @author Oliver Gierke
*/
public class BeanComponentDefinitionBuilder {
private final Element defaultSource;
private final ParserContext context;
/**
* Creates a new {@link BeanComponentDefinitionBuilder} using the given {@link Element} as default source and the
* given {@link ParserContext}.
*
* @param defaultSource must not be {@literal null}.
* @param context must not be {@literal null}.
*/
public BeanComponentDefinitionBuilder(Element defaultSource, ParserContext context) {
Assert.notNull(defaultSource);
Assert.notNull(context);
this.defaultSource = defaultSource;
this.context = context;
}
/**
* Creates a {@link BeanComponentDefinition} from the given {@link BeanDefinitionBuilder}. Will generate a bean name.
*
* @param builder must not be {@literal null}.
* @return
*/
public BeanComponentDefinition getComponent(BeanDefinitionBuilder builder) {
Assert.notNull(builder);
AbstractBeanDefinition definition = builder.getRawBeanDefinition();
String name = BeanDefinitionReaderUtils.generateBeanName(definition, context.getRegistry(), context.isNested());
return getComponent(builder, name);
}
/**
* Creates a {@link BeanComponentDefinition} from the given {@link BeanDefinitionBuilder} and inspects the backing
* {@link Element}s id attribute for a name. It will use this one if found or the given fallback if not.
*
* @param builder must not be {@literal null}.
* @param fallback must not be {@literal null} or empty.
* @return
*/
public BeanComponentDefinition getComponentIdButFallback(BeanDefinitionBuilder builder, String fallback) {
Assert.hasText(fallback);
String id = defaultSource.getAttribute("id");
return getComponent(builder, StringUtils.hasText(id) ? id : fallback);
}
/**
* Creates a {@link BeanComponentDefinition} from the given {@link BeanDefinitionBuilder} using the given name.
*
* @param builder must not be {@literal null}.
* @param name must not be {@literal null} or empty.
* @return
*/
public BeanComponentDefinition getComponent(BeanDefinitionBuilder builder, String name) {
return getComponent(builder, name, defaultSource);
}
/**
* Creates a new {@link BeanComponentDefinition} from the given {@link BeanDefinitionBuilder} using the given name and
* raw source object.
*
* @param builder must not be {@literal null}.
* @param name must not be {@literal null}.
* @param rawSource
* @return
*/
public BeanComponentDefinition getComponent(BeanDefinitionBuilder builder, String name, Object rawSource) {
Assert.notNull(builder);
Assert.hasText(name);
AbstractBeanDefinition definition = builder.getRawBeanDefinition();
definition.setSource(context.extractSource(rawSource));
return new BeanComponentDefinition(definition, name);
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.config;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
import org.springframework.util.Assert;
import org.w3c.dom.Element;
/**
* {@link AuditingHandlerBeanDefinitionParser} that will register am {@link IsNewAwareAuditingHandler}. Needs to get the
* bean id of the
*
* @author Oliver Gierke
*/
public class IsNewAwareAuditingHandlerBeanDefinitionParser extends AuditingHandlerBeanDefinitionParser {
private final String isNewStrategyFactoryBeanId;
/**
* Creates a new {@link IsNewAwareAuditingHandlerBeanDefinitionParser}.
*
* @param isNewStrategyFactoryBeanId must not be {@literal null} or empty.
*/
public IsNewAwareAuditingHandlerBeanDefinitionParser(String isNewStrategyFactoryBeanId) {
Assert.hasText(isNewStrategyFactoryBeanId);
this.isNewStrategyFactoryBeanId = isNewStrategyFactoryBeanId;
}
/*
* (non-Javadoc)
* @see org.springframework.data.config.AuditingHandlerBeanDefinitionParser#getBeanClass(org.w3c.dom.Element)
*/
@Override
protected Class<?> getBeanClass(Element element) {
return IsNewAwareAuditingHandler.class;
}
/*
* (non-Javadoc)
* @see org.springframework.data.config.AuditingHandlerBeanDefinitionParser#doParse(org.w3c.dom.Element, org.springframework.beans.factory.support.BeanDefinitionBuilder)
*/
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
builder.addConstructorArgReference(isNewStrategyFactoryBeanId);
super.doParse(element, builder);
}
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.config;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Utility methods for {@link BeanDefinitionParser} implementations.
*
* @author Oliver Gierke
*/
public abstract class ParsingUtils {
private ParsingUtils() {
}
/**
* Configures a property value for the given property name reading the attribute of the given name from the given
* {@link Element} if the attribute is configured.
*
* @param builder must not be {@literal null}.
* @param element must not be {@literal null}.
* @param attrName must not be {@literal null} or empty.
* @param propertyName must not be {@literal null} or empty.
*/
public static void setPropertyValue(BeanDefinitionBuilder builder, Element element, String attrName,
String propertyName) {
Assert.notNull(builder, "BeanDefinitionBuilder must not be null!");
Assert.notNull(element, "Element must not be null!");
Assert.hasText(attrName, "Attribute name must not be null!");
Assert.hasText(propertyName, "Property name must not be null!");
String attr = element.getAttribute(attrName);
if (StringUtils.hasText(attr)) {
builder.addPropertyValue(propertyName, attr);
}
}
/**
* Sets the property with the given attribute name on the given {@link BeanDefinitionBuilder} to the value of the
* attribute with the given name if the attribute is configured.
*
* @param builder must not be {@literal null}.
* @param element must not be {@literal null}.
* @param attribute must not be {@literal null} or empty.
*/
public static void setPropertyValue(BeanDefinitionBuilder builder, Element element, String attribute) {
setPropertyValue(builder, element, attribute, attribute);
}
/**
* Configures a bean property reference with the value of the attribute of the given name if it is configured.
*
* @param builder must not be {@literal null}.
* @param element must not be {@literal null}.
* @param attribute must not be {@literal null} or empty.
* @param property must not be {@literal null}or empty.
*/
public static void setPropertyReference(BeanDefinitionBuilder builder, Element element, String attribute,
String property) {
Assert.notNull(builder, "BeanDefinitionBuilder must not be null!");
Assert.notNull(element, "Element must not be null!");
Assert.hasText(attribute, "Attribute name must not be null!");
Assert.hasText(property, "Property name must not be null!");
String value = element.getAttribute(attribute);
if (StringUtils.hasText(value)) {
builder.addPropertyReference(property, value);
}
}
/**
* Returns the {@link BeanDefinition} built by the given {@link BeanDefinitionBuilder} enriched with source
* information derived from the given {@link Element}.
*
* @param builder must not be {@literal null}.
* @param context must not be {@literal null}.
* @param element must not be {@literal null}.
* @return
*/
public static AbstractBeanDefinition getSourceBeanDefinition(BeanDefinitionBuilder builder, ParserContext context,
Element element) {
Assert.notNull(element, "Element must not be null!");
Assert.notNull(context, "ParserContext must not be null!");
return getSourceBeanDefinition(builder, context.extractSource(element));
}
/**
* Returns the {@link AbstractBeanDefinition} built by the given builder with the given extracted source applied.
*
* @param builder must not be {@literal null}.
* @param source
* @return
*/
public static AbstractBeanDefinition getSourceBeanDefinition(BeanDefinitionBuilder builder, Object source) {
Assert.notNull(builder, "Builder must not be null!");
AbstractBeanDefinition definition = builder.getRawBeanDefinition();
definition.setSource(source);
return definition;
}
}

View File

@@ -0,0 +1,239 @@
/*
* Copyright 2010-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.config;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.HashSet;
import java.util.regex.Pattern;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.parsing.ReaderContext;
import org.springframework.beans.factory.xml.XmlReaderContext;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AspectJTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.core.type.filter.RegexPatternTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.Assert;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
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
*/
public class TypeFilterParser {
private static final String FILTER_TYPE_ATTRIBUTE = "type";
private static final String FILTER_EXPRESSION_ATTRIBUTE = "expression";
private final ReaderContext readerContext;
private final ClassLoader classLoader;
/**
* Creates a new {@link TypeFilterParser} with the given {@link ReaderContext}.
*
* @param readerContext must not be {@literal null}.
*/
public TypeFilterParser(XmlReaderContext readerContext) {
this(readerContext, readerContext.getResourceLoader().getClassLoader());
}
/**
* Constructor to ease testing as {@link XmlReaderContext#getBeanClassLoader()} is final and thus cannot be mocked
* easily.
*
* @param readerContext must not be {@literal null}.
* @param classLoader must not be {@literal null}.
*/
TypeFilterParser(ReaderContext readerContext, ClassLoader classLoader) {
Assert.notNull(readerContext, "ReaderContext must not be null!");
Assert.notNull(classLoader, "ClassLoader must not be null!");
this.readerContext = readerContext;
this.classLoader = classLoader;
}
/**
* Returns all {@link TypeFilter} declared in nested elements of the given {@link Element}. Allows to selectively
* retrieve including or excluding filters based on the given {@link Type}.
*
* @param element must not be {@literal null}.
* @param type must not be {@literal null}.
* @return
*/
public Iterable<TypeFilter> parseTypeFilters(Element element, Type type) {
NodeList nodeList = element.getChildNodes();
Collection<TypeFilter> filters = new HashSet<TypeFilter>();
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
Element childElement = type.getElement(node);
if (childElement != null) {
try {
filters.add(createTypeFilter(childElement, classLoader));
} catch (RuntimeException e) {
readerContext.error(e.getMessage(), readerContext.extractSource(element), e.getCause());
}
}
}
return filters;
}
/**
* Createsa a {@link TypeFilter} instance from the given {@link Element} and {@link ClassLoader}.
*
* @param element must not be {@literal null}.
* @param classLoader must not be {@literal null}.
* @return
*/
protected TypeFilter createTypeFilter(Element element, ClassLoader classLoader) {
String filterType = element.getAttribute(FILTER_TYPE_ATTRIBUTE);
String expression = element.getAttribute(FILTER_EXPRESSION_ATTRIBUTE);
try {
FilterType filter = FilterType.fromString(filterType);
return filter.getFilter(expression, classLoader);
} 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.
*
* @author Oliver Gierke
* @see #getFilter(String, ClassLoader)
*/
private static enum FilterType {
ANNOTATION {
@Override
@SuppressWarnings("unchecked")
public TypeFilter getFilter(String expression, ClassLoader classLoader) throws ClassNotFoundException {
return new AnnotationTypeFilter((Class<Annotation>) classLoader.loadClass(expression));
}
},
ASSIGNABLE {
@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);
}
},
REGEX {
@Override
public TypeFilter getFilter(String expression, ClassLoader classLoader) {
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 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;
}
}
throw new IllegalArgumentException("Unsupported filter type: " + typeString);
}
}
public static enum Type {
INCLUDE("include-filter"), EXCLUDE("exclude-filter");
private String elementName;
private Type(String 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) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
String localName = node.getLocalName();
if (elementName.equals(localName)) {
return (Element) node;
}
}
return null;
}
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright 2011-12 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.convert;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
/**
* {@link TypeInformationMapper} implementation that can be either set up using a {@link MappingContext} or manually set
* up {@link Map} of {@link String} aliases to types. If a {@link MappingContext} is used the {@link Map} will be build
* inspecting the {@link PersistentEntity} instances for type alias information.
*
* @author Oliver Gierke
*/
public class ConfigurableTypeInformationMapper implements TypeInformationMapper {
private final Map<TypeInformation<?>, Object> typeMap;
/**
* Creates a new {@link ConfigurableTypeMapper} for the given type map.
*
* @param sourceTypeMap must not be {@literal null}.
*/
public ConfigurableTypeInformationMapper(Map<? extends Class<?>, String> sourceTypeMap) {
Assert.notNull(sourceTypeMap);
this.typeMap = new HashMap<TypeInformation<?>, Object>(sourceTypeMap.size());
for (Entry<? extends Class<?>, String> entry : sourceTypeMap.entrySet()) {
TypeInformation<?> key = ClassTypeInformation.from(entry.getKey());
String value = entry.getValue();
if (typeMap.containsValue(value)) {
throw new IllegalArgumentException(String.format(
"Detected mapping ambiguity! String %s cannot be mapped to more than one type!", value));
}
this.typeMap.put(key, value);
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.convert.TypeInformationMapper#createAliasFor(org.springframework.data.util.TypeInformation)
*/
public Object createAliasFor(TypeInformation<?> type) {
return typeMap.get(type);
}
/*
* (non-Javadoc)
* @see org.springframework.data.convert.TypeInformationMapper#resolveTypeFrom(java.lang.Object)
*/
public TypeInformation<?> resolveTypeFrom(Object alias) {
if (alias == null) {
return null;
}
for (Entry<TypeInformation<?>, Object> entry : typeMap.entrySet()) {
if (entry.getValue().equals(alias)) {
return entry.getKey();
}
}
return null;
}
}

View File

@@ -0,0 +1,184 @@
/*
* Copyright 2011-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.convert;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
/**
* Default implementation of {@link MongoTypeMapper} allowing configuration of the key to lookup and store type
* information in {@link DBObject}. The key defaults to {@link #DEFAULT_TYPE_KEY}. Actual type-to-{@link String}
* conversion and back is done in {@link #getTypeString(TypeInformation)} or {@link #getTypeInformation(String)}
* respectively.
*
* @author Oliver Gierke
*/
public class DefaultTypeMapper<S> implements TypeMapper<S> {
private final TypeAliasAccessor<S> accessor;
private final List<? extends TypeInformationMapper> mappers;
/**
* Creates a new {@link DefaultTypeMapper} using the given {@link TypeAliasAccessor}. It will use a
* {@link SimpleTypeInformationMapper} to calculate type aliases.
*
* @param accessor must not be {@literal null}.
*/
public DefaultTypeMapper(TypeAliasAccessor<S> accessor) {
this(accessor, Arrays.asList(SimpleTypeInformationMapper.INSTANCE));
}
/**
* Creates a new {@link DefaultTypeMapper} using the given {@link TypeAliasAccessor} and {@link TypeInformationMapper}
* s.
*
* @param accessor must not be {@literal null}.
* @param mappers must not be {@literal null}.
*/
public DefaultTypeMapper(TypeAliasAccessor<S> accessor, List<? extends TypeInformationMapper> mappers) {
this(accessor, null, mappers);
}
/**
* Creates a new {@link DefaultTypeMapper} using the given {@link TypeAliasAccessor}, {@link MappingContext} and
* additional {@link TypeInformationMapper}s. Will register a {@link MappingContextTypeInformationMapper} before the
* given additional mappers.
*
* @param accessor must not be {@literal null}.
* @param mappingContext
* @param additionalMappers must not be {@literal null}.
*/
public DefaultTypeMapper(TypeAliasAccessor<S> accessor,
MappingContext<? extends PersistentEntity<?, ?>, ?> mappingContext,
List<? extends TypeInformationMapper> additionalMappers) {
Assert.notNull(accessor);
Assert.notNull(additionalMappers);
List<TypeInformationMapper> mappers = new ArrayList<TypeInformationMapper>(additionalMappers.size() + 1);
if (mappingContext != null) {
mappers.add(new MappingContextTypeInformationMapper(mappingContext));
}
mappers.addAll(additionalMappers);
this.mappers = Collections.unmodifiableList(mappers);
this.accessor = accessor;
}
/*
* (non-Javadoc)
* @see org.springframework.data.convert.TypeMapper#readType(java.lang.Object)
*/
public TypeInformation<?> readType(S source) {
Assert.notNull(source);
Object alias = accessor.readAliasFrom(source);
if (alias == null) {
return null;
}
for (TypeInformationMapper mapper : mappers) {
TypeInformation<?> type = mapper.resolveTypeFrom(alias);
if (type != null) {
return type;
}
}
return null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.convert.TypeMapper#readType(java.lang.Object, org.springframework.data.util.TypeInformation)
*/
@SuppressWarnings("unchecked")
public <T> TypeInformation<? extends T> readType(S source, TypeInformation<T> basicType) {
Assert.notNull(source);
Class<?> documentsTargetType = getDefaultedTypeToBeUsed(source);
if (documentsTargetType == null) {
return basicType;
}
Class<T> rawType = basicType == null ? null : basicType.getType();
boolean isMoreConcreteCustomType = rawType == null ? true : rawType.isAssignableFrom(documentsTargetType)
&& !rawType.equals(documentsTargetType);
return isMoreConcreteCustomType ? (TypeInformation<? extends T>) ClassTypeInformation.from(documentsTargetType)
: basicType;
}
/**
* Returns the type discovered through {@link #readType(Object)} but defaulted to the one returned by
* {@link #getFallbackTypeFor(Object)}.
*
* @param source
* @return
*/
private Class<?> getDefaultedTypeToBeUsed(S source) {
TypeInformation<?> documentsTargetTypeInformation = readType(source);
documentsTargetTypeInformation = documentsTargetTypeInformation == null ? getFallbackTypeFor(source)
: documentsTargetTypeInformation;
return documentsTargetTypeInformation == null ? null : documentsTargetTypeInformation.getType();
}
/**
* Returns the type fallback {@link TypeInformation} in case none could be extracted from the given source.
*
* @param source will never be {@literal null}.
* @return
*/
protected TypeInformation<?> getFallbackTypeFor(S source) {
return null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.convert.TypeMapper#writeType(java.lang.Class, java.lang.Object)
*/
public void writeType(Class<?> type, S dbObject) {
writeType(ClassTypeInformation.from(type), dbObject);
}
/*
* (non-Javadoc)
* @see org.springframework.data.convert.TypeMapper#writeType(org.springframework.data.util.TypeInformation, java.lang.Object)
*/
public void writeType(TypeInformation<?> info, S sink) {
Assert.notNull(info);
for (TypeInformationMapper mapper : mappers) {
Object alias = mapper.createAliasFor(info);
if (alias != null) {
accessor.writeTypeTo(sink, alias);
return;
}
}
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.convert;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.context.MappingContext;
/**
* Combined {@link EntityReader} and {@link EntityWriter} and add the ability to access a {@link MappingContext} and
* {@link ConversionService}.
*
* @param <E> the concrete {@link PersistentEntity} implementation the converter is based on.
* @param <P> the concrete {@link PersistentProperty} implementation the converter is based on.
* @param <T> the most common type the {@link EntityConverter} can actually convert.
* @param <S> the store specific source and sink an {@link EntityConverter} can deal with.
* @author Oliver Gierke
*/
public interface EntityConverter<E extends PersistentEntity<?, P>, P extends PersistentProperty<P>, T, S> extends
EntityReader<T, S>, EntityWriter<T, S> {
/**
* Returns the underlying {@link MappingContext} used by the converter.
*
* @return never {@literal null}
*/
MappingContext<? extends E, P> getMappingContext();
/**
* Returns the underlying {@link ConversionService} used by the converter.
*
* @return never {@literal null}.
*/
ConversionService getConversionService();
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.convert;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.model.ParameterValueProvider;
/**
* SPI to abstract strategies to create instances for {@link PersistentEntity}s.
*
* @author Oliver Gierke
*/
public interface EntityInstantiator {
/**
* Creates a new instance of the given entity using the given source to pull data from.
*
* @param entity will not be {@literal null}.
* @param provider will not be {@literal null}.
* @return
*/
<T, E extends PersistentEntity<? extends T, P>, P extends PersistentProperty<P>> T createInstance(E entity,
ParameterValueProvider<P> provider);
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.convert;
import java.util.Collections;
import java.util.Map;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.util.Assert;
/**
* Simple value object allowing access to {@link EntityInstantiator} instances for a given type falling back to a
* default one.
*
* @author Oliver Gierke
*/
public class EntityInstantiators {
private final EntityInstantiator fallback;
private final Map<Class<?>, EntityInstantiator> customInstantiators;
/**
* Creates a new {@link EntityInstantiators} using the default fallback instantiator and no custom ones.
*/
public EntityInstantiators() {
this(Collections.<Class<?>, EntityInstantiator> emptyMap());
}
/**
* Creates a new {@link EntityInstantiators} using the given {@link EntityInstantiator} as fallback.
*
* @param fallback must not be {@literal null}.
*/
public EntityInstantiators(EntityInstantiator fallback) {
this(fallback, Collections.<Class<?>, EntityInstantiator> emptyMap());
}
/**
* Creates a new {@link EntityInstantiators} using the default fallback instantiator and the given custom ones.
*
* @param customInstantiators must not be {@literal null}.
*/
public EntityInstantiators(Map<Class<?>, EntityInstantiator> customInstantiators) {
this(ReflectionEntityInstantiator.INSTANCE, customInstantiators);
}
/**
* Creates a new {@link EntityInstantiator} using the given fallback {@link EntityInstantiator} and the given custom
* ones.
*
* @param fallback must not be {@literal null}.
* @param customInstantiators must not be {@literal null}.
*/
public EntityInstantiators(EntityInstantiator defaultInstantiator,
Map<Class<?>, EntityInstantiator> customInstantiators) {
Assert.notNull(defaultInstantiator);
Assert.notNull(customInstantiators);
this.fallback = defaultInstantiator;
this.customInstantiators = customInstantiators;
}
/**
* Returns the {@link EntityInstantiator} to be used to create the given {@link PersistentEntity}.
*
* @param entity must not be {@literal null}.
* @return will never be {@literal null}.
*/
public EntityInstantiator getInstantiatorFor(PersistentEntity<?, ?> entity) {
Assert.notNull(entity);
Class<?> type = entity.getType();
if (!customInstantiators.containsKey(type)) {
return fallback;
}
EntityInstantiator instantiator = customInstantiators.get(entity.getType());
return instantiator == null ? fallback : instantiator;
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.convert;
/**
* Interface to read object from store specific sources.
*
* @author Oliver Gierke
*/
public interface EntityReader<T, S> {
/**
* Reads the given source into the given type.
*
* @param type they type to convert the given source to.
* @param source the source to create an object of the given type from.
* @return
*/
<R extends T> R read(Class<R> type, S source);
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.convert;
/**
* Interface to write objects into store specific sinks.
*
* @param <T> the entity type the converter can handle
* @param <S> the store specific sink the converter is able to write to
* @author Oliver Gierke
*/
public interface EntityWriter<T, S> {
void write(T source, S sink);
}

View File

@@ -0,0 +1,125 @@
/*
* Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.convert;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
/**
* {@link TypeInformationMapper} implementation that can be either set up using a {@link MappingContext} or manually set
* up {@link Map} of {@link String} aliases to types. If a {@link MappingContext} is used the {@link Map} will be build
* inspecting the {@link PersistentEntity} instances for type alias information.
*
* @author Oliver Gierke
*/
public class MappingContextTypeInformationMapper implements TypeInformationMapper {
private final Map<TypeInformation<?>, Object> typeMap;
private final MappingContext<? extends PersistentEntity<?, ?>, ?> mappingContext;
/**
* Creates a {@link MappingContextTypeInformationMapper} from the given {@link MappingContext}. Inspects all
* {@link PersistentEntity} instances for alias information and builds a {@link Map} of aliases to types from it.
*
* @param mappingContext must not be {@literal null}.
*/
public MappingContextTypeInformationMapper(MappingContext<? extends PersistentEntity<?, ?>, ?> mappingContext) {
Assert.notNull(mappingContext);
this.typeMap = new HashMap<TypeInformation<?>, Object>();
this.mappingContext = mappingContext;
for (PersistentEntity<?, ?> entity : mappingContext.getPersistentEntities()) {
safelyAddToCache(entity.getTypeInformation(), entity.getTypeAlias());
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.convert.TypeInformationMapper#createAliasFor(org.springframework.data.util.TypeInformation)
*/
public Object createAliasFor(TypeInformation<?> type) {
Object key = typeMap.get(type);
if (key != null) {
return key;
}
PersistentEntity<?, ?> entity = mappingContext.getPersistentEntity(type);
if (entity == null) {
return null;
}
Object alias = entity.getTypeAlias();
safelyAddToCache(type, alias);
return alias;
}
/**
* Adds the given alias to the cache in a {@literal null}-safe manner.
*
* @param key must not be {@literal null}.
* @param alias can be {@literal null}.
*/
private void safelyAddToCache(TypeInformation<?> key, Object alias) {
if (alias == null) {
return;
}
if (typeMap.containsValue(alias)) {
throw new IllegalArgumentException(String.format(
"Detected mapping ambiguity! String %s cannot be mapped to more than one type!", alias));
}
typeMap.put(key, alias);
}
/*
* (non-Javadoc)
* @see org.springframework.data.convert.TypeInformationMapper#resolveTypeFrom(java.lang.Object)
*/
public TypeInformation<?> resolveTypeFrom(Object alias) {
if (alias == null) {
return null;
}
for (Entry<TypeInformation<?>, Object> entry : typeMap.entrySet()) {
if (entry.getValue().equals(alias)) {
return entry.getKey();
}
}
for (PersistentEntity<?, ?> entity : mappingContext.getPersistentEntities()) {
if (alias.equals(entity.getTypeAlias())) {
return entity.getTypeInformation();
}
}
return null;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.convert;
import static java.lang.annotation.ElementType.*;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.convert.converter.Converter;
/**
* Annotation to clarify intended usage of a {@link Converter} as reading converter in case the conversion types leave
* room for disambiguation.
*
* @author Oliver Gierke
*/
@Target(TYPE)
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface ReadingConverter {
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.convert;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.BeanUtils;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PreferredConstructor;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.data.mapping.model.MappingInstantiationException;
import org.springframework.data.mapping.model.ParameterValueProvider;
/**
* {@link EntityInstantiator} that uses the {@link PersistentEntity}'s {@link PreferredConstructor} to instantiate an
* instance of the entity via reflection.
*
* @author Oliver Gierke
*/
public enum ReflectionEntityInstantiator implements EntityInstantiator {
INSTANCE;
@SuppressWarnings("unchecked")
public <T, E extends PersistentEntity<? extends T, P>, P extends PersistentProperty<P>> T createInstance(E entity,
ParameterValueProvider<P> provider) {
PreferredConstructor<? extends T, P> constructor = entity.getPersistenceConstructor();
if (constructor == null) {
try {
Class<?> clazz = entity.getType();
if (clazz.isArray()) {
Class<?> ctype = clazz;
int dims = 0;
while (ctype.isArray()) {
ctype = ctype.getComponentType();
dims++;
}
return (T) Array.newInstance(clazz, dims);
} else {
return BeanUtils.instantiateClass(entity.getType());
}
} catch (BeanInstantiationException e) {
throw new MappingInstantiationException(e.getMessage(), e);
}
}
List<Object> params = new ArrayList<Object>();
if (null != provider && constructor.hasParameters()) {
for (Parameter<?, P> parameter : constructor.getParameters()) {
params.add(provider.getParameterValue(parameter));
}
}
try {
return BeanUtils.instantiateClass(constructor.getConstructor(), params.toArray());
} catch (BeanInstantiationException e) {
throw new MappingInstantiationException(e.getMessage(), e);
}
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.convert;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* Basic {@link TypeInformationMapper} implementation that interprets the alias handles as fully qualified class name
* and tries to load a class with the given name to build {@link TypeInformation}. Returns the fully qualified class
* name for alias creation.
*
* @author Oliver Gierke
*/
public class SimpleTypeInformationMapper implements TypeInformationMapper {
public static final SimpleTypeInformationMapper INSTANCE = new SimpleTypeInformationMapper();
/**
* Returns the {@link TypeInformation} that shall be used when the given {@link String} value is found as type hint.
* The implementation will simply interpret the given value as fully-qualified class name and try to load the class.
* Will return {@literal null} in case the given {@link String} is empty.
*
* @param value the type to load, must not be {@literal null}.
* @return the type to be used for the given {@link String} representation or {@literal null} if nothing found or the
* class cannot be loaded.
*/
public TypeInformation<?> resolveTypeFrom(Object alias) {
if (!(alias instanceof String)) {
return null;
}
String value = (String) alias;
if (!StringUtils.hasText(value)) {
return null;
}
try {
return ClassTypeInformation.from(ClassUtils.forName(value, null));
} catch (ClassNotFoundException e) {
return null;
}
}
/**
* Turn the given type information into the String representation that shall be stored. Default implementation simply
* returns the fully-qualified class name.
*
* @param typeInformation must not be {@literal null}.
* @return the String representation to be stored or {@literal null} if no type information shall be stored.
*/
public String createAliasFor(TypeInformation<?> type) {
return type.getType().getName();
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.convert;
/**
* Interface to abstract implementations of how to access a type alias from a given source or sink.
*
* @author Oliver Gierke
*/
public interface TypeAliasAccessor<S> {
/**
* Reads the type alias to be used from the given source.
*
* @param source
* @return can be {@literal null} in case no alias was found.
*/
Object readAliasFrom(S source);
/**
* Writes the given type alias to the given sink.
*
* @param sink
* @param alias
*/
void writeTypeTo(S sink, Object alias);
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.convert;
import org.springframework.data.util.TypeInformation;
/**
* Interface to abstract the mapping from a type alias to the actual type.
*
* @author Oliver Gierke
*/
public interface TypeInformationMapper {
/**
* Returns the actual {@link TypeInformation} to be used for the given alias.
*
* @param alias can be {@literal null}.
* @return
*/
TypeInformation<?> resolveTypeFrom(Object alias);
/**
* Returns the alias to be used for the given {@link TypeInformation}.
*
* @param type
* @return
*/
Object createAliasFor(TypeInformation<?> type);
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.convert;
import org.springframework.data.util.TypeInformation;
/**
* Interface to define strategies how to store type information in a store specific sink or source.
*
* @author Oliver Gierke
*/
public interface TypeMapper<S> {
/**
* Reads the {@link TypeInformation} from the given source.
*
* @param source must not be {@literal null}.
* @return
*/
TypeInformation<?> readType(S source);
/**
* Returns the {@link TypeInformation} from the given source if it is a more concrete type than the given default one.
*
* @param source must not be {@literal null}.
* @param defaultType
* @return
*/
<T> TypeInformation<? extends T> readType(S source, TypeInformation<T> defaultType);
/**
* Writes type information for the given type into the given sink.
*
* @param type must not be {@literal null}.
* @param dbObject must not be {@literal null}.
*/
void writeType(Class<?> type, S dbObject);
/**
* Writes type information for the given {@link TypeInformation} into the given sink.
*
* @param type must not be {@literal null}.
* @param dbObject must not be {@literal null}.
*/
void writeType(TypeInformation<?> type, S dbObject);
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.convert;
import static java.lang.annotation.ElementType.*;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.convert.converter.Converter;
/**
* Annotation to clarify intended usage of a {@link Converter} as writing converter in case the conversion types leave
* room for disambiguation.
*
* @author Oliver Gierke
*/
@Target(TYPE)
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface WritingConverter {
}

View File

@@ -0,0 +1,6 @@
/**
* General purpose conversion framework to read objects from a data store abstraction and write it back.
*
* @see org.springframework.data.convert.EntityConverter
*/
package org.springframework.data.convert;

View File

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

@@ -0,0 +1,28 @@
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.crossstore;
/**
* Interface introduced to objects exposing ChangeSet information
*
* @author Rod Johnson
* @author Thomas Risberg
*/
public interface ChangeSetBacked {
ChangeSet getChangeSet();
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.crossstore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.support.TransactionSynchronization;
public class ChangeSetBackedTransactionSynchronization implements TransactionSynchronization {
protected final Logger log = LoggerFactory.getLogger(getClass());
private final ChangeSetPersister<Object> changeSetPersister;
private final ChangeSetBacked entity;
private int changeSetTxStatus = -1;
public ChangeSetBackedTransactionSynchronization(ChangeSetPersister<Object> changeSetPersister, ChangeSetBacked entity) {
this.changeSetPersister = changeSetPersister;
this.entity = entity;
}
public void afterCommit() {
log.debug("After Commit called for " + entity);
changeSetPersister.persistState(entity, entity.getChangeSet());
changeSetTxStatus = 0;
}
public void afterCompletion(int status) {
log.debug("After Completion called with status = " + status);
if (changeSetTxStatus == 0) {
if (status == STATUS_COMMITTED) {
// this is good
log.debug("ChangedSetBackedTransactionSynchronization completed successfully for " + this.entity);
} else {
// this could be bad - TODO: compensate
log.error("ChangedSetBackedTransactionSynchronization failed for " + this.entity);
}
}
}
public void beforeCommit(boolean readOnly) {
}
public void beforeCompletion() {
}
public void flush() {
}
public void resume() {
throw new IllegalStateException(
"ChangedSetBackedTransactionSynchronization does not support transaction suspension currently.");
}
public void suspend() {
throw new IllegalStateException(
"ChangedSetBackedTransactionSynchronization does not support transaction suspension currently.");
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.crossstore;
import org.springframework.dao.DataAccessException;
/**
* Interface to be implemented by classes that can synchronize between data stores and ChangeSets.
*
* @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
* @return
* @throws DataAccessException
*/
K getPersistentId(ChangeSetBacked entity, ChangeSet cs) throws DataAccessException;
/**
* Return key
*
* @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 {
private static final long serialVersionUID = -8604207973816331140L;
}
}

View File

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

@@ -0,0 +1,4 @@
/**
* Support for cross-store persistence.
*/
package org.springframework.data.crossstore;

View File

@@ -0,0 +1,87 @@
/*
* Copyright 2008-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.domain;
import java.io.Serializable;
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.
*
* @param <U> the auditing type. Typically some kind of user.
* @param <ID> the type of the audited type's identifier
* @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();
/**
* 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();
/**
* 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();
/**
* 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();
/**
* Sets the date of the last modification.
*
* @param lastModifiedDate the date of the last modification to set
*/
void setLastModifiedDate(final DateTime lastModifiedDate);
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2008-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.domain;
/**
* Interface for components that are aware of the application's current auditor. This will be some kind of user mostly.
*
* @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();
}

View File

@@ -0,0 +1,120 @@
/*
* Copyright 2008-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.domain;
import java.util.Iterator;
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.
*
* @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 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 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 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 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();
/*
* (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 sorting parameters for the page.
*
* @return
*/
Sort getSort();
}

View File

@@ -0,0 +1,255 @@
/*
* Copyright 2008-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.domain;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* Basic {@code Page} implementation.
*
* @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;
/**
* 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()
*/
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;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result = 31 * result + (int) (total ^ total >>> 32);
result = 31 * result + (pageable == null ? 0 : pageable.hashCode());
result = 31 * result + content.hashCode();
return result;
}
}

View File

@@ -0,0 +1,164 @@
/*
* Copyright 2008-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.domain;
import java.io.Serializable;
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;
/**
* 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);
}
/**
* 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));
}
/**
* 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 >= size) {
throw new IllegalArgumentException("Page size must not be less than or equal to zero!");
}
this.page = page;
this.size = size;
this.sort = sort;
}
/*
* (non-Javadoc)
*
* @see org.springframework.data.domain.Pageable#getPageSize()
*/
public int getPageSize() {
return size;
}
/*
* (non-Javadoc)
*
* @see org.springframework.data.domain.Pageable#getPageNumber()
*/
public int getPageNumber() {
return page;
}
/*
* (non-Javadoc)
*
* @see org.springframework.data.domain.Pageable#getFirstItem()
*/
public int getOffset() {
return page * size;
}
/*
* (non-Javadoc)
*
* @see org.springframework.data.domain.Pageable#getSort()
*/
public Sort getSort() {
return sort;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof PageRequest)) {
return false;
}
PageRequest that = (PageRequest) obj;
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);
return pageEqual && sizeEqual && sortEqual;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result = 31 * result + page;
result = 31 * result + size;
result = 31 * result + (null == sort ? 0 : sort.hashCode());
return result;
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2008-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.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 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 sorting parameters.
*
* @return
*/
Sort getSort();
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2008-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.domain;
import java.io.Serializable;
/**
* Simple interface for entities.
*
* @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 if the {@code Persistable} is new or was persisted already.
*
* @return if the object is new
*/
boolean isNew();
}

View File

@@ -0,0 +1,359 @@
/*
* Copyright 2008-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.domain;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
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
* {@literal null} or empty strings. The direction defaults to {@link Sort#DEFAULT_DIRECTION}.
*
* @author Oliver Gierke
*/
public class Sort implements Iterable<org.springframework.data.domain.Sort.Order>, Serializable {
private static final long serialVersionUID = 5737186511678863905L;
public static final Direction DEFAULT_DIRECTION = Direction.ASC;
private final List<Order> orders;
/**
* Creates a new {@link Sort} instance using the given {@link Order}s.
*
* @param orders must not be {@literal null}.
*/
public Sort(Order... orders) {
this(Arrays.asList(orders));
}
/**
* Creates a new {@link Sort} instance.
*
* @param orders must not be {@literal null} or contain {@literal null}.
*/
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!");
}
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) {
this(DEFAULT_DIRECTION, properties);
}
/**
* Creates a new {@link Sort} instance.
*
* @param direction defaults to {@linke 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));
}
/**
* 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());
for (String property : properties) {
this.orders.add(new Order(direction, property));
}
}
/**
* Returns a new {@link Sort} consisting of the {@link Order}s of the current {@link Sort} combined with the given
* ones.
*
* @param sort can be {@literal null}.
* @return
*/
public Sort and(Sort sort) {
if (sort == null) {
return this;
}
ArrayList<Order> these = new ArrayList<Order>(this.orders);
for (Order order : sort) {
these.add(order);
}
return new Sort(these);
}
/**
* 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);
}
}
}
/**
* PropertyPath implements the pairing of an {@link Direction} and a property. It is used to provide input for
* {@link Sort}
*
* @author Oliver Gierke
*/
public static class Order implements Serializable {
private static final long serialVersionUID = 1522511010900108987L;
private final Direction direction;
private final String property;
/**
* Creates a new {@link Order} instance. if order is {@literal null} then order defaults to
* {@link Sort#DEFAULT_DIRECTION}
*
* @param direction can be {@literal null}, will default to {@link Sort#DEFAULT_DIRECTION}
* @param property must not be {@literal null} or empty.
*/
public Order(Direction direction, String property) {
if (!StringUtils.hasText(property)) {
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. Direction defaults to
* {@link Sort#DEFAULT_DIRECTION}.
*
* @param property must not be {@literal null} or empty.
*/
public Order(String property) {
this(DEFAULT_DIRECTION, property);
}
/**
* @deprecated use {@link Sort#Sort(Direction, List)} instead.
*/
@Deprecated
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) {
return new Sort(this.direction, properties);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result = 31 * result + direction.hashCode();
result = 31 * result + property.hashCode();
return result;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Order)) {
return false;
}
Order that = (Order) obj;
return this.direction.equals(that.direction) && this.property.equals(that.property);
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("%s: %s", property, direction);
}
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.domain.jaxb;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.domain.jaxb.SpringDataJaxb.OrderDto;
/**
* XmlAdapter to convert {@link Order} instances into {@link OrderDto}s and vice versa.
*
* @author Oliver Gierke
*/
public class OrderAdapter extends XmlAdapter<OrderDto, Order> {
public static final OrderAdapter INSTANCE = new OrderAdapter();
/*
* (non-Javadoc)
* @see javax.xml.bind.annotation.adapters.XmlAdapter#marshal(java.lang.Object)
*/
@Override
public OrderDto marshal(Order order) throws Exception {
if (order == null) {
return null;
}
OrderDto dto = new OrderDto();
dto.direction = order.getDirection();
dto.property = order.getProperty();
return dto;
}
/*
* (non-Javadoc)
* @see javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal(java.lang.Object)
*/
@Override
public Order unmarshal(OrderDto source) throws Exception {
return source == null ? null : new Order(source.direction, source.property);
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.domain.jaxb;
import java.util.Collections;
import java.util.List;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.jaxb.SpringDataJaxb.PageDto;
import org.springframework.hateoas.Link;
/**
* {@link XmlAdapter} to convert {@link Page} instances into {@link PageDto} instances and vice versa.
*
* @author Oliver Gierke
*/
public class PageAdapter extends XmlAdapter<PageDto, Page<Object>> {
/*
* (non-Javadoc)
* @see javax.xml.bind.annotation.adapters.XmlAdapter#marshal(java.lang.Object)
*/
@Override
public PageDto marshal(Page<Object> source) throws Exception {
if (source == null) {
return null;
}
PageDto dto = new PageDto();
dto.content = source.getContent();
dto.add(getLinks(source));
return dto;
}
/*
* (non-Javadoc)
* @see javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal(java.lang.Object)
*/
@Override
public Page<Object> unmarshal(PageDto v) throws Exception {
return null;
}
/**
* Return additional links that shall be added to the {@link PageDto}.
*
* @param source the source {@link Page}.
* @return
*/
protected List<Link> getLinks(Page<?> source) {
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.domain.jaxb;
import java.util.Collections;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.jaxb.SpringDataJaxb.OrderDto;
import org.springframework.data.domain.jaxb.SpringDataJaxb.PageRequestDto;
import org.springframework.data.domain.jaxb.SpringDataJaxb.SortDto;
/**
* {@link XmlAdapter} to convert {@link Pageable} instances int a {@link PageRequestDto} and vice versa.
*
* @author Oliver Gierke
*/
class PageableAdapter extends XmlAdapter<PageRequestDto, Pageable> {
/*
* (non-Javadoc)
* @see javax.xml.bind.annotation.adapters.XmlAdapter#marshal(java.lang.Object)
*/
@Override
public PageRequestDto marshal(Pageable request) throws Exception {
SortDto sortDto = SortAdapter.INSTANCE.marshal(request.getSort());
PageRequestDto dto = new PageRequestDto();
dto.orders = sortDto == null ? Collections.<OrderDto> emptyList() : sortDto.orders;
dto.page = request.getPageNumber();
dto.size = request.getPageSize();
return dto;
}
/*
* (non-Javadoc)
* @see javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal(java.lang.Object)
*/
@Override
public Pageable unmarshal(PageRequestDto v) throws Exception {
if (v.orders.isEmpty()) {
return new PageRequest(v.page, v.size);
}
SortDto sortDto = new SortDto();
sortDto.orders = v.orders;
Sort sort = SortAdapter.INSTANCE.unmarshal(sortDto);
return new PageRequest(v.page, v.size, sort);
}
}

View File

@@ -0,0 +1,42 @@
package org.springframework.data.domain.jaxb;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.jaxb.SpringDataJaxb.SortDto;
/**
* {@link XmlAdapter} to convert {@link Sort} instances into {@link SortDto} instances and vice versa.
*
* @author Oliver Gierke
*/
public class SortAdapter extends XmlAdapter<SortDto, Sort> {
public static final SortAdapter INSTANCE = new SortAdapter();
/*
* (non-Javadoc)
* @see javax.xml.bind.annotation.adapters.XmlAdapter#marshal(java.lang.Object)
*/
@Override
public SortDto marshal(Sort source) throws Exception {
if (source == null) {
return null;
}
SortDto dto = new SortDto();
dto.orders = SpringDataJaxb.marshal(source, OrderAdapter.INSTANCE);
return dto;
}
/*
* (non-Javadoc)
* @see javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal(java.lang.Object)
*/
@Override
public Sort unmarshal(SortDto source) throws Exception {
return source == null ? null : new Sort(SpringDataJaxb.unmarshal(source.orders, OrderAdapter.INSTANCE));
}
}

View File

@@ -0,0 +1,153 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.domain.jaxb;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.util.Assert;
/**
* Helper class containing utility methods to implement JAXB {@link XmlAdapter}s as well as the DTO types to be
* marshalled by JAXB.
*
* @author Oliver Gierke
*/
public class SpringDataJaxb {
public static final String NAMESPACE = "http://www.springframework.org/schema/data/jaxb";
/**
* The DTO for {@link Pageable}s/{@link PageRequest}s.
*
* @author Oliver Gierke
*/
@XmlRootElement(name = "page-request", namespace = NAMESPACE)
@XmlAccessorType(XmlAccessType.FIELD)
public static class PageRequestDto {
@XmlAttribute
int page, size;
@XmlElement(name = "order", namespace = NAMESPACE)
List<OrderDto> orders = new ArrayList<OrderDto>();
}
/**
* The DTO for {@link Sort}.
*
* @author Oliver Gierke
*/
@XmlRootElement(name = "sort", namespace = NAMESPACE)
@XmlAccessorType(XmlAccessType.FIELD)
public static class SortDto {
@XmlElement(name = "order", namespace = SpringDataJaxb.NAMESPACE)
List<OrderDto> orders = new ArrayList<OrderDto>();
}
/**
* The DTO for {@link Order}.
*
* @author Oliver Gierke
*/
@XmlRootElement(name = "order", namespace = NAMESPACE)
@XmlAccessorType(XmlAccessType.FIELD)
public static class OrderDto {
@XmlAttribute
String property;
@XmlAttribute
Direction direction;
}
/**
* The DTO for {@link Page}.
*
* @author Oliver Gierke
*/
@XmlRootElement(name = "page", namespace = NAMESPACE)
@XmlAccessorType(XmlAccessType.FIELD)
public static class PageDto extends ResourceSupport {
@XmlAnyElement
@XmlElementWrapper(name = "content")
List<Object> content;
}
/**
* Unmarshals each element of the given {@link Collection} using the given {@link XmlAdapter}.
*
* @param source
* @param adapter must not be {@literal null}.
* @return
* @throws Exception
*/
public static <T, S> List<T> unmarshal(Collection<S> source, XmlAdapter<S, T> adapter) throws Exception {
Assert.notNull(adapter);
if (source == null || source.isEmpty()) {
return Collections.emptyList();
}
List<T> result = new ArrayList<T>(source.size());
for (S element : source) {
result.add(adapter.unmarshal(element));
}
return result;
}
/**
* Marshals each of the elements of the given {@link Iterable} using the given {@link XmlAdapter}.
*
* @param source
* @param adapter must not be {@literal null}.
* @return
* @throws Exception
*/
public static <T, S> List<S> marshal(Iterable<T> source, XmlAdapter<S, T> adapter) throws Exception {
Assert.notNull(adapter);
if (source == null) {
return Collections.emptyList();
}
List<S> result = new ArrayList<S>();
for (T element : source) {
result.add(adapter.marshal(element));
}
return result;
}
}

View File

@@ -0,0 +1,21 @@
/**
* Central domain abstractions especially to be used in combination with the {@link org.springframework.data.repository.Repository} abstraction.
*
* @see org.springframework.data.repository.Repository
*/
@XmlSchema(xmlns = { @XmlNs(prefix = "sd", namespaceURI = org.springframework.data.domain.jaxb.SpringDataJaxb.NAMESPACE) })
@XmlJavaTypeAdapters({
@XmlJavaTypeAdapter(value = org.springframework.data.domain.jaxb.PageableAdapter.class, type = Pageable.class),
@XmlJavaTypeAdapter(value = org.springframework.data.domain.jaxb.SortAdapter.class, type = Sort.class),
@XmlJavaTypeAdapter(value = org.springframework.data.domain.jaxb.PageAdapter.class, type = Page.class) })
package org.springframework.data.domain.jaxb;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlSchema;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

View File

@@ -0,0 +1,7 @@
/**
* 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

@@ -0,0 +1,93 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.history;
import java.lang.annotation.Annotation;
import org.joda.time.DateTime;
import org.springframework.data.util.AnnotationDetectionFieldCallback;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
* A {@link RevisionMetadata} implementation that inspects the given object for fields with the configured annotations
* and returns the field's values on calls to {@link #getRevisionDate()} and {@link #getRevisionNumber()}.
*
* @author Oliver Gierke
*/
public class AnnotationRevisionMetadata<N extends Number & Comparable<N>> implements RevisionMetadata<N> {
private final Object entity;
private final N revisionNumber;
private final DateTime revisionDate;
/**
* Creates a new {@link AnnotationRevisionMetadata} inspecing the given entity for the given annotations. If no
* annotations will be provided these values will not be looked up from the entity and return {@literal null}.
*
* @param entity must not be {@literal null}.
* @param revisionNumberAnnotation
* @param revisionTimeStampAnnotation
*/
public AnnotationRevisionMetadata(final Object entity, Class<? extends Annotation> revisionNumberAnnotation,
Class<? extends Annotation> revisionTimeStampAnnotation) {
Assert.notNull(entity);
this.entity = entity;
if (revisionNumberAnnotation != null) {
AnnotationDetectionFieldCallback numberCallback = new AnnotationDetectionFieldCallback(revisionNumberAnnotation);
ReflectionUtils.doWithFields(entity.getClass(), numberCallback);
this.revisionNumber = numberCallback.getValue(entity);
} else {
this.revisionNumber = null;
}
if (revisionTimeStampAnnotation != null) {
AnnotationDetectionFieldCallback revisionCallback = new AnnotationDetectionFieldCallback(
revisionTimeStampAnnotation);
ReflectionUtils.doWithFields(entity.getClass(), revisionCallback);
this.revisionDate = new DateTime(revisionCallback.getValue(entity));
} else {
this.revisionDate = null;
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.history.RevisionMetadata#getRevisionNumber()
*/
public N getRevisionNumber() {
return revisionNumber;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.history.RevisionMetadata#getRevisionDate()
*/
public DateTime getRevisionDate() {
return revisionDate;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.history.RevisionMetadata#getDelegate()
*/
@SuppressWarnings("unchecked")
public <T> T getDelegate() {
return (T) entity;
}
}

View File

@@ -0,0 +1,132 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.history;
import org.joda.time.DateTime;
import org.springframework.util.Assert;
/**
* Wrapper to contain {@link RevisionMetadata} as well as the revisioned entity.
*
* @author Oliver Gierke
* @author Philipp Huegelmeyer
*/
public final class Revision<N extends Number & Comparable<N>, T> implements Comparable<Revision<N, ?>> {
private final RevisionMetadata<N> metadata;
private final T entity;
/**
* Creates a new {@link Revision} consisting of the given {@link RevisionMetadata} and entity.
*
* @param metadata must not be {@literal null}.
* @param entity must not be {@literal null}.
*/
public Revision(RevisionMetadata<N> metadata, T entity) {
Assert.notNull(metadata);
Assert.notNull(entity);
this.metadata = metadata;
this.entity = entity;
}
/**
* Returns the revision number of the revision.
*
* @return the revision number.
*/
public N getRevisionNumber() {
return metadata.getRevisionNumber();
}
/**
* Returns the revision date of the revision.
*
* @return
*/
public DateTime getRevisionDate() {
return metadata.getRevisionDate();
}
/**
* Returns the underlying entity.
*
* @return the entity
*/
public T getEntity() {
return entity;
}
/**
* Returns the {@link RevisionMetadata} for the current {@link Revision}.
*
* @return the metadata
*/
public RevisionMetadata<N> getMetadata() {
return metadata;
}
/*
* (non-Javadoc)
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(Revision<N, ?> that) {
return getRevisionNumber().compareTo(that.getRevisionNumber());
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
@SuppressWarnings("unchecked")
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Revision)) {
return false;
}
Revision<N, T> that = (Revision<N, T>) obj;
boolean sameRevisionNumber = this.metadata.getRevisionNumber().equals(that.metadata.getRevisionNumber());
return !sameRevisionNumber ? false : this.entity.equals(that.entity);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result += 31 * metadata.hashCode();
result += 31 * entity.hashCode();
return result;
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("Revision %s of entity %s - Revision metadata %s", getRevisionNumber(), entity, metadata);
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.history;
import org.joda.time.DateTime;
/**
* Metadata about a revision.
*
* @author Philipp Huegelmeyer
* @author Oliver Gierke
*/
public interface RevisionMetadata<N extends Number & Comparable<N>> {
/**
* Returns the revision number of the revision.
*
* @return
*/
N getRevisionNumber();
/**
* Returns the date of the revision.
*
* @return
*/
DateTime getRevisionDate();
/**
* Returns the underlying revision metadata which might provider more detailed implementation specific information.
*
* @return
*/
<T> T getDelegate();
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.history;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.springframework.util.Assert;
/**
* Simple wrapper class for a {@link List} of {@link Revisions} allowing to canonically access the latest revision.
* Allows iterating over the underlying {@link Revisions} starting with older revisions.
*
* @author Oliver Gierke
*/
public class Revisions<N extends Number & Comparable<N>, T> implements Iterable<Revision<N, T>> {
private final List<Revision<N, T>> revisions;
private final boolean latestLast;
/**
* Creates a new {@link Revisions} instance containing the given revisions. Will make sure they are ordered
* ascendingly.
*
* @param revisions must not be {@literal null}.
*/
public Revisions(List<? extends Revision<N, T>> revisions) {
this(revisions, true);
}
/**
* Creates a new {@link Revisions} instance using the given revisions.
*
* @param revisions must not be {@literal null}.
* @param latestLast
*/
private Revisions(List<? extends Revision<N, T>> revisions, boolean latestLast) {
Assert.notNull(revisions);
this.revisions = new ArrayList<Revision<N, T>>(revisions);
this.latestLast = latestLast;
Collections.sort(this.revisions);
if (!latestLast) {
Collections.reverse(this.revisions);
}
}
/**
* Returns the latest revision of the revisions backing the wrapper independently of the order.
*
* @return
*/
public Revision<N, T> getLatestRevision() {
int index = latestLast ? revisions.size() - 1 : 0;
return revisions.get(index);
}
/**
* Reverses the current {@link Revisions}. By default this will return the revisions with the latest revision first.
*
* @return
*/
public Revisions<N, T> reverse() {
List<Revision<N, T>> result = new ArrayList<Revision<N, T>>(revisions);
Collections.reverse(result);
return new Revisions<N, T>(result, !latestLast);
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
public Iterator<Revision<N, T>> iterator() {
return revisions.iterator();
}
/**
* Returns the content of the {@link Revisions} instance.
*
* @return
*/
public List<Revision<N, T>> getContent() {
return Collections.unmodifiableList(revisions);
}
}

View File

@@ -0,0 +1,5 @@
/**
* Basic interfaces and value objects for historiography API.
*/
package org.springframework.data.history;

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping;
/**
* Value object to capture {@link Association}s.
*
* @param the {@link PersistentProperty}s the association connects.
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class Association<P extends PersistentProperty<P>> {
private final P inverse;
private final P obverse;
/**
* Creates a new {@link Association} between the two given {@link PersistentProperty}s.
*
* @param inverse
* @param obverse
*/
public Association(P inverse, P obverse) {
this.inverse = inverse;
this.obverse = obverse;
}
public P getInverse() {
return inverse;
}
public P getObverse() {
return obverse;
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping;
/**
* Callback interface to implement functionality to be applied to a collection of {@link Association}s.
*
* @author Jon Brisbin <jbrisbin@vmware.com>
* @author Oliver Gierke
*/
public interface AssociationHandler<P extends PersistentProperty<P>> {
/**
* Processes the given {@link Association}.
*
* @param association
*/
void doWithAssociation(Association<P> association);
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping;
import org.springframework.data.convert.EntityInstantiator;
import org.springframework.data.util.TypeInformation;
/**
* Represents a persistent entity.
*
* @author Oliver Gierke
* @author Graeme Rocher
* @author Jon Brisbin
* @author Patryk Wasik
*/
public interface PersistentEntity<T, P extends PersistentProperty<P>> {
/**
* The entity name including any package prefix.
*
* @return must never return {@literal null}
*/
String getName();
/**
* Returns the {@link PreferredConstructor} to be used to instantiate objects of this {@link PersistentEntity}.
*
* @return {@literal null} in case no suitable constructor for automatic construction can be found. This usually
* indicates that the instantiation of the object of tthat persistent entity is done through either a customer
* {@link EntityInstantiator} or handled by custom conversion mechanisms entirely.
*/
PreferredConstructor<T, P> getPersistenceConstructor();
/**
* Returns whether the given {@link PersistentProperty} is referred to by a constructor argument of the
* {@link PersistentEntity}.
*
* @param property
* @return true if the given {@link PersistentProperty} is referred to by a constructor argument or {@literal false}
* if not or {@literal null}.
*/
boolean isConstructorArgument(PersistentProperty<?> property);
/**
* Returns whether the given {@link PersistentProperty} is the id property of the entity.
*
* @param property
* @return
*/
boolean isIdProperty(PersistentProperty<?> property);
/**
* Returns whether the given {@link PersistentProperty} is the version property of the entity.
*
* @param property
* @return
*/
boolean isVersionProperty(PersistentProperty<?> property);
/**
* Returns the id property of the {@link PersistentEntity}. Can be {@literal null} in case this is an entity
* completely handled by a custom conversion.
*
* @return the id property of the {@link PersistentEntity}.
*/
P getIdProperty();
/**
* Returns the version property of the {@link PersistentEntity}. Can be {@literal null} in case no version property is
* available on the entity.
*
* @return the version property of the {@link PersistentEntity}.
*/
P getVersionProperty();
/**
* Obtains a {@link PersistentProperty} instance by name.
*
* @param name The name of the property
* @return the {@link PersistentProperty} or {@literal null} if it doesn't exist.
*/
P getPersistentProperty(String name);
/**
* Returns whether the {@link PersistentEntity} has an id property. If this call returns {@literal true},
* {@link #getIdProperty()} will return a non-{@literal null} value.
*
* @return
*/
boolean hasIdProperty();
/**
* Returns whether the {@link PersistentEntity} has a version property. If this call returns {@literal true},
* {@link #getVersionProperty()} will return a non-{@literal null} value.
*
* @return
*/
boolean hasVersionProperty();
/**
* Returns the resolved Java type of this entity.
*
* @return The underlying Java class for this entity
*/
Class<T> getType();
/**
* Returns the alias to be used when storing type information. Might be {@literal null} to indicate that there was no
* alias defined through the mapping metadata.
*
* @return
*/
Object getTypeAlias();
/**
* Returns the {@link TypeInformation} backing this {@link PersistentEntity}.
*
* @return
*/
TypeInformation<T> getTypeInformation();
/**
* Applies the given {@link PropertyHandler} to all {@link PersistentProperty}s contained in this
* {@link PersistentEntity}.
*
* @param handler must not be {@literal null}.
*/
void doWithProperties(PropertyHandler<P> handler);
/**
* Applies the given {@link AssociationHandler} to all {@link Association} contained in this {@link PersistentEntity}.
*
* @param handler must not be {@literal null}.
*/
void doWithAssociations(AssociationHandler<P> handler);
}

View File

@@ -0,0 +1,174 @@
/*
* Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import org.springframework.data.util.TypeInformation;
/**
* @author Graeme Rocher
* @author Jon Brisbin
* @author Oliver Gierke
*/
public interface PersistentProperty<P extends PersistentProperty<P>> {
PersistentEntity<?, P> getOwner();
/**
* The name of the property
*
* @return The property name
*/
String getName();
/**
* The type of the property
*
* @return The property type
*/
Class<?> getType();
/**
* Returns the {@link TypeInformation} of the property.
*
* @return
*/
TypeInformation<?> getTypeInformation();
/**
* Returns the {@link TypeInformation} if the property references a {@link PersistentEntity}. Will return
* {@literal null} in case it refers to a simple type. Will return {@link Collection}'s component type or the
* {@link Map}'s value type transparently.
*
* @return
*/
Iterable<? extends TypeInformation<?>> getPersistentEntityType();
/**
* Returns the getter method to access the property value if available. Might return {@literal null} in case there is
* no getter method with a return type assignable to the actual property's type.
*
* @return the getter method to access the property value if available, otherwise {@literal null}.
*/
Method getGetter();
/**
* Returns the setter method to set a property value. Might return {@literal null} in case there is no setter
* available.
*
* @returnthe setter method to set a property value if available, otherwise {@literal null}.
*/
Method getSetter();
Field getField();
String getSpelExpression();
Association<P> getAssociation();
/**
* Returns whether the type of the {@link PersistentProperty} is actually to be regarded as {@link PersistentEntity}
* in turn.
*
* @return
*/
boolean isEntity();
/**
* Returns whether the property is a <em>potential</em> identifier property of the owning {@link PersistentEntity}.
* This method is mainly used by {@link PersistentEntity} implementation to discover id property candidates on
* {@link PersistentEntity} creation you should rather call {@link PersistentEntity#isIdProperty(PersistentProperty)}
* to determine whether the current property is the id property of that {@link PersistentEntity} under consideration.
*
* @return
*/
boolean isIdProperty();
/**
* Returns whether the current property is a <em>potential</em> version property of the owning
* {@link PersistentEntity}. This method is mainly used by {@link PersistentEntity} implementation to discover version
* property candidates on {@link PersistentEntity} creation you should rather call
* {@link PersistentEntity#isVersionProperty(PersistentProperty)} to determine whether the current property is the
* version property of that {@link PersistentEntity} under consideration.
*
* @return
*/
boolean isVersionProperty();
/**
* Returns whether the property is a {@link Collection}, {@link Iterable} or an array.
*
* @return
*/
boolean isCollectionLike();
/**
* Returns whether the property is a {@link Map}.
*
* @return
*/
boolean isMap();
/**
* Returns whether the property is an array.
*
* @return
*/
boolean isArray();
/**
* Returns whether the property is transient.
*
* @return
*/
boolean isTransient();
boolean shallBePersisted();
/**
* Returns whether the property is an {@link Association}.
*
* @return
*/
boolean isAssociation();
/**
* Returns the component type of the type if it is a {@link java.util.Collection}. Will return the type of the key if
* the property is a {@link java.util.Map}.
*
* @return the component type, the map's key type or {@literal null} if neither {@link java.util.Collection} nor
* {@link java.util.Map}.
*/
Class<?> getComponentType();
/**
* Returns the raw type as it's pulled from from the reflected property.
*
* @return the raw type of the property.
*/
Class<?> getRawType();
/**
* Returns the type of the values if the property is a {@link java.util.Map}.
*
* @return the map's value type or {@literal null} if no {@link java.util.Map}
*/
Class<?> getMapValueType();
}

View File

@@ -0,0 +1,299 @@
/*
* Copyright 2011-2013 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping;
import static org.springframework.util.ObjectUtils.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* Value object to encapsulate the constructor to be used when mapping persistent data to objects.
*
* @author Oliver Gierke
* @author Jon Brisbin
*/
public class PreferredConstructor<T, P extends PersistentProperty<P>> {
private final Constructor<T> constructor;
private final List<Parameter<Object, P>> parameters;
/**
* Creates a new {@link PreferredConstructor} from the given {@link Constructor} and {@link Parameter}s.
*
* @param constructor
* @param parameters
*/
public PreferredConstructor(Constructor<T> constructor, Parameter<Object, P>... parameters) {
Assert.notNull(constructor);
Assert.notNull(parameters);
ReflectionUtils.makeAccessible(constructor);
this.constructor = constructor;
this.parameters = Arrays.asList(parameters);
}
/**
* Returns the underlying {@link Constructor}.
*
* @return
*/
public Constructor<T> getConstructor() {
return constructor;
}
/**
* Returns the {@link Parameter}s of the constructor.
*
* @return
*/
public Iterable<Parameter<Object, P>> getParameters() {
return parameters;
}
/**
* Returns whether the constructor has {@link Parameter}s.
*
* @see #isNoArgConstructor()
* @return
*/
public boolean hasParameters() {
return !parameters.isEmpty();
}
/**
* Returns whether the constructor does not have any arguments.
*
* @see #hasParameters()
* @return
*/
public boolean isNoArgConstructor() {
return parameters.isEmpty();
}
/**
* Returns whether the constructor was explicitly selected (by {@link PersistenceConstructor}).
*
* @return
*/
public boolean isExplicitlyAnnotated() {
return constructor.isAnnotationPresent(PersistenceConstructor.class);
}
/**
* Returns whether the given {@link PersistentProperty} is referenced in a constructor argument of the
* {@link PersistentEntity} backing this {@link PreferredConstructor}.
*
* @param property must not be {@literal null}.
* @return
*/
public boolean isConstructorParameter(PersistentProperty<?> property) {
Assert.notNull(property);
for (Parameter<?, P> parameter : parameters) {
if (parameter.maps(property)) {
return true;
}
}
return false;
}
/**
* Returns whether the given {@link Parameter} is one referring to an enclosing class. That is in case the class this
* {@link PreferredConstructor} belongs to is a member class actually. If that's the case the compiler creates a first
* constructor argument of the enclosing class type.
*
* @param parameter must not be {@literal null}.
* @return
*/
public boolean isEnclosingClassParameter(Parameter<?, P> parameter) {
Assert.notNull(parameter);
if (parameters.isEmpty()) {
return false;
}
return parameters.get(0).equals(parameter) && parameter.isEnclosingClassParameter();
}
/**
* Value object to represent constructor parameters.
*
* @param <T> the type of the parameter
* @author Oliver Gierke
*/
public static class Parameter<T, P extends PersistentProperty<P>> {
private final String name;
private final TypeInformation<T> type;
private final String key;
private final PersistentEntity<T, P> entity;
private Boolean enclosingClassCache;
/**
* Creates a new {@link Parameter} with the given name, {@link TypeInformation} as well as an array of
* {@link Annotation}s. Will insprect the annotations for an {@link Value} annotation to lookup a key or an SpEL
* expression to be evaluated.
*
* @param name the name of the parameter, can be {@literal null}
* @param type must not be {@literal null}
* @param annotations must not be {@literal null} but can be empty
* @param entity can be {@literal null}.
*/
public Parameter(String name, TypeInformation<T> type, Annotation[] annotations, PersistentEntity<T, P> entity) {
Assert.notNull(type);
Assert.notNull(annotations);
this.name = name;
this.type = type;
this.key = getValue(annotations);
this.entity = entity;
}
private String getValue(Annotation[] annotations) {
for (Annotation anno : annotations) {
if (anno.annotationType() == Value.class) {
return ((Value) anno).value();
}
}
return null;
}
/**
* Returns the name of the parameter or {@literal null} if none was given.
*
* @return
*/
public String getName() {
return name;
}
/**
* Returns the {@link TypeInformation} of the parameter.
*
* @return
*/
public TypeInformation<T> getType() {
return type;
}
/**
* Returns the raw resolved type of the parameter.
*
* @return
*/
public Class<T> getRawType() {
return type.getType();
}
/**
* Returns the key to be used when looking up a source data structure to populate the actual parameter value.
*
* @return
*/
public String getSpelExpression() {
return key;
}
/**
* Returns whether the constructor parameter is equipped with a SpEL expression.
*
* @return
*/
public boolean hasSpelExpression() {
return StringUtils.hasText(getSpelExpression());
}
/**
* Returns whether the {@link Parameter} maps the given {@link PersistentProperty}.
*
* @param property
* @return
*/
boolean maps(PersistentProperty<?> property) {
P referencedProperty = entity == null ? null : entity.getPersistentProperty(name);
return property == null ? false : property.equals(referencedProperty);
}
private boolean isEnclosingClassParameter() {
if (enclosingClassCache == null) {
Class<T> owningType = entity.getType();
this.enclosingClassCache = owningType.isMemberClass() && type.getType().equals(owningType.getEnclosingClass());
}
return enclosingClassCache;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Parameter)) {
return false;
}
Parameter<?, ?> that = (Parameter<?, ?>) obj;
boolean nameEquals = this.name == null ? that.name == null : this.name.equals(that.name);
boolean keyEquals = this.key == null ? that.key == null : this.key.equals(that.key);
boolean typeEquals = nullSafeEquals(this.type, that.type);
boolean entityEquals = this.entity == null ? that.entity == null : this.entity.equals(that.entity);
return nameEquals && keyEquals && typeEquals && entityEquals;
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result += 31 * nullSafeHashCode(this.name);
result += 31 * nullSafeHashCode(this.key);
result += 31 * nullSafeHashCode(this.type);
result += 31 * nullSafeHashCode(this.entity);
return result;
}
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping;
/**
* Callback interface to do something with all plain {@link PersistentProperty} instances <em>except</em> associations
* and transient properties.
*
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public interface PropertyHandler<P extends PersistentProperty<P>> {
void doWithPersistentProperty(P persistentProperty);
}

View File

@@ -0,0 +1,358 @@
/*
* Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Abstraction of a {@link PropertyPath} of a domain class.
*
* @author Oliver Gierke
*/
public class PropertyPath implements Iterable<PropertyPath> {
private static final String DELIMITERS = "_\\.";
private static final String ALL_UPPERCASE = "[A-Z0-9._$]+";
private static final Pattern SPLITTER = Pattern.compile("(?:[%s]?([%s]*?[^%s]+))".replaceAll("%s", DELIMITERS));
private final TypeInformation<?> owningType;
private final String name;
private final TypeInformation<?> type;
private final boolean isCollection;
private PropertyPath next;
/**
* Creates a leaf {@link PropertyPath} (no nested ones) with the given name inside the given owning type.
*
* @param name must not be {@literal null} or empty.
* @param owningType must not be {@literal null}.
*/
PropertyPath(String name, Class<?> owningType) {
this(name, ClassTypeInformation.from(owningType), null);
}
/**
* Creates a leaf {@link PropertyPath} (no nested ones with the given name and owning type.
*
* @param name must not be {@literal null} or empty.
* @param owningType must not be {@literal null}.
* @param base the {@link PropertyPath} previously found.
*/
PropertyPath(String name, TypeInformation<?> owningType, PropertyPath base) {
Assert.hasText(name);
Assert.notNull(owningType);
String propertyName = name.matches(ALL_UPPERCASE) ? name : StringUtils.uncapitalize(name);
TypeInformation<?> type = owningType.getProperty(propertyName);
if (type == null) {
throw new PropertyReferenceException(propertyName, owningType, base);
}
this.owningType = owningType;
this.isCollection = type.isCollectionLike();
this.type = type.getActualType();
this.name = propertyName;
}
/**
* Returns the owning type of the {@link PropertyPath}.
*
* @return the owningType will never be {@literal null}.
*/
public TypeInformation<?> getOwningType() {
return owningType;
}
/**
* Returns the name of the {@link PropertyPath}.
*
* @return the name will never be {@literal null}.
*/
public String getSegment() {
return name;
}
/**
* Returns the leaf property of the {@link PropertyPath}.
*
* @return will never be {@literal null}.
*/
public PropertyPath getLeafProperty() {
PropertyPath result = this;
while (result.hasNext()) {
result = result.next();
}
return result;
}
/**
* 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 PropertyPath}.
*
* @return the next nested {@link PropertyPath} or {@literal null} if no nested {@link PropertyPath} available.
* @see #hasNext()
*/
public PropertyPath next() {
return next;
}
/**
* Returns whether there is a nested {@link PropertyPath}. If this returns {@literal true} you can expect
* {@link #next()} to return a non- {@literal null} value.
*
* @return
*/
public boolean hasNext() {
return next != null;
}
/**
* Returns the {@link PropertyPath} in dot notation.
*
* @return
*/
public String toDotPath() {
if (hasNext()) {
return getSegment() + "." + next().toDotPath();
}
return getSegment();
}
/**
* Returns whether the {@link PropertyPath} is actually a collection.
*
* @return
*/
public boolean isCollection() {
return isCollection;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || !getClass().equals(obj.getClass())) {
return false;
}
PropertyPath that = (PropertyPath) obj;
return this.name.equals(that.name) && this.type.equals(that.type)
&& ObjectUtils.nullSafeEquals(this.next, that.next);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = 17;
result += 31 * name.hashCode();
result += 31 * type.hashCode();
result += 31 * (next == null ? 0 : next.hashCode());
return result;
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
public Iterator<PropertyPath> iterator() {
return new Iterator<PropertyPath>() {
private PropertyPath current = PropertyPath.this;
public boolean hasNext() {
return current != null;
}
public PropertyPath next() {
PropertyPath result = current;
this.current = current.next();
return result;
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/**
* Extracts the {@link PropertyPath} chain from the given source {@link String} and type.
*
* @param source
* @param type
* @return
*/
public static PropertyPath from(String source, Class<?> type) {
return from(source, ClassTypeInformation.from(type));
}
/**
* Extracts the {@link PropertyPath} chain from the given source {@link String} and {@link TypeInformation}.
*
* @param source must not be {@literal null}.
* @param type
* @return
*/
public static PropertyPath from(String source, TypeInformation<?> type) {
List<String> iteratorSource = new ArrayList<String>();
Matcher matcher = SPLITTER.matcher("_" + source);
while (matcher.find()) {
iteratorSource.add(matcher.group(1));
}
Iterator<String> parts = iteratorSource.iterator();
PropertyPath result = null;
PropertyPath current = null;
while (parts.hasNext()) {
if (result == null) {
result = create(parts.next(), type, null);
current = result;
} else {
current = create(parts.next(), current);
}
}
return result;
}
/**
* Creates a new {@link PropertyPath} as subordinary of the given {@link PropertyPath}.
*
* @param source
* @param base
* @return
*/
private static PropertyPath create(String source, PropertyPath base) {
PropertyPath propertyPath = create(source, base.type, base);
base.next = propertyPath;
return propertyPath;
}
/**
* Factory method to create a new {@link PropertyPath} 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 PropertyPath create(String source, TypeInformation<?> type, PropertyPath base) {
return create(source, type, "", base);
}
/**
* Tries to look up a chain of {@link PropertyPath}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 PropertyPath} from
* the calculated head and recombined new tail and additional tail.
*
* @param source
* @param type
* @param addTail
* @return
*/
private static PropertyPath create(String source, TypeInformation<?> type, String addTail, PropertyPath base) {
PropertyReferenceException exception = null;
PropertyPath current = null;
try {
current = new PropertyPath(source, type, base);
if (StringUtils.hasText(addTail)) {
current.next = create(addTail, current.type, current);
}
return current;
} catch (PropertyReferenceException e) {
if (current != null) {
throw e;
}
exception = e;
}
Pattern pattern = Pattern.compile("\\p{Lu}+\\p{Ll}*$");
Matcher matcher = pattern.matcher(source);
if (matcher.find() && matcher.start() != 0) {
int position = matcher.start();
String head = source.substring(0, position);
String tail = source.substring(position);
return create(head, type, tail + addTail, base);
}
throw exception;
}
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
/**
* Exception being thrown when creating {@link PropertyPath} instances.
*
* @author Oliver Gierke
*/
public class PropertyReferenceException extends RuntimeException {
private static final long serialVersionUID = -5254424051438976570L;
private static final String ERROR_TEMPLATE = "No property %s found for type %s";
private final String propertyName;
private final TypeInformation<?> type;
private final PropertyPath base;
/**
* Creates a new {@link PropertyReferenceException}.
*
* @param propertyName the name of the property not found on the given type.
* @param type the type the property could not be found on.
* @param base the base {@link PropertyPath}.
*/
public PropertyReferenceException(String propertyName, TypeInformation<?> type, PropertyPath base) {
Assert.hasText(propertyName);
Assert.notNull(type);
this.propertyName = propertyName;
this.type = type;
this.base = base;
}
/**
* Returns the name of the property not found.
*
* @return will not be {@literal null} or empty.
*/
public String getPropertyName() {
return propertyName;
}
/**
* Returns the type the property could not be found on.
*
* @return the type
*/
public TypeInformation<?> getType() {
return type;
}
/*
* (non-Javadoc)
* @see java.lang.Throwable#getMessage()
*/
@Override
public String getMessage() {
return String.format(ERROR_TEMPLATE, propertyName, type.getType().getName());
}
/**
* Returns the {@link PropertyPath} which could be resolved so far.
*
* @return
*/
public PropertyPath getBaseProperty() {
return base;
}
}

View File

@@ -0,0 +1,503 @@
/*
* Copyright 2011-2012 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.context;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mapping.model.MutablePersistentEntity;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;
import org.springframework.util.ReflectionUtils.FieldFilter;
/**
* Base class to build mapping metadata and thus create instances of {@link PersistentEntity} and
* {@link PersistentProperty}.
* <p>
* The implementation uses a {@link ReentrantReadWriteLock} to make sure {@link PersistentEntity} are completely
* populated before accessing them from outside.
*
* @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>
* @author Oliver Gierke
*/
public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?, P>, P extends PersistentProperty<P>>
implements MappingContext<E, P>, ApplicationContextAware, ApplicationEventPublisherAware,
ApplicationListener<ContextRefreshedEvent> {
private final ConcurrentMap<TypeInformation<?>, E> persistentEntities = new ConcurrentHashMap<TypeInformation<?>, E>();
private ApplicationContext applicationContext;
private ApplicationEventPublisher applicationEventPublisher;
private Set<? extends Class<?>> initialEntitySet = new HashSet<Class<?>>();
private boolean strict = false;
private SimpleTypeHolder simpleTypeHolder = new SimpleTypeHolder();
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock read = lock.readLock();
private final Lock write = lock.writeLock();
/*
* (non-Javadoc)
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
// Default publisher
if (this.applicationEventPublisher == null) {
this.applicationEventPublisher = applicationContext;
}
}
/*
* (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;
}
/**
* 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 the {@link SimpleTypeHolder} to be used by the {@link MappingContext}. Allows customization of what
* types will be regarded as simple types and thus not recursively analysed. Setting this to {@literal null} will
* reset the context to use the default {@link SimpleTypeHolder}.
*
* @param simpleTypes
*/
public void setSimpleTypeHolder(SimpleTypeHolder simpleTypes) {
this.simpleTypeHolder = simpleTypes == null ? new SimpleTypeHolder() : simpleTypes;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.MappingContext#getPersistentEntities()
*/
public Collection<E> getPersistentEntities() {
try {
read.lock();
return persistentEntities.values();
} finally {
read.unlock();
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.MappingContext#getPersistentEntity(java.lang.Class)
*/
public E getPersistentEntity(Class<?> type) {
Assert.notNull(type);
return getPersistentEntity(ClassTypeInformation.from(type));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.MappingContext#getPersistentEntity(org.springframework.data.util.TypeInformation)
*/
public E getPersistentEntity(TypeInformation<?> type) {
Assert.notNull(type);
try {
read.lock();
E entity = persistentEntities.get(type);
if (entity != null) {
return entity;
}
} finally {
read.unlock();
}
if (strict) {
throw new MappingException("Unknown persistent entity " + type);
}
if (!shouldCreatePersistentEntityFor(type)) {
return null;
}
return addPersistentEntity(type);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.MappingContext#getPersistentEntity(org.springframework.data.mapping.PersistentProperty)
*/
public E getPersistentEntity(P persistentProperty) {
if (persistentProperty == null) {
return null;
}
try {
read.lock();
return persistentEntities.get(persistentProperty.getTypeInformation());
} finally {
read.unlock();
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.MappingContext#getPersistentPropertyPath(java.lang.Class, java.lang.String)
*/
public PersistentPropertyPath<P> getPersistentPropertyPath(PropertyPath propertyPath) {
List<P> result = new ArrayList<P>();
E current = getPersistentEntity(propertyPath.getOwningType());
for (PropertyPath segment : propertyPath) {
P persistentProperty = current.getPersistentProperty(segment.getSegment());
if (persistentProperty == null) {
throw new IllegalArgumentException(String.format("No property %s found on %s!", segment.getSegment(),
current.getName()));
}
result.add(persistentProperty);
if (segment.hasNext()) {
current = getPersistentEntity(segment.getType());
}
}
return new DefaultPersistentPropertyPath<P>(result);
}
/**
* Adds the given type to the {@link MappingContext}.
*
* @param type
* @return
*/
protected E addPersistentEntity(Class<?> type) {
return addPersistentEntity(ClassTypeInformation.from(type));
}
/**
* Adds the given {@link TypeInformation} to the {@link MappingContext}.
*
* @param typeInformation
* @return
*/
protected E addPersistentEntity(TypeInformation<?> typeInformation) {
E persistentEntity = persistentEntities.get(typeInformation);
if (persistentEntity != null) {
return persistentEntity;
}
Class<?> type = typeInformation.getType();
try {
write.lock();
final E entity = createPersistentEntity(typeInformation);
// Eagerly cache the entity as we might have to find it during recursive lookups.
persistentEntities.put(typeInformation, entity);
BeanInfo info = Introspector.getBeanInfo(type);
final Map<String, PropertyDescriptor> descriptors = new HashMap<String, PropertyDescriptor>();
for (PropertyDescriptor descriptor : info.getPropertyDescriptors()) {
descriptors.put(descriptor.getName(), descriptor);
}
ReflectionUtils.doWithFields(type, new PersistentPropertyCreator(entity, descriptors),
PersistentFieldFilter.INSTANCE);
try {
entity.verify();
} catch (MappingException e) {
persistentEntities.remove(typeInformation);
throw e;
}
// Inform listeners
if (null != applicationEventPublisher) {
applicationEventPublisher.publishEvent(new MappingContextEvent<E, P>(this, entity));
}
return entity;
} catch (IntrospectionException e) {
throw new MappingException(e.getMessage(), e);
} finally {
write.unlock();
}
}
/**
* Creates the concrete {@link PersistentEntity} instance.
*
* @param <T>
* @param typeInformation
* @return
*/
protected abstract <T> E createPersistentEntity(TypeInformation<T> typeInformation);
/**
* Creates the concrete instance of {@link PersistentProperty}.
*
* @param field
* @param descriptor
* @param owner
* @param simpleTypeHolder
* @return
*/
protected abstract P createPersistentProperty(Field field, PropertyDescriptor descriptor, E owner,
SimpleTypeHolder simpleTypeHolder);
/*
* (non-Javadoc)
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
*/
public void onApplicationEvent(ContextRefreshedEvent event) {
if (!event.getApplicationContext().equals(applicationContext)) {
return;
}
initialize();
}
/**
* Initializes the mapping context. Will add the types configured through {@link #setInitialEntitySet(Set)} to the
* context.
*/
public void initialize() {
for (Class<?> initialEntity : initialEntitySet) {
addPersistentEntity(initialEntity);
}
}
/**
* Returns whether a {@link PersistentEntity} instance should be created for the given {@link TypeInformation}. By
* default this will reject this for all types considered simple, but it might be necessary to tweak that in case you
* have registered custom converters for top level types (which renders them to be considered simple) but still need
* meta-information about them.
*
* @param type will never be {@literal null}.
* @return
*/
protected boolean shouldCreatePersistentEntityFor(TypeInformation<?> type) {
return !simpleTypeHolder.isSimpleType(type.getType());
}
/**
* {@link FieldCallback} to create {@link PersistentProperty} instances.
*
* @author Oliver Gierke
*/
private final class PersistentPropertyCreator implements FieldCallback {
private final E entity;
private final Map<String, PropertyDescriptor> descriptors;
/**
* Creates a new {@link PersistentPropertyCreator} for the given {@link PersistentEntity} and
* {@link PropertyDescriptor}s.
*
* @param entity
* @param descriptors
*/
private PersistentPropertyCreator(E entity, Map<String, PropertyDescriptor> descriptors) {
this.entity = entity;
this.descriptors = descriptors;
}
public void doWith(Field field) {
PropertyDescriptor descriptor = descriptors.get(field.getName());
ReflectionUtils.makeAccessible(field);
P property = createPersistentProperty(field, descriptor, entity, simpleTypeHolder);
if (property.isTransient()) {
return;
}
entity.addPersistentProperty(property);
if (property.isAssociation()) {
entity.addAssociation(property.getAssociation());
}
if (entity.getType().equals(property.getRawType())) {
return;
}
if (!property.isEntity()) {
return;
}
for (TypeInformation<?> candidate : property.getPersistentEntityType()) {
addPersistentEntity(candidate);
}
}
}
/**
* {@link FieldFilter} rejecting static fields as well as artifically introduced ones. See
* {@link PersistentFieldFilter#UNMAPPED_FIELDS} for details.
*
* @author Oliver Gierke
*/
private static enum PersistentFieldFilter implements FieldFilter {
INSTANCE;
private static final Iterable<FieldMatch> UNMAPPED_FIELDS;
static {
Set<FieldMatch> matches = new HashSet<FieldMatch>();
matches.add(new FieldMatch("class", null));
matches.add(new FieldMatch("this\\$.*", null));
matches.add(new FieldMatch("metaClass", "groovy.lang.MetaClass"));
UNMAPPED_FIELDS = Collections.unmodifiableCollection(matches);
}
/*
* (non-Javadoc)
* @see org.springframework.util.ReflectionUtils.FieldFilter#matches(java.lang.reflect.Field)
*/
public boolean matches(Field field) {
if (Modifier.isStatic(field.getModifiers())) {
return false;
}
for (FieldMatch candidate : UNMAPPED_FIELDS) {
if (candidate.matches(field)) {
return false;
}
}
return true;
}
}
/**
* Value object to help defining field eclusion based on name patterns and types.
*
* @since 1.4
* @author Oliver Gierke
*/
static class FieldMatch {
private final String namePattern;
private final String typeName;
/**
* Creates a new {@link FieldMatch} for the given name pattern and type name. At least one of the paramters must not
* be {@literal null}.
*
* @param namePattern a regex pattern to match field names, can be {@literal null}.
* @param typeName the name of the type to exclude, can be {@literal null}.
*/
public FieldMatch(String namePattern, String typeName) {
Assert.isTrue(!(namePattern == null && typeName == null), "Either name patter or type name must be given!");
this.namePattern = namePattern;
this.typeName = typeName;
}
/**
* Returns whether the given {@link Field} matches the defined {@link FieldMatch}.
*
* @param field must not be {@literal null}.
* @return
*/
public boolean matches(Field field) {
if (namePattern != null && !field.getName().matches(namePattern)) {
return false;
}
if (typeName != null && !field.getType().getName().equals(typeName)) {
return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,235 @@
/*
* Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.context;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Abstraction of a path of {@link PersistentProperty}s.
*
* @author Oliver Gierke
*/
class DefaultPersistentPropertyPath<T extends PersistentProperty<T>> implements PersistentPropertyPath<T> {
private enum PropertyNameConverter implements Converter<PersistentProperty<?>, String> {
INSTANCE;
public String convert(PersistentProperty<?> source) {
return source.getName();
}
}
private final List<T> properties;
/**
* Creates a new {@link DefaultPersistentPropertyPath} for the given {@link PersistentProperty}s.
*
* @param properties must not be {@literal null}.
*/
public DefaultPersistentPropertyPath(List<T> properties) {
Assert.notNull(properties);
Assert.isTrue(!properties.isEmpty());
this.properties = properties;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.PersistentPropertyPath#toDotPath()
*/
public String toDotPath() {
return toPath(null, null);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.PersistentPropertyPath#toDotPath(org.springframework.core.convert.converter.Converter)
*/
public String toDotPath(Converter<? super T, String> converter) {
return toPath(null, converter);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.PersistentPropertyPath#toPath(java.lang.String)
*/
public String toPath(String delimiter) {
return toPath(delimiter, null);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.PersistentPropertyPath#toPath(java.lang.String, org.springframework.core.convert.converter.Converter)
*/
public String toPath(String delimiter, Converter<? super T, String> converter) {
@SuppressWarnings("unchecked")
Converter<? super T, String> converterToUse = (Converter<? super T, String>) (converter == null ? PropertyNameConverter.INSTANCE
: converter);
String delimiterToUse = delimiter == null ? "." : delimiter;
List<String> result = new ArrayList<String>();
for (T property : properties) {
result.add(converterToUse.convert(property));
}
return StringUtils.collectionToDelimitedString(result, delimiterToUse);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.PersistentPropertyPath#getLeafProperty()
*/
public T getLeafProperty() {
return properties.get(properties.size() - 1);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.PersistentPropertyPath#getBaseProperty()
*/
public T getBaseProperty() {
return properties.get(0);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.PersistentPropertyPath#isBasePathOf(org.springframework.data.mapping.context.PersistentPropertyPath)
*/
public boolean isBasePathOf(PersistentPropertyPath<T> path) {
if (path == null) {
return false;
}
Iterator<T> iterator = path.iterator();
for (T property : this) {
if (!iterator.hasNext()) {
return false;
}
T reference = iterator.next();
if (!property.equals(reference)) {
return false;
}
}
return true;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.PersistentPropertyPath#getExtensionForBaseOf(org.springframework.data.mapping.context.PersistentPropertyPath)
*/
public PersistentPropertyPath<T> getExtensionForBaseOf(PersistentPropertyPath<T> base) {
if (!base.isBasePathOf(this)) {
return this;
}
List<T> properties = new ArrayList<T>();
Iterator<T> iterator = iterator();
for (int i = 0; i < base.getLength(); i++) {
iterator.next();
}
while (iterator.hasNext()) {
properties.add(iterator.next());
}
return new DefaultPersistentPropertyPath<T>(properties);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.PersistentPropertyPath#getParentPath()
*/
public PersistentPropertyPath<T> getParentPath() {
int size = properties.size();
if (size <= 1) {
return this;
}
return new DefaultPersistentPropertyPath<T>(properties.subList(0, size - 1));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.PersistentPropertyPath#getLength()
*/
public int getLength() {
return properties.size();
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
public Iterator<T> iterator() {
return properties.iterator();
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || !getClass().equals(obj.getClass())) {
return false;
}
DefaultPersistentPropertyPath<?> that = (DefaultPersistentPropertyPath<?>) obj;
return this.properties.equals(that.properties);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return properties.hashCode();
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return toDotPath();
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.context;
import java.util.Collection;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.util.TypeInformation;
/**
* This interface defines the overall context including all known PersistentEntity instances and methods to obtain
* instances on demand. it is used internally to establish associations between entities and also at runtime to obtain
* entities by name.
*
* @author Oliver Gierke
* @author Jon Brisbin
* @author Graeme Rocher
*/
public interface MappingContext<E extends PersistentEntity<?, P>, P extends PersistentProperty<P>> {
/**
* Returns all {@link PersistentEntity}s held in the context.
*
* @return
*/
Collection<E> getPersistentEntities();
/**
* Returns a {@link PersistentEntity} for the given {@link Class}. Will return {@literal null} for types that are
* considered simple ones.
*
* @see org.springframework.data.mapping.model.SimpleTypeHolder#isSimpleType(Class)
* @param type must not be {@literal null}.
* @return
*/
E getPersistentEntity(Class<?> type);
/**
* Returns a {@link PersistentEntity} for the given {@link TypeInformation}. Will return {@literal null} for types
* that are considered simple ones.
*
* @see org.springframework.data.mapping.model.SimpleTypeHolder#isSimpleType(Class)
* @param type must not be {@literal null}.
* @return
*/
E getPersistentEntity(TypeInformation<?> type);
/**
* Returns the {@link PersistentEntity} mapped by the given {@link PersistentProperty}.
*
* @param persistentProperty
* @return the {@link PersistentEntity} mapped by the given {@link PersistentProperty} or null if no
* {@link PersistentEntity} exists for it or the {@link PersistentProperty} does not refer to an entity (the
* type of the property is considered simple see
* {@link org.springframework.data.mapping.model.SimpleTypeHolder#isSimpleType(Class)}).
*/
E getPersistentEntity(P persistentProperty);
/**
* Returns all {@link PersistentProperty}s for the given path expression based on the given {@link PropertyPath}.
*
* @param <T>
* @param type
* @param path
* @return
*/
PersistentPropertyPath<P> getPersistentPropertyPath(PropertyPath propertyPath);
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.context;
import org.springframework.context.ApplicationEvent;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.util.Assert;
/**
* Base implementation of an {@link ApplicationEvent} refering to a {@link PersistentEntity}.
*
* @author Oliver Gierke
* @author Jon Brisbin
* @param <E> the {@link PersistentEntity} the context was created for
* @param <P> the {@link PersistentProperty} the {@link PersistentEntity} consists of
*/
public class MappingContextEvent<E extends PersistentEntity<?, P>, P extends PersistentProperty<P>> extends
ApplicationEvent {
private static final long serialVersionUID = 1336466833846092490L;
private final MappingContext<?, ?> source;
private final E entity;
/**
* Creates a new {@link MappingContextEvent} for the given {@link MappingContext} and {@link PersistentEntity}.
*
* @param source must not be {@literal null}.
* @param entity must not be {@literal null}.
*/
public MappingContextEvent(MappingContext<?, ?> source, E entity) {
super(source);
Assert.notNull(source);
Assert.notNull(entity);
this.source = source;
this.entity = entity;
}
/**
* Returns the {@link PersistentEntity} the event was created for.
*
* @return
*/
public E getPersistentEntity() {
return entity;
}
/**
* Returns whether the {@link MappingContextEvent} was triggered by the given {@link MappingContext}.
*
* @param context the {@link MappingContext} that potentially created the event.
* @return
*/
public boolean wasEmittedBy(MappingContext<?, ?> context) {
return this.source.equals(context);
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.context;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mapping.PersistentProperty;
/**
* Abstraction of a path of {@link PersistentProperty}s.
*
* @author Oliver Gierke
*/
public interface PersistentPropertyPath<T extends PersistentProperty<T>> extends Iterable<T> {
/**
* Returns the dot based path notation using {@link PersistentProperty#getName()}.
*
* @return
*/
String toDotPath();
/**
* Returns the dot based path notation using the given {@link Converter} to translate individual
* {@link PersistentProperty}s to path segments.
*
* @param converter
* @return
*/
String toDotPath(Converter<? super T, String> converter);
/**
* Returns a {@link String} path with the given delimiter based on the {@link PersistentProperty#getName()}.
*
* @param delimiter will default to {@code .} if {@literal null} is given.
* @return
*/
String toPath(String delimiter);
/**
* Returns a {@link String} path with the given delimiter using the given {@link Converter} for
* {@link PersistentProperty} to String conversion.
*
* @param delimiter will default to {@code .} if {@literal null} is given.
* @param converter will default to use {@link PersistentProperty#getName()}.
* @return
*/
String toPath(String delimiter, Converter<? super T, String> converter);
/**
* Returns the last property in the {@link PersistentPropertyPath}. So for {@code foo.bar} it will return the
* {@link PersistentProperty} for {@code bar}. For a simple {@code foo} it returns {@link PersistentProperty} for
* {@code foo}.
*
* @return
*/
T getLeafProperty();
/**
* Returns the first property in the {@link PersistentPropertyPath}. So for {@code foo.bar} it will return the
* {@link PersistentProperty} for {@code foo}. For a simple {@code foo} it returns {@link PersistentProperty} for
* {@code foo}.
*
* @return
*/
T getBaseProperty();
/**
* Returns whether the given {@link PersistentPropertyPath} is a base path of the current one. This means that the
* current {@link PersistentPropertyPath} is basically an extension of the given one.
*
* @param path
* @return
*/
boolean isBasePathOf(PersistentPropertyPath<T> path);
/**
* Returns the sub-path of the current one as if it was based on the given base path. So for a current path
* {@code foo.bar} and a given base {@code foo} it would return {@code bar}. If the given path is not a base of the
* the current one the current {@link PersistentPropertyPath} will be returned as is.
*
* @param base
* @return
*/
PersistentPropertyPath<T> getExtensionForBaseOf(PersistentPropertyPath<T> base);
/**
* Returns the parent path of the current {@link PersistentPropertyPath}, i.e. the path without the leaf property.
* This happens up to the base property. So for a direct property reference calling this method will result in
* returning the property.
*
* @return
*/
PersistentPropertyPath<T> getParentPath();
/**
* Returns the length of the {@link PersistentPropertyPath}.
*
* @return
*/
int getLength();
}

View File

@@ -0,0 +1,4 @@
/**
* Mapping context API and implementation base classes.
*/
package org.springframework.data.mapping.context;

View File

@@ -0,0 +1,312 @@
/*
* Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.data.annotation.Reference;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
/**
* Simple impementation of {@link PersistentProperty}.
*
* @author Jon Brisbin
* @author Oliver Gierke
*/
public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>> implements PersistentProperty<P> {
protected final String name;
protected final PropertyDescriptor propertyDescriptor;
protected final TypeInformation<?> information;
protected final Class<?> rawType;
protected final Field field;
protected final Association<P> association;
protected final PersistentEntity<?, P> owner;
private final SimpleTypeHolder simpleTypeHolder;
public AbstractPersistentProperty(Field field, PropertyDescriptor propertyDescriptor, PersistentEntity<?, P> owner,
SimpleTypeHolder simpleTypeHolder) {
Assert.notNull(field);
Assert.notNull(simpleTypeHolder);
Assert.notNull(owner);
this.name = field.getName();
this.rawType = field.getType();
this.information = owner.getTypeInformation().getProperty(this.name);
this.propertyDescriptor = propertyDescriptor;
this.field = field;
this.association = isAssociation() ? createAssociation() : null;
this.owner = owner;
this.simpleTypeHolder = simpleTypeHolder;
}
protected abstract Association<P> createAssociation();
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getOwner()
*/
public PersistentEntity<?, P> getOwner() {
return owner;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getName()
*/
public String getName() {
return name;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getType()
*/
public Class<?> getType() {
return information.getType();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getRawType()
*/
public Class<?> getRawType() {
return this.rawType;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getTypeInformation()
*/
public TypeInformation<?> getTypeInformation() {
return information;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getPersistentEntityType()
*/
public Iterable<? extends TypeInformation<?>> getPersistentEntityType() {
List<TypeInformation<?>> result = new ArrayList<TypeInformation<?>>();
TypeInformation<?> type = getTypeInformation();
if (isEntity()) {
result.add(type);
}
if (type.isCollectionLike() || isMap()) {
TypeInformation<?> nestedType = getTypeInformationIfNotSimpleType(getTypeInformation().getActualType());
if (nestedType != null) {
result.add(nestedType);
}
}
return result;
}
private TypeInformation<?> getTypeInformationIfNotSimpleType(TypeInformation<?> information) {
return information == null || simpleTypeHolder.isSimpleType(information.getType()) ? null : information;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getGetter()
*/
public Method getGetter() {
if (propertyDescriptor == null) {
return null;
}
Method getter = propertyDescriptor.getReadMethod();
if (getter == null) {
return null;
}
return rawType.isAssignableFrom(getter.getReturnType()) ? getter : null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getSetter()
*/
public Method getSetter() {
if (propertyDescriptor == null) {
return null;
}
Method setter = propertyDescriptor.getWriteMethod();
if (setter == null) {
return null;
}
return setter.getParameterTypes()[0].isAssignableFrom(rawType) ? setter : null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getField()
*/
public Field getField() {
return field;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getSpelExpression()
*/
public String getSpelExpression() {
return null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#isTransient()
*/
public boolean isTransient() {
return false;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#shallBePersisted()
*/
public boolean shallBePersisted() {
return !isTransient();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#isAssociation()
*/
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;
}
public Association<P> getAssociation() {
return association;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#isCollectionLike()
*/
public boolean isCollectionLike() {
return information.isCollectionLike();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#isMap()
*/
public boolean isMap() {
return Map.class.isAssignableFrom(getType());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#isArray()
*/
public boolean isArray() {
return getType().isArray();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#isEntity()
*/
public boolean isEntity() {
TypeInformation<?> actualType = information.getActualType();
boolean isComplexType = actualType == null ? false : !simpleTypeHolder.isSimpleType(actualType.getType());
return isComplexType && !isTransient() && !isCollectionLike() && !isMap();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getComponentType()
*/
public Class<?> getComponentType() {
if (!isMap() && !isCollectionLike()) {
return null;
}
TypeInformation<?> componentType = information.getComponentType();
return componentType == null ? null : componentType.getType();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#getMapValueType()
*/
public Class<?> getMapValueType() {
return isMap() ? information.getMapValueType().getType() : null;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof AbstractPersistentProperty)) {
return false;
}
AbstractPersistentProperty<?> that = (AbstractPersistentProperty<?>) obj;
return this.field.equals(that.field);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return this.field.hashCode();
}
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Reference;
import org.springframework.data.annotation.Transient;
import org.springframework.data.annotation.Version;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.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, SimpleTypeHolder simpleTypeHolder) {
super(field, propertyDescriptor, owner, simpleTypeHolder);
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.model.AbstractPersistentProperty#getSpelExpression()
*/
@Override
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()
*/
@Override
public boolean isTransient() {
boolean isTransient = super.isTransient() || field.isAnnotationPresent(Transient.class);
return isTransient || field.isAnnotationPresent(Value.class) || field.isAnnotationPresent(Autowired.class);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#isIdProperty()
*/
public boolean isIdProperty() {
return AnnotationUtils.getAnnotation(field, Id.class) != null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentProperty#isVersionProperty()
*/
public boolean isVersionProperty() {
return AnnotationUtils.getAnnotation(field, Version.class) != null;
}
/**
* 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

@@ -0,0 +1,283 @@
/*
* Copyright 2011-2012 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
import java.io.Serializable;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
import org.springframework.data.annotation.TypeAlias;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.AssociationHandler;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PreferredConstructor;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Simple value object to capture information of {@link PersistentEntity}s.
*
* @author Oliver Gierke
* @author Jon Brisbin
* @author Patryk Wasik
*/
public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implements MutablePersistentEntity<T, P> {
private final PreferredConstructor<T, P> constructor;
private final TypeInformation<T> information;
private final Set<P> properties;
private final Set<Association<P>> associations;
private P idProperty;
private P versionProperty;
/**
* Creates a new {@link BasicPersistentEntity} from the given {@link TypeInformation}.
*
* @param information must not be {@literal null}.
*/
public BasicPersistentEntity(TypeInformation<T> information) {
this(information, null);
}
/**
* Creates a new {@link BasicPersistentEntity} for the given {@link TypeInformation} and {@link Comparator}. The given
* {@link Comparator} will be used to define the order of the {@link PersistentProperty} instances added to the
* entity.
*
* @param information must not be {@literal null}
* @param comparator
*/
public BasicPersistentEntity(TypeInformation<T> information, Comparator<P> comparator) {
Assert.notNull(information);
this.information = information;
this.properties = comparator == null ? new HashSet<P>() : new TreeSet<P>(comparator);
this.constructor = new PreferredConstructorDiscoverer<T, P>(information, this).getConstructor();
this.associations = comparator == null ? new HashSet<Association<P>>() : new TreeSet<Association<P>>(
new AssociationComparator<P>(comparator));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getPersistenceConstructor()
*/
public PreferredConstructor<T, P> getPersistenceConstructor() {
return constructor;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#isConstructorArgument(org.springframework.data.mapping.PersistentProperty)
*/
public boolean isConstructorArgument(PersistentProperty<?> property) {
return constructor == null ? false : constructor.isConstructorParameter(property);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#isIdProperty(org.springframework.data.mapping.PersistentProperty)
*/
public boolean isIdProperty(PersistentProperty<?> property) {
return this.idProperty == null ? false : this.idProperty.equals(property);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#isVersionProperty(org.springframework.data.mapping.PersistentProperty)
*/
public boolean isVersionProperty(PersistentProperty<?> property) {
return this.versionProperty == null ? false : this.versionProperty.equals(property);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getName()
*/
public String getName() {
return getType().getName();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getIdProperty()
*/
public P getIdProperty() {
return idProperty;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getVersionProperty()
*/
public P getVersionProperty() {
return versionProperty;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#hasIdProperty()
*/
public boolean hasIdProperty() {
return idProperty != null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#hasVersionProperty()
*/
public boolean hasVersionProperty() {
return versionProperty != null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.MutablePersistentEntity#addPersistentProperty(P)
*/
public void addPersistentProperty(P property) {
Assert.notNull(property);
properties.add(property);
if (property.isIdProperty()) {
if (this.idProperty != null) {
throw new MappingException(String.format(
"Attempt to add id property %s but already have property %s registered "
+ "as id. Check your mapping configuration!", property.getField(), idProperty.getField()));
}
this.idProperty = property;
}
if (property.isVersionProperty()) {
if (this.versionProperty != null) {
throw new MappingException(String.format(
"Attempt to add version property %s but already have property %s registered "
+ "as version. Check your mapping configuration!", property.getField(), versionProperty.getField()));
}
this.versionProperty = property;
}
}
/* (non-Javadoc)
* @see org.springframework.data.mapping.MutablePersistentEntity#addAssociation(org.springframework.data.mapping.model.Association)
*/
public void addAssociation(Association<P> association) {
associations.add(association);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getPersistentProperty(java.lang.String)
*/
public P getPersistentProperty(String name) {
for (P property : properties) {
if (property.getName().equals(name)) {
return property;
}
}
return null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getType()
*/
public Class<T> getType() {
return information.getType();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getTypeAlias()
*/
public Object getTypeAlias() {
TypeAlias alias = getType().getAnnotation(TypeAlias.class);
return alias == null ? null : StringUtils.hasText(alias.value()) ? alias.value() : null;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#getTypeInformation()
*/
public TypeInformation<T> getTypeInformation() {
return information;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#doWithProperties(org.springframework.data.mapping.PropertyHandler)
*/
public void doWithProperties(PropertyHandler<P> handler) {
Assert.notNull(handler);
for (P property : properties) {
if (!property.isTransient() && !property.isAssociation()) {
handler.doWithPersistentProperty(property);
}
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.PersistentEntity#doWithAssociations(org.springframework.data.mapping.AssociationHandler)
*/
public void doWithAssociations(AssociationHandler<P> handler) {
Assert.notNull(handler);
for (Association<P> association : associations) {
handler.doWithAssociation(association);
}
}
/* (non-Javadoc)
* @see org.springframework.data.mapping.MutablePersistentEntity#verify()
*/
public void verify() {
}
/**
* Simple {@link Comparator} adaptor to delegate ordering to the inverse properties of the association.
*
* @author Oliver Gierke
*/
private static final class AssociationComparator<P extends PersistentProperty<P>> implements
Comparator<Association<P>>, Serializable {
private static final long serialVersionUID = 4508054194886854513L;
private final Comparator<P> delegate;
public AssociationComparator(Comparator<P> delegate) {
Assert.notNull(delegate);
this.delegate = delegate;
}
public int compare(Association<P> left, Association<P> right) {
return delegate.compare(left.getInverse(), right.getInverse());
}
}
}

View File

@@ -0,0 +1,175 @@
/*
* Copyright 2011-2012 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
* Value object to allow creation of objects using the metamodel, setting and getting properties.
*
* @author Oliver Gierke
*/
public class BeanWrapper<E extends PersistentEntity<T, ?>, T> {
private final T bean;
private final ConversionService conversionService;
/**
* Creates a new {@link BeanWrapper} for the given bean instance and {@link ConversionService}. If
* {@link ConversionService} is {@literal null} no property type conversion will take place.
*
* @param <E>
* @param <T>
* @param bean must not be {@literal null}
* @param conversionService
* @return
*/
public static <E extends PersistentEntity<T, ?>, T> BeanWrapper<E, T> create(T bean,
ConversionService conversionService) {
return new BeanWrapper<E, T>(bean, conversionService);
}
private BeanWrapper(T bean, ConversionService conversionService) {
Assert.notNull(bean);
this.bean = bean;
this.conversionService = conversionService;
}
/**
* Sets the given {@link PersistentProperty} to the given value. Will do type conversion if a
* {@link ConversionService} is configured. Will use the accessor method of the given {@link PersistentProperty} if it
* has one or field access otherwise.
*
* @param property must not be {@literal null}.
* @param value
*/
public void setProperty(PersistentProperty<?> property, Object value) {
setProperty(property, value, false);
}
/**
* Sets the given {@link PersistentProperty} to the given value. Will do type conversion if a
* {@link ConversionService} is configured.
*
* @param property must not be {@literal null}.
* @param value
* @param fieldAccessOnly whether to only try accessing the field ({@literal true}) or try using the getter first.
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
public void setProperty(PersistentProperty<?> property, Object value, boolean fieldAccessOnly) {
Method setter = property.getSetter();
try {
if (fieldAccessOnly || null == setter) {
Object valueToSet = getPotentiallyConvertedValue(value, property.getType());
ReflectionUtils.makeAccessible(property.getField());
ReflectionUtils.setField(property.getField(), bean, valueToSet);
return;
}
Class<?>[] paramTypes = setter.getParameterTypes();
Object valueToSet = getPotentiallyConvertedValue(value, paramTypes[0]);
ReflectionUtils.makeAccessible(setter);
ReflectionUtils.invokeMethod(setter, bean, valueToSet);
} catch (IllegalStateException e) {
throw new MappingException("Could not set object property!", e);
}
}
/**
* Returns the value of the given {@link PersistentProperty} of the underlying bean instance.
*
* @param <S>
* @param property
* @return
*/
public Object getProperty(PersistentProperty<?> property) {
return getProperty(property, property.getType(), false);
}
/**
* Returns the value of the given {@link PersistentProperty} potentially converted to the given type.
*
* @param <S>
* @param property must not be {@literal null}.
* @param type
* @param fieldAccessOnly
* @return
*/
public <S> S getProperty(PersistentProperty<?> property, Class<? extends S> type, boolean fieldAccessOnly) {
try {
Object obj;
Field field = property.getField();
Method getter = property.getGetter();
if (fieldAccessOnly || null == getter) {
ReflectionUtils.makeAccessible(field);
obj = ReflectionUtils.getField(field, bean);
} else {
ReflectionUtils.makeAccessible(getter);
obj = ReflectionUtils.invokeMethod(getter, bean);
}
return getPotentiallyConvertedValue(obj, type);
} catch (IllegalStateException e) {
throw new MappingException(String.format("Could not read property %s of %s!", property.toString(),
bean.toString()), e);
}
}
/**
* Converts the given source value if it is not assignable to the given target type.
*
* @param source
* @param targetType
* @return
*/
@SuppressWarnings("unchecked")
private <S> S getPotentiallyConvertedValue(Object source, Class<S> targetType) {
boolean conversionServiceAvailable = conversionService != null;
boolean conversionNeeded = source == null || !targetType.isAssignableFrom(source.getClass());
if (conversionServiceAvailable && conversionNeeded) {
return conversionService.convert(source, targetType);
}
return (S) source;
}
/**
* Returns the underlying bean instance.
*
* @return
*/
public T getBean() {
return bean;
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
/**
* {@link ParameterValueProvider} implementation that evaluates the {@link Parameter}s key against
* {@link SpelExpressionParser} and {@link EvaluationContext}.
*
* @author Oliver Gierke
*/
public class DefaultSpELExpressionEvaluator implements SpELExpressionEvaluator {
private final Object source;
private final SpELContext factory;
/**
* @param parser
* @param factory
*/
public DefaultSpELExpressionEvaluator(Object source, SpELContext factory) {
this.source = source;
this.factory = factory;
}
/* (non-Javadoc)
* @see org.springframework.data.mapping.model.SpELExpressionEvaluator#evaluate(java.lang.String)
*/
@SuppressWarnings("unchecked")
public <T> T evaluate(String expression) {
Expression parseExpression = factory.getParser().parseExpression(expression);
return (T) parseExpression.getValue(factory.getEvaluationContext(source));
}
}

View File

@@ -0,0 +1,37 @@
/* Copyright (C) 2010 SpringSource
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
/**
* Thrown when an error occurs reading the mapping between object and datastore
*
* @author Graeme Rocher
* @since 1.0
*/
public class IllegalMappingException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = 1L;
public IllegalMappingException(String s, Throwable throwable) {
super(s, throwable);
}
public IllegalMappingException(String s) {
super(s);
}
}

View File

@@ -0,0 +1,169 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.support.IsNewStrategy;
import org.springframework.data.support.IsNewStrategyFactory;
import org.springframework.data.support.IsNewStrategyFactorySupport;
import org.springframework.util.Assert;
/**
* An {@link IsNewStrategyFactory} using a {@link MappingContext} to determine the {@link IsNewStrategy} to be returned
* for a particular type. It will look for a version and id property on the {@link PersistentEntity} and return a
* strategy instance that will refelctively inspect the property for {@literal null} values or {@literal null} or a
* value of 0 in case of a version property.
*
* @author Oliver Gierke
*/
public class MappingContextIsNewStrategyFactory extends IsNewStrategyFactorySupport {
private final MappingContext<? extends PersistentEntity<?, ?>, ?> context;
/**
* Creates a new {@link MappingContextIsNewStrategyFactory} using the given {@link MappingContext}.
*
* @param context must not be {@literal null}.
*/
public MappingContextIsNewStrategyFactory(MappingContext<? extends PersistentEntity<?, ?>, ?> context) {
Assert.notNull(context, "MappingContext must not be null!");
this.context = context;
}
/*
* (non-Javadoc)
* @see org.springframework.data.support.IsNewStrategyFactorySupport#getFallBackStrategy(java.lang.Class)
*/
@Override
protected IsNewStrategy doGetIsNewStrategy(Class<?> type) {
PersistentEntity<?, ?> entity = context.getPersistentEntity(type);
if (entity == null) {
return null;
}
if (entity.hasVersionProperty()) {
return new PropertyIsNullOrZeroNumberIsNewStrategy(entity.getVersionProperty());
} else if (entity.hasIdProperty()) {
return new PropertyIsNullIsNewStrategy(entity.getIdProperty());
} else {
throw new MappingException(String.format("Cannot determine IsNewStrategy for type %s!", type));
}
}
/**
* {@link IsNewStrategy} implementation that will inspect a given {@link PersistentProperty} and call
* {@link #decideIsNew(Object)} with the value retrieved by reflection.
*
* @author Oliver Gierke
*/
static abstract class PersistentPropertyInspectingIsNewStrategy implements IsNewStrategy {
private final PersistentProperty<?> property;
/**
* Creates a new {@link PersistentPropertyInspectingIsNewStrategy} using the given {@link PersistentProperty}.
*
* @param property must not be {@literal null}.
*/
public PersistentPropertyInspectingIsNewStrategy(PersistentProperty<?> property) {
Assert.notNull(property, "PersistentProperty must not be null!");
this.property = property;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.IsNewStrategy#isNew(java.lang.Object)
*/
public boolean isNew(Object entity) {
BeanWrapper<PersistentEntity<Object, ?>, Object> wrapper = BeanWrapper.create(entity, null);
Object propertyValue = wrapper.getProperty(property);
return decideIsNew(propertyValue);
}
protected abstract boolean decideIsNew(Object property);
}
/**
* {@link IsNewStrategy} that does a check against {@literal null} for the given value and considers the object new if
* the value given is {@literal null}.
*
* @author Oliver Gierke
*/
static class PropertyIsNullIsNewStrategy extends PersistentPropertyInspectingIsNewStrategy {
/**
* Creates a new {@link PropertyIsNullIsNewStrategy} using the given {@link PersistentProperty}.
*
* @param property must not be {@literal null}.
*/
public PropertyIsNullIsNewStrategy(PersistentProperty<?> property) {
super(property);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.MappingContextIsNewStrategyFactory.PersistentPropertyInspectingIsNewStrategy#decideIsNew(java.lang.Object)
*/
@Override
protected boolean decideIsNew(Object property) {
return property == null;
}
}
/**
* {@link IsNewStrategy} that considers property values of {@literal null} or 0 (in case of a {@link Number})
* implementation as indicators for the new state.
*
* @author Oliver Gierke
*/
static class PropertyIsNullOrZeroNumberIsNewStrategy extends PersistentPropertyInspectingIsNewStrategy {
/**
* Creates a new {@link PropertyIsNullOrZeroNumberIsNewStrategy} instance using the given {@link PersistentProperty}
* .
*
* @param property must not be {@literal null}.
*/
public PropertyIsNullOrZeroNumberIsNewStrategy(PersistentProperty<?> property) {
super(property);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.MappingContextIsNewStrategyFactory.PersistentPropertyInspectingIsNewStrategy#decideIsNew(java.lang.Object)
*/
@Override
protected boolean decideIsNew(Object property) {
if (property == null) {
return true;
}
if (!(property instanceof Number)) {
return false;
}
return ((Number) property).longValue() == 0;
}
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.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);
}
public MappingException(String s, Throwable throwable) {
super(s, throwable);
}
}

View File

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

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
/**
* Interface capturing mutator methods for {@link PersistentEntity}s.
*
* @author Oliver Gierke
*/
public interface MutablePersistentEntity<T, P extends PersistentProperty<P>> extends PersistentEntity<T, P> {
/**
* 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);
/**
* 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.
*
* @throws MappingException in case the entity is invalid
*/
void verify() throws MappingException;
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2011-2012 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
/**
* Callback interface to lookup values for a given {@link Parameter}.
*
* @author Oliver Gierke
*/
public interface ParameterValueProvider<P extends PersistentProperty<P>> {
/**
* Returns the value to be used for the given {@link Parameter} (usually when entity instances are created).
*
* @param parameter must not be {@literal null}.
* @return
*/
<T> T getParameterValue(Parameter<T, P> parameter);
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PreferredConstructor;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.util.Assert;
/**
* {@link ParameterValueProvider} based on a {@link PersistentEntity} to use a {@link PropertyValueProvider} to lookup
* the value of the property referenced by the given {@link Parameter}. Additionally a
* {@link DefaultSpELExpressionEvaluator} can be configured to get property value resolution trumped by a SpEL
* expression evaluation.
*
* @author Oliver Gierke
*/
public class PersistentEntityParameterValueProvider<P extends PersistentProperty<P>> implements
ParameterValueProvider<P> {
private final PersistentEntity<?, P> entity;
private final PropertyValueProvider<P> provider;
private final Object parent;
/**
* Creates a new {@link PersistentEntityParameterValueProvider} for the given {@link PersistentEntity} and
* {@link PropertyValueProvider}.
*
* @param entity must not be {@literal null}.
* @param provider must not be {@literal null}.
* @param parent the parent object being created currently, can be {@literal null}.
*/
public PersistentEntityParameterValueProvider(PersistentEntity<?, P> entity, PropertyValueProvider<P> provider,
Object parent) {
Assert.notNull(entity);
Assert.notNull(provider);
this.entity = entity;
this.provider = provider;
this.parent = parent;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.ParameterValueProvider#getParameterValue(org.springframework.data.mapping.PreferredConstructor.Parameter)
*/
@SuppressWarnings("unchecked")
public <T> T getParameterValue(Parameter<T, P> parameter) {
PreferredConstructor<?, P> constructor = entity.getPersistenceConstructor();
if (constructor.isEnclosingClassParameter(parameter)) {
return (T) parent;
}
P property = entity.getPersistentProperty(parameter.getName());
if (property == null) {
throw new MappingException(String.format("No property %s found on entity %s to bind constructor parameter to!",
parameter.getName(), entity.getType()));
}
return provider.getPropertyValue(property);
}
}

View File

@@ -0,0 +1,133 @@
/*
* Copyright 2011-2012 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.util.List;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PreferredConstructor;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
/**
* Helper class to find a {@link PreferredConstructor}.
*
* @author Oliver Gierke
*/
public class PreferredConstructorDiscoverer<T, P extends PersistentProperty<P>> {
private final ParameterNameDiscoverer nameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
private PreferredConstructor<T, P> constructor;
/**
* Creates a new {@link PreferredConstructorDiscoverer} for the given type.
*
* @param type must not be {@literal null}.
*/
public PreferredConstructorDiscoverer(Class<T> type) {
this(ClassTypeInformation.from(type), null);
}
/**
* Creates a new {@link PreferredConstructorDiscoverer} for the given {@link PersistentEntity}.
*
* @param entity must not be {@literal null}.
*/
public PreferredConstructorDiscoverer(PersistentEntity<T, P> entity) {
this(entity.getTypeInformation(), entity);
}
/**
* Creates a new {@link PreferredConstructorDiscoverer} for the given type.
*
* @param type must not be {@literal null}.
* @param entity
*/
protected PreferredConstructorDiscoverer(TypeInformation<T> type, PersistentEntity<T, P> entity) {
boolean noArgConstructorFound = false;
int numberOfArgConstructors = 0;
Class<?> rawOwningType = type.getType();
for (Constructor<?> constructor : rawOwningType.getDeclaredConstructors()) {
PreferredConstructor<T, P> preferredConstructor = buildPreferredConstructor(constructor, type, entity);
// 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) {
this.constructor = null;
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private PreferredConstructor<T, P> buildPreferredConstructor(Constructor<?> constructor,
TypeInformation<T> typeInformation, PersistentEntity<T, P> entity) {
List<TypeInformation<?>> parameterTypes = typeInformation.getParameterTypes(constructor);
if (parameterTypes.isEmpty()) {
return new PreferredConstructor<T, P>((Constructor<T>) constructor);
}
String[] parameterNames = nameDiscoverer.getParameterNames(constructor);
Parameter<Object, P>[] 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, entity);
}
return new PreferredConstructor<T, P>((Constructor<T>) constructor, parameters);
}
/**
* Returns the discovered {@link PreferredConstructor}.
*
* @return
*/
public PreferredConstructor<T, P> getConstructor() {
return constructor;
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
import org.springframework.data.mapping.PersistentProperty;
/**
* SPI for components to provide values for as {@link PersistentProperty}.
*
* @author Oliver Gierke
*/
public interface PropertyValueProvider<P extends PersistentProperty<P>> {
/**
* Returns a value for the given {@link PersistentProperty}.
*
* @param property will never be {@literal null}.
* @return
*/
<T> T getPropertyValue(P property);
}

View File

@@ -0,0 +1,129 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import org.springframework.util.Assert;
/**
* Simple container to hold a set of types to be considered simple types.
*
* @author Oliver Gierke
*/
public class SimpleTypeHolder {
private static final Set<Class<?>> DEFAULTS = new HashSet<Class<?>>();
static {
DEFAULTS.add(boolean.class);
DEFAULTS.add(boolean[].class);
DEFAULTS.add(long.class);
DEFAULTS.add(long[].class);
DEFAULTS.add(short.class);
DEFAULTS.add(short[].class);
DEFAULTS.add(int.class);
DEFAULTS.add(int[].class);
DEFAULTS.add(byte.class);
DEFAULTS.add(byte[].class);
DEFAULTS.add(float.class);
DEFAULTS.add(float[].class);
DEFAULTS.add(double.class);
DEFAULTS.add(double[].class);
DEFAULTS.add(char.class);
DEFAULTS.add(char[].class);
DEFAULTS.add(Boolean.class);
DEFAULTS.add(Long.class);
DEFAULTS.add(Short.class);
DEFAULTS.add(Integer.class);
DEFAULTS.add(Byte.class);
DEFAULTS.add(Float.class);
DEFAULTS.add(Double.class);
DEFAULTS.add(Character.class);
DEFAULTS.add(String.class);
DEFAULTS.add(Date.class);
DEFAULTS.add(Locale.class);
DEFAULTS.add(Class.class);
DEFAULTS.add(Enum.class);
}
private final Set<Class<?>> simpleTypes;
/**
* Creates a new {@link SimpleTypeHolder} containing the default types.
*
* @see #SimpleTypeHolder(Set, boolean)
*/
@SuppressWarnings("unchecked")
public SimpleTypeHolder() {
this(Collections.EMPTY_SET, true);
}
/**
* Creates a new {@link SimpleTypeHolder} to carry the given custom simple types. Registration of default simple types
* can be deactivated by passing {@literal false} for {@code registerDefaults}.
*
* @param customSimpleTypes
* @param registerDefaults
*/
public SimpleTypeHolder(Set<? extends Class<?>> customSimpleTypes, boolean registerDefaults) {
Assert.notNull(customSimpleTypes);
this.simpleTypes = new HashSet<Class<?>>(customSimpleTypes);
if (registerDefaults) {
this.simpleTypes.addAll(DEFAULTS);
}
}
/**
* Copy constructor to create a new {@link SimpleTypeHolder} that carries the given additional custom simple types.
*
* @param customSimpleTypes must not be {@literal null}
* @param source must not be {@literal null}
*/
public SimpleTypeHolder(Set<? extends Class<?>> customSimpleTypes, SimpleTypeHolder source) {
Assert.notNull(customSimpleTypes);
Assert.notNull(source);
this.simpleTypes = new HashSet<Class<?>>(customSimpleTypes);
this.simpleTypes.addAll(source.simpleTypes);
}
/**
* Returns whether the given type is considered a simple one.
*
* @param type
* @return
*/
public boolean isSimpleType(Class<?> type) {
Assert.notNull(type);
if (Object.class.equals(type)) {
return true;
}
for (Class<?> clazz : simpleTypes) {
if (type == clazz || clazz.isAssignableFrom(type)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
/**
* Simple factory to create {@link SpelExpressionParser} and {@link EvaluationContext} instances.
*
* @author Oliver Gierke
*/
public class SpELContext {
private final SpelExpressionParser parser;
private final PropertyAccessor accessor;
private final BeanFactory factory;
/**
* Creates a new {@link SpELContext} with the given {@link PropertyAccessor}. Defaults the
* {@link SpelExpressionParser}.
*
* @param accessor
*/
public SpELContext(PropertyAccessor accessor) {
this(accessor, null, null);
}
/**
* Creates a new {@link SpELContext} using the given {@link SpelExpressionParser} and {@link PropertyAccessor}. Will
* default the {@link SpelExpressionParser} in case the given value for it is {@literal null}.
*
* @param parser
* @param accessor
*/
public SpELContext(SpelExpressionParser parser, PropertyAccessor accessor) {
this(accessor, parser, null);
}
/**
* Copy constructor to create a {@link SpELContext} using the given one's {@link PropertyAccessor} and
* {@link SpelExpressionParser} as well as the given {@link BeanFactory}.
*
* @param source
* @param factory
*/
public SpELContext(SpELContext source, BeanFactory factory) {
this(source.accessor, source.parser, factory);
}
/**
* Creates a new {@link SpELContext} using the given {@link SpelExpressionParser}, {@link PropertyAccessor} and
* {@link BeanFactory}. Will default the {@link SpelExpressionParser} in case the given value for it is
* {@literal null}.
*
* @param accessor
* @param parser
* @param factory
*/
private SpELContext(PropertyAccessor accessor, SpelExpressionParser parser, BeanFactory factory) {
this.parser = parser == null ? new SpelExpressionParser() : parser;
this.accessor = accessor;
this.factory = factory;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.SpELContext#getParser()
*/
public ExpressionParser getParser() {
return this.parser;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.SpELContext#getEvaluationContext(java.lang.Object)
*/
public EvaluationContext getEvaluationContext(Object source) {
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(source);
if (accessor != null) {
evaluationContext.addPropertyAccessor(accessor);
}
if (factory != null) {
evaluationContext.setBeanResolver(new BeanFactoryResolver(factory));
}
return evaluationContext;
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
/**
* SPI for components that can evaluate Spring EL expressions.
*
* @author Oliver Gierke
*/
public interface SpELExpressionEvaluator {
/**
* Evaluates the given expression.
*
* @param expression
* @return
*/
<T> T evaluate(String expression);
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.util.Assert;
/**
* {@link ParameterValueProvider} that can be used to front a {@link ParameterValueProvider} delegate to prefer a Spel
* expression evaluation over directly resolving the parameter value with the delegate.
*
* @author Oliver Gierke
*/
public class SpELExpressionParameterValueProvider<P extends PersistentProperty<P>> implements ParameterValueProvider<P> {
private final SpELExpressionEvaluator evaluator;
private final ParameterValueProvider<P> delegate;
private final ConversionService conversionService;
/**
* Creates a new {@link SpELExpressionParameterValueProvider} using the given {@link SpELExpressionEvaluator},
* {@link ConversionService} and {@link ParameterValueProvider} delegate to forward calls to, that resolve parameters
* that do not have a Spel expression configured with them.
*
* @param evaluator must not be {@literal null}.
* @param conversionService must not be {@literal null}.
* @param delegate must not be {@literal null}.
*/
public SpELExpressionParameterValueProvider(SpELExpressionEvaluator evaluator, ConversionService conversionService,
ParameterValueProvider<P> delegate) {
Assert.notNull(evaluator, "SpELExpressionEvaluator must not be null!");
Assert.notNull(conversionService, "ConversionService must not be null!");
Assert.notNull(delegate, "ParameterValueProvider delegate must not be null!");
this.evaluator = evaluator;
this.conversionService = conversionService;
this.delegate = delegate;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.ParameterValueProvider#getParameterValue(org.springframework.data.mapping.PreferredConstructor.Parameter)
*/
public <T> T getParameterValue(Parameter<T, P> parameter) {
if (!parameter.hasSpelExpression()) {
return delegate == null ? null : delegate.getParameterValue(parameter);
}
Object object = evaluator.evaluate(parameter.getSpelExpression());
return object == null ? null : potentiallyConvertSpelValue(object, parameter);
}
/**
* Hook to allow to massage the value resulting from the Spel expression evaluation. Default implementation will
* leverage the configured {@link ConversionService} to massage the value into the parameter type.
*
* @param object the value to massage, will never be {@literal null}.
* @param parameter the {@link Parameter} we create the value for
* @return
*/
protected <T> T potentiallyConvertSpelValue(Object object, Parameter<T, P> parameter) {
return conversionService.convert(object, parameter.getRawType());
}
}

Some files were not shown because too many files have changed in this diff Show More