001/*
002 * Copyright 2002-2018 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.support;
018
019import java.util.LinkedHashMap;
020import java.util.Map;
021
022import org.springframework.beans.BeanMetadataElement;
023import org.springframework.beans.Mergeable;
024import org.springframework.lang.Nullable;
025
026/**
027 * Tag collection class used to hold managed Map values, which may
028 * include runtime bean references (to be resolved into bean objects).
029 *
030 * @author Juergen Hoeller
031 * @author Rob Harrop
032 * @since 27.05.2003
033 * @param <K> the key type
034 * @param <V> the value type
035 */
036@SuppressWarnings("serial")
037public class ManagedMap<K, V> extends LinkedHashMap<K, V> implements Mergeable, BeanMetadataElement {
038
039        @Nullable
040        private Object source;
041
042        @Nullable
043        private String keyTypeName;
044
045        @Nullable
046        private String valueTypeName;
047
048        private boolean mergeEnabled;
049
050
051        public ManagedMap() {
052        }
053
054        public ManagedMap(int initialCapacity) {
055                super(initialCapacity);
056        }
057
058
059        /**
060         * Set the configuration source {@code Object} for this metadata element.
061         * <p>The exact type of the object will depend on the configuration mechanism used.
062         */
063        public void setSource(@Nullable Object source) {
064                this.source = source;
065        }
066
067        @Override
068        @Nullable
069        public Object getSource() {
070                return this.source;
071        }
072
073        /**
074         * Set the default key type name (class name) to be used for this map.
075         */
076        public void setKeyTypeName(@Nullable String keyTypeName) {
077                this.keyTypeName = keyTypeName;
078        }
079
080        /**
081         * Return the default key type name (class name) to be used for this map.
082         */
083        @Nullable
084        public String getKeyTypeName() {
085                return this.keyTypeName;
086        }
087
088        /**
089         * Set the default value type name (class name) to be used for this map.
090         */
091        public void setValueTypeName(@Nullable String valueTypeName) {
092                this.valueTypeName = valueTypeName;
093        }
094
095        /**
096         * Return the default value type name (class name) to be used for this map.
097         */
098        @Nullable
099        public String getValueTypeName() {
100                return this.valueTypeName;
101        }
102
103        /**
104         * Set whether merging should be enabled for this collection,
105         * in case of a 'parent' collection value being present.
106         */
107        public void setMergeEnabled(boolean mergeEnabled) {
108                this.mergeEnabled = mergeEnabled;
109        }
110
111        @Override
112        public boolean isMergeEnabled() {
113                return this.mergeEnabled;
114        }
115
116        @Override
117        @SuppressWarnings("unchecked")
118        public Object merge(@Nullable Object parent) {
119                if (!this.mergeEnabled) {
120                        throw new IllegalStateException("Not allowed to merge when the 'mergeEnabled' property is set to 'false'");
121                }
122                if (parent == null) {
123                        return this;
124                }
125                if (!(parent instanceof Map)) {
126                        throw new IllegalArgumentException("Cannot merge with object of type [" + parent.getClass() + "]");
127                }
128                Map<K, V> merged = new ManagedMap<>();
129                merged.putAll((Map<K, V>) parent);
130                merged.putAll(this);
131                return merged;
132        }
133
134}