org.simpleflatmapper.reflect.meta.PropertyFinder Maven / Gradle / Ivy
package org.simpleflatmapper.reflect.meta;
import org.simpleflatmapper.reflect.InstantiatorDefinition;
import org.simpleflatmapper.reflect.TypeAffinity;
import org.simpleflatmapper.reflect.getter.NullGetter;
import org.simpleflatmapper.reflect.setter.NullSetter;
import org.simpleflatmapper.util.ConstantPredicate;
import org.simpleflatmapper.util.Predicate;
import org.simpleflatmapper.util.TypeHelper;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public abstract class PropertyFinder {
private final boolean selfScoreFullName;
protected PropertyFinder(boolean selfScoreFullName) {
this.selfScoreFullName = selfScoreFullName;
}
public final PropertyMeta findProperty(PropertyNameMatcher propertyNameMatcher, Object[] properties, TypeAffinity typeAffinity, PropertyFilter propertyFilter) {
return findProperty(propertyNameMatcher, properties, toTypeAffinityScorer(typeAffinity), propertyFilter);
}
public final PropertyMeta findProperty(PropertyNameMatcher propertyNameMatcher, Object[] properties, TypeAffinityScorer typeAffinity, PropertyFilter propertyFilter) {
return findProperty(propertyNameMatcher, properties, typeAffinity, new DefaultPropertyFinderProbe(propertyNameMatcher), propertyFilter);
}
public final PropertyMeta findProperty(PropertyNameMatcher propertyNameMatcher, Object[] properties, TypeAffinity typeAffinity, PropertyFinderProbe propertyFinderProbe, PropertyFilter propertyFilter) {
return findProperty(propertyNameMatcher, properties, toTypeAffinityScorer(typeAffinity), propertyFinderProbe, propertyFilter);
}
@SuppressWarnings("unchecked")
public final PropertyMeta findProperty(PropertyNameMatcher propertyNameMatcher, Object[] properties, TypeAffinityScorer typeAffinity, PropertyFinderProbe propertyFinderProbe, PropertyFilter propertyFilter) {
MatchingProperties matchingProperties = new MatchingProperties(propertyFinderProbe);
lookForProperties(propertyNameMatcher, properties, matchingProperties, PropertyMatchingScore.newInstance(selfScoreFullName), true, IDENTITY_TRANSFORMER, typeAffinity, propertyFilter);
return (PropertyMeta)matchingProperties.selectBestMatch();
}
private TypeAffinityScorer toTypeAffinityScorer(TypeAffinity typeAffinity) {
return new TypeAffinityScorer(typeAffinity);
}
public abstract void lookForProperties(
PropertyNameMatcher propertyNameMatcher,
Object[] properties,
FoundProperty matchingProperties,
PropertyMatchingScore score,
boolean allowSelfReference,
PropertyFinderTransformer propertyFinderTransformer,
TypeAffinityScorer typeAffinityScorer,
PropertyFilter propertyFilter);
public abstract List getEligibleInstantiatorDefinitions();
public abstract PropertyFinder getSubPropertyFinder(PropertyMeta owner);
public abstract PropertyFinder getOrCreateSubPropertyFinder(SubPropertyMeta subPropertyMeta);
public abstract Type getOwnerType();
public boolean selfScoreFullName() {
return selfScoreFullName;
}
protected static class MatchingProperties implements FoundProperty {
private final List> matchedProperties = new ArrayList>();
private final PropertyFinderProbe propertyFinderProbe;
public MatchingProperties(PropertyFinderProbe propertyFinderProbe) {
this.propertyFinderProbe = propertyFinderProbe;
}
@Override
public > void found(P propertyMeta,
Runnable selectionCallback,
PropertyMatchingScore score, TypeAffinityScorer typeAffinityScorer) {
propertyFinderProbe.found(propertyMeta, score);
matchedProperties.add(new MatchedProperty(propertyMeta, selectionCallback, score, propertyMeta.typeAffinityScore(typeAffinityScorer)));
}
public PropertyMeta selectBestMatch() {
if (matchedProperties.isEmpty()) return null;
Collections.sort(matchedProperties);
MatchedProperty selectedMatchedProperty = matchedProperties.get(0);
selectedMatchedProperty.select();
propertyFinderProbe.select(selectedMatchedProperty.propertyMeta);
return selectedMatchedProperty.propertyMeta;
}
}
public static class MatchedProperty> implements Comparable>{
private final P propertyMeta;
private final Runnable selectionCallback;
private final PropertyMatchingScore score;
private final int typeAffinityScore;
public MatchedProperty(P propertyMeta, Runnable selectionCallback, PropertyMatchingScore score, int typeAffinityScore) {
this.propertyMeta = propertyMeta;
this.selectionCallback = selectionCallback;
this.score = score;
this.typeAffinityScore = typeAffinityScore;
}
public PropertyMatchingScore getScore() {
return score;
}
public P getPropertyMeta() {
return propertyMeta;
}
@Override
public int compareTo(MatchedProperty o) {
int i = this.score.compareTo(o.score);
if (i == 0) {
i = (typeAffinityScore > o.typeAffinityScore) ? -1 : ((typeAffinityScore == o.typeAffinityScore) ? 0 : 1);
if (i == 0) {
return compare(this.propertyMeta, o.propertyMeta);
} else {
return i;
}
} else {
return i;
}
}
private int compare(PropertyMeta p1, PropertyMeta p2) {
if (p1.isConstructorProperty()) {
if (!p2.isConstructorProperty()) {
return -1;
}
} else if (p2.isConstructorProperty()) {
return 1;
} else if (!p1.isSelf()) {
if (p2.isSelf()) {
return -1;
}
} else if (!p2.isSelf()) {
return 1;
} else if (!p1.isSubProperty()) {
if (p2.isSubProperty()) {
return -1;
}
} else if (!p2.isSubProperty()) {
return 1;
}
return getterSetterCompare(p1, p2);
}
private int getterSetterCompare(PropertyMeta p1, PropertyMeta p2) {
return nbGetterSetter(p2) - nbGetterSetter(p1);
}
private int nbGetterSetter(PropertyMeta p) {
int c = 0;
if (!NullGetter.isNull(p.getGetter())) {
c++;
}
if (!NullSetter.isNull(p.getSetter())) {
c++;
}
return c;
}
public void select() {
if (selectionCallback != null) selectionCallback.run();
}
@Override
public String toString() {
return "MatchedProperty{" +
"propertyMeta=" + propertyMeta.getPath() +
", score=" + score + ":" + nbGetterSetter(propertyMeta) +
", getter=" + propertyMeta.getGetter() +
", setter=" + propertyMeta.getSetter() +
'}';
}
}
public static class TypeAffinityScorer {
private Class[] affinities;
public TypeAffinityScorer(TypeAffinity typeAffinity) {
if (typeAffinity == null || typeAffinity.getAffinities() == null) {
affinities = new Class[0];
} else {
affinities = typeAffinity.getAffinities();
}
}
public TypeAffinityScorer(Class[] affinities) {
this.affinities = affinities;
}
public int score(Type type) {
for(int i = 0; i < affinities.length; i++) {
if (TypeHelper.isAssignable(type, affinities[i])) {
return affinities.length - i;
}
}
return -1;
}
}
public interface FoundProperty {
> void found(P propertyMeta,
Runnable selectionCallback,
PropertyMatchingScore score,
TypeAffinityScorer typeAffinityScorer);
}
public void manualMatch(PropertyMeta prop) {
if (prop.isSubProperty()) {
SubPropertyMeta subPropertyMeta = (SubPropertyMeta) prop;
PropertyMeta ownerProperty = subPropertyMeta.getOwnerProperty();
manualMatch(ownerProperty);
PropertyFinder subPropertyFinder = getOrCreateSubPropertyFinder(subPropertyMeta);
subPropertyFinder.manualMatch(subPropertyMeta.getSubProperty());
}
}
public interface PropertyFinderTransformer {
PropertyFinder apply(PropertyFinder propertyFinder);
}
public static PropertyFinderTransformer IDENTITY_TRANSFORMER = new PropertyFinderTransformer() {
@Override
public PropertyFinder apply(PropertyFinder propertyFinder) {
return propertyFinder;
}
@Override
public String toString() {
return "IDENTITY_TRANSFORMER";
}
};
public interface PropertyFinderProbe {
void found(PropertyMeta propertyMeta, PropertyMatchingScore score);
void select(PropertyMeta propertyMeta);
}
public static class DefaultPropertyFinderProbe implements PropertyFinderProbe {
private static final boolean DEBUG = Boolean.getBoolean("org.simpleflatmapper.probe.propertyFinder");
private final PropertyNameMatcher propertyNameMatcher;
public DefaultPropertyFinderProbe(PropertyNameMatcher propertyNameMatcher) {
this.propertyNameMatcher = propertyNameMatcher;
}
@Override
public void found(PropertyMeta propertyMeta, PropertyMatchingScore score) {
if (DEBUG) {
System.out.println("PropertyFinder for '" + propertyNameMatcher + "' - found " + score + " " + propertyMeta.getPath());
}
}
@Override
public void select(PropertyMeta propertyMeta) {
if (DEBUG) {
System.out.println("PropertyFinder for '" + propertyNameMatcher + "' - select " + propertyMeta.getPath());
}
}
}
public static final class PropertyFilter {
private final Predicate> propertyMetaPredicate;
private final Predicate> pathMetaPredicate;
private static final PropertyFilter TRUE_FILTER = new PropertyFilter(ConstantPredicate.>truePredicate(), ConstantPredicate.>truePredicate());
public PropertyFilter(Predicate> predicate) {
this.propertyMetaPredicate = predicate;
this.pathMetaPredicate = predicate;
}
public PropertyFilter(Predicate> propertyMetaPredicate, Predicate> pathMetaPredicate) {
this.propertyMetaPredicate = propertyMetaPredicate;
this.pathMetaPredicate = pathMetaPredicate;
}
public static PropertyFilter trueFilter() {
return TRUE_FILTER;
}
public boolean testProperty(PropertyMeta propertyMeta) {
return propertyMetaPredicate.test(propertyMeta);
}
public boolean testPath(PropertyMeta propertyMeta) {
return pathMetaPredicate.test(propertyMeta);
}
}
}