001 // Copyright 2006, 2007, 2008, 2009, 2010, 2011 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.tapestry5.ioc.internal;
016
017 import java.util.Map;
018
019 import org.apache.tapestry5.ioc.MappedConfiguration;
020 import org.apache.tapestry5.ioc.ObjectLocator;
021 import org.apache.tapestry5.ioc.def.ContributionDef;
022
023 /**
024 * A wrapper around a Map that provides the {@link org.apache.tapestry5.ioc.MappedConfiguration} interface, and provides
025 * two forms of validation for mapped configurations:
026 * <ul>
027 * <li>If either key or value is null, then a warning is logged</li>
028 * <li>If the key has previously been stored (by some other {@link org.apache.tapestry5.ioc.def.ContributionDef}, then a
029 * warning is logged</li>
030 * </ul>
031 * <p/>
032 * When a warning is logged, the key/value pair is not added to the delegate.
033 * <p/>
034 * Handles instantiation of instances.
035 *
036 * @param <K>
037 * @param <V>
038 */
039 public class ValidatingMappedConfigurationWrapper<K, V> extends AbstractConfigurationImpl<V> implements
040 MappedConfiguration<K, V>
041 {
042 private final TypeCoercerProxy typeCoercer;
043
044 private final Map<K, V> map;
045
046 private final Map<K, MappedConfigurationOverride<K, V>> overrides;
047
048 private final String serviceId;
049
050 private final ContributionDef contributionDef;
051
052 private final Class<K> expectedKeyType;
053
054 private final Class<V> expectedValueType;
055
056 private final Map<K, ContributionDef> keyToContributor;
057
058 public ValidatingMappedConfigurationWrapper(Class<V> expectedValueType, ObjectLocator locator,
059 TypeCoercerProxy typeCoercer, Map<K, V> map, Map<K, MappedConfigurationOverride<K, V>> overrides,
060 String serviceId, ContributionDef contributionDef, Class<K> expectedKeyType,
061 Map<K, ContributionDef> keyToContributor)
062 {
063 super(expectedValueType, locator);
064
065 this.typeCoercer = typeCoercer;
066 this.map = map;
067 this.overrides = overrides;
068 this.serviceId = serviceId;
069 this.contributionDef = contributionDef;
070 this.expectedKeyType = expectedKeyType;
071 this.expectedValueType = expectedValueType;
072 this.keyToContributor = keyToContributor;
073 }
074
075 public void add(K key, V value)
076 {
077 validateKey(key);
078
079 if (value == null)
080 throw new NullPointerException(IOCMessages.contributionWasNull(serviceId));
081
082 V coerced = typeCoercer.coerce(value, expectedValueType);
083
084 ContributionDef existing = keyToContributor.get(key);
085
086 if (existing != null)
087 throw new IllegalArgumentException(IOCMessages.contributionDuplicateKey(serviceId, existing));
088
089 map.put(key, coerced);
090
091 // Remember that this key is provided by this contribution, when looking
092 // for future conflicts.
093
094 keyToContributor.put(key, contributionDef);
095 }
096
097 private void validateKey(K key)
098 {
099 if (key == null)
100 throw new NullPointerException(IOCMessages.contributionKeyWasNull(serviceId));
101
102 // Key types don't get coerced; not worth the effort, keys are almost always String or Class
103 // anyway.
104
105 if (!expectedKeyType.isInstance(key))
106 throw new IllegalArgumentException(IOCMessages.contributionWrongKeyType(serviceId, key.getClass(),
107 expectedKeyType));
108 }
109
110 public void addInstance(K key, Class<? extends V> clazz)
111 {
112 add(key, instantiate(clazz));
113 }
114
115 public void override(K key, V value)
116 {
117 validateKey(key);
118
119 V coerced = value == null ? null : typeCoercer.coerce(value, expectedValueType);
120
121 MappedConfigurationOverride<K, V> existing = overrides.get(key);
122
123 if (existing != null)
124 throw new IllegalArgumentException(String.format(
125 "Contribution key %s has already been overridden (by %s).", key, existing.getContribDef()));
126
127 overrides.put(key, new MappedConfigurationOverride<K, V>(contributionDef, map, key, coerced));
128 }
129
130 public void overrideInstance(K key, Class<? extends V> clazz)
131 {
132 override(key, instantiate(clazz));
133 }
134 }