diff --git a/src/main/java/org/springframework/data/mapping/PropertyPath.java b/src/main/java/org/springframework/data/mapping/PropertyPath.java index bc95d9aae..b93bb7b8d 100644 --- a/src/main/java/org/springframework/data/mapping/PropertyPath.java +++ b/src/main/java/org/springframework/data/mapping/PropertyPath.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2014 the original author or authors. + * Copyright 2011-2017 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. @@ -36,6 +36,7 @@ import org.springframework.util.StringUtils; * Abstraction of a {@link PropertyPath} of a domain class. * * @author Oliver Gierke + * @author Christoph Strobl */ @EqualsAndHashCode public class PropertyPath implements Streamable { @@ -43,6 +44,7 @@ public class PropertyPath implements Streamable { 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 static final Pattern SPLITTER_FOR_QUOTED = Pattern.compile("(?:[%s]?([%s]*?[^%s]+))".replaceAll("%s", "\\.")); private final TypeInformation owningType; private final String name; @@ -59,7 +61,7 @@ public class PropertyPath implements Streamable { * @param owningType must not be {@literal null}. */ PropertyPath(String name, Class owningType) { - this(name, ClassTypeInformation.from(owningType), Collections.emptyList()); + this(name, ClassTypeInformation.from(owningType), Collections. emptyList()); } /** @@ -210,8 +212,9 @@ public class PropertyPath implements Streamable { } /** - * Extracts the {@link PropertyPath} chain from the given source {@link String} and {@link TypeInformation}. - * + * Extracts the {@link PropertyPath} chain from the given source {@link String} and {@link TypeInformation}.
+ * Uses {@link #SPLITTER} by default and {@link #SPLITTER_FOR_QUOTED} for {@link Pattern#quote(String) quoted} literals. + * * @param source must not be {@literal null}. * @param type * @return @@ -222,7 +225,9 @@ public class PropertyPath implements Streamable { Assert.notNull(type, "TypeInformation must not be null or empty!"); List iteratorSource = new ArrayList<>(); - Matcher matcher = SPLITTER.matcher("_" + source); + + Matcher matcher = isQuoted(source) ? SPLITTER_FOR_QUOTED.matcher(source.replace("\\Q", "").replace("\\E", "")) + : SPLITTER.matcher("_" + source); while (matcher.find()) { iteratorSource.add(matcher.group(1)); @@ -245,6 +250,10 @@ public class PropertyPath implements Streamable { return result; } + private static boolean isQuoted(String source) { + return source.matches("^\\\\Q.*\\\\E$"); + } + /** * Creates a new {@link PropertyPath} as subordinary of the given {@link PropertyPath}. * diff --git a/src/main/java/org/springframework/data/mapping/model/AbstractPersistentProperty.java b/src/main/java/org/springframework/data/mapping/model/AbstractPersistentProperty.java index 17f8877c8..f43ae97c7 100644 --- a/src/main/java/org/springframework/data/mapping/model/AbstractPersistentProperty.java +++ b/src/main/java/org/springframework/data/mapping/model/AbstractPersistentProperty.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2015 the original author or authors. + * Copyright 2011-2017 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. @@ -23,6 +23,7 @@ import java.lang.reflect.Method; import java.util.Collections; import java.util.Map; import java.util.Optional; +import java.util.regex.Pattern; import org.springframework.data.annotation.Reference; import org.springframework.data.mapping.Association; @@ -66,8 +67,7 @@ public abstract class AbstractPersistentProperty

this.name = property.getName(); this.rawType = property.getType(); - - this.information = PropertyPath.from(property.getName(), owner.getTypeInformation()).getTypeInformation(); + this.information = PropertyPath.from(Pattern.quote(property.getName()), owner.getTypeInformation()).getTypeInformation(); this.property = property; this.association = isAssociation() ? Optional.of(createAssociation()) : Optional.empty(); this.owner = owner; diff --git a/src/test/java/org/springframework/data/mapping/PropertyPathUnitTests.java b/src/test/java/org/springframework/data/mapping/PropertyPathUnitTests.java index 743b15c5d..70cd17a6d 100755 --- a/src/test/java/org/springframework/data/mapping/PropertyPathUnitTests.java +++ b/src/test/java/org/springframework/data/mapping/PropertyPathUnitTests.java @@ -21,6 +21,7 @@ import static org.springframework.data.mapping.PropertyPath.*; import java.util.Iterator; import java.util.Map; import java.util.Set; +import java.util.regex.Pattern; import org.junit.Rule; import org.junit.Test; @@ -32,6 +33,7 @@ import org.springframework.data.util.TypeInformation; * Unit tests for {@link PropertyPath}. * * @author Oliver Gierke + * @author Christoph Strobl */ @SuppressWarnings("unused") public class PropertyPathUnitTests { @@ -333,11 +335,22 @@ public class PropertyPathUnitTests { from("userAme", Foo.class); } + @Test // DATACMNS-867 + public void preservesUnderscoresForQuotedNames() { + + PropertyPath path = from(Pattern.quote("var_name_with_underscore"), Foo.class); + + assertThat(path).isNotNull(); + assertThat(path.getSegment()).isEqualTo("var_name_with_underscore"); + assertThat(path.hasNext()).isFalse(); + } + private class Foo { String userName; String _email; String UUID; + String var_name_with_underscore; } private class Bar { diff --git a/src/test/java/org/springframework/data/mapping/model/AbstractPersistentPropertyUnitTests.java b/src/test/java/org/springframework/data/mapping/model/AbstractPersistentPropertyUnitTests.java index 5efd9ce0c..9d3dcbdb6 100755 --- a/src/test/java/org/springframework/data/mapping/model/AbstractPersistentPropertyUnitTests.java +++ b/src/test/java/org/springframework/data/mapping/model/AbstractPersistentPropertyUnitTests.java @@ -43,6 +43,7 @@ import org.springframework.util.ReflectionUtils; * Unit tests for {@link AbstractPersistentProperty}. * * @author Oliver Gierke + * @author Christoph Strobl */ public class AbstractPersistentPropertyUnitTests { @@ -192,6 +193,13 @@ public class AbstractPersistentPropertyUnitTests { assertThat(property.isEntity()).isFalse(); } + @Test // DATACMNS-867 + public void resolvesFieldNameWithUnderscoresCorrectly() { + + SamplePersistentProperty property = getProperty(TestClassComplex.class, "var_name_with_underscores"); + assertThat(property.getName()).isEqualTo("var_name_with_underscores"); + } + private BasicPersistentEntity getEntity(Class type) { return new BasicPersistentEntity<>(ClassTypeInformation.from(type)); } @@ -244,6 +252,7 @@ public class AbstractPersistentPropertyUnitTests { Map map; Collection collection; transient Object transientField; + String var_name_with_underscores; } class AccessorTestClass {