DATAMONGO-866 - Polishing for new field naming strategy configuration support.
Use the camel case split logic from Spring Data Commons (introduced for DATACMNS-486) in a common CamelCaseSplittingFieldNamingStrategy super class. MappingMongoConverterParser now also rejects the configuration if both abbreviate-field-names and field-naming-strategy-ref are configured.
This commit is contained in:
@@ -237,6 +237,7 @@ public abstract class AbstractMongoConfiguration {
|
||||
* Configures a {@link FieldNamingStrategy} on the {@link MongoMappingContext} instance created.
|
||||
*
|
||||
* @return
|
||||
* @since 1.5
|
||||
*/
|
||||
protected FieldNamingStrategy fieldNamingStrategy() {
|
||||
return abbreviateFieldNames() ? new CamelCaseAbbreviatingFieldNamingStrategy()
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.ReaderContext;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
@@ -209,18 +210,7 @@ public class MappingMongoConverterParser implements BeanDefinitionParser {
|
||||
mappingContextBuilder.addPropertyValue("simpleTypeHolder", simpleTypesDefinition);
|
||||
}
|
||||
|
||||
String abbreviateFieldNames = element.getAttribute("abbreviate-field-names");
|
||||
|
||||
if ("true".equals(abbreviateFieldNames)) {
|
||||
mappingContextBuilder.addPropertyValue("fieldNamingStrategy", new RootBeanDefinition(
|
||||
CamelCaseAbbreviatingFieldNamingStrategy.class));
|
||||
}
|
||||
|
||||
String fieldNamingStrategy = element.getAttribute("field-naming-strategy-ref");
|
||||
|
||||
if (StringUtils.hasText(fieldNamingStrategy)) {
|
||||
mappingContextBuilder.addPropertyValue("fieldNamingStrategy", new RuntimeBeanReference(fieldNamingStrategy));
|
||||
}
|
||||
parseFieldNamingStrategy(element, parserContext.getReaderContext(), mappingContextBuilder);
|
||||
|
||||
ctxRef = converterId == null || DEFAULT_CONVERTER_BEAN_NAME.equals(converterId) ? MAPPING_CONTEXT_BEAN_NAME
|
||||
: converterId + "." + MAPPING_CONTEXT_BEAN_NAME;
|
||||
@@ -229,6 +219,32 @@ public class MappingMongoConverterParser implements BeanDefinitionParser {
|
||||
return ctxRef;
|
||||
}
|
||||
|
||||
private static void parseFieldNamingStrategy(Element element, ReaderContext context, BeanDefinitionBuilder builder) {
|
||||
|
||||
String abbreviateFieldNames = element.getAttribute("abbreviate-field-names");
|
||||
String fieldNamingStrategy = element.getAttribute("field-naming-strategy-ref");
|
||||
|
||||
if (StringUtils.hasText(fieldNamingStrategy) && StringUtils.hasText(abbreviateFieldNames)) {
|
||||
|
||||
context
|
||||
.error("Only one of the attributes abbreviate-field-names and field-naming-strategy-ref can be configured!",
|
||||
element);
|
||||
return;
|
||||
}
|
||||
|
||||
Object value = null;
|
||||
|
||||
if ("true".equals(abbreviateFieldNames)) {
|
||||
value = new RootBeanDefinition(CamelCaseAbbreviatingFieldNamingStrategy.class);
|
||||
} else if (StringUtils.hasText(fieldNamingStrategy)) {
|
||||
value = new RuntimeBeanReference(fieldNamingStrategy);
|
||||
}
|
||||
|
||||
if (value != null) {
|
||||
builder.addPropertyValue("fieldNamingStrategy", value);
|
||||
}
|
||||
}
|
||||
|
||||
private BeanDefinition getCustomConversions(Element element, ParserContext parserContext) {
|
||||
|
||||
List<Element> customConvertersElements = DomUtils.getChildElementsByTagName(element, "custom-converters");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2014 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.
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.mapping;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* {@link FieldNamingStrategy} that abbreviates field names by using the very first letter of the camel case parts of
|
||||
* the {@link MongoPersistentProperty}'s name.
|
||||
@@ -24,23 +22,18 @@ import java.util.Locale;
|
||||
* @since 1.3
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class CamelCaseAbbreviatingFieldNamingStrategy implements FieldNamingStrategy {
|
||||
public class CamelCaseAbbreviatingFieldNamingStrategy extends CamelCaseSplittingFieldNamingStrategy {
|
||||
|
||||
private static final String CAMEL_CASE_PATTERN = "(?<!(^|[A-Z]))(?=[A-Z])|(?<!^)(?=[A-Z][a-z])";
|
||||
public CamelCaseAbbreviatingFieldNamingStrategy() {
|
||||
super("");
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.mapping.FieldNamingStrategy#getFieldName(org.springframework.data.mongodb.core.mapping.MongoPersistentProperty)
|
||||
* @see org.springframework.data.mongodb.core.mapping.CamelCaseSplittingFieldNamingStrategy#preparePart(java.lang.String)
|
||||
*/
|
||||
public String getFieldName(MongoPersistentProperty property) {
|
||||
|
||||
String[] parts = property.getName().split(CAMEL_CASE_PATTERN);
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
for (String part : parts) {
|
||||
builder.append(part.substring(0, 1).toLowerCase(Locale.US));
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
@Override
|
||||
protected String preparePart(String part) {
|
||||
return part.substring(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2014 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.mongodb.core.mapping;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.util.ParsingUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Configurable {@link FieldNamingStrategy} that splits up camel-case property names and reconcatenates them using a
|
||||
* configured delimiter. Individual parts of the name can be manipulated using {@link #preparePart(String)}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @since 1.5
|
||||
*/
|
||||
public class CamelCaseSplittingFieldNamingStrategy implements FieldNamingStrategy {
|
||||
|
||||
private final String delimiter;
|
||||
|
||||
/**
|
||||
* Creates a new {@link CamelCaseSplittingFieldNamingStrategy}.
|
||||
*
|
||||
* @param delimiter must not be {@literal null}.
|
||||
*/
|
||||
public CamelCaseSplittingFieldNamingStrategy(String delimiter) {
|
||||
|
||||
Assert.notNull(delimiter, "Delimiter must not be null!");
|
||||
this.delimiter = delimiter;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.mapping.FieldNamingStrategy#getFieldName(org.springframework.data.mongodb.core.mapping.MongoPersistentProperty)
|
||||
*/
|
||||
@Override
|
||||
public String getFieldName(MongoPersistentProperty property) {
|
||||
|
||||
List<String> parts = ParsingUtils.splitCamelCaseToLower(property.getName());
|
||||
List<String> result = new ArrayList<String>();
|
||||
|
||||
for (String part : parts) {
|
||||
|
||||
String candidate = preparePart(part);
|
||||
|
||||
if (StringUtils.hasText(candidate)) {
|
||||
result.add(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
return StringUtils.collectionToDelimitedString(result, delimiter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to prepare the uncapitalized part obtained from the split up of the camel case source. Default
|
||||
* implementation returns the part as is.
|
||||
*
|
||||
* @param part
|
||||
* @return
|
||||
*/
|
||||
protected String preparePart(String part) {
|
||||
return part;
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ package org.springframework.data.mongodb.core.mapping;
|
||||
* @see DocumentField
|
||||
* @see PropertyNameFieldNamingStrategy
|
||||
* @see CamelCaseAbbreviatingFieldNamingStrategy
|
||||
* @see LowerCaseWithUnderscoresFieldNamingStrategy
|
||||
* @see SnakeCaseFieldNamingStrategy
|
||||
* @since 1.3
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 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.mongodb.core.mapping;
|
||||
|
||||
/**
|
||||
* {@link FieldNamingStrategy} that translates typical camel case Java
|
||||
* property names to lower case JSON element names, separated by
|
||||
* underscores.
|
||||
*
|
||||
* Source: jackson-databind, Apache 2 License
|
||||
* https://github.com/FasterXML/jackson-databind/blob/2.3/src/main/java/com/fasterxml/jackson/databind/PropertyNamingStrategy.java
|
||||
*
|
||||
* @since 1.5
|
||||
* @author Ryan Tenney
|
||||
*/
|
||||
public class LowerCaseWithUnderscoresFieldNamingStrategy implements FieldNamingStrategy {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.mapping.FieldNamingStrategy#getFieldName(org.springframework.data.mongodb.core.mapping.MongoPersistentProperty)
|
||||
*/
|
||||
public String getFieldName(MongoPersistentProperty property) {
|
||||
String input = property.getName();
|
||||
int length = input.length();
|
||||
StringBuilder result = new StringBuilder(length * 2);
|
||||
int resultLength = 0;
|
||||
boolean wasPrevTranslated = false;
|
||||
for (int i = 0; i < length; i++) {
|
||||
char c = input.charAt(i);
|
||||
if (i > 0 || c != '_') { // skip first starting underscore
|
||||
if (Character.isUpperCase(c)) {
|
||||
if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '_') {
|
||||
result.append('_');
|
||||
resultLength++;
|
||||
}
|
||||
c = Character.toLowerCase(c);
|
||||
wasPrevTranslated = true;
|
||||
}
|
||||
else {
|
||||
wasPrevTranslated = false;
|
||||
}
|
||||
result.append(c);
|
||||
resultLength++;
|
||||
}
|
||||
}
|
||||
return resultLength > 0 ? result.toString() : input;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2013-2014 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.mongodb.core.mapping;
|
||||
|
||||
/**
|
||||
* {@link FieldNamingStrategy} that translates typical camel case Java property names to lower case JSON element names,
|
||||
* separated by underscores.
|
||||
*
|
||||
* @since 1.5
|
||||
* @author Ryan Tenney
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class SnakeCaseFieldNamingStrategy extends CamelCaseSplittingFieldNamingStrategy {
|
||||
|
||||
/**
|
||||
* Creates a new {@link SnakeCaseFieldNamingStrategy}.
|
||||
*/
|
||||
public SnakeCaseFieldNamingStrategy() {
|
||||
super("_");
|
||||
}
|
||||
}
|
||||
@@ -22,8 +22,12 @@ import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
@@ -49,6 +53,7 @@ import com.mongodb.DBObject;
|
||||
public class MappingMongoConverterParserIntegrationTests {
|
||||
|
||||
DefaultListableBeanFactory factory;
|
||||
public @Rule ExpectedException exception = ExpectedException.none();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
@@ -104,6 +109,21 @@ public class MappingMongoConverterParserIntegrationTests {
|
||||
assertThat(strategy.getBeanClassName(), is(CamelCaseAbbreviatingFieldNamingStrategy.class.getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-866
|
||||
*/
|
||||
@Test
|
||||
public void rejectsInvalidFieldNamingStrategyConfiguration() {
|
||||
|
||||
exception.expect(BeanDefinitionParsingException.class);
|
||||
exception.expectMessage("abbreviate-field-names");
|
||||
exception.expectMessage("field-naming-strategy-ref");
|
||||
|
||||
BeanDefinitionRegistry factory = new DefaultListableBeanFactory();
|
||||
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
|
||||
reader.loadBeanDefinitions(new ClassPathResource("namespace/converter-invalid.xml"));
|
||||
}
|
||||
|
||||
@Component
|
||||
public static class SampleConverter implements Converter<Person, DBObject> {
|
||||
public DBObject convert(Person source) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2014 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.
|
||||
@@ -25,19 +25,23 @@ import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link LowerCaseWithUnderscoresFieldNamingStrategy}.
|
||||
* Unit tests for {@link SnakeCaseFieldNamingStrategy}.
|
||||
*
|
||||
* @author Ryan Tenney
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class LowerCaseWithUnderscoresFieldNamingStrategyUnitTests {
|
||||
public class SnakeCaseFieldNamingStrategyUnitTests {
|
||||
|
||||
FieldNamingStrategy strategy = new LowerCaseWithUnderscoresFieldNamingStrategy();
|
||||
FieldNamingStrategy strategy = new SnakeCaseFieldNamingStrategy();
|
||||
|
||||
@Mock MongoPersistentProperty property;
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-866
|
||||
*/
|
||||
@Test
|
||||
public void foo() {
|
||||
public void rendersSnakeCaseFieldNames() {
|
||||
|
||||
assertFieldNameForPropertyName("fooBar", "foo_bar");
|
||||
assertFieldNameForPropertyName("FooBar", "foo_bar");
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<mongo:mapping-converter abbreviate-field-names="true" field-naming-strategy-ref="foo" />
|
||||
|
||||
</beans>
|
||||
Reference in New Issue
Block a user