Browse Source
Algorithm is now was follows: 1. If there's no explicit constructor use the default constructor. 2. If there's a single explicit constructor, use this one. 3. If there's multiple explicit constructors and one has been annotated with @PersistenceConstructor use the annotated one. 4. If there's multiple explicit constructors and none annotated use the no-arg one or throw an exception to resolve the ambiguity. Test cases for that algorithm are located in PreferredConstructorDiscovererUnitTests. Refactored PreferredConstructor and Parameter classes a little to make them immutable value objects. Introduced ability to lookup parameter TypeInformation for constructors on TypeInformation interface. Extended generics in mapping subsystem to allow defining a concrete subtype of PersistentProperty as well. Removed some generics related compiler warnings as well.pull/2/head
32 changed files with 665 additions and 405 deletions
@ -1,57 +0,0 @@
@@ -1,57 +0,0 @@
|
||||
/* |
||||
* 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; |
||||
|
||||
import java.beans.PropertyDescriptor; |
||||
import java.lang.reflect.Field; |
||||
|
||||
import org.springframework.data.mapping.model.MappingContext; |
||||
import org.springframework.data.mapping.model.PersistentProperty; |
||||
import org.springframework.data.util.TypeInformation; |
||||
|
||||
/** |
||||
* Simple implementation of {@link MappingContext} creating {@link BasicPersistentEntity} and |
||||
* {@link AnnotationBasedPersistentProperty} instances. |
||||
* |
||||
* @author Jon Brisbin <jbrisbin@vmware.com> |
||||
* @author Oliver Gierke |
||||
*/ |
||||
public class BasicMappingContext extends AbstractMappingContext<BasicPersistentEntity<?>, PersistentProperty> { |
||||
|
||||
/* |
||||
* (non-Javadoc) |
||||
* @see org.springframework.data.mapping.AbstractMappingContext#createPersistentEntity(org.springframework.data.util.TypeInformation) |
||||
*/ |
||||
@Override |
||||
@SuppressWarnings("rawtypes") |
||||
protected BasicPersistentEntity<?> createPersistentEntity( |
||||
TypeInformation typeInformation) { |
||||
|
||||
return new BasicPersistentEntity(typeInformation); |
||||
} |
||||
|
||||
/* |
||||
* (non-Javadoc) |
||||
* @see org.springframework.data.mapping.AbstractMappingContext#createPersistentProperty(java.lang.reflect.Field, java.beans.PropertyDescriptor, org.springframework.data.util.TypeInformation, org.springframework.data.mapping.BasicPersistentEntity) |
||||
*/ |
||||
@Override |
||||
protected PersistentProperty createPersistentProperty(Field field, |
||||
PropertyDescriptor descriptor, |
||||
BasicPersistentEntity<?> owner) { |
||||
|
||||
return new AnnotationBasedPersistentProperty(field, descriptor, owner); |
||||
} |
||||
} |
||||
@ -0,0 +1,58 @@
@@ -0,0 +1,58 @@
|
||||
/* |
||||
* 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; |
||||
|
||||
import org.springframework.data.mapping.model.Association; |
||||
import org.springframework.data.mapping.model.PersistentEntity; |
||||
import org.springframework.data.mapping.model.PersistentProperty; |
||||
|
||||
|
||||
/** |
||||
* Interface capturing mutator methods for {@link PersistentEntity}s. |
||||
* |
||||
* @author Oliver Gierke |
||||
*/ |
||||
public interface MutablePersistentEntity<T, P extends PersistentProperty<P>> |
||||
extends PersistentEntity<T, P> { |
||||
|
||||
/** |
||||
* Sets the id property for the entity. |
||||
* |
||||
* @param property |
||||
*/ |
||||
void setIdProperty(P property); |
||||
|
||||
/** |
||||
* Adds a {@link PersistentProperty} to the entity. |
||||
* |
||||
* @param property |
||||
*/ |
||||
void addPersistentProperty(P property); |
||||
|
||||
/** |
||||
* Adds an {@link Association} to the entity. |
||||
* |
||||
* @param association |
||||
*/ |
||||
void addAssociation(Association<P> association); |
||||
|
||||
/** |
||||
* Callback method to trigger validation of the {@link PersistentEntity}. As |
||||
* {@link MutablePersistentEntity} is not immutable there might be some |
||||
* verification steps necessary after the object has reached is final state. |
||||
*/ |
||||
void verify(); |
||||
} |
||||
@ -0,0 +1,118 @@
@@ -0,0 +1,118 @@
|
||||
/* |
||||
* 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; |
||||
|
||||
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.model.PreferredConstructor; |
||||
import org.springframework.data.mapping.model.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> { |
||||
|
||||
private final ParameterNameDiscoverer nameDiscoverer = |
||||
new LocalVariableTableParameterNameDiscoverer(); |
||||
|
||||
private PreferredConstructor<T> constructor; |
||||
|
||||
public PreferredConstructorDiscoverer(Class<T> type) { |
||||
this(ClassTypeInformation.from(type)); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new {@link PreferredConstructorDiscoverer} for the given type. |
||||
* |
||||
* @param owningType |
||||
*/ |
||||
protected PreferredConstructorDiscoverer(TypeInformation<T> owningType) { |
||||
|
||||
boolean noArgConstructorFound = false; |
||||
int numberOfArgConstructors = 0; |
||||
Class<?> rawOwningType = owningType.getType(); |
||||
|
||||
for (Constructor<?> constructor : rawOwningType.getDeclaredConstructors()) { |
||||
|
||||
PreferredConstructor<T> preferredConstructor = |
||||
buildPreferredConstructor(constructor, owningType); |
||||
|
||||
// Explicitly defined constructor trumps all
|
||||
if (preferredConstructor.isExplicitlyAnnotated()) { |
||||
this.constructor = preferredConstructor; |
||||
return; |
||||
} |
||||
|
||||
// No-arg constructor trumps custom ones
|
||||
if (this.constructor == null || preferredConstructor.isNoArgConstructor()) { |
||||
this.constructor = preferredConstructor; |
||||
} |
||||
|
||||
if (preferredConstructor.isNoArgConstructor()) { |
||||
noArgConstructorFound = true; |
||||
} else { |
||||
numberOfArgConstructors++; |
||||
} |
||||
} |
||||
|
||||
if (!noArgConstructorFound && numberOfArgConstructors > 1) { |
||||
throw new IllegalArgumentException( |
||||
"Multiple no-arg constructors found! Annotate one with @PreferedConstructor explicitly to select it to be used in persistence operations."); |
||||
} |
||||
} |
||||
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" }) |
||||
private PreferredConstructor<T> buildPreferredConstructor( |
||||
Constructor<?> constructor, TypeInformation<T> typeInformation) { |
||||
|
||||
List<TypeInformation<?>> parameterTypes = typeInformation.getParameterTypes(constructor); |
||||
|
||||
if (parameterTypes.isEmpty()) { |
||||
return new PreferredConstructor<T>((Constructor<T>) constructor); |
||||
} |
||||
|
||||
String[] parameterNames = nameDiscoverer.getParameterNames(constructor); |
||||
Parameter<?>[] parameters = new Parameter[parameterTypes.size()]; |
||||
Annotation[][] parameterAnnotations = constructor.getParameterAnnotations(); |
||||
|
||||
for (int i = 0; i < parameterTypes.size(); i++) { |
||||
|
||||
String name = parameterNames == null ? null : parameterNames[i]; |
||||
TypeInformation<?> type = parameterTypes.get(i); |
||||
Annotation[] annotations = parameterAnnotations[i]; |
||||
|
||||
parameters[i] = new Parameter(name, type, annotations); |
||||
} |
||||
|
||||
return new PreferredConstructor<T>((Constructor<T>) constructor, |
||||
parameters); |
||||
} |
||||
|
||||
|
||||
public PreferredConstructor<T> getConstructor() { |
||||
return constructor; |
||||
} |
||||
} |
||||
@ -0,0 +1,133 @@
@@ -0,0 +1,133 @@
|
||||
/* |
||||
* 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; |
||||
|
||||
import static org.hamcrest.Matchers.*; |
||||
import static org.junit.Assert.*; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.data.annotation.PersistenceConstructor; |
||||
import org.springframework.data.mapping.model.PreferredConstructor; |
||||
import org.springframework.data.mapping.model.PreferredConstructor.Parameter; |
||||
|
||||
|
||||
/** |
||||
* Unit tests for {@link PreferredConstructorDiscoverer}. |
||||
* |
||||
* @author Oliver Gierke |
||||
*/ |
||||
public class PreferredConstructorDiscovererUnitTests { |
||||
|
||||
@Test |
||||
public void findsNoArgConstructorForClassWithoutExplicitConstructor() { |
||||
|
||||
PreferredConstructorDiscoverer<EntityWithoutConstructor> discoverer = |
||||
new PreferredConstructorDiscoverer<EntityWithoutConstructor>( |
||||
EntityWithoutConstructor.class); |
||||
PreferredConstructor<EntityWithoutConstructor> constructor = |
||||
discoverer.getConstructor(); |
||||
|
||||
assertThat(constructor, is(notNullValue())); |
||||
assertThat(constructor.isNoArgConstructor(), is(true)); |
||||
assertThat(constructor.isExplicitlyAnnotated(), is(false)); |
||||
} |
||||
|
||||
|
||||
@Test |
||||
public void findsNoArgConstructorForClassWithMultipleConstructorsAndNoArgOne() { |
||||
|
||||
PreferredConstructorDiscoverer<ClassWithEmptyConstructor> discoverer = |
||||
new PreferredConstructorDiscoverer<ClassWithEmptyConstructor>( |
||||
ClassWithEmptyConstructor.class); |
||||
PreferredConstructor<ClassWithEmptyConstructor> constructor = |
||||
discoverer.getConstructor(); |
||||
|
||||
assertThat(constructor, is(notNullValue())); |
||||
assertThat(constructor.isNoArgConstructor(), is(true)); |
||||
assertThat(constructor.isExplicitlyAnnotated(), is(false)); |
||||
} |
||||
|
||||
|
||||
@Test(expected=IllegalArgumentException.class) |
||||
public void throwsExceptionForMultipleConstructorsAndNoNoArgConstructorWithoutAnnotation() { |
||||
|
||||
new PreferredConstructorDiscoverer<ClassWithMultipleConstructorsWithoutEmptyOne>( |
||||
ClassWithMultipleConstructorsWithoutEmptyOne.class); |
||||
} |
||||
|
||||
@Test |
||||
public void usesConstructorWithAnnotationOverEveryOther() { |
||||
|
||||
PreferredConstructorDiscoverer<ClassWithMultipleConstructorsAndAnnotation> discoverer = |
||||
new PreferredConstructorDiscoverer<ClassWithMultipleConstructorsAndAnnotation>( |
||||
ClassWithMultipleConstructorsAndAnnotation.class); |
||||
PreferredConstructor<ClassWithMultipleConstructorsAndAnnotation> constructor = |
||||
discoverer.getConstructor(); |
||||
|
||||
assertThat(constructor, is(notNullValue())); |
||||
assertThat(constructor.isNoArgConstructor(), is(false)); |
||||
assertThat(constructor.isExplicitlyAnnotated(), is(true)); |
||||
|
||||
List<Parameter<?>> parameters = constructor.getParameters(); |
||||
|
||||
assertThat(parameters.size(), is(1)); |
||||
assertThat(parameters.get(0).getType().getType(), typeCompatibleWith(Long.class)); |
||||
} |
||||
|
||||
static class EntityWithoutConstructor { |
||||
|
||||
} |
||||
|
||||
static class ClassWithEmptyConstructor { |
||||
|
||||
public ClassWithEmptyConstructor() { |
||||
} |
||||
} |
||||
|
||||
static class ClassWithMultipleConstructorsAndEmptyOne { |
||||
|
||||
public ClassWithMultipleConstructorsAndEmptyOne(String value) { |
||||
} |
||||
|
||||
|
||||
public ClassWithMultipleConstructorsAndEmptyOne() { |
||||
} |
||||
} |
||||
|
||||
static class ClassWithMultipleConstructorsWithoutEmptyOne { |
||||
|
||||
public ClassWithMultipleConstructorsWithoutEmptyOne(String value) { |
||||
} |
||||
|
||||
public ClassWithMultipleConstructorsWithoutEmptyOne(Long value) { |
||||
} |
||||
} |
||||
|
||||
static class ClassWithMultipleConstructorsAndAnnotation { |
||||
|
||||
public ClassWithMultipleConstructorsAndAnnotation() { |
||||
} |
||||
|
||||
public ClassWithMultipleConstructorsAndAnnotation(String value) { |
||||
} |
||||
|
||||
@PersistenceConstructor |
||||
public ClassWithMultipleConstructorsAndAnnotation(Long value) { |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue