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 015package org.apache.tapestry5.ioc.internal; 016 017import java.util.Map; 018 019import org.apache.tapestry5.ioc.MappedConfiguration; 020import org.apache.tapestry5.ioc.ObjectLocator; 021import 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 */ 039public 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 @Override 076 public void add(K key, V value) 077 { 078 validateKey(key); 079 080 if (value == null) 081 throw new NullPointerException(IOCMessages.contributionWasNull(serviceId)); 082 083 V coerced = typeCoercer.coerce(value, expectedValueType); 084 085 ContributionDef existing = keyToContributor.get(key); 086 087 if (existing != null) 088 throw new IllegalArgumentException(IOCMessages.contributionDuplicateKey(serviceId, key, existing)); 089 090 map.put(key, coerced); 091 092 // Remember that this key is provided by this contribution, when looking 093 // for future conflicts. 094 095 keyToContributor.put(key, contributionDef); 096 } 097 098 private void validateKey(K key) 099 { 100 if (key == null) 101 throw new NullPointerException(IOCMessages.contributionKeyWasNull(serviceId)); 102 103 // Key types don't get coerced; not worth the effort, keys are almost always String or Class 104 // anyway. 105 106 if (!expectedKeyType.isInstance(key)) 107 throw new IllegalArgumentException(IOCMessages.contributionWrongKeyType(serviceId, key.getClass(), 108 expectedKeyType)); 109 } 110 111 @Override 112 public void addInstance(K key, Class<? extends V> clazz) 113 { 114 add(key, instantiate(clazz)); 115 } 116 117 @Override 118 public void override(K key, V value) 119 { 120 validateKey(key); 121 122 V coerced = value == null ? null : typeCoercer.coerce(value, expectedValueType); 123 124 MappedConfigurationOverride<K, V> existing = overrides.get(key); 125 126 if (existing != null) 127 throw new IllegalArgumentException(String.format( 128 "Contribution key %s has already been overridden (by %s).", key, existing.getContribDef())); 129 130 overrides.put(key, new MappedConfigurationOverride<K, V>(contributionDef, map, key, coerced)); 131 } 132 133 @Override 134 public void overrideInstance(K key, Class<? extends V> clazz) 135 { 136 override(key, instantiate(clazz)); 137 } 138}