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.beans.factory.config;
018
019import java.util.LinkedHashMap;
020import java.util.Map;
021
022import org.springframework.beans.BeanUtils;
023import org.springframework.beans.TypeConverter;
024import org.springframework.core.ResolvableType;
025
026/**
027 * Simple factory for shared Map instances. Allows for central setup
028 * of Maps via the "map" element in XML bean definitions.
029 *
030 * @author Juergen Hoeller
031 * @since 09.12.2003
032 * @see SetFactoryBean
033 * @see ListFactoryBean
034 */
035public class MapFactoryBean extends AbstractFactoryBean<Map<Object, Object>> {
036
037        private Map<?, ?> sourceMap;
038
039        @SuppressWarnings("rawtypes")
040        private Class<? extends Map> targetMapClass;
041
042
043        /**
044         * Set the source Map, typically populated via XML "map" elements.
045         */
046        public void setSourceMap(Map<?, ?> sourceMap) {
047                this.sourceMap = sourceMap;
048        }
049
050        /**
051         * Set the class to use for the target Map. Can be populated with a fully
052         * qualified class name when defined in a Spring application context.
053         * <p>Default is a linked HashMap, keeping the registration order.
054         * @see java.util.LinkedHashMap
055         */
056        @SuppressWarnings("rawtypes")
057        public void setTargetMapClass(Class<? extends Map> targetMapClass) {
058                if (targetMapClass == null) {
059                        throw new IllegalArgumentException("'targetMapClass' must not be null");
060                }
061                if (!Map.class.isAssignableFrom(targetMapClass)) {
062                        throw new IllegalArgumentException("'targetMapClass' must implement [java.util.Map]");
063                }
064                this.targetMapClass = targetMapClass;
065        }
066
067
068        @Override
069        @SuppressWarnings("rawtypes")
070        public Class<Map> getObjectType() {
071                return Map.class;
072        }
073
074        @Override
075        @SuppressWarnings("unchecked")
076        protected Map<Object, Object> createInstance() {
077                if (this.sourceMap == null) {
078                        throw new IllegalArgumentException("'sourceMap' is required");
079                }
080                Map<Object, Object> result = null;
081                if (this.targetMapClass != null) {
082                        result = BeanUtils.instantiateClass(this.targetMapClass);
083                }
084                else {
085                        result = new LinkedHashMap<Object, Object>(this.sourceMap.size());
086                }
087                Class<?> keyType = null;
088                Class<?> valueType = null;
089                if (this.targetMapClass != null) {
090                        ResolvableType mapType = ResolvableType.forClass(this.targetMapClass).asMap();
091                        keyType = mapType.resolveGeneric(0);
092                        valueType = mapType.resolveGeneric(1);
093                }
094                if (keyType != null || valueType != null) {
095                        TypeConverter converter = getBeanTypeConverter();
096                        for (Map.Entry<?, ?> entry : this.sourceMap.entrySet()) {
097                                Object convertedKey = converter.convertIfNecessary(entry.getKey(), keyType);
098                                Object convertedValue = converter.convertIfNecessary(entry.getValue(), valueType);
099                                result.put(convertedKey, convertedValue);
100                        }
101                }
102                else {
103                        result.putAll(this.sourceMap);
104                }
105                return result;
106        }
107
108}