Skip to content

Commit 9dba73d

Browse files
committed
Add ConversionService support for ByteBuffers
Add ByteBufferConverter that is registered by default with the DefaultConversionService. Allows conversion from/to a ByteBuffer and byte[] or to any type that can be converted via a byte[]. Issue: SPR-10712
1 parent 638ba3f commit 9dba73d

File tree

3 files changed

+229
-0
lines changed

3 files changed

+229
-0
lines changed
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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.core.convert.support;
18+
19+
import java.nio.ByteBuffer;
20+
import java.util.Collections;
21+
import java.util.HashSet;
22+
import java.util.Set;
23+
24+
import org.springframework.core.convert.ConversionService;
25+
import org.springframework.core.convert.TypeDescriptor;
26+
import org.springframework.core.convert.converter.ConditionalGenericConverter;
27+
28+
/**
29+
* Converts a {@link ByteBuffer} directly to and from {@code byte[]}s and indirectly to
30+
* any type that the {@link ConversionService} support via {@code byte[]}.
31+
*
32+
* @author Phillip Webb
33+
*/
34+
public class ByteBufferConverter implements ConditionalGenericConverter {
35+
36+
private static final TypeDescriptor BYTE_BUFFER_TYPE = TypeDescriptor.valueOf(ByteBuffer.class);
37+
38+
private static final TypeDescriptor BYTE_ARRAY_TYPE = TypeDescriptor.valueOf(byte[].class);
39+
40+
private static final Set<ConvertiblePair> CONVERTIBLE_PAIRS;
41+
static {
42+
Set<ConvertiblePair> convertiblePairs = new HashSet<ConvertiblePair>();
43+
convertiblePairs.add(new ConvertiblePair(ByteBuffer.class, Object.class));
44+
convertiblePairs.add(new ConvertiblePair(Object.class, ByteBuffer.class));
45+
CONVERTIBLE_PAIRS = Collections.unmodifiableSet(convertiblePairs);
46+
}
47+
48+
49+
private ConversionService conversionService;
50+
51+
52+
public ByteBufferConverter(ConversionService conversionService) {
53+
this.conversionService = conversionService;
54+
}
55+
56+
57+
@Override
58+
public Set<ConvertiblePair> getConvertibleTypes() {
59+
return CONVERTIBLE_PAIRS;
60+
}
61+
62+
@Override
63+
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
64+
if (sourceType.isAssignableTo(BYTE_BUFFER_TYPE)) {
65+
return matchesFromByteBuffer(targetType);
66+
}
67+
if (targetType.isAssignableTo(BYTE_BUFFER_TYPE)) {
68+
return matchesToByteBuffer(sourceType);
69+
}
70+
return false;
71+
}
72+
73+
private boolean matchesFromByteBuffer(TypeDescriptor targetType) {
74+
return (targetType.isAssignableTo(BYTE_ARRAY_TYPE) || this.conversionService.canConvert(
75+
BYTE_ARRAY_TYPE, targetType));
76+
}
77+
78+
private boolean matchesToByteBuffer(TypeDescriptor sourceType) {
79+
return (sourceType.isAssignableTo(BYTE_ARRAY_TYPE) || this.conversionService.canConvert(
80+
sourceType, BYTE_ARRAY_TYPE));
81+
}
82+
83+
@Override
84+
public Object convert(Object source, TypeDescriptor sourceType,
85+
TypeDescriptor targetType) {
86+
if (sourceType.isAssignableTo(BYTE_BUFFER_TYPE)) {
87+
return convertFromByteBuffer((ByteBuffer) source, targetType);
88+
}
89+
if (targetType.isAssignableTo(BYTE_BUFFER_TYPE)) {
90+
return convertToByteBuffer(source, sourceType);
91+
}
92+
// Should not happen
93+
throw new IllegalStateException("Unexpected source/target types");
94+
}
95+
96+
private Object convertFromByteBuffer(ByteBuffer source, TypeDescriptor targetType) {
97+
byte[] bytes = new byte[source.remaining()];
98+
source.get(bytes);
99+
if (targetType.isAssignableTo(BYTE_ARRAY_TYPE)) {
100+
return bytes;
101+
}
102+
return this.conversionService.convert(bytes, BYTE_ARRAY_TYPE, targetType);
103+
}
104+
105+
private Object convertToByteBuffer(Object source, TypeDescriptor sourceType) {
106+
byte[] bytes = (byte[]) (source instanceof byte[] ? source
107+
: this.conversionService.convert(source, sourceType, BYTE_ARRAY_TYPE));
108+
ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length);
109+
byteBuffer.put(bytes);
110+
byteBuffer.rewind();
111+
return byteBuffer;
112+
}
113+
114+
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public DefaultConversionService() {
5353
public static void addDefaultConverters(ConverterRegistry converterRegistry) {
5454
addScalarConverters(converterRegistry);
5555
addCollectionConverters(converterRegistry);
56+
addBinaryConverters(converterRegistry);
5657
addFallbackConverters(converterRegistry);
5758
}
5859

@@ -109,6 +110,11 @@ private static void addCollectionConverters(ConverterRegistry converterRegistry)
109110
converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService));
110111
}
111112

113+
private static void addBinaryConverters(ConverterRegistry converterRegistry) {
114+
ConversionService conversionService = (ConversionService) converterRegistry;
115+
converterRegistry.addConverter(new ByteBufferConverter(conversionService));
116+
}
117+
112118
private static void addFallbackConverters(ConverterRegistry converterRegistry) {
113119
ConversionService conversionService = (ConversionService) converterRegistry;
114120
converterRegistry.addConverter(new ObjectToObjectConverter());
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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.core.convert.support;
18+
19+
import java.nio.ByteBuffer;
20+
21+
import org.junit.Before;
22+
import org.junit.Test;
23+
import org.springframework.core.convert.converter.Converter;
24+
25+
import static org.hamcrest.Matchers.*;
26+
import static org.junit.Assert.*;
27+
28+
/**
29+
* Tests for {@link ByteBufferConverter}.
30+
*
31+
* @author Phillip Webb
32+
*/
33+
public class ByteBufferConverterTests {
34+
35+
private GenericConversionService conversionService;
36+
37+
@Before
38+
public void setup() {
39+
this.conversionService = new GenericConversionService();
40+
this.conversionService.addConverter(new ByteBufferConverter(conversionService));
41+
this.conversionService.addConverter(new ByteArrayToOtherTypeConverter());
42+
this.conversionService.addConverter(new OtherTypeToByteArrayConverter());
43+
}
44+
45+
@Test
46+
public void byteArrayToByteBuffer() throws Exception {
47+
byte[] bytes = new byte[] { 1, 2, 3 };
48+
ByteBuffer convert = this.conversionService.convert(bytes, ByteBuffer.class);
49+
assertThat(bytes, not(sameInstance(convert.array())));
50+
assertThat(bytes, equalTo(convert.array()));
51+
}
52+
53+
@Test
54+
public void byteBufferToByteArray() throws Exception {
55+
byte[] bytes = new byte[] { 1, 2, 3 };
56+
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
57+
byte[] convert = this.conversionService.convert(byteBuffer, byte[].class);
58+
assertThat(bytes, not(sameInstance(convert)));
59+
assertThat(bytes, equalTo(convert));
60+
}
61+
62+
@Test
63+
public void byteBufferToOtherType() throws Exception {
64+
byte[] bytes = new byte[] { 1, 2, 3 };
65+
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
66+
OtherType convert = this.conversionService.convert(byteBuffer, OtherType.class);
67+
assertThat(bytes, not(sameInstance(convert.bytes)));
68+
assertThat(bytes, equalTo(convert.bytes));
69+
}
70+
71+
@Test
72+
public void otherTypeToByteBuffer() throws Exception {
73+
byte[] bytes = new byte[] { 1, 2, 3 };
74+
OtherType otherType = new OtherType(bytes);
75+
ByteBuffer convert = this.conversionService.convert(otherType, ByteBuffer.class);
76+
assertThat(bytes, not(sameInstance(convert.array())));
77+
assertThat(bytes, equalTo(convert.array()));
78+
}
79+
80+
private static class OtherType {
81+
82+
private byte[] bytes;
83+
84+
public OtherType(byte[] bytes) {
85+
this.bytes = bytes;
86+
}
87+
88+
}
89+
90+
private static class ByteArrayToOtherTypeConverter implements
91+
Converter<byte[], OtherType> {
92+
93+
@Override
94+
public OtherType convert(byte[] source) {
95+
return new OtherType(source);
96+
}
97+
}
98+
99+
private static class OtherTypeToByteArrayConverter implements
100+
Converter<OtherType, byte[]> {
101+
102+
@Override
103+
public byte[] convert(OtherType source) {
104+
return source.bytes;
105+
}
106+
107+
}
108+
109+
}

0 commit comments

Comments
 (0)