diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/ByteBufferConverter.java b/spring-core/src/main/java/org/springframework/core/convert/support/ByteBufferConverter.java new file mode 100644 index 00000000000..d58a8f9bc4d --- /dev/null +++ b/spring-core/src/main/java/org/springframework/core/convert/support/ByteBufferConverter.java @@ -0,0 +1,114 @@ +/* + * Copyright 2002-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.core.convert.support; + +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.converter.ConditionalGenericConverter; + +/** + * Converts a {@link ByteBuffer} directly to and from {@code byte[]}s and indirectly to + * any type that the {@link ConversionService} support via {@code byte[]}. + * + * @author Phillip Webb + */ +public class ByteBufferConverter implements ConditionalGenericConverter { + + private static final TypeDescriptor BYTE_BUFFER_TYPE = TypeDescriptor.valueOf(ByteBuffer.class); + + private static final TypeDescriptor BYTE_ARRAY_TYPE = TypeDescriptor.valueOf(byte[].class); + + private static final Set CONVERTIBLE_PAIRS; + static { + Set convertiblePairs = new HashSet(); + convertiblePairs.add(new ConvertiblePair(ByteBuffer.class, Object.class)); + convertiblePairs.add(new ConvertiblePair(Object.class, ByteBuffer.class)); + CONVERTIBLE_PAIRS = Collections.unmodifiableSet(convertiblePairs); + } + + + private ConversionService conversionService; + + + public ByteBufferConverter(ConversionService conversionService) { + this.conversionService = conversionService; + } + + + @Override + public Set getConvertibleTypes() { + return CONVERTIBLE_PAIRS; + } + + @Override + public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { + if (sourceType.isAssignableTo(BYTE_BUFFER_TYPE)) { + return matchesFromByteBuffer(targetType); + } + if (targetType.isAssignableTo(BYTE_BUFFER_TYPE)) { + return matchesToByteBuffer(sourceType); + } + return false; + } + + private boolean matchesFromByteBuffer(TypeDescriptor targetType) { + return (targetType.isAssignableTo(BYTE_ARRAY_TYPE) || this.conversionService.canConvert( + BYTE_ARRAY_TYPE, targetType)); + } + + private boolean matchesToByteBuffer(TypeDescriptor sourceType) { + return (sourceType.isAssignableTo(BYTE_ARRAY_TYPE) || this.conversionService.canConvert( + sourceType, BYTE_ARRAY_TYPE)); + } + + @Override + public Object convert(Object source, TypeDescriptor sourceType, + TypeDescriptor targetType) { + if (sourceType.isAssignableTo(BYTE_BUFFER_TYPE)) { + return convertFromByteBuffer((ByteBuffer) source, targetType); + } + if (targetType.isAssignableTo(BYTE_BUFFER_TYPE)) { + return convertToByteBuffer(source, sourceType); + } + // Should not happen + throw new IllegalStateException("Unexpected source/target types"); + } + + private Object convertFromByteBuffer(ByteBuffer source, TypeDescriptor targetType) { + byte[] bytes = new byte[source.remaining()]; + source.get(bytes); + if (targetType.isAssignableTo(BYTE_ARRAY_TYPE)) { + return bytes; + } + return this.conversionService.convert(bytes, BYTE_ARRAY_TYPE, targetType); + } + + private Object convertToByteBuffer(Object source, TypeDescriptor sourceType) { + byte[] bytes = (byte[]) (source instanceof byte[] ? source + : this.conversionService.convert(source, sourceType, BYTE_ARRAY_TYPE)); + ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length); + byteBuffer.put(bytes); + byteBuffer.rewind(); + return byteBuffer; + } + +} diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java b/spring-core/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java index 86e580983b3..8ef74e18fce 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java @@ -53,6 +53,7 @@ public class DefaultConversionService extends GenericConversionService { public static void addDefaultConverters(ConverterRegistry converterRegistry) { addScalarConverters(converterRegistry); addCollectionConverters(converterRegistry); + addBinaryConverters(converterRegistry); addFallbackConverters(converterRegistry); } @@ -109,6 +110,11 @@ public class DefaultConversionService extends GenericConversionService { converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService)); } + private static void addBinaryConverters(ConverterRegistry converterRegistry) { + ConversionService conversionService = (ConversionService) converterRegistry; + converterRegistry.addConverter(new ByteBufferConverter(conversionService)); + } + private static void addFallbackConverters(ConverterRegistry converterRegistry) { ConversionService conversionService = (ConversionService) converterRegistry; converterRegistry.addConverter(new ObjectToObjectConverter()); diff --git a/spring-core/src/test/java/org/springframework/core/convert/support/ByteBufferConverterTests.java b/spring-core/src/test/java/org/springframework/core/convert/support/ByteBufferConverterTests.java new file mode 100644 index 00000000000..87b3db4dcc8 --- /dev/null +++ b/spring-core/src/test/java/org/springframework/core/convert/support/ByteBufferConverterTests.java @@ -0,0 +1,109 @@ +/* + * Copyright 2002-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.core.convert.support; + +import java.nio.ByteBuffer; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.core.convert.converter.Converter; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +/** + * Tests for {@link ByteBufferConverter}. + * + * @author Phillip Webb + */ +public class ByteBufferConverterTests { + + private GenericConversionService conversionService; + + @Before + public void setup() { + this.conversionService = new GenericConversionService(); + this.conversionService.addConverter(new ByteBufferConverter(conversionService)); + this.conversionService.addConverter(new ByteArrayToOtherTypeConverter()); + this.conversionService.addConverter(new OtherTypeToByteArrayConverter()); + } + + @Test + public void byteArrayToByteBuffer() throws Exception { + byte[] bytes = new byte[] { 1, 2, 3 }; + ByteBuffer convert = this.conversionService.convert(bytes, ByteBuffer.class); + assertThat(bytes, not(sameInstance(convert.array()))); + assertThat(bytes, equalTo(convert.array())); + } + + @Test + public void byteBufferToByteArray() throws Exception { + byte[] bytes = new byte[] { 1, 2, 3 }; + ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); + byte[] convert = this.conversionService.convert(byteBuffer, byte[].class); + assertThat(bytes, not(sameInstance(convert))); + assertThat(bytes, equalTo(convert)); + } + + @Test + public void byteBufferToOtherType() throws Exception { + byte[] bytes = new byte[] { 1, 2, 3 }; + ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); + OtherType convert = this.conversionService.convert(byteBuffer, OtherType.class); + assertThat(bytes, not(sameInstance(convert.bytes))); + assertThat(bytes, equalTo(convert.bytes)); + } + + @Test + public void otherTypeToByteBuffer() throws Exception { + byte[] bytes = new byte[] { 1, 2, 3 }; + OtherType otherType = new OtherType(bytes); + ByteBuffer convert = this.conversionService.convert(otherType, ByteBuffer.class); + assertThat(bytes, not(sameInstance(convert.array()))); + assertThat(bytes, equalTo(convert.array())); + } + + private static class OtherType { + + private byte[] bytes; + + public OtherType(byte[] bytes) { + this.bytes = bytes; + } + + } + + private static class ByteArrayToOtherTypeConverter implements + Converter { + + @Override + public OtherType convert(byte[] source) { + return new OtherType(source); + } + } + + private static class OtherTypeToByteArrayConverter implements + Converter { + + @Override + public byte[] convert(OtherType source) { + return source.bytes; + } + + } + +}