Skip to content

Commit c664010

Browse files
committed
Added conversion support for Java 8's ZoneId class and the 'of' method convention
Issue: SPR-1528
1 parent 0c00b0d commit c664010

File tree

5 files changed

+154
-86
lines changed

5 files changed

+154
-86
lines changed

spring-beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
import org.springframework.beans.propertyeditors.URIEditor;
6666
import org.springframework.beans.propertyeditors.URLEditor;
6767
import org.springframework.beans.propertyeditors.UUIDEditor;
68+
import org.springframework.beans.propertyeditors.ZoneIdEditor;
6869
import org.springframework.core.convert.ConversionService;
6970
import org.springframework.core.io.Resource;
7071
import org.springframework.core.io.support.ResourceArrayPropertyEditor;
@@ -84,6 +85,19 @@
8485
*/
8586
public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
8687

88+
private static Class<?> zoneIdClass;
89+
90+
static {
91+
try {
92+
zoneIdClass = PropertyEditorRegistrySupport.class.getClassLoader().loadClass("java.time.ZoneId");
93+
}
94+
catch (ClassNotFoundException ex) {
95+
// Java 8 ZoneId class not available
96+
zoneIdClass = null;
97+
}
98+
}
99+
100+
87101
private ConversionService conversionService;
88102

89103
private boolean defaultEditorsActive = false;
@@ -202,6 +216,9 @@ private void createDefaultEditors() {
202216
this.defaultEditors.put(URI.class, new URIEditor());
203217
this.defaultEditors.put(URL.class, new URLEditor());
204218
this.defaultEditors.put(UUID.class, new UUIDEditor());
219+
if (zoneIdClass != null) {
220+
this.defaultEditors.put(zoneIdClass, new ZoneIdEditor());
221+
}
205222

206223
// Default instances of collection editors.
207224
// Can be overridden by registering custom instances of those as custom editors.

spring-beans/src/main/java/org/springframework/beans/propertyeditors/TimeZoneEditor.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,11 +21,12 @@
2121

2222
/**
2323
* Editor for {@code java.util.TimeZone}, translating timezone IDs into
24-
* TimeZone objects. Does not expose a text representation for TimeZone objects.
24+
* TimeZone objects. Exposed the TimeZone ID as a text representation.
2525
*
2626
* @author Juergen Hoeller
2727
* @since 3.0
2828
* @see java.util.TimeZone
29+
* @see ZoneIdEditor
2930
*/
3031
public class TimeZoneEditor extends PropertyEditorSupport {
3132

@@ -34,13 +35,10 @@ public void setAsText(String text) throws IllegalArgumentException {
3435
setValue(TimeZone.getTimeZone(text));
3536
}
3637

37-
/**
38-
* This implementation returns {@code null} to indicate that
39-
* there is no appropriate text representation.
40-
*/
4138
@Override
4239
public String getAsText() {
43-
return null;
40+
TimeZone value = (TimeZone) getValue();
41+
return (value != null ? value.getID() : "");
4442
}
4543

4644
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.beans.propertyeditors;
18+
19+
import java.beans.PropertyEditorSupport;
20+
import java.time.ZoneId;
21+
22+
/**
23+
* Editor for {@code java.time.ZoneId}, translating zone ID Strings into
24+
* ZoneId objects. Exposed the TimeZone ID as a text representation.
25+
*
26+
* @author Juergen Hoeller
27+
* @since 4.0
28+
* @see java.time.ZoneId
29+
* @see TimeZoneEditor
30+
*/
31+
public class ZoneIdEditor extends PropertyEditorSupport {
32+
33+
@Override
34+
public void setAsText(String text) throws IllegalArgumentException {
35+
setValue(ZoneId.of(text));
36+
}
37+
38+
@Override
39+
public String getAsText() {
40+
ZoneId value = (ZoneId) getValue();
41+
return (value != null ? value.getId() : "");
42+
}
43+
44+
}

spring-core/src/main/java/org/springframework/core/convert/support/ObjectToObjectConverter.java

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,13 +29,13 @@
2929
import org.springframework.util.ReflectionUtils;
3030

3131
/**
32-
* Generic Converter that attempts to convert a source Object to a target type
32+
* Generic converter that attempts to convert a source Object to a target type
3333
* by delegating to methods on the target type.
3434
*
35-
* <p>Calls the static {@code valueOf(sourceType)} method on the target type
36-
* to perform the conversion, if such a method exists. Else calls the target type's
37-
* Constructor that accepts a single sourceType argument, if such a Constructor exists.
38-
* Else throws a ConversionFailedException.
35+
* <p>Calls a static {@code valueOf(sourceType)} or Java 8 style {@code of(sourceType)} method
36+
* on the target type to perform the conversion, if such a method exists. Otherwise, it calls
37+
* the target type's constructor that accepts a single {@code sourceType} argument, if such
38+
* a constructor exists. If neither strategy works, it throws a ConversionFailedException.
3939
*
4040
* @author Keith Donald
4141
* @author Juergen Hoeller
@@ -92,7 +92,11 @@ static boolean hasValueOfMethodOrConstructor(Class<?> clazz, Class<?> sourcePara
9292
}
9393

9494
private static Method getValueOfMethodOn(Class<?> clazz, Class<?> sourceParameterType) {
95-
return ClassUtils.getStaticMethod(clazz, "valueOf", sourceParameterType);
95+
Method method = ClassUtils.getStaticMethod(clazz, "valueOf", sourceParameterType);
96+
if (method == null) {
97+
method = ClassUtils.getStaticMethod(clazz, "of", sourceParameterType);
98+
}
99+
return method;
96100
}
97101

98102
private static Constructor<?> getConstructor(Class<?> clazz, Class<?> sourceParameterType) {

spring-core/src/test/java/org/springframework/core/convert/support/DefaultConversionTests.java

Lines changed: 77 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,10 @@
1616

1717
package org.springframework.core.convert.support;
1818

19-
import static org.hamcrest.Matchers.equalTo;
20-
import static org.junit.Assert.*;
21-
import static org.junit.Assert.assertArrayEquals;
22-
import static org.junit.Assert.assertEquals;
23-
import static org.junit.Assert.assertFalse;
24-
import static org.junit.Assert.assertNull;
25-
import static org.junit.Assert.assertSame;
26-
import static org.junit.Assert.assertThat;
27-
import static org.junit.Assert.assertTrue;
28-
2919
import java.awt.Color;
3020
import java.math.BigDecimal;
3121
import java.math.BigInteger;
22+
import java.time.ZoneId;
3223
import java.util.AbstractList;
3324
import java.util.ArrayList;
3425
import java.util.Arrays;
@@ -45,13 +36,17 @@
4536
import java.util.Set;
4637

4738
import org.junit.Test;
39+
4840
import org.springframework.core.MethodParameter;
4941
import org.springframework.core.convert.ConversionFailedException;
5042
import org.springframework.core.convert.ConverterNotFoundException;
5143
import org.springframework.core.convert.TypeDescriptor;
5244
import org.springframework.core.convert.converter.Converter;
5345
import org.springframework.core.convert.converter.ConverterRegistry;
5446

47+
import static org.hamcrest.Matchers.*;
48+
import static org.junit.Assert.*;
49+
5550
/**
5651
* @author Keith Donald
5752
* @author Juergen Hoeller
@@ -709,6 +704,11 @@ public void convertObjectToStringStringConstructorPresent() {
709704
assertEquals("123456789", conversionService.convert(new SSN("123456789"), String.class));
710705
}
711706

707+
@Test
708+
public void convertObjectToStringWithJavaTimeOfMethodPresent() {
709+
assertTrue(conversionService.convert(ZoneId.of("GMT+1"), String.class).startsWith("GMT+"));
710+
}
711+
712712
@Test
713713
public void convertObjectToStringNotSupported() {
714714
assertFalse(conversionService.canConvert(TestEntity.class, String.class));
@@ -725,73 +725,16 @@ public void convertObjectToObjectConstructor() {
725725
assertEquals("123456789", conversionService.convert(new SSN("123456789"), String.class));
726726
}
727727

728+
@Test
729+
public void convertObjectToObjectWithJavaTimeOfMethod() {
730+
assertEquals(ZoneId.of("GMT+1"), conversionService.convert("GMT+1", ZoneId.class));
731+
}
732+
728733
@Test(expected=ConverterNotFoundException.class)
729734
public void convertObjectToObjectNoValueOFMethodOrConstructor() {
730735
conversionService.convert(new Long(3), SSN.class);
731736
}
732737

733-
public Object assignableTarget;
734-
735-
private static class SSN {
736-
737-
private String value;
738-
739-
public SSN(String value) {
740-
this.value = value;
741-
}
742-
743-
@Override
744-
public boolean equals(Object o) {
745-
if (!(o instanceof SSN)) {
746-
return false;
747-
}
748-
SSN ssn = (SSN) o;
749-
return this.value.equals(ssn.value);
750-
}
751-
752-
@Override
753-
public int hashCode() {
754-
return value.hashCode();
755-
}
756-
757-
@Override
758-
public String toString() {
759-
return value;
760-
}
761-
}
762-
763-
private static class ISBN {
764-
765-
private String value;
766-
767-
private ISBN(String value) {
768-
this.value = value;
769-
}
770-
771-
@Override
772-
public boolean equals(Object o) {
773-
if (!(o instanceof ISBN)) {
774-
return false;
775-
}
776-
ISBN isbn = (ISBN) o;
777-
return this.value.equals(isbn.value);
778-
}
779-
780-
@Override
781-
public int hashCode() {
782-
return value.hashCode();
783-
}
784-
785-
@Override
786-
public String toString() {
787-
return value;
788-
}
789-
790-
public static ISBN valueOf(String value) {
791-
return new ISBN(value);
792-
}
793-
}
794-
795738
@Test
796739
public void convertObjectToObjectFinderMethod() {
797740
TestEntity e = conversionService.convert(1L, TestEntity.class);
@@ -864,4 +807,66 @@ public List<?> getList() {
864807
}
865808
}
866809

810+
public Object assignableTarget;
811+
812+
private static class SSN {
813+
814+
private String value;
815+
816+
public SSN(String value) {
817+
this.value = value;
818+
}
819+
820+
@Override
821+
public boolean equals(Object o) {
822+
if (!(o instanceof SSN)) {
823+
return false;
824+
}
825+
SSN ssn = (SSN) o;
826+
return this.value.equals(ssn.value);
827+
}
828+
829+
@Override
830+
public int hashCode() {
831+
return value.hashCode();
832+
}
833+
834+
@Override
835+
public String toString() {
836+
return value;
837+
}
838+
}
839+
840+
private static class ISBN {
841+
842+
private String value;
843+
844+
private ISBN(String value) {
845+
this.value = value;
846+
}
847+
848+
@Override
849+
public boolean equals(Object o) {
850+
if (!(o instanceof ISBN)) {
851+
return false;
852+
}
853+
ISBN isbn = (ISBN) o;
854+
return this.value.equals(isbn.value);
855+
}
856+
857+
@Override
858+
public int hashCode() {
859+
return value.hashCode();
860+
}
861+
862+
@Override
863+
public String toString() {
864+
return value;
865+
}
866+
867+
public static ISBN valueOf(String value) {
868+
return new ISBN(value);
869+
}
870+
}
871+
867872
}

0 commit comments

Comments
 (0)