// 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.apache.tapestry5.ioc.internal; import org.apache.tapestry5.ioc.MappedConfiguration; import org.apache.tapestry5.ioc.ObjectLocator; import org.apache.tapestry5.ioc.def.ContributionDef; import java.util.Map; /** * A wrapper around a Map that provides the {@link org.apache.tapestry5.ioc.MappedConfiguration} interface, and provides * two forms of validation for mapped configurations: * <ul> * <li>If either key or value is null, then a warning is logged</li> * <li>If the key has previously been stored (by some other {@link org.apache.tapestry5.ioc.def.ContributionDef}, then a * warning is logged</li> * </ul> * * When a warning is logged, the key/value pair is not added to the delegate. * * Handles instantiation of instances. * * @param <K> * @param <V> */ public class ValidatingMappedConfigurationWrapper<K, V> extends AbstractConfigurationImpl<V> implements MappedConfiguration<K, V> { private final TypeCoercerProxy typeCoercer; private final Map<K, V> map; private final Map<K, MappedConfigurationOverride<K, V>> overrides; private final String serviceId; private final ContributionDef contributionDef; private final Class<K> expectedKeyType; private final Class<V> expectedValueType; private final Map<K, ContributionDef> keyToContributor; public ValidatingMappedConfigurationWrapper(Class<V> expectedValueType, ObjectLocator locator, TypeCoercerProxy typeCoercer, Map<K, V> map, Map<K, MappedConfigurationOverride<K, V>> overrides, String serviceId, ContributionDef contributionDef, Class<K> expectedKeyType, Map<K, ContributionDef> keyToContributor) { super(expectedValueType, locator); this.typeCoercer = typeCoercer; this.map = map; this.overrides = overrides; this.serviceId = serviceId; this.contributionDef = contributionDef; this.expectedKeyType = expectedKeyType; this.expectedValueType = expectedValueType; this.keyToContributor = keyToContributor; } @Override public void add(K key, V value) { validateKey(key); if (value == null) throw new NullPointerException(IOCMessages.contributionWasNull(serviceId)); V coerced = typeCoercer.coerce(value, expectedValueType); ContributionDef existing = keyToContributor.get(key); if (existing != null) throw new IllegalArgumentException(IOCMessages.contributionDuplicateKey(serviceId, key, existing)); map.put(key, coerced); // Remember that this key is provided by this contribution, when looking // for future conflicts. keyToContributor.put(key, contributionDef); } private void validateKey(K key) { if (key == null) throw new NullPointerException(IOCMessages.contributionKeyWasNull(serviceId)); // Key types don't get coerced; not worth the effort, keys are almost always String or Class // anyway. if (!expectedKeyType.isInstance(key)) throw new IllegalArgumentException(IOCMessages.contributionWrongKeyType(serviceId, key.getClass(), expectedKeyType)); } @Override public void addInstance(K key, Class<? extends V> clazz) { add(key, instantiate(clazz)); } @Override public void override(K key, V value) { validateKey(key); V coerced = value == null ? null : typeCoercer.coerce(value, expectedValueType); MappedConfigurationOverride<K, V> existing = overrides.get(key); if (existing != null) throw new IllegalArgumentException(String.format( "Contribution key %s has already been overridden (by %s).", key, existing.getContribDef())); overrides.put(key, new MappedConfigurationOverride<K, V>(contributionDef, map, key, coerced)); } @Override public void overrideInstance(K key, Class<? extends V> clazz) { override(key, instantiate(clazz)); } }