From 17f8a244de04acda9c6cb4d1dac80135a76ab470 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 27 Jun 2016 12:02:34 +0200 Subject: [PATCH] Fix property names with successive capital letters Previously, if a property name had successive capital letters, the generated meta-data would clean it in such a way it is defined as a regular word. For instance a `myFOO` property would be written as `my-foo` in the meta-data. It turns out this decision is wrong as the binder has no way to compute back the name of the property and therefore `my-foo` wouldn't bind to `setMyFOO` as it should. This commit updates the meta-data name generation algorithm to properly identify such cases: `myFOO` now translates to `my-f-o-o`. While the generated name is a bit ugly, it now provides a consistent binding experience. Closes gh-5330 --- .../metadata/ConfigurationMetadata.java | 37 +++++++++---------- .../metadata/ConfigurationMetadataTests.java | 30 ++++++++++----- .../PropertiesConfigurationFactoryTests.java | 17 ++++++++- 3 files changed, 54 insertions(+), 30 deletions(-) diff --git a/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/metadata/ConfigurationMetadata.java b/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/metadata/ConfigurationMetadata.java index c9e7b93f5d..c963cd1612 100644 --- a/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/metadata/ConfigurationMetadata.java +++ b/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/metadata/ConfigurationMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -17,11 +17,10 @@ package org.springframework.boot.configurationprocessor.metadata; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.ListIterator; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.springframework.util.CollectionUtils; import org.springframework.util.LinkedMultiValueMap; @@ -38,7 +37,7 @@ import org.springframework.util.ObjectUtils; */ public class ConfigurationMetadata { - private static final Pattern CAMEL_CASE_PATTERN = Pattern.compile("([^A-Z-])([A-Z])"); + private static final List SEPARATORS = Arrays.asList('-', '_'); private final MultiValueMap items; @@ -160,23 +159,23 @@ public class ConfigurationMetadata { } static String toDashedCase(String name) { - Matcher matcher = CAMEL_CASE_PATTERN.matcher(name); - StringBuffer result = new StringBuffer(); - while (matcher.find()) { - matcher.appendReplacement(result, getDashed(matcher)); - } - matcher.appendTail(result); - return result.toString().toLowerCase(); - } + StringBuilder sb = new StringBuilder(); + Character previous = null; + for (char current : name.toCharArray()) { + if (SEPARATORS.contains(current)) { + sb.append("-"); + } + else if (Character.isUpperCase(current) && previous != null + && !SEPARATORS.contains(previous)) { + sb.append("-").append(current); + } + else { + sb.append(current); + } + previous = current; - private static String getDashed(Matcher matcher) { - String first = matcher.group(1); - String second = matcher.group(2); - if (first.equals("_")) { - // not a word for the binder - return first + second; } - return first + "-" + second; + return sb.toString().toLowerCase(); } private static > List flattenValues( diff --git a/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/metadata/ConfigurationMetadataTests.java b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/metadata/ConfigurationMetadataTests.java index 3f60e7406e..5372586b66 100644 --- a/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/metadata/ConfigurationMetadataTests.java +++ b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/metadata/ConfigurationMetadataTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-2016 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. @@ -33,35 +33,45 @@ public class ConfigurationMetadataTests { assertThat(toDashedCase("simpleCamelCase"), is("simple-camel-case")); } + @Test + public void toDashedCaseUpperCamelCaseSuffix() { + assertThat(toDashedCase("myDLQ"), is("my-d-l-q")); + } + + @Test + public void toDashedCaseUpperCamelCaseMiddle() { + assertThat(toDashedCase("someDLQKey"), is("some-d-l-q-key")); + } + @Test public void toDashedCaseWordsUnderscore() { - assertThat(toDashedCase("Word_With_underscore"), is("word_with_underscore")); + assertThat(toDashedCase("Word_With_underscore"), is("word-with-underscore")); } @Test public void toDashedCaseWordsSeveralUnderscores() { assertThat(toDashedCase("Word___With__underscore"), - is("word___with__underscore")); + is("word---with--underscore")); } @Test public void toDashedCaseLowerCaseUnderscore() { - assertThat(toDashedCase("lower_underscore"), is("lower_underscore")); + assertThat(toDashedCase("lower_underscore"), is("lower-underscore")); } @Test - public void toDashedCaseUpperUnderscore() { - assertThat(toDashedCase("UPPER_UNDERSCORE"), is("upper_underscore")); + public void toDashedCaseUpperUnderscoreSuffix() { + assertThat(toDashedCase("my_DLQ"), is("my-d-l-q")); } @Test - public void toDashedCaseMultipleUnderscores() { - assertThat(toDashedCase("super___crazy"), is("super___crazy")); + public void toDashedCaseUpperUnderscoreMiddle() { + assertThat(toDashedCase("some_DLQ_key"), is("some-d-l-q-key")); } @Test - public void toDashedCaseUppercase() { - assertThat(toDashedCase("UPPERCASE"), is("uppercase")); + public void toDashedCaseMultipleUnderscores() { + assertThat(toDashedCase("super___crazy"), is("super---crazy")); } @Test diff --git a/spring-boot/src/test/java/org/springframework/boot/bind/PropertiesConfigurationFactoryTests.java b/spring-boot/src/test/java/org/springframework/boot/bind/PropertiesConfigurationFactoryTests.java index 49f0392eea..c7a1f1506a 100644 --- a/spring-boot/src/test/java/org/springframework/boot/bind/PropertiesConfigurationFactoryTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/bind/PropertiesConfigurationFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -170,6 +170,12 @@ public class PropertiesConfigurationFactoryTests { assertEquals("baz", foo.fooBarURI); } + @Test + public void propertyWithAllUpperCaseInTheMiddleCanBeBound() throws Exception { + Foo foo = createFoo("foo-d-l-q-bar:baz"); + assertEquals("baz", foo.fooDLQBar); + } + private Foo createFoo(final String values) throws Exception { setupFactory(); return bindFoo(values); @@ -203,6 +209,8 @@ public class PropertiesConfigurationFactoryTests { private String fooBarURI; + private String fooDLQBar; + public String getSpringFooBaz() { return this.spring_foo_baz; } @@ -243,6 +251,13 @@ public class PropertiesConfigurationFactoryTests { this.fooBarURI = fooBarURI; } + public String getFooDLQBar() { + return this.fooDLQBar; + } + + public void setFooDLQBar(String fooDLQBar) { + this.fooDLQBar = fooDLQBar; + } } }