org.simpleflatmapper.reflect.meta.MapPropertyFinder Maven / Gradle / Ivy
package org.simpleflatmapper.reflect.meta;
import org.simpleflatmapper.converter.ContextFactory;
import org.simpleflatmapper.reflect.InstantiatorDefinition;
import org.simpleflatmapper.converter.ContextualConverter;
import org.simpleflatmapper.reflect.property.MapTypeProperty;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
@SuppressWarnings({ "unchecked", "rawtypes" })
public class MapPropertyFinder, K, V> extends PropertyFinder {
public static int NONE = 0;
public static int COLUMN_AS_KEY = 1;
public static int KEY_VALUE = 2;
private final ClassMeta> keyValueClassMeta;
private final ClassMeta valueMetaData;
private final ClassMeta mapMeta;
private final ContextualConverter keyConverter;
private final ContextFactory keyContextFactory;
private final Map> finders = new HashMap>();
private final Map, PropertyFinder> findersByKey = new HashMap, PropertyFinder>();
private final Map> keys = new HashMap>();
private final PropertyFinder> keyValuePropertyFinder;
private final Type keyValueType;
private final MapKeyValueElementPropertyMeta elementPropertyMeta;
private int keyValueMode = NONE;
public MapPropertyFinder(ClassMeta mapMeta, ClassMeta valueMetaData, ContextualConverter keyConverter, ContextFactory keyContextFactory, boolean selfScoreFullName) {
super(selfScoreFullName);
this.mapMeta = mapMeta;
this.valueMetaData = valueMetaData;
this.keyConverter = keyConverter;
this.keyValueType = getKeyValueType(mapMeta);
this.keyValueClassMeta = mapMeta.getReflectionService().getClassMeta(keyValueType);
this.keyContextFactory = keyContextFactory;
this.keyValuePropertyFinder = keyValueClassMeta.newPropertyFinder();
this.elementPropertyMeta =
new MapKeyValueElementPropertyMeta(mapMeta.getType(), valueMetaData.getReflectionService(), keyValueType);
}
private Type getKeyValueType(ClassMeta mapMeta) {
final Type mapType = mapMeta.getType();
if (mapType instanceof ParameterizedType) {
return new ParameterizedType() {
@Override
public Type[] getActualTypeArguments() {
return ((ParameterizedType) mapType).getActualTypeArguments();
}
@Override
public Type getRawType() {
return MapKeyValueElementPropertyMeta.KeyValue.class;
}
@Override
public Type getOwnerType() {
return null;
}
};
} else {
return MapKeyValueElementPropertyMeta.KeyValue.class;
}
}
@Override
public void lookForProperties(
final PropertyNameMatcher propertyNameMatcher,
Object[] properties, final FoundProperty matchingProperties,
final PropertyMatchingScore score, boolean allowSelfReference, PropertyFinderTransformer propertyFinderTransformer, TypeAffinityScorer typeAffinityScorer, PropertyFilter propertyFilter) {
if (isKeyValueEnabled(properties)) {
propertyFinderTransformer.apply(keyValuePropertyFinder).lookForProperties(propertyNameMatcher,
properties, new FoundProperty() {
@Override
public void found(final PropertyMeta propertyMeta, final Runnable selectionCallback, final PropertyMatchingScore score, TypeAffinityScorer typeAffinityScorer) {
Runnable sCallback = new Runnable() {
@Override
public void run() {
selectionCallback.run();
keyValueMode = KEY_VALUE;
findersByKey.put(elementPropertyMeta, keyValuePropertyFinder);
}
};
matchingProperties.found(new SubPropertyMeta(propertyMeta.getReflectService(), elementPropertyMeta, propertyMeta), sCallback, score.matches(propertyNameMatcher), typeAffinityScorer);
}
},
score,
false, propertyFinderTransformer, typeAffinityScorer, propertyFilter);
}
if (isColunnKeyEnabled(properties)) {
// classic keys set
for (final PropertyNameMatcherKeyValuePair keyValue : propertyNameMatcher.keyValuePairs()) {
final PropertyNameMatcher keyMatcher = keyValue.getKey();
final PropertyNameMatcher valueMatcher = keyValue.getValue();
final PropertyFinder propertyFinder = getPropertyFinder(keyMatcher);
propertyFinderTransformer.apply(propertyFinder).lookForProperties(valueMatcher,
properties, new FoundProperty() {
@Override
public > void found(final P propertyMeta, final Runnable selectionCallback, final PropertyMatchingScore score, TypeAffinityScorer typeAffinityScorer) {
final PropertyMeta keyProperty = keyProperty(keyMatcher);
Runnable sCallback = new Runnable() {
@Override
public void run() {
finders.put(keyMatcher, propertyFinder);
findersByKey.put(keyProperty, propertyFinder);
selectionCallback.run();
keyValueMode = COLUMN_AS_KEY;
}
};
if (keyProperty != null) {
matchingProperties.found(newSubPropertyMeta(keyProperty, propertyMeta), sCallback, score.matches(keyMatcher), typeAffinityScorer);
}
}
},
score,
true, propertyFinderTransformer, typeAffinityScorer, propertyFilter);
}
}
}
private boolean isColunnKeyEnabled(Object[] properties) {
return keyConverter != null || keyValueMode == COLUMN_AS_KEY || (keyValueMode != KEY_VALUE && MapTypeProperty.isColumnKeyEnabled(properties));
}
private boolean isKeyValueEnabled(Object[] properties) {
return keyConverter == null || keyValueMode == KEY_VALUE || (keyValueMode != COLUMN_AS_KEY && MapTypeProperty.isKeyValueEnabled(properties));
}
private PropertyFinder getPropertyFinder(PropertyNameMatcher keyMatcher) {
PropertyFinder propertyFinder = finders.get(keyMatcher);
if (propertyFinder == null) {
propertyFinder = valueMetaData.newPropertyFinder();
}
return propertyFinder;
}
private PropertyMeta newSubPropertyMeta(PropertyMeta keyProperty, PropertyMeta propertyMeta) {
return
new SubPropertyMeta(
valueMetaData.getReflectionService(),
(PropertyMeta) keyProperty,
(PropertyMeta) propertyMeta);
}
private PropertyMeta keyProperty(PropertyNameMatcher propertyNameMatcher) {
String keyStringValue = propertyNameMatcher.toString();
PropertyMeta propertyMeta = (PropertyMeta) keys.get(keyStringValue);
if (propertyMeta == null) {
K key;
try {
key = keyConverter.convert(keyStringValue, keyContextFactory.newContext());
} catch (Exception e) {
// invalid key..
return null;
}
MapElementPropertyMeta mapElementPropertyMeta = new MapElementPropertyMeta(
propertyNameMatcher,
mapMeta.getType(), valueMetaData.getReflectionService(),
valueMetaData,
key);
keys.put(keyStringValue, mapElementPropertyMeta);
propertyMeta =
(PropertyMeta)
mapElementPropertyMeta;
}
return propertyMeta;
}
@Override
public List getEligibleInstantiatorDefinitions() {
return mapMeta.getInstantiatorDefinitions();
}
@Override
public PropertyFinder getSubPropertyFinder(PropertyMeta owner) {
return findersByKey.get(owner);
}
@Override
public PropertyFinder getOrCreateSubPropertyFinder(SubPropertyMeta subPropertyMeta) {
return getSubPropertyFinder(subPropertyMeta.getOwnerProperty());
}
@Override
public Type getOwnerType() {
return mapMeta.getType();
}
}