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 }