001/*
002 * Copyright 2002-2017 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.core.convert.converter;
018
019import java.util.Set;
020
021import org.springframework.core.convert.TypeDescriptor;
022import org.springframework.lang.Nullable;
023import org.springframework.util.Assert;
024
025/**
026 * Generic converter interface for converting between two or more types.
027 *
028 * <p>This is the most flexible of the Converter SPI interfaces, but also the most complex.
029 * It is flexible in that a GenericConverter may support converting between multiple source/target
030 * type pairs (see {@link #getConvertibleTypes()}. In addition, GenericConverter implementations
031 * have access to source/target {@link TypeDescriptor field context} during the type conversion
032 * process. This allows for resolving source and target field metadata such as annotations and
033 * generics information, which can be used to influence the conversion logic.
034 *
035 * <p>This interface should generally not be used when the simpler {@link Converter} or
036 * {@link ConverterFactory} interface is sufficient.
037 *
038 * <p>Implementations may additionally implement {@link ConditionalConverter}.
039 *
040 * @author Keith Donald
041 * @author Juergen Hoeller
042 * @since 3.0
043 * @see TypeDescriptor
044 * @see Converter
045 * @see ConverterFactory
046 * @see ConditionalConverter
047 */
048public interface GenericConverter {
049
050        /**
051         * Return the source and target types that this converter can convert between.
052         * <p>Each entry is a convertible source-to-target type pair.
053         * <p>For {@link ConditionalConverter conditional converters} this method may return
054         * {@code null} to indicate all source-to-target pairs should be considered.
055         */
056        @Nullable
057        Set<ConvertiblePair> getConvertibleTypes();
058
059        /**
060         * Convert the source object to the targetType described by the {@code TypeDescriptor}.
061         * @param source the source object to convert (may be {@code null})
062         * @param sourceType the type descriptor of the field we are converting from
063         * @param targetType the type descriptor of the field we are converting to
064         * @return the converted object
065         */
066        @Nullable
067        Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
068
069
070        /**
071         * Holder for a source-to-target class pair.
072         */
073        final class ConvertiblePair {
074
075                private final Class<?> sourceType;
076
077                private final Class<?> targetType;
078
079                /**
080                 * Create a new source-to-target pair.
081                 * @param sourceType the source type
082                 * @param targetType the target type
083                 */
084                public ConvertiblePair(Class<?> sourceType, Class<?> targetType) {
085                        Assert.notNull(sourceType, "Source type must not be null");
086                        Assert.notNull(targetType, "Target type must not be null");
087                        this.sourceType = sourceType;
088                        this.targetType = targetType;
089                }
090
091                public Class<?> getSourceType() {
092                        return this.sourceType;
093                }
094
095                public Class<?> getTargetType() {
096                        return this.targetType;
097                }
098
099                @Override
100                public boolean equals(@Nullable Object other) {
101                        if (this == other) {
102                                return true;
103                        }
104                        if (other == null || other.getClass() != ConvertiblePair.class) {
105                                return false;
106                        }
107                        ConvertiblePair otherPair = (ConvertiblePair) other;
108                        return (this.sourceType == otherPair.sourceType && this.targetType == otherPair.targetType);
109                }
110
111                @Override
112                public int hashCode() {
113                        return (this.sourceType.hashCode() * 31 + this.targetType.hashCode());
114                }
115
116                @Override
117                public String toString() {
118                        return (this.sourceType.getName() + " -> " + this.targetType.getName());
119                }
120        }
121
122}