package org.skyscreamer.yoga.selector; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import org.skyscreamer.yoga.annotations.Core; import org.skyscreamer.yoga.annotations.ExtraField; import org.skyscreamer.yoga.configuration.EntityConfigurationRegistry; import org.skyscreamer.yoga.configuration.NullEntityConfigurationRegistry; import org.skyscreamer.yoga.configuration.YogaEntityConfiguration; import org.skyscreamer.yoga.metadata.PropertyUtil; @SuppressWarnings( { "rawtypes", "unchecked" } ) public class CoreSelector implements Selector { private EntityConfigurationRegistry _entityConfigurationRegistry; protected final ConcurrentHashMap<Class, Map> _coreFields = new ConcurrentHashMap<Class, Map>(); protected final ConcurrentHashMap<Class, Map> _allFields = new ConcurrentHashMap<Class, Map>(); public CoreSelector( EntityConfigurationRegistry entityConfigurationRegistry ) { _entityConfigurationRegistry = entityConfigurationRegistry; } public CoreSelector() { _entityConfigurationRegistry = new NullEntityConfigurationRegistry(); } public void setEntityConfigurationRegistry( EntityConfigurationRegistry entityConfigurationRegistry ) { _entityConfigurationRegistry = entityConfigurationRegistry; } public EntityConfigurationRegistry getEntityConfigurationRegistry() { return _entityConfigurationRegistry; } @Override public <T> Property<T> getProperty( Class<T> instanceType, String fieldName ) { Map<String, Property<T>> properties = getAllPossibleFieldMap( instanceType ); return properties != null ? properties.get( fieldName ) : null; } private <T> Map<String, Property<T>> getProperties( Class<T> instanceType, ConcurrentHashMap map ) { Map properties = ( Map ) map.get( instanceType ); if (properties == null) { properties = createCoreFieldsCollection( instanceType ); Map existingProperties = ( Map ) map.putIfAbsent( instanceType, properties ); if (existingProperties != null) { properties = existingProperties; } } return properties; } private <T> Map<String, Property<T>> createCoreFieldsCollection( Class<T> instanceType ) { Map<String, Property<T>> response = new HashMap<String, Property<T>>(); List<PropertyDescriptor> readableProperties = PropertyUtil.getReadableProperties( instanceType ); YogaEntityConfiguration<T> config = getConfig( instanceType ); Collection<String> allowedCoreFields = config != null ? config.getCoreFields() : null; Collection<Property<T>> properties = config == null ? null : config.getProperties(); if (allowedCoreFields == null) { for ( PropertyDescriptor descriptor : readableProperties ) { if (descriptor.getReadMethod().isAnnotationPresent( Core.class )) { response.put( descriptor.getName(), createProperty( properties, instanceType, descriptor ) ); } } } else { for ( PropertyDescriptor descriptor : readableProperties ) { if (allowedCoreFields.contains( descriptor.getName() )) { response.put( descriptor.getName(), createProperty( properties, instanceType, descriptor ) ); } } } return response; } protected <T> Property<T> createProperty( Collection<Property<T>> properties, Class<T> instanceType, PropertyDescriptor desc ) { if (properties != null) { for ( Property<T> property : properties ) { if (property.name().equals( desc.getName() )) { return property; } } } return new PojoProperty<T>( desc ); } private <T> YogaEntityConfiguration<T> getConfig( Class<T> instanceType ) { return _entityConfigurationRegistry != null ? _entityConfigurationRegistry .getEntityConfiguration( instanceType ) : null; } @Override public <T> Map<String, Property<T>> getAllPossibleFieldMap( Class<T> instanceType ) { Map response = _allFields.get( instanceType ); if (response == null) { Map existing = _allFields.putIfAbsent( instanceType, response = createAllFieldMap( instanceType ) ); if (existing != null) response = existing; } return response; } protected <T> Map<String, Property<T>> createAllFieldMap( Class<T> instanceType ) { Map<String, Property<T>> response = new TreeMap<String, Property<T>>(); YogaEntityConfiguration<T> config = getConfig( instanceType ); // Get the getters List<PropertyDescriptor> getters = PropertyUtil.getReadableProperties( instanceType ); Map<String, Property<T>> getterMap = new TreeMap<String, Property<T>>(); Collection<Property<T>> properties = config == null ? null : config.getProperties(); for ( PropertyDescriptor descriptor : getters ) { String name = descriptor.getName(); getterMap.put( name, createProperty( properties, instanceType, descriptor ) ); } // Add @ExtraField methods from the YogaEntityConfiguration, if one // exists if (config != null) { response.putAll( getProperties( instanceType, _coreFields ) ); Collection<String> selectableFields = config == null ? null : config.getSelectableFields(); if( selectableFields != null ) { for( String field : selectableFields ) { response.put( field, getterMap.get( field ) ); } } else { response = getterMap; } for ( Method method : config.getExtraFieldMethods() ) { String name = method.getAnnotation( ExtraField.class ).value(); response.put( name, new ExtraFieldProperty<T>( name, config, method ) ); } } else { return getterMap; } return response; } @Override public <T> Collection<Property<T>> getSelectedFields( Class<T> instanceType ) { return getProperties( instanceType, _coreFields ).values(); } @Override public boolean containsField( Class<?> instanceType, String fieldName ) { return getAllPossibleFieldMap( instanceType ).containsKey( fieldName ); } @Override public boolean isInfluencedExternally() { return false; } @Override public Selector getChildSelector( Class<?> instanceType, String fieldName ) { return this; } }