/** * == @Spearal ==> * * Copyright (C) 2014 Franck WOLFF & William DRAI (http://www.spearal.io) * * 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.spearal.impl; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import org.spearal.SpearalContext; import org.spearal.SpearalPropertyFilter; import org.spearal.configuration.AliasStrategy; import org.spearal.configuration.CoderProvider; import org.spearal.configuration.CoderProvider.Coder; import org.spearal.configuration.Configurable; import org.spearal.configuration.ConverterProvider; import org.spearal.configuration.ConverterProvider.Converter; import org.spearal.configuration.FilteredBeanDescriptorFactory; import org.spearal.configuration.FilteredBeanDescriptorFactory.FilteredBeanDescriptor; import org.spearal.configuration.Introspector; import org.spearal.configuration.PartialObjectFactory; import org.spearal.configuration.PartialObjectFactory.PartialObjectProxy; import org.spearal.configuration.PropertyFactory; import org.spearal.configuration.PropertyFactory.Property; import org.spearal.configuration.PropertyInstantiatorProvider; import org.spearal.configuration.PropertyInstantiatorProvider.PropertyInstantiator; import org.spearal.configuration.Repeatable; import org.spearal.configuration.Securizer; import org.spearal.configuration.TypeInstantiatorProvider; import org.spearal.configuration.TypeInstantiatorProvider.TypeInstantiator; import org.spearal.configuration.TypeLoader; import org.spearal.configuration.UnfilterablePropertiesProvider; import org.spearal.impl.cache.AnyMap.ValueProvider; import org.spearal.impl.cache.CopyOnWriteDualIdentityMap; import org.spearal.impl.cache.CopyOnWriteMap; import org.spearal.impl.cache.DualIdentityMap; /** * @author Franck WOLFF * @author William DRAI */ public class SpearalContextImpl implements SpearalContext { private Introspector introspector; private TypeLoader loader; private Securizer securizer; private PartialObjectFactory partialObjectFactory; private AliasStrategy aliasStrategy; private final List<TypeInstantiatorProvider> typeInstantiatorProviders; private final CopyOnWriteMap<Type, Object, TypeInstantiator> typeInstantiatorsCache; private final List<PropertyInstantiatorProvider> propertyInstantiatorProviders; private final CopyOnWriteMap<Property, Object, PropertyInstantiator> propertyInstantiatorsCache; private final List<ConverterProvider> converterProviders; private final CopyOnWriteDualIdentityMap<Class<?>, Type, Converter<?>> convertersCache; private final List<CoderProvider> coderProviders; private final CopyOnWriteMap<Class<?>, Object, Coder> codersCache; private final List<UnfilterablePropertiesProvider> unfilterablePropertiesProviders; private final CopyOnWriteMap<Class<?>, Object, String[]> unfilterablePropertiesCache; private final List<PropertyFactory> propertyFactories; private final List<FilteredBeanDescriptorFactory> descriptorFactories; private static final String[] EMPTY_STRING_ARRAY = new String[] {}; public SpearalContextImpl() { this.typeInstantiatorProviders = new ArrayList<TypeInstantiatorProvider>(); this.typeInstantiatorsCache = new CopyOnWriteMap<Type, Object, TypeInstantiator>(true, new ValueProvider<Type, Object, TypeInstantiator>() { @Override public TypeInstantiator createValue(SpearalContext context, Type key, Object unused) { for (TypeInstantiatorProvider provider : typeInstantiatorProviders) { TypeInstantiator instantiator = provider.getInstantiator(key); if (instantiator != null) return instantiator; } throw new RuntimeException("Could not find any instantiator for: " + key); } } ); this.propertyInstantiatorProviders = new ArrayList<PropertyInstantiatorProvider>(); this.propertyInstantiatorsCache = new CopyOnWriteMap<Property, Object, PropertyInstantiator>(false, new ValueProvider<Property, Object, PropertyInstantiator>() { @Override public PropertyInstantiator createValue(SpearalContext context, Property key, Object unused) { for (PropertyInstantiatorProvider provider : propertyInstantiatorProviders) { PropertyInstantiator instantiator = provider.getInstantiator(key); if (instantiator != null) return instantiator; } throw new RuntimeException("Could not find any instantiator for: " + key); } } ); this.converterProviders = new ArrayList<ConverterProvider>(); this.convertersCache = new CopyOnWriteDualIdentityMap<Class<?>, Type, Converter<?>>( new DualIdentityMap.ValueProvider<Class<?>, Type, Converter<?>>() { @Override public Converter<?> createValue(SpearalContext context, Class<?> valueClass, Type targetType) { for (ConverterProvider provider : converterProviders) { Converter<?> converter = provider.getConverter(valueClass, targetType); if (converter != null) return converter; } throw new RuntimeException("No converter found from: " + valueClass + " to: " + targetType); } } ); this.coderProviders = new ArrayList<CoderProvider>(); this.codersCache = new CopyOnWriteMap<Class<?>, Object, Coder>(true, new ValueProvider<Class<?>, Object, Coder>() { @Override public Coder createValue(SpearalContext context, Class<?> key, Object param) { for (CoderProvider provider : coderProviders) { Coder coder = provider.getCoder(key); if (coder != null) return coder; } throw new RuntimeException("Not coder found for type: " + key); } } ); this.unfilterablePropertiesProviders = new ArrayList<UnfilterablePropertiesProvider>(); this.unfilterablePropertiesCache = new CopyOnWriteMap<Class<?>, Object, String[]>(true, new ValueProvider<Class<?>, Object, String[]>() { @Override public String[] createValue(SpearalContext context, Class<?> key, Object unused) { for (UnfilterablePropertiesProvider unfilterablePropertiesProvider : unfilterablePropertiesProviders) { String[] unfilterableProperties = unfilterablePropertiesProvider.getUnfilterableProperties(key); if (unfilterableProperties != null) return unfilterableProperties; } return EMPTY_STRING_ARRAY; } } ); this.propertyFactories = new ArrayList<PropertyFactory>(); this.descriptorFactories = new ArrayList<FilteredBeanDescriptorFactory>(); } @Override public Securizer getSecurizer() { return securizer; } @Override public void configure(Configurable configurable) { configure(configurable, false); } @Override public void configure(Configurable configurable, boolean append) { boolean added = false; if (configurable instanceof Repeatable) { if (configurable instanceof TypeInstantiatorProvider) { typeInstantiatorProviders.add((append ? typeInstantiatorProviders.size() : 0), (TypeInstantiatorProvider)configurable); added = true; } if (configurable instanceof PropertyInstantiatorProvider) { propertyInstantiatorProviders.add((append ? propertyInstantiatorProviders.size() : 0), (PropertyInstantiatorProvider)configurable); added = true; } if (configurable instanceof ConverterProvider) { converterProviders.add((append ? converterProviders.size() : 0), (ConverterProvider)configurable); added = true; } if (configurable instanceof CoderProvider) { coderProviders.add((append ? coderProviders.size() : 0), (CoderProvider)configurable); added = true; } if (configurable instanceof UnfilterablePropertiesProvider) { unfilterablePropertiesProviders.add((append ? unfilterablePropertiesProviders.size() : 0), (UnfilterablePropertiesProvider)configurable); added = true; } if (configurable instanceof PropertyFactory) { propertyFactories.add((append ? propertyFactories.size() : 0), (PropertyFactory)configurable); added = true; } if (configurable instanceof FilteredBeanDescriptorFactory) { descriptorFactories.add((append ? descriptorFactories.size() : 0), (FilteredBeanDescriptorFactory)configurable); added = true; } } else { if (configurable instanceof AliasStrategy) { aliasStrategy = (AliasStrategy)configurable; added = true; } if (configurable instanceof Introspector) { introspector = (Introspector)configurable; added = true; } if (configurable instanceof TypeLoader) { loader = (TypeLoader)configurable; added = true; } if (configurable instanceof Securizer) { securizer = (Securizer)configurable; added = true; } if (configurable instanceof PartialObjectFactory) { partialObjectFactory = (PartialObjectFactory)configurable; added = true; } } if (!added) throw new RuntimeException("Unsupported configurable: " + configurable); } @Override public String alias(Class<?> cls) { return aliasStrategy.alias(cls); } @Override public String unalias(String aliasedClassName) { return aliasStrategy.unalias(aliasedClassName); } @Override public Class<?> loadClass(String classNames, Type target) throws SecurityException { return loader.loadClass(this, classNames, target); } @Override public Property[] getProperties(Class<?> cls) { return introspector.getProperties(this, cls); } @Override public Object newInstance(Class<?> cls) { if (cls.isInterface()) cls = Proxy.getProxyClass(cls.getClassLoader(), cls, PartialObjectProxy.class); try { Property[] properties = getProperties(cls); return instantiatePartial(cls, properties); } catch (InstantiationException e) { throw new RuntimeException("Could not create instance of " + cls, e); } catch (IllegalAccessException e) { throw new RuntimeException("Could not create instance of " + cls, e); } } @Override public Object instantiate(Type type, Object param) throws InstantiationException { return typeInstantiatorsCache.getOrPutIfAbsent(this, type).instantiate(this, type, param); } @Override public Object instantiate(Property property, Object param) throws InstantiationException { return propertyInstantiatorsCache.getOrPutIfAbsent(this, property).instantiate(this, property, param); } @Override public Object instantiatePartial(Class<?> cls, Property[] partialProperties) throws InstantiationException, IllegalAccessException { return partialObjectFactory.instantiatePartial(this, cls, partialProperties); } @Override public Object convert(Object value, Type targetType) { Class<?> valueClass = (value != null ? value.getClass() : null); if (valueClass == targetType) return value; return convertersCache.getOrPutIfAbsent(this, valueClass, targetType) .convert(this, value, targetType); } @Override public Coder getCoder(Class<?> valueClass) { return codersCache.getOrPutIfAbsent(this, valueClass); } @Override public String[] getUnfilterableProperties(Class<?> valueClass) { return unfilterablePropertiesCache.getOrPutIfAbsent(this, valueClass); } @Override public Property createProperty(String name, Field field, Method getter, Method setter) { for (PropertyFactory factory : propertyFactories) { Property property = factory.createProperty(name, field, getter, setter); if (property != null) return property; } throw new UnsupportedOperationException( "Could not create property for: " + name + " {" + "field=" + field + ", " + "getter=" + getter + ", " + "setter=" + setter + "}" ); } @Override public FilteredBeanDescriptor createDescriptor(SpearalPropertyFilter filter, Object value) { for (FilteredBeanDescriptorFactory factory : descriptorFactories) { FilteredBeanDescriptor descriptor = factory.createDescription(this, filter, value); if (descriptor != null) return descriptor; } throw new UnsupportedOperationException("Could not create bean descriptor for: " + value); } }