diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/annotation/TypeAlias.java b/spring-data-commons-core/src/main/java/org/springframework/data/annotation/TypeAlias.java new file mode 100644 index 000000000..d9f2ca138 --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/annotation/TypeAlias.java @@ -0,0 +1,44 @@ +/* + * 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(); +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/convert/ConfigurableTypeInformationMapper.java b/spring-data-commons-core/src/main/java/org/springframework/data/convert/ConfigurableTypeInformationMapper.java new file mode 100644 index 000000000..03a8d46fa --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/convert/ConfigurableTypeInformationMapper.java @@ -0,0 +1,105 @@ +/* + * 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 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, Object> typeMap; + + /** + * Creates a {@link ConfigurableTypeInformationMapper} 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 + */ + public ConfigurableTypeInformationMapper(MappingContext, ?> mappingContext) { + + Assert.notNull(mappingContext); + this.typeMap = new HashMap, Object>(); + + for (PersistentEntity entity : mappingContext.getPersistentEntities()) { + Object alias = entity.getTypeAlias(); + if (alias != null) { + typeMap.put(entity.getTypeInformation(), alias); + } + } + } + + /** + * Creates a new {@link ConfigurableTypeMapper} for the given type map. + * + * @param sourceTypeMap must not be {@literal null}. + */ + public ConfigurableTypeInformationMapper(Map, String> sourceTypeMap) { + + Assert.notNull(sourceTypeMap); + + this.typeMap = new HashMap, Object>(sourceTypeMap.size()); + + for (Entry, 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) { + + Object key = typeMap.get(type); + return key == null ? null : key; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.convert.TypeInformationMapper#resolveTypeFrom(java.lang.Object) + */ + public TypeInformation resolveTypeFrom(Object alias) { + + for (Entry, Object> entry : typeMap.entrySet()) { + if (entry.getValue().equals(alias)) { + return entry.getKey(); + } + } + + return null; + } +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/convert/DefaultTypeMapper.java b/spring-data-commons-core/src/main/java/org/springframework/data/convert/DefaultTypeMapper.java new file mode 100644 index 000000000..bc2f803fb --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/convert/DefaultTypeMapper.java @@ -0,0 +1,155 @@ +/* + * 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 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 implements TypeMapper { + + private final TypeAliasAccessor accessor; + private final List mappers; + + public DefaultTypeMapper(TypeAliasAccessor accessor) { + this(accessor, Arrays.asList(SimpleTypeInformationMapper.INSTANCE)); + } + + public DefaultTypeMapper(TypeAliasAccessor accessor, List mappers) { + + this(accessor, null, mappers); + } + + public DefaultTypeMapper(TypeAliasAccessor accessor, MappingContext, ?> mappingContext, List additionalMappers) { + + Assert.notNull(accessor); + + List mappers = new ArrayList(additionalMappers.size() + 1); + if (mappingContext != null) { + mappers.add(new ConfigurableTypeInformationMapper(mappingContext)); + } + mappers.addAll(additionalMappers); + + this.mappers = Collections.unmodifiableList(mappers); + this.accessor = accessor; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.convert.MongoTypeMapper#readType(com.mongodb.DBObject) + */ + public TypeInformation readType(S source) { + + Assert.notNull(source); + Object alias = accessor.readAliasFrom(source); + + 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 TypeInformation readType(S source, TypeInformation basicType) { + + Assert.notNull(source); + Class documentsTargetType = getDefaultedTypeToBeUsed(source); + + if (documentsTargetType == null) { + return basicType; + } + + Class rawType = basicType == null ? null : basicType.getType(); + + boolean isMoreConcreteCustomType = rawType == null ? true : rawType.isAssignableFrom(documentsTargetType) + && !rawType.equals(documentsTargetType); + return isMoreConcreteCustomType ? (TypeInformation) 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.mongodb.core.convert.MongoTypeMapper#writeType(java.lang.Class, com.mongodb.DBObject) + */ + public void writeType(Class type, S dbObject) { + writeType(ClassTypeInformation.from(type), dbObject); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.convert.MongoTypeMapper#writeType(java.lang.Class, com.mongodb.DBObject) + */ + 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; + } + } + } +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/convert/EntityConverter.java b/spring-data-commons-core/src/main/java/org/springframework/data/convert/EntityConverter.java new file mode 100644 index 000000000..14dbc474c --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/convert/EntityConverter.java @@ -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.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; + +/** + * + * @param the concrete {@link PersistentEntity} implementation the converter is based on. + * @param

the concrete {@link PersistentProperty} implementation the converter is based on. + * @param the most common type the {@link EntityConverter} can actually convert. + * @param the store specific source and sink an {@link EntityConverter} can deal with. + * @author Oliver Gierke + */ +public interface EntityConverter, P extends PersistentProperty

, T, S> extends + EntityReader, EntityWriter { + + /** + * Returns the underlying {@link MappingContext} used by the converter. + * + * @return never {@literal null} + */ + MappingContext getMappingContext(); + + /** + * Returns the underlying {@link ConversionService} used by the converter. + * + * @return never {@literal null}. + */ + ConversionService getConversionService(); + +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/convert/EntityReader.java b/spring-data-commons-core/src/main/java/org/springframework/data/convert/EntityReader.java new file mode 100644 index 000000000..427afe863 --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/convert/EntityReader.java @@ -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 { + + /** + * 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 read(Class type, S source); +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/convert/EntityWriter.java b/spring-data-commons-core/src/main/java/org/springframework/data/convert/EntityWriter.java new file mode 100644 index 000000000..af1a3080c --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/convert/EntityWriter.java @@ -0,0 +1,29 @@ +/* + * 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 the entity type the converter can handle + * @param the store specific sink the converter is able to write to + * @author Oliver Gierke + */ +public interface EntityWriter { + + void write(T source, S sink); +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/convert/SimpleTypeInformationMapper.java b/spring-data-commons-core/src/main/java/org/springframework/data/convert/SimpleTypeInformationMapper.java new file mode 100644 index 000000000..05c2f9b70 --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/convert/SimpleTypeInformationMapper.java @@ -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 source) { + + if (!(source instanceof String)) { + return null; + } + + String value = (String) source; + + 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 == null ? null : type.getType().getName(); + } +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/convert/TypeAliasAccessor.java b/spring-data-commons-core/src/main/java/org/springframework/data/convert/TypeAliasAccessor.java new file mode 100644 index 000000000..23a30a818 --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/convert/TypeAliasAccessor.java @@ -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 { + + /** + * Reads the type alias to be used from the given source. + * + * @param source + * @return + */ + Object readAliasFrom(S source); + + /** + * Writes the given type alias to the given sink. + * + * @param sink + * @param alias + */ + void writeTypeTo(S sink, Object alias); +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/convert/TypeInformationMapper.java b/spring-data-commons-core/src/main/java/org/springframework/data/convert/TypeInformationMapper.java new file mode 100644 index 000000000..6eede2cb4 --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/convert/TypeInformationMapper.java @@ -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 + * @return + */ + TypeInformation resolveTypeFrom(Object alias); + + /** + * Returns the alias to be used for the given {@link TypeInformation}. + * + * @param type + * @return + */ + Object createAliasFor(TypeInformation type); +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/convert/TypeMapper.java b/spring-data-commons-core/src/main/java/org/springframework/data/convert/TypeMapper.java new file mode 100644 index 000000000..360cb3809 --- /dev/null +++ b/spring-data-commons-core/src/main/java/org/springframework/data/convert/TypeMapper.java @@ -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 { + + /** + * 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 + */ + TypeInformation readType(S source, TypeInformation 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); +} diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PersistentEntity.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PersistentEntity.java index 33738dd57..6dc37e33d 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PersistentEntity.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/PersistentEntity.java @@ -47,6 +47,14 @@ public interface PersistentEntity> { * @return The underlying Java class for this entity */ Class 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}. diff --git a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java index f4cddc226..9253c6d8e 100644 --- a/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java +++ b/spring-data-commons-core/src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 by the original author(s). + * 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. @@ -21,6 +21,7 @@ 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; @@ -29,6 +30,7 @@ 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. @@ -141,6 +143,16 @@ public class BasicPersistentEntity> implement public Class 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) diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/convert/ConfigurableTypeInformationMapperUnitTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/convert/ConfigurableTypeInformationMapperUnitTests.java new file mode 100644 index 000000000..4b47bbf8d --- /dev/null +++ b/spring-data-commons-core/src/test/java/org/springframework/data/convert/ConfigurableTypeInformationMapperUnitTests.java @@ -0,0 +1,123 @@ +/* + * 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 org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.data.annotation.TypeAlias; +import org.springframework.data.mapping.Association; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.context.AbstractMappingContext; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty; +import org.springframework.data.mapping.model.BasicPersistentEntity; +import org.springframework.data.mapping.model.SimpleTypeHolder; +import org.springframework.data.util.ClassTypeInformation; +import org.springframework.data.util.TypeInformation; + +/** + * Unit tests for {@link ConfigurableTypeMapper}. + * + * @author Oliver Gierke + */ +public class ConfigurableTypeInformationMapperUnitTests> { + + ConfigurableTypeInformationMapper mapper; + + @Before + public void setUp() { + mapper = new ConfigurableTypeInformationMapper(Collections.singletonMap(String.class, "1")); + } + + @Test(expected = IllegalArgumentException.class) + public void rejectsNullTypeMap() { + new ConfigurableTypeInformationMapper((Map, String>) null); + } + + @Test(expected = IllegalArgumentException.class) + public void rejectsNullMappingContext() { + new ConfigurableTypeInformationMapper((MappingContext) null); + } + + @Test(expected = IllegalArgumentException.class) + public void rejectsNonBijectionalMap() { + Map, String> map = new HashMap, String>(); + map.put(String.class, "1"); + map.put(Object.class, "1"); + + new ConfigurableTypeInformationMapper(map); + } + + @Test + @SuppressWarnings("unchecked") + public void extractsAliasInfoFromMappingContext() { + + AbstractMappingContext,T> mappingContext = new AbstractMappingContext, T>() { + + @Override + protected BasicPersistentEntity createPersistentEntity(TypeInformation typeInformation) { + return (BasicPersistentEntity) new BasicPersistentEntity(typeInformation); + } + + @Override + protected T createPersistentProperty(Field field, PropertyDescriptor descriptor, + BasicPersistentEntity owner, SimpleTypeHolder simpleTypeHolder) { + return (T) new AnnotationBasedPersistentProperty(field, descriptor, owner, simpleTypeHolder) { + @Override + protected Association createAssociation() { + return null; + } + }; + } + }; + + mappingContext.setInitialEntitySet(Collections.singleton(Entity.class)); + mappingContext.afterPropertiesSet(); + + mapper = new ConfigurableTypeInformationMapper(mappingContext); + + assertThat(mapper.createAliasFor(ClassTypeInformation.from(Entity.class)), is((Object) "foo")); + } + + @Test + public void writesMapKeyForType() { + + assertThat(mapper.createAliasFor(ClassTypeInformation.from(String.class)), is((Object) "1")); + assertThat(mapper.createAliasFor(ClassTypeInformation.from(Object.class)), is(nullValue())); + } + + @Test + @SuppressWarnings("rawtypes") + public void readsTypeForMapKey() { + + assertThat(mapper.resolveTypeFrom("1"), is((TypeInformation) ClassTypeInformation.from(String.class))); + assertThat(mapper.resolveTypeFrom("unmapped"), is(nullValue())); + } + + @TypeAlias("foo") + class Entity { + + } +} diff --git a/spring-data-commons-core/src/test/java/org/springframework/data/mapping/model/BasicPersistentEntityUnitTests.java b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/model/BasicPersistentEntityUnitTests.java index edf617ab7..dc1a8a82e 100644 --- a/spring-data-commons-core/src/test/java/org/springframework/data/mapping/model/BasicPersistentEntityUnitTests.java +++ b/spring-data-commons-core/src/test/java/org/springframework/data/mapping/model/BasicPersistentEntityUnitTests.java @@ -10,6 +10,8 @@ import java.util.SortedSet; import org.junit.Test; import org.mockito.Mockito; +import org.springframework.data.annotation.TypeAlias; +import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentEntitySpec; import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.Person; @@ -18,7 +20,7 @@ import org.springframework.test.util.ReflectionTestUtils; /** * Unit test for {@link BasicPersistentEntity}. - * + * * @author Oliver Gierke */ public class BasicPersistentEntityUnitTests> { @@ -27,53 +29,77 @@ public class BasicPersistentEntityUnitTests> { public void assertInvariants() { PersistentEntitySpec.assertInvariants(createEntity(null)); } - + @Test(expected = IllegalArgumentException.class) public void rejectsNullTypeInformation() { new BasicPersistentEntity(null); } - + @Test(expected = IllegalArgumentException.class) public void rejectsNullProperty() { createEntity(null).addPersistentProperty(null); } - + + @Test + public void returnsNullForTypeAliasIfNoneConfigured() { + + PersistentEntity entity = new BasicPersistentEntity(ClassTypeInformation.from(Entity.class)); + assertThat(entity.getTypeAlias(), is(nullValue())); + } + + @Test + public void returnsTypeAliasIfAnnotated() { + + PersistentEntity entity = new BasicPersistentEntity( + ClassTypeInformation.from(AliasedEntity.class)); + assertThat(entity.getTypeAlias(), is((Object) "foo")); + } + /** * @see DATACMNS-50 */ @Test @SuppressWarnings("unchecked") public void considersComparatorForPropertyOrder() { - - BasicPersistentEntity entity = createEntity(new Comparator() { + + BasicPersistentEntity entity = createEntity(new Comparator() { public int compare(T o1, T o2) { return o1.getName().compareTo(o2.getName()); } }); - + T lastName = (T) Mockito.mock(PersistentProperty.class); when(lastName.getName()).thenReturn("lastName"); - + T firstName = (T) Mockito.mock(PersistentProperty.class); when(firstName.getName()).thenReturn("firstName"); - + T ssn = (T) Mockito.mock(PersistentProperty.class); when(ssn.getName()).thenReturn("ssn"); - + entity.addPersistentProperty(lastName); entity.addPersistentProperty(firstName); entity.addPersistentProperty(ssn); - + SortedSet properties = (SortedSet) ReflectionTestUtils.getField(entity, "properties"); - + assertThat(properties.size(), is(3)); Iterator iterator = properties.iterator(); assertThat(iterator.next(), is(entity.getPersistentProperty("firstName"))); assertThat(iterator.next(), is(entity.getPersistentProperty("lastName"))); assertThat(iterator.next(), is(entity.getPersistentProperty("ssn"))); } - + private BasicPersistentEntity createEntity(Comparator comparator) { return new BasicPersistentEntity(ClassTypeInformation.from(Person.class), comparator); } + + @TypeAlias("foo") + static class AliasedEntity { + + } + + static class Entity { + + } }