// 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));
}
}