package org.simpleflatmapper.reflect.meta; import org.simpleflatmapper.reflect.InstantiatorDefinition; import org.simpleflatmapper.converter.Converter; import org.simpleflatmapper.util.Predicate; import java.lang.reflect.Type; import java.util.*; @SuppressWarnings({ "unchecked", "rawtypes" }) public class MapPropertyFinder<T extends Map<K, V>, K, V> extends PropertyFinder<T> { private final ClassMeta<V> valueMetaData; private final ClassMeta<T> mapMeta; private final Converter<? super CharSequence, ? extends K> keyConverter; private final Map<PropertyNameMatcher, PropertyFinder<V>> finders = new HashMap<PropertyNameMatcher, PropertyFinder<V>>(); private final Map<PropertyMeta<?, ?>, PropertyFinder<V>> findersByKey = new HashMap<PropertyMeta<?, ?>, PropertyFinder<V>>(); private final Map<String, MapElementPropertyMeta<?, K, V>> keys = new HashMap<String, MapElementPropertyMeta<?, K, V>>(); public MapPropertyFinder(ClassMeta<T> mapMeta, ClassMeta<V> valueMetaData, Converter<? super CharSequence, ? extends K> keyConverter, Predicate<PropertyMeta<?, ?>> propertyFilter) { super(propertyFilter); this.mapMeta = mapMeta; this.valueMetaData = valueMetaData; this.keyConverter = keyConverter; } @Override public void lookForProperties( final PropertyNameMatcher propertyNameMatcher, final FoundProperty matchingProperties, final PropertyMatchingScore score, boolean allowSelfReference, PropertyFinderTransformer propertyFinderTransformer) { for(final PropertyNameMatcherKeyValuePair keyValue : propertyNameMatcher.keyValuePairs()) { final PropertyNameMatcher keyMatcher = keyValue.getKey(); final PropertyNameMatcher valueMatcher = keyValue.getValue(); final PropertyFinder<V> propertyFinder = getPropertyFinder(keyMatcher); propertyFinderTransformer.apply(propertyFinder).lookForProperties(valueMatcher, new FoundProperty<V>() { @Override public <P extends PropertyMeta<V, ?>> void found(final P propertyMeta, final Runnable selectionCallback, final PropertyMatchingScore score) { final PropertyMeta<T, ?> keyProperty = keyProperty(keyMatcher); Runnable sCallback = new Runnable() { @Override public void run() { finders.put(keyMatcher, propertyFinder); findersByKey.put(keyProperty, propertyFinder); selectionCallback.run(); } }; if (keyProperty != null) { if (propertyMeta instanceof SelfPropertyMeta) { matchingProperties.found(keyProperty, sCallback, score); } else { matchingProperties.found(newSubPropertyMeta(keyProperty, propertyMeta), sCallback, score); } } } }, score, true, propertyFinderTransformer); } } private PropertyFinder<V> getPropertyFinder(PropertyNameMatcher keyMatcher) { PropertyFinder<V> propertyFinder = finders.get(keyMatcher); if (propertyFinder == null) { propertyFinder = valueMetaData.newPropertyFinder(propertyFilter); } return propertyFinder; } private <E> PropertyMeta<T, E> newSubPropertyMeta(PropertyMeta<T, ?> keyProperty, PropertyMeta<V, ?> propertyMeta) { return new SubPropertyMeta<T, V, E>( valueMetaData.getReflectionService(), (PropertyMeta<T, V>) keyProperty, (PropertyMeta<V, E>) propertyMeta); } private <E> PropertyMeta<T, E> keyProperty(PropertyNameMatcher propertyNameMatcher) { String keyStringValue = propertyNameMatcher.toString(); PropertyMeta<T, E> propertyMeta = (PropertyMeta<T, E>) keys.get(keyStringValue); if (propertyMeta == null) { K key; try { key = keyConverter.convert(keyStringValue); } catch (Exception e) { // invalid key.. return null; } MapElementPropertyMeta<T, K, V> mapElementPropertyMeta = new MapElementPropertyMeta<T, K, V>( propertyNameMatcher, mapMeta.getType(), valueMetaData.getReflectionService(), valueMetaData, key); keys.put(keyStringValue, mapElementPropertyMeta); propertyMeta = (PropertyMeta<T, E>) mapElementPropertyMeta; } return propertyMeta; } @Override public List<InstantiatorDefinition> getEligibleInstantiatorDefinitions() { return mapMeta.getInstantiatorDefinitions(); } @Override public PropertyFinder<?> getSubPropertyFinder(PropertyMeta<?, ?> owner) { return findersByKey.get(owner); } @Override public Type getOwnerType() { return mapMeta.getType(); } }